From 5ca3ee947431afe7af3103db8353a61510155583 Mon Sep 17 00:00:00 2001 From: Attila Dusnoki Date: Mon, 30 Jan 2017 11:29:04 +0100 Subject: [PATCH 01/15] Permission API --- components/script/dom/mod.rs | 2 + components/script/dom/navigator.rs | 10 +- components/script/dom/permissions.rs | 262 ++++++++++++++++++ components/script/dom/permissionstatus.rs | 55 ++++ .../script/dom/webidls/Navigator.webidl | 6 + .../dom/webidls/PermissionStatus.webidl | 47 ++++ .../script/dom/webidls/Permissions.webidl | 14 + .../script/dom/webidls/WorkerNavigator.webidl | 7 + components/script/dom/workernavigator.rs | 12 +- tests/html/permission-test.html | 37 +++ 10 files changed, 449 insertions(+), 3 deletions(-) create mode 100644 components/script/dom/permissions.rs create mode 100644 components/script/dom/permissionstatus.rs create mode 100644 components/script/dom/webidls/PermissionStatus.webidl create mode 100644 components/script/dom/webidls/Permissions.webidl create mode 100644 tests/html/permission-test.html 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, mime_types: MutNullableJS, service_worker: MutNullableJS, - vr: MutNullableJS + vr: MutNullableJS, + permissions: MutNullableJS, } 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 { self.vr.or_init(|| VR::new(&self.global())) } + + // https://w3c.github.io/permissions/#navigator-and-workernavigator-extension + fn Permissions(&self) -> Root { + 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; + fn permission_query(cx: *mut JSContext, promise: &Rc, + descriptor: &Self::Descriptor, status: &Self::Status); + fn permission_request(cx: *mut JSContext, promise: &Rc, + 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 { + 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 { + // 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 { + // 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 { + // 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 { + 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, + _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, + 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, + query: DOMRefCell, +} + +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 { + 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 query(object permissionDesc); + + Promise request(object permissionDesc); + + Promise 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, } 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 { + 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 @@ + + +Permission Test + + + + + +

+    
+
+

From f3ddee5dbcecd51c35b7d40c7fc6d767cbfafb27 Mon Sep 17 00:00:00 2001
From: Zakor Gyula 
Date: Mon, 30 Jan 2017 11:29:30 +0100
Subject: [PATCH 02/15] Modify CodegenRust.py to import structs from sequence
 return values

---
 components/script/dom/bindings/codegen/CodegenRust.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index 84bd4adf3d6..b1f188a7b45 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -1845,6 +1845,8 @@ class CGImports(CGWrapper):
                 return type.flatMemberTypes
             if type.isDictionary():
                 return [type] + getTypesFromDictionary(type)
+            if type.isSequence():
+                return componentTypes(type.inner)
             return [type]
 
         def isImportable(type):

From 5287cd3beaebd8a7fe778e71e9693160cee38434 Mon Sep 17 00:00:00 2001
From: Zakor Gyula 
Date: Mon, 30 Jan 2017 11:30:06 +0100
Subject: [PATCH 03/15] Bluetooth Permission API integration

---
 components/atoms/static_atoms.txt             |   1 +
 components/bluetooth/lib.rs                   |  23 +-
 components/bluetooth_traits/lib.rs            |   3 +-
 components/script/dom/bluetooth.rs            | 229 +++++++++++++++++-
 components/script/dom/bluetoothdevice.rs      |  24 +-
 .../script/dom/bluetoothpermissionresult.rs   | 128 ++++++++++
 .../dom/bluetoothremotegattcharacteristic.rs  |  11 +-
 .../dom/bluetoothremotegattdescriptor.rs      |   5 +-
 .../script/dom/bluetoothremotegattserver.rs   |   6 +-
 .../script/dom/bluetoothremotegattservice.rs  |  15 +-
 components/script/dom/mod.rs                  |   1 +
 components/script/dom/permissions.rs          |  43 ++++
 .../script/dom/webidls/BluetoothDevice.webidl |   2 +-
 .../webidls/BluetoothPermissionResult.webidl  |  31 +++
 tests/html/bluetooth-permission.html          |  68 ++++++
 tests/wpt/mozilla/meta/MANIFEST.json          |   2 +-
 .../connect/device-goes-out-of-range.html     |   3 +-
 17 files changed, 550 insertions(+), 45 deletions(-)
 create mode 100644 components/script/dom/bluetoothpermissionresult.rs
 create mode 100644 components/script/dom/webidls/BluetoothPermissionResult.webidl
 create mode 100644 tests/html/bluetooth-permission.html

diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt
index 8e28f90e04e..df9cd426984 100644
--- a/components/atoms/static_atoms.txt
+++ b/components/atoms/static_atoms.txt
@@ -65,3 +65,4 @@ characteristicvaluechanged
 fullscreenchange
 fullscreenerror
 gattserverdisconnected
+onchange
diff --git a/components/bluetooth/lib.rs b/components/bluetooth/lib.rs
index a22e2ba6c91..a5f5f5837d7 100644
--- a/components/bluetooth/lib.rs
+++ b/components/bluetooth/lib.rs
@@ -256,6 +256,9 @@ impl BluetoothManager {
                 BluetoothRequest::GetAvailability(sender) => {
                     let _ = sender.send(self.get_availability());
                 },
+                BluetoothRequest::MatchesFilter(id, filters, sender) => {
+                    let _ = sender.send(self.device_matches_filter(&id, &filters));
+                },
                 BluetoothRequest::Exit => {
                     break
                 },
@@ -425,6 +428,17 @@ impl BluetoothManager {
         self.cached_devices.contains_key(device_id) && self.address_to_id.values().any(|v| v == device_id)
     }
 
+    fn device_matches_filter(&mut self,
+                             device_id: &str,
+                             filters: &BluetoothScanfilterSequence)
+                             -> BluetoothResult {
+        let mut adapter = try!(self.get_adapter());
+        match self.get_device(&mut adapter, device_id) {
+            Some(ref device) => Ok(matches_filters(device, filters)),
+            None => Ok(false),
+        }
+    }
+
     // Service
 
     fn get_and_cache_gatt_services(&mut self,
@@ -561,6 +575,9 @@ impl BluetoothManager {
                       -> BluetoothResponseResult {
         // Step 6.
         let mut adapter = try!(self.get_adapter());
+
+        // Step 7.
+        // Note: There are no requiredServiceUUIDS, we scan for all devices.
         if let Ok(ref session) = adapter.create_discovery_session() {
             if session.start_discovery().is_ok() {
                 if !is_mock_adapter(&adapter) {
@@ -570,8 +587,6 @@ impl BluetoothManager {
             let _ = session.stop_discovery();
         }
 
-        // Step 7.
-        // Note: There are no requiredServiceUUIDS, we scan for all devices.
         let mut matched_devices = self.get_and_cache_devices(&mut adapter);
 
         // Step 8.
@@ -582,8 +597,6 @@ impl BluetoothManager {
         }
 
         // Step 9.
-        // TODO: After the permission API implementation
-        //       https://w3c.github.io/permissions/#prompt-the-user-to-choose
         if let Some(address) = self.select_device(matched_devices, &adapter) {
             let device_id = match self.address_to_id.get(&address) {
                 Some(id) => id.clone(),
@@ -602,7 +615,7 @@ impl BluetoothManager {
                 return Ok(BluetoothResponse::RequestDevice(message));
             }
         }
-        // TODO: Step 10 - 11: Implement the permission API.
+        // Step 10.
         return Err(BluetoothError::NotFound);
         // Step 12: Missing, because it is optional.
     }
diff --git a/components/bluetooth_traits/lib.rs b/components/bluetooth_traits/lib.rs
index 85421990f71..e54d256fe0c 100644
--- a/components/bluetooth_traits/lib.rs
+++ b/components/bluetooth_traits/lib.rs
@@ -12,7 +12,7 @@ pub mod blocklist;
 pub mod scanfilter;
 
 use ipc_channel::ipc::IpcSender;
-use scanfilter::RequestDeviceoptions;
+use scanfilter::{BluetoothScanfilterSequence, RequestDeviceoptions};
 
 #[derive(Deserialize, Serialize)]
 pub enum BluetoothError {
@@ -92,6 +92,7 @@ pub enum BluetoothRequest {
     SetRepresentedToNull(Vec, Vec, Vec),
     IsRepresentedDeviceNull(String, IpcSender),
     GetAvailability(IpcSender),
+    MatchesFilter(String, BluetoothScanfilterSequence, IpcSender>),
     Test(String, IpcSender>),
     Exit,
 }
diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs
index 82c39782133..b932046acc3 100644
--- a/components/script/dom/bluetooth.rs
+++ b/components/script/dom/bluetooth.rs
@@ -11,8 +11,14 @@ use core::clone::Clone;
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::BluetoothBinding::{self, BluetoothDataFilterInit, BluetoothLEScanFilterInit};
 use dom::bindings::codegen::Bindings::BluetoothBinding::{BluetoothMethods, RequestDeviceOptions};
+use dom::bindings::codegen::Bindings::BluetoothPermissionResultBinding::AllowedBluetoothDevice;
+use dom::bindings::codegen::Bindings::BluetoothPermissionResultBinding::BluetoothPermissionData;
+use dom::bindings::codegen::Bindings::BluetoothPermissionResultBinding::BluetoothPermissionDescriptor;
+use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerBinding::
+    BluetoothRemoteGATTServerMethods;
 use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
-use dom::bindings::codegen::UnionTypes::StringOrUnsignedLong;
+use dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState;
+use dom::bindings::codegen::UnionTypes::{StringOrStringSequence, StringOrUnsignedLong};
 use dom::bindings::error::Error::{self, Network, Security, Type};
 use dom::bindings::error::Fallible;
 use dom::bindings::js::{JS, Root};
@@ -20,14 +26,19 @@ use dom::bindings::refcounted::{Trusted, TrustedPromise};
 use dom::bindings::reflector::{DomObject, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::bluetoothdevice::BluetoothDevice;
+use dom::bluetoothpermissionresult::BluetoothPermissionResult;
 use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID, UUID};
 use dom::eventtarget::EventTarget;
 use dom::globalscope::GlobalScope;
+use dom::permissions::{get_descriptor_permission_state, PermissionAlgorithm};
 use dom::promise::Promise;
 use ipc_channel::ipc::{self, IpcSender};
 use ipc_channel::router::ROUTER;
-use js::jsapi::{JSAutoCompartment, JSContext};
+use js::conversions::ConversionResult;
+use js::jsapi::{JSAutoCompartment, JSContext, JSObject};
+use js::jsval::{ObjectValue, UndefinedValue};
 use script_thread::Runnable;
+use std::cell::RefCell;
 use std::collections::HashMap;
 use std::rc::Rc;
 use std::str::FromStr;
@@ -46,6 +57,48 @@ const SERVICE_DATA_ERROR: &'static str = "'serviceData', if present, must be non
 const SERVICE_ERROR: &'static str = "'services', if present, must contain at least one service.";
 const OPTIONS_ERROR: &'static str = "Fields of 'options' conflict with each other.
  Either 'acceptAllDevices' member must be true, or 'filters' member must be set to a value.";
+const BT_DESC_CONVERSION_ERROR: &'static str = "Can't convert to an IDL value of type BluetoothPermissionDescriptor";
+
+
+thread_local!(pub static EXTRA_PERMISSION_DATA: RefCell =
+              RefCell::new(BluetoothPermissionData { allowedDevices: Vec::new() }));
+
+pub fn add_new_allowed_device(allowed_device: AllowedBluetoothDevice) {
+    EXTRA_PERMISSION_DATA.with(|epdata| {
+        epdata.borrow_mut().allowedDevices.push(allowed_device);
+    });
+}
+
+fn get_allowed_devices() -> Vec {
+    EXTRA_PERMISSION_DATA.with(|epdata| {
+        epdata.borrow().allowedDevices.clone()
+    })
+}
+
+pub fn allowed_devices_contains_id(id: DOMString) -> bool {
+    EXTRA_PERMISSION_DATA.with(|epdata| {
+        epdata.borrow_mut().allowedDevices.iter().any(|d| d.deviceId == id)
+    })
+}
+
+impl Clone for StringOrStringSequence {
+    fn clone(&self) -> StringOrStringSequence {
+        match self {
+            &StringOrStringSequence::String(ref s) => StringOrStringSequence::String(s.clone()),
+            &StringOrStringSequence::StringSequence(ref v) => StringOrStringSequence::StringSequence(v.clone()),
+        }
+    }
+}
+
+impl Clone for AllowedBluetoothDevice {
+    fn clone(&self) -> AllowedBluetoothDevice {
+        AllowedBluetoothDevice {
+            deviceId: self.deviceId.clone(),
+            mayUseGATT: self.mayUseGATT,
+            allowedServices: self.allowedServices.clone(),
+        }
+    }
+}
 
 struct BluetoothContext {
     promise: Option,
@@ -107,7 +160,8 @@ impl Bluetooth {
     fn request_bluetooth_devices(&self,
                                  p: &Rc,
                                  filters: &Option>,
-                                 optional_services: &Option>) {
+                                 optional_services: &Option>,
+                                 sender: IpcSender) {
         // TODO: Step 1: Triggered by user activation.
 
         // Step 2.2: There are no requiredServiceUUIDS, we scan for all devices.
@@ -161,11 +215,15 @@ impl Bluetooth {
         let option = RequestDeviceoptions::new(BluetoothScanfilterSequence::new(uuid_filters),
                                                ServiceUUIDSequence::new(optional_services_uuids));
 
-        // TODO: Step 3 - 5: Implement the permission API.
+        // Step 3 - 5
+        // FIXME The following call will create a popup, which will mess up the testing...
+        // Maybe create a call to the lower level to check if we are testing or not
+        // if let PermissionState::Denied = get_descriptor_permission_state(PermissionName::Bluetooth, None) {
+        //     return p.reject_error(p.global().get_cx(), Error::NotFound);
+        // }
 
         // Note: Steps 6 - 8 are implemented in
         // components/net/bluetooth_thread.rs in request_device function.
-        let sender = response_async(p, self);
         self.get_bluetooth_thread().send(BluetoothRequest::RequestDevice(option, sender)).unwrap();
     }
 }
@@ -438,7 +496,8 @@ impl BluetoothMethods for Bluetooth {
         }
 
         // Step 2.
-        self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices);
+        let sender = response_async(&p, self);
+        self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices, sender);
         //Note: Step 3 - 4. in response function, Step 5. in handle_response function.
         return p;
     }
@@ -463,7 +522,7 @@ impl AsyncBluetoothListener for Bluetooth {
     fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc) {
         match response {
             // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
-            // Step 13 - 14.
+            // Step 11, 13 - 14.
             BluetoothResponse::RequestDevice(device) => {
                 let mut device_instance_map = self.device_instance_map.borrow_mut();
                 if let Some(existing_device) = device_instance_map.get(&device.id.clone()) {
@@ -473,7 +532,16 @@ impl AsyncBluetoothListener for Bluetooth {
                                                      DOMString::from(device.id.clone()),
                                                      device.name.map(DOMString::from),
                                                      &self);
-                device_instance_map.insert(device.id, JS::from_ref(&bt_device));
+                device_instance_map.insert(device.id.clone(), JS::from_ref(&bt_device));
+                add_new_allowed_device(
+                    AllowedBluetoothDevice {
+                        // TODO fix this
+                        // allowedServices only relevant if the device store it as an inter slot as well
+                        allowedServices: StringOrStringSequence::String(DOMString::from("all".to_owned())),
+                        deviceId: DOMString::from(device.id),
+                        mayUseGATT: true,
+                    }
+                );
                 // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
                 // Step 5.
                 promise.resolve_native(promise_cx, &bt_device);
@@ -487,3 +555,148 @@ impl AsyncBluetoothListener for Bluetooth {
         }
     }
 }
+
+impl PermissionAlgorithm for Bluetooth {
+    type Descriptor = BluetoothPermissionDescriptor;
+    type Status = BluetoothPermissionResult;
+
+    #[allow(unsafe_code)]
+    fn create_descriptor(cx: *mut JSContext,
+                         permission_descriptor_obj: *mut JSObject)
+                         -> Result {
+        rooted!(in(cx) let mut property = UndefinedValue());
+        property.handle_mut().set(ObjectValue(permission_descriptor_obj));
+        unsafe {
+            match BluetoothPermissionDescriptor::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(BT_DESC_CONVERSION_ERROR))),
+            }
+        }
+    }
+
+    #[allow(unrooted_must_root)]
+    // https://webbluetoothcg.github.io/web-bluetooth/#query-the-bluetooth-permission
+    fn permission_query(cx: *mut JSContext, promise: &Rc,
+                        descriptor: &BluetoothPermissionDescriptor,
+                        status: &BluetoothPermissionResult) {
+        // Step 1.
+        // TODO: `environment settings object` is not implemented in Servo yet.
+
+        // Step 2.
+        status.set_state(get_descriptor_permission_state(status.get_query(), None));
+
+        // Step 3.
+        if let PermissionState::Denied = status.get_state() {
+            status.set_devices(Vec::new());
+            return promise.resolve_native(cx, status);
+        }
+
+        // Step 4.
+        let mut matching_devices: Vec> = Vec::new();
+
+        // TODO: Step 5: Create a map between the current setting object and BluetoothPermissionData
+        // extra permission data, which replaces the exisitng EXTRA_PERMISSION_DATA global variable.
+        // For this also use the extra permission data constraints from the specification:
+        // https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothpermissiondata
+
+        // Step 5.
+        let allowed_devices = get_allowed_devices();
+
+        let bluetooth = status.get_bluetooth();
+        let device_map = bluetooth.get_device_map().borrow();
+
+        // Step 6.
+        for allowed_device in allowed_devices {
+            // Step 6.1.
+            if let Some(ref id) = descriptor.deviceId {
+                if &allowed_device.deviceId != id {
+                    continue;
+                }
+            }
+            let device_id = String::from(allowed_device.deviceId.as_ref());
+            if let Some(ref filters) = descriptor.filters {
+                let mut scan_filters: Vec = Vec::new();
+
+                // NOTE(zakorgy): This canonicalizing step is missing from the specification.
+                // But there is an issue for this: https://github.com/WebBluetoothCG/web-bluetooth/issues/347
+                for filter in filters {
+                    match canonicalize_filter(&filter) {
+                        Ok(f) => scan_filters.push(f),
+                        Err(error) => return promise.reject_error(cx, error),
+                    }
+                }
+
+                // Step 6.2.
+                // Instead of creating an internal slot we send an ipc message to the Bluetooth thread
+                // to check if one of the filters matches.
+                let (sender, receiver) = ipc::channel().unwrap();
+                status.get_bluetooth_thread()
+                      .send(BluetoothRequest::MatchesFilter(device_id.clone(),
+                                                            BluetoothScanfilterSequence::new(scan_filters),
+                                                            sender)).unwrap();
+
+                match receiver.recv().unwrap() {
+                    Ok(true) => (),
+                    Ok(false) => continue,
+                    Err(error) => return promise.reject_error(cx, Error::from(error)),
+                };
+            }
+
+            // Step 6.4.
+            // TODO: Implement this correctly, not just using device ids here.
+            // https://webbluetoothcg.github.io/web-bluetooth/#get-the-bluetoothdevice-representing
+            if let Some(ref device) = device_map.get(&device_id) {
+                matching_devices.push(JS::from_ref(&*device));
+            }
+        }
+
+        // Step 7.
+        status.set_devices(matching_devices);
+
+        // https://w3c.github.io/permissions/#dom-permissions-query
+        // Step 7.
+        promise.resolve_native(cx, status);
+    }
+
+    // NOTE(zakorgy): There is no link for this algorithm until this PR for the spec is pending:
+    // https://github.com/WebBluetoothCG/web-bluetooth/pull/349
+    fn permission_request(cx: *mut JSContext, promise: &Rc,
+                          descriptor: &BluetoothPermissionDescriptor,
+                          status: &BluetoothPermissionResult) {
+        // Step 1.
+        if descriptor.filters.is_some() == descriptor.acceptAllDevices {
+            return promise.reject_error(cx, Error::Type(OPTIONS_ERROR.to_owned()));
+        }
+
+        // Step 2.
+        let sender = response_async(promise, status);
+        let bluetooth = status.get_bluetooth();
+        bluetooth.request_bluetooth_devices(promise, &descriptor.filters, &descriptor.optionalServices, sender);
+
+        // NOTE: Step 3. is in BluetoothPermissionResult's `handle_response` function.
+    }
+
+    #[allow(unrooted_must_root)]
+    // https://webbluetoothcg.github.io/web-bluetooth/#revoke-bluetooth-access
+    fn permission_revoke(_descriptor: &BluetoothPermissionDescriptor, status: &BluetoothPermissionResult) {
+        // Step 1.
+        let allowed_devices = get_allowed_devices();
+        // Step 2.
+        let bluetooth = status.get_bluetooth();
+        let device_map = bluetooth.get_device_map().borrow();
+        for (id, device) in device_map.iter() {
+            let id = DOMString::from(id.clone());
+            // Step 2.1.
+            if allowed_devices.iter().any(|d| d.deviceId == id) &&
+               !device.is_represented_device_null() {
+                // Note: We don't need to update the allowed_services,
+                // because we store it in the lower level
+                // where it is already up-to-date
+                continue;
+            }
+            // Step 2.2 - 2.4
+            let _ = device.get_gatt().Disconnect();
+        }
+    }
+}
diff --git a/components/script/dom/bluetoothdevice.rs b/components/script/dom/bluetoothdevice.rs
index cd3d3dc07fa..16892f36013 100644
--- a/components/script/dom/bluetoothdevice.rs
+++ b/components/script/dom/bluetoothdevice.rs
@@ -15,7 +15,7 @@ use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{JS, MutNullableJS, Root};
 use dom::bindings::reflector::{DomObject, reflect_dom_object};
 use dom::bindings::str::DOMString;
-use dom::bluetooth::{AsyncBluetoothListener, Bluetooth, response_async};
+use dom::bluetooth::{allowed_devices_contains_id, AsyncBluetoothListener, Bluetooth, response_async};
 use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
 use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
 use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor;
@@ -74,6 +74,12 @@ impl BluetoothDevice {
                            BluetoothDeviceBinding::Wrap)
     }
 
+    pub fn get_gatt(&self) -> Root {
+        self.gatt.or_init(|| {
+            BluetoothRemoteGATTServer::new(&self.global(), self)
+        })
+    }
+
     fn get_context(&self) -> Root {
         Root::from_ref(&self.context)
     }
@@ -157,7 +163,7 @@ impl BluetoothDevice {
     #[allow(unrooted_must_root)]
     pub fn clean_up_disconnected_device(&self) {
         // Step 1.
-        self.Gatt().set_connected(false);
+        self.get_gatt().set_connected(false);
 
         // TODO: Step 2: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
 
@@ -193,7 +199,7 @@ impl BluetoothDevice {
         for (id, device) in context.get_device_map().borrow().iter() {
             // Step 2.1 - 2.2.
             if id == &self.Id().to_string() {
-                if device.Gatt().Connected() {
+                if device.get_gatt().Connected() {
                     return Ok(());
                 }
                 // TODO: Step 2.3: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer.
@@ -220,11 +226,13 @@ impl BluetoothDeviceMethods for BluetoothDevice {
     }
 
     // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-gatt
-    fn Gatt(&self) -> Root {
-        // TODO: Step 1 - 2: Implement the Permission API.
-        self.gatt.or_init(|| {
-            BluetoothRemoteGATTServer::new(&self.global(), self)
-        })
+    fn GetGatt(&self) -> Option> {
+        // Step 1.
+        if allowed_devices_contains_id(self.id.clone()) && !self.is_represented_device_null() {
+            return Some(self.get_gatt())
+        }
+        // Step 2.
+        None
     }
 
     #[allow(unrooted_must_root)]
diff --git a/components/script/dom/bluetoothpermissionresult.rs b/components/script/dom/bluetoothpermissionresult.rs
new file mode 100644
index 00000000000..75e7b43e17e
--- /dev/null
+++ b/components/script/dom/bluetoothpermissionresult.rs
@@ -0,0 +1,128 @@
+/* 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 bluetooth_traits::{BluetoothRequest, BluetoothResponse};
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::BluetoothPermissionResultBinding::{self, BluetoothPermissionResultMethods};
+use dom::bindings::codegen::Bindings::BluetoothPermissionResultBinding::AllowedBluetoothDevice;
+use dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods;
+use dom::bindings::codegen::Bindings::PermissionStatusBinding::{PermissionName, PermissionState};
+use dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionStatusBinding::PermissionStatusMethods;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
+use dom::bindings::codegen::UnionTypes::StringOrStringSequence;
+use dom::bindings::error::Error;
+use dom::bindings::js::{JS, Root};
+use dom::bindings::reflector::{DomObject, reflect_dom_object};
+use dom::bindings::str::DOMString;
+use dom::bluetooth::{add_new_allowed_device, AsyncBluetoothListener, Bluetooth};
+use dom::bluetoothdevice::BluetoothDevice;
+use dom::globalscope::GlobalScope;
+use dom::permissionstatus::PermissionStatus;
+use dom::promise::Promise;
+use ipc_channel::ipc::IpcSender;
+use js::jsapi::JSContext;
+use std::rc::Rc;
+
+// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothpermissionresult
+#[dom_struct]
+pub struct BluetoothPermissionResult {
+    status: PermissionStatus,
+    devices: DOMRefCell>>,
+}
+
+impl BluetoothPermissionResult {
+    #[allow(unrooted_must_root)]
+    pub fn new_inherited(status: &PermissionStatus) -> BluetoothPermissionResult {
+        let result = BluetoothPermissionResult {
+            status: PermissionStatus::new_inherited(status.get_query()),
+            devices: DOMRefCell::new(Vec::new()),
+        };
+        result.status.set_state(status.State());
+        result
+    }
+
+    pub fn new(global: &GlobalScope, status: &PermissionStatus) -> Root {
+        reflect_dom_object(box BluetoothPermissionResult::new_inherited(status),
+                           global,
+                           BluetoothPermissionResultBinding::Wrap)
+    }
+
+    pub fn get_bluetooth(&self) -> Root {
+        self.global().as_window().Navigator().Bluetooth()
+    }
+
+    pub fn get_bluetooth_thread(&self) -> IpcSender {
+        self.global().as_window().bluetooth_thread()
+    }
+
+    pub fn get_query(&self) -> PermissionName {
+        self.status.get_query()
+    }
+
+    pub fn set_state(&self, state: PermissionState) {
+        self.status.set_state(state)
+    }
+
+    pub fn get_state(&self) -> PermissionState {
+        self.status.State()
+    }
+
+    #[allow(unrooted_must_root)]
+    pub fn set_devices(&self, devices: Vec>) {
+        *self.devices.borrow_mut() = devices;
+    }
+}
+
+impl BluetoothPermissionResultMethods for BluetoothPermissionResult {
+    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothpermissionresult-devices
+    fn Devices(&self) -> Vec> {
+        let device_vec: Vec> =
+            self.devices.borrow().iter().map(|d| Root::from_ref(&**d)).collect();
+        device_vec
+    }
+}
+
+impl AsyncBluetoothListener for BluetoothPermissionResult {
+    fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc) {
+        match response {
+            // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
+            // Step 11, 13 - 14.
+            BluetoothResponse::RequestDevice(device) => {
+                let bluetooth = &self.get_bluetooth();
+                let mut device_instance_map = bluetooth.get_device_map().borrow_mut();
+                if let Some(ref existing_device) = device_instance_map.get(&device.id) {
+                    // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothpermissionresult
+                    // Step 3.
+                    self.set_devices(vec!(JS::from_ref(&*existing_device)));
+
+                    // https://w3c.github.io/permissions/#dom-permissions-request
+                    // Step 8.
+                    return promise.resolve_native(promise_cx, self);
+                }
+                let bt_device = BluetoothDevice::new(&self.global(),
+                                                     DOMString::from(device.id.clone()),
+                                                     device.name.map(DOMString::from),
+                                                     bluetooth);
+                device_instance_map.insert(device.id.clone(), JS::from_ref(&bt_device));
+                add_new_allowed_device(
+                    AllowedBluetoothDevice {
+                        // TODO fix this
+                        // allowedServices only relevant if the device store it as an internal slot as well
+                        allowedServices: StringOrStringSequence::String(DOMString::from("all".to_owned())),
+                        deviceId: DOMString::from(device.id),
+                        mayUseGATT: true,
+                    }
+                );
+                // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothpermissionresult
+                // Step 3.
+                self.set_devices(vec!(JS::from_ref(&bt_device)));
+
+                // https://w3c.github.io/permissions/#dom-permissions-request
+                // Step 8.
+                promise.resolve_native(promise_cx, self);
+            },
+            _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())),
+        }
+    }
+}
diff --git a/components/script/dom/bluetoothremotegattcharacteristic.rs b/components/script/dom/bluetoothremotegattcharacteristic.rs
index 636b9dc96f4..81210e4b170 100644
--- a/components/script/dom/bluetoothremotegattcharacteristic.rs
+++ b/components/script/dom/bluetoothremotegattcharacteristic.rs
@@ -7,7 +7,6 @@ use bluetooth_traits::blocklist::{Blocklist, uuid_is_blocklisted};
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::BluetoothCharacteristicPropertiesBinding::
     BluetoothCharacteristicPropertiesMethods;
-use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
 use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding;
 use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::
     BluetoothRemoteGATTCharacteristicMethods;
@@ -104,7 +103,7 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
     // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor
     fn GetDescriptor(&self, descriptor: BluetoothDescriptorUUID) -> Rc {
         get_gatt_children(self, true, BluetoothUUID::descriptor, Some(descriptor), self.get_instance_id(),
-                          self.Service().Device().Gatt().Connected(), GATTType::Descriptor)
+                          self.Service().Device().get_gatt().Connected(), GATTType::Descriptor)
     }
 
     #[allow(unrooted_must_root)]
@@ -113,7 +112,7 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
                       descriptor: Option)
                       -> Rc {
         get_gatt_children(self, false, BluetoothUUID::descriptor, descriptor, self.get_instance_id(),
-                          self.Service().Device().Gatt().Connected(), GATTType::Descriptor)
+                          self.Service().Device().get_gatt().Connected(), GATTType::Descriptor)
     }
 
     // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-value
@@ -134,7 +133,7 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
         }
 
         // Step 2.
-        if !self.Service().Device().Gatt().Connected() {
+        if !self.Service().Device().get_gatt().Connected() {
             p.reject_error(p_cx, Network);
             return p;
         }
@@ -174,7 +173,7 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
         }
 
         // Step 4.
-        if !self.Service().Device().Gatt().Connected() {
+        if !self.Service().Device().get_gatt().Connected() {
             p.reject_error(p_cx, Network);
             return p;
         }
@@ -210,7 +209,7 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
         }
 
         // Step 2.
-        if !self.Service().Device().Gatt().Connected() {
+        if !self.Service().Device().get_gatt().Connected() {
             p.reject_error(p_cx, Network);
             return p;
         }
diff --git a/components/script/dom/bluetoothremotegattdescriptor.rs b/components/script/dom/bluetoothremotegattdescriptor.rs
index 5290c096d3a..c0ddd6dabe5 100644
--- a/components/script/dom/bluetoothremotegattdescriptor.rs
+++ b/components/script/dom/bluetoothremotegattdescriptor.rs
@@ -5,7 +5,6 @@
 use bluetooth_traits::{BluetoothRequest, BluetoothResponse};
 use bluetooth_traits::blocklist::{Blocklist, uuid_is_blocklisted};
 use dom::bindings::cell::DOMRefCell;
-use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
 use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::
     BluetoothRemoteGATTCharacteristicMethods;
 use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding;
@@ -98,7 +97,7 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor {
         }
 
         // Step 2.
-        if !self.Characteristic().Service().Device().Gatt().Connected() {
+        if !self.Characteristic().Service().Device().get_gatt().Connected() {
             p.reject_error(p_cx, Network);
             return p;
         }
@@ -131,7 +130,7 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor {
         }
 
         // Step 4.
-        if !self.Characteristic().Service().Device().Gatt().Connected() {
+        if !self.Characteristic().Service().Device().get_gatt().Connected() {
             p.reject_error(p_cx, Network);
             return p;
         }
diff --git a/components/script/dom/bluetoothremotegattserver.rs b/components/script/dom/bluetoothremotegattserver.rs
index 11e0528f5be..fdc18fc7d0d 100644
--- a/components/script/dom/bluetoothremotegattserver.rs
+++ b/components/script/dom/bluetoothremotegattserver.rs
@@ -103,16 +103,16 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
     #[allow(unrooted_must_root)]
     // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice
     fn GetPrimaryService(&self, service: BluetoothServiceUUID) -> Rc {
-        // TODO: Step 1: Implement the Permission API and the allowedServices BluetoothDevice internal slot.
+        // Step 1. is in get_gatt_children
         // Step 2.
         get_gatt_children(self, true, BluetoothUUID::service, Some(service), String::from(self.Device().Id()),
-                          self.Device().Gatt().Connected(), GATTType::PrimaryService)
+                          self.Device().get_gatt().Connected(), GATTType::PrimaryService)
     }
 
     #[allow(unrooted_must_root)]
     // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices
     fn GetPrimaryServices(&self, service: Option) -> Rc {
-        // TODO: Step 1: Implement the Permission API and the allowedServices BluetoothDevice internal slot.
+        // Step 1. is in get_gatt_children
         // Step 2.
         get_gatt_children(self, false, BluetoothUUID::service, service, String::from(self.Device().Id()),
                           self.Connected(), GATTType::PrimaryService)
diff --git a/components/script/dom/bluetoothremotegattservice.rs b/components/script/dom/bluetoothremotegattservice.rs
index da06678da0b..9b0ce5a3a3d 100644
--- a/components/script/dom/bluetoothremotegattservice.rs
+++ b/components/script/dom/bluetoothremotegattservice.rs
@@ -3,7 +3,6 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use bluetooth_traits::{BluetoothResponse, GATTType};
-use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
 use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
 use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding;
 use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
@@ -87,7 +86,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
                          characteristic: BluetoothCharacteristicUUID)
                          -> Rc {
         get_gatt_children(self, true, BluetoothUUID::characteristic, Some(characteristic), self.get_instance_id(),
-                          self.Device().Gatt().Connected(), GATTType::Characteristic)
+                          self.Device().get_gatt().Connected(), GATTType::Characteristic)
     }
 
     #[allow(unrooted_must_root)]
@@ -96,7 +95,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
                           characteristic: Option)
                           -> Rc {
         get_gatt_children(self, false, BluetoothUUID::characteristic, characteristic, self.get_instance_id(),
-                          self.Device().Gatt().Connected(), GATTType::Characteristic)
+                          self.Device().get_gatt().Connected(), GATTType::Characteristic)
     }
 
     #[allow(unrooted_must_root)]
@@ -105,7 +104,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
                           service: BluetoothServiceUUID)
                           -> Rc {
         get_gatt_children(self, false, BluetoothUUID::service, Some(service), self.get_instance_id(),
-                          self.Device().Gatt().Connected(), GATTType::IncludedService)
+                          self.Device().get_gatt().Connected(), GATTType::IncludedService)
     }
 
 
@@ -115,7 +114,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
                           service: Option)
                           -> Rc {
         get_gatt_children(self, false, BluetoothUUID::service, service, self.get_instance_id(),
-                          self.Device().Gatt().Connected(), GATTType::IncludedService)
+                          self.Device().get_gatt().Connected(), GATTType::IncludedService)
     }
 
     // https://webbluetoothcg.github.io/web-bluetooth/#dom-serviceeventhandlers-onserviceadded
@@ -151,12 +150,12 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTService {
             // Step 7.
             BluetoothResponse::GetIncludedServices(services_vec, single) => {
                 if single {
-                    promise.resolve_native(promise_cx, &device.get_or_create_service(&services_vec[0], &device.Gatt()));
-                    return;
+                    return promise.resolve_native(promise_cx,
+                                                  &device.get_or_create_service(&services_vec[0], &device.get_gatt()));
                 }
                 let mut services = vec!();
                 for service in services_vec {
-                    let bt_service = device.get_or_create_service(&service, &device.Gatt());
+                    let bt_service = device.get_or_create_service(&service, &device.get_gatt());
                     services.push(bt_service);
                 }
                 promise.resolve_native(promise_cx, &services);
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index b1541a225b7..4b3c0c02cbb 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -223,6 +223,7 @@ pub mod bluetooth;
 pub mod bluetoothadvertisingevent;
 pub mod bluetoothcharacteristicproperties;
 pub mod bluetoothdevice;
+pub mod bluetoothpermissionresult;
 pub mod bluetoothremotegattcharacteristic;
 pub mod bluetoothremotegattdescriptor;
 pub mod bluetoothremotegattserver;
diff --git a/components/script/dom/permissions.rs b/components/script/dom/permissions.rs
index ebece7374a6..104376e2e0c 100644
--- a/components/script/dom/permissions.rs
+++ b/components/script/dom/permissions.rs
@@ -7,6 +7,8 @@ use dom::bindings::codegen::Bindings::PermissionsBinding::{self, PermissionsMeth
 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;
@@ -79,6 +81,20 @@ impl PermissionsMethods for Permissions {
 
         // Step 2.
         match root_desc.name {
+            PermissionName::Bluetooth => {
+                let bluetooth_desc = match Bluetooth::create_descriptor(cx, permissionDesc) {
+                    Ok(descriptor) => descriptor,
+                    Err(error) => {
+                        p.reject_error(cx, error);
+                        return p;
+                    },
+                };
+                // Step 5.
+                let result = BluetoothPermissionResult::new(&self.global(), &status);
+                // Step 6.
+                Bluetooth::permission_query(cx, &p, &bluetooth_desc, &result);
+                // Step 7. in permission_query
+            },
             _ => {
                 // Step 6.
                 Permissions::permission_query(cx, &p, &root_desc, &status);
@@ -113,6 +129,20 @@ impl PermissionsMethods for Permissions {
 
         // Step 2.
         match root_desc.name {
+            PermissionName::Bluetooth => {
+                let bluetooth_desc = match Bluetooth::create_descriptor(cx, permissionDesc) {
+                    Ok(descriptor) => descriptor,
+                    Err(error) => {
+                        p.reject_error(cx, error);
+                        return p;
+                    },
+                };
+                // Step 5.
+                let result = BluetoothPermissionResult::new(&self.global(), &status);
+                // Step 6.
+                Bluetooth::permission_request(cx, &p, &bluetooth_desc, &result);
+                // Step 7 - 8. in permission_request
+            },
             _ => {
                 // Step 6.
                 Permissions::permission_request(cx, &p, &root_desc, &status);
@@ -145,6 +175,19 @@ impl PermissionsMethods for Permissions {
 
         // Step 2.
         match root_desc.name {
+            PermissionName::Bluetooth => {
+                let bluetooth_desc = match Bluetooth::create_descriptor(cx, permissionDesc) {
+                    Ok(descriptor) => descriptor,
+                    Err(error) => {
+                        let p = Promise::new(&self.global());
+                        p.reject_error(cx, error);
+                        return p;
+                    },
+                };
+                let result = BluetoothPermissionResult::new(&self.global(), &status);
+                // Step 3 - 4. in permission_revoke
+                Bluetooth::permission_revoke(&bluetooth_desc, &result);
+            },
             _ => {
                 Permissions::permission_revoke(&root_desc, &status);
             },
diff --git a/components/script/dom/webidls/BluetoothDevice.webidl b/components/script/dom/webidls/BluetoothDevice.webidl
index eac8b533392..1eb9f495ec0 100644
--- a/components/script/dom/webidls/BluetoothDevice.webidl
+++ b/components/script/dom/webidls/BluetoothDevice.webidl
@@ -8,7 +8,7 @@
 interface BluetoothDevice : EventTarget {
   readonly attribute DOMString id;
   readonly attribute DOMString? name;
-  readonly attribute BluetoothRemoteGATTServer gatt;
+  readonly attribute BluetoothRemoteGATTServer? gatt;
 
   Promise watchAdvertisements();
   void unwatchAdvertisements();
diff --git a/components/script/dom/webidls/BluetoothPermissionResult.webidl b/components/script/dom/webidls/BluetoothPermissionResult.webidl
new file mode 100644
index 00000000000..47d19999001
--- /dev/null
+++ b/components/script/dom/webidls/BluetoothPermissionResult.webidl
@@ -0,0 +1,31 @@
+/* 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://webbluetoothcg.github.io/web-bluetooth/#bluetoothpermissionresult
+
+dictionary BluetoothPermissionDescriptor : PermissionDescriptor {
+  DOMString deviceId;
+  // These match RequestDeviceOptions.
+  sequence filters;
+  sequence optionalServices/* = []*/;
+  boolean acceptAllDevices = false;
+};
+
+dictionary AllowedBluetoothDevice {
+  required DOMString deviceId;
+  required boolean mayUseGATT;
+  // An allowedServices of "all" means all services are allowed.
+  required (DOMString or sequence) allowedServices;
+};
+
+dictionary BluetoothPermissionData {
+  required sequence allowedDevices/* = []*/;
+};
+
+// [Pref="dom.bluetooth.enabled"]
+interface BluetoothPermissionResult : PermissionStatus {
+  // attribute FrozenArray devices;
+  // Workaround until FrozenArray get implemented.
+  sequence devices();
+};
diff --git a/tests/html/bluetooth-permission.html b/tests/html/bluetooth-permission.html
new file mode 100644
index 00000000000..a53448c98b9
--- /dev/null
+++ b/tests/html/bluetooth-permission.html
@@ -0,0 +1,68 @@
+
+
+Bluetooth Permission Test
+
+    
+    
+    

+    
+
+
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index 86ef8cfc0ff..c73fdd4a390 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -24367,7 +24367,7 @@
    "testharness"
   ],
   "mozilla/bluetooth/connect/device-goes-out-of-range.html": [
-   "9e5dc423f92c3f73273d221e7fb7b8d905716db3",
+   "3ade30929f621b7c8c93b87e5392309729d68e42",
    "testharness"
   ],
   "mozilla/bluetooth/connect/get-same-gatt-server.html": [
diff --git a/tests/wpt/mozilla/tests/mozilla/bluetooth/connect/device-goes-out-of-range.html b/tests/wpt/mozilla/tests/mozilla/bluetooth/connect/device-goes-out-of-range.html
index f189a9971f2..cb20839271d 100644
--- a/tests/wpt/mozilla/tests/mozilla/bluetooth/connect/device-goes-out-of-range.html
+++ b/tests/wpt/mozilla/tests/mozilla/bluetooth/connect/device-goes-out-of-range.html
@@ -10,8 +10,9 @@ promise_test(t => {
         filters: [{services: [heart_rate.name]}]
     })
     .then(device => {
+        var gatt_server = device.gatt;
         window.testRunner.setBluetoothMockDataSet(adapter_type.empty);
-        return promise_rejects(t, 'NetworkError', device.gatt.connect());
+        return promise_rejects(t, 'NetworkError', gatt_server.connect());
     });
 }, 'Device goes out of range. Reject with NetworkError.');
 

From 52680bba6b21bf8d0110645bf2fbb79912722601 Mon Sep 17 00:00:00 2001
From: Attila Dusnoki 
Date: Tue, 31 Jan 2017 13:57:36 +0100
Subject: [PATCH 04/15] Fix Permissions and Bluetooth prefs

---
 components/script/dom/webidls/BluetoothPermissionResult.webidl | 2 +-
 components/script/dom/webidls/Navigator.webidl                 | 2 +-
 components/script/dom/webidls/PermissionStatus.webidl          | 2 +-
 components/script/dom/webidls/Permissions.webidl               | 2 +-
 components/script/dom/webidls/WorkerNavigator.webidl           | 2 +-
 resources/prefs.json                                           | 1 +
 6 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/components/script/dom/webidls/BluetoothPermissionResult.webidl b/components/script/dom/webidls/BluetoothPermissionResult.webidl
index 47d19999001..6029e0b2034 100644
--- a/components/script/dom/webidls/BluetoothPermissionResult.webidl
+++ b/components/script/dom/webidls/BluetoothPermissionResult.webidl
@@ -23,7 +23,7 @@ dictionary BluetoothPermissionData {
   required sequence allowedDevices/* = []*/;
 };
 
-// [Pref="dom.bluetooth.enabled"]
+[Pref="dom.bluetooth.enabled"]
 interface BluetoothPermissionResult : PermissionStatus {
   // attribute FrozenArray devices;
   // Workaround until FrozenArray get implemented.
diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl
index a461b01932b..e04616ef40f 100644
--- a/components/script/dom/webidls/Navigator.webidl
+++ b/components/script/dom/webidls/Navigator.webidl
@@ -66,5 +66,5 @@ partial interface Navigator {
 // https://w3c.github.io/permissions/#navigator-and-workernavigator-extension
 [Exposed=(Window)]
 partial interface Navigator {
-  readonly attribute Permissions permissions;
+  [Pref="dom.permissions.enabled"] readonly attribute Permissions permissions;
 };
diff --git a/components/script/dom/webidls/PermissionStatus.webidl b/components/script/dom/webidls/PermissionStatus.webidl
index 168d797685e..9fb1a5ce966 100644
--- a/components/script/dom/webidls/PermissionStatus.webidl
+++ b/components/script/dom/webidls/PermissionStatus.webidl
@@ -28,7 +28,7 @@ enum PermissionName {
   "persistent-storage",
 };
 
-[Exposed=(Window,Worker)]
+[Pref="dom.permissions.enabled", Exposed=(Window,Worker)]
 interface PermissionStatus : EventTarget {
   readonly attribute PermissionState state;
   attribute EventHandler onchange;
diff --git a/components/script/dom/webidls/Permissions.webidl b/components/script/dom/webidls/Permissions.webidl
index 279acb09ba3..56841956e8d 100644
--- a/components/script/dom/webidls/Permissions.webidl
+++ b/components/script/dom/webidls/Permissions.webidl
@@ -4,7 +4,7 @@
 
 // https://w3c.github.io/permissions/#permissions-interface
 
-[Exposed=(Window,Worker)]
+[Pref="dom.permissions.enabled", Exposed=(Window,Worker)]
 interface Permissions {
   Promise query(object permissionDesc);
 
diff --git a/components/script/dom/webidls/WorkerNavigator.webidl b/components/script/dom/webidls/WorkerNavigator.webidl
index 10c258f167b..c60eda76896 100644
--- a/components/script/dom/webidls/WorkerNavigator.webidl
+++ b/components/script/dom/webidls/WorkerNavigator.webidl
@@ -13,5 +13,5 @@ WorkerNavigator implements NavigatorLanguage;
 
 [Exposed=(Worker)]
 partial interface WorkerNavigator {
-  readonly attribute Permissions permissions;
+  [Pref="dom.permissions.enabled"] readonly attribute Permissions permissions;
 };
diff --git a/resources/prefs.json b/resources/prefs.json
index 74c8fa7f9fe..2e8200d3b63 100644
--- a/resources/prefs.json
+++ b/resources/prefs.json
@@ -4,6 +4,7 @@
   "dom.forcetouch.enabled": false,
   "dom.mouseevent.which.enabled": false,
   "dom.mozbrowser.enabled": false,
+  "dom.permissions.enabled": false,
   "dom.serviceworker.timeout_seconds": 60,
   "dom.testable_crash.enabled": false,
   "dom.testbinding.enabled": false,

From 74c1350518c2da765344137be1e0021eef4b6198 Mon Sep 17 00:00:00 2001
From: Attila Dusnoki 
Date: Wed, 1 Feb 2017 09:18:55 +0100
Subject: [PATCH 05/15] Update expected results for BluetoothPermissionResult

---
 .../mozilla/meta/mozilla/bluetooth/interfaces.html.ini   | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/tests/wpt/mozilla/meta/mozilla/bluetooth/interfaces.html.ini b/tests/wpt/mozilla/meta/mozilla/bluetooth/interfaces.html.ini
index 372cbf3def3..546d57e0b3d 100644
--- a/tests/wpt/mozilla/meta/mozilla/bluetooth/interfaces.html.ini
+++ b/tests/wpt/mozilla/meta/mozilla/bluetooth/interfaces.html.ini
@@ -51,18 +51,9 @@
   [BluetoothPermissionResult interface: existence and properties of interface object]
     expected: FAIL
 
-  [BluetoothPermissionResult interface object length]
-    expected: FAIL
-
-  [BluetoothPermissionResult interface object name]
-    expected: FAIL
-
   [BluetoothPermissionResult interface: existence and properties of interface prototype object]
     expected: FAIL
 
-  [BluetoothPermissionResult interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
   [BluetoothPermissionResult interface: attribute devices]
     expected: FAIL
 

From cce362dc39fea02c3817d5b8623aaa03275781c8 Mon Sep 17 00:00:00 2001
From: Attila Dusnoki 
Date: Wed, 1 Feb 2017 09:38:16 +0100
Subject: [PATCH 06/15] Fix popup build error

---
 components/script/dom/permissions.rs | 29 +++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/components/script/dom/permissions.rs b/components/script/dom/permissions.rs
index 104376e2e0c..5279f844962 100644
--- a/components/script/dom/permissions.rs
+++ b/components/script/dom/permissions.rs
@@ -256,17 +256,7 @@ pub fn get_descriptor_permission_state(permission_name: PermissionName ,
     // 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;
-        }
+        return prompt_user(permission_name);
     }
 
     // TODO: Step 3: Store the invocation results
@@ -276,6 +266,23 @@ pub fn get_descriptor_permission_state(permission_name: PermissionName ,
     PermissionState::Granted
 }
 
+#[cfg(target_os = "linux")]
+fn prompt_user(permission_name: PermissionName) -> PermissionState {
+    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,
+    };
+}
+
+#[cfg(not(target_os = "linux"))]
+fn prompt_user(_permission_name: PermissionName) -> 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 {

From 04fe75b9cc4498360c023dbfd3cf924932e806af Mon Sep 17 00:00:00 2001
From: Zakor Gyula 
Date: Mon, 6 Feb 2017 13:48:50 +0100
Subject: [PATCH 07/15] bluetooth test fix

---
 tests/html/bluetooth-permission.html | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/html/bluetooth-permission.html b/tests/html/bluetooth-permission.html
index a53448c98b9..ebfe40e8e78 100644
--- a/tests/html/bluetooth-permission.html
+++ b/tests/html/bluetooth-permission.html
@@ -6,7 +6,8 @@