diff options
author | Zakor Gyula <zakorgy@inf.u-szeged.hu> | 2017-01-30 11:30:06 +0100 |
---|---|---|
committer | Attila Dusnoki <dati91@gmail.com> | 2017-02-13 14:58:06 +0100 |
commit | 5287cd3beaebd8a7fe778e71e9693160cee38434 (patch) | |
tree | 3e1ee818d1f163f0d537f8d6711a63066b0274c9 /components | |
parent | f3ddee5dbcecd51c35b7d40c7fc6d767cbfafb27 (diff) | |
download | servo-5287cd3beaebd8a7fe778e71e9693160cee38434.tar.gz servo-5287cd3beaebd8a7fe778e71e9693160cee38434.zip |
Bluetooth Permission API integration
Diffstat (limited to 'components')
-rw-r--r-- | components/atoms/static_atoms.txt | 1 | ||||
-rw-r--r-- | components/bluetooth/lib.rs | 23 | ||||
-rw-r--r-- | components/bluetooth_traits/lib.rs | 3 | ||||
-rw-r--r-- | components/script/dom/bluetooth.rs | 229 | ||||
-rw-r--r-- | components/script/dom/bluetoothdevice.rs | 24 | ||||
-rw-r--r-- | components/script/dom/bluetoothpermissionresult.rs | 128 | ||||
-rw-r--r-- | components/script/dom/bluetoothremotegattcharacteristic.rs | 11 | ||||
-rw-r--r-- | components/script/dom/bluetoothremotegattdescriptor.rs | 5 | ||||
-rw-r--r-- | components/script/dom/bluetoothremotegattserver.rs | 6 | ||||
-rw-r--r-- | components/script/dom/bluetoothremotegattservice.rs | 15 | ||||
-rw-r--r-- | components/script/dom/mod.rs | 1 | ||||
-rw-r--r-- | components/script/dom/permissions.rs | 43 | ||||
-rw-r--r-- | components/script/dom/webidls/BluetoothDevice.webidl | 2 | ||||
-rw-r--r-- | components/script/dom/webidls/BluetoothPermissionResult.webidl | 31 |
14 files changed, 479 insertions, 43 deletions
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<bool> { + 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<String>, Vec<String>, Vec<String>), IsRepresentedDeviceNull(String, IpcSender<bool>), GetAvailability(IpcSender<BluetoothResponseResult>), + MatchesFilter(String, BluetoothScanfilterSequence, IpcSender<BluetoothResult<bool>>), Test(String, IpcSender<BluetoothResult<()>>), 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<BluetoothPermissionData> = + 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<AllowedBluetoothDevice> { + 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<T: AsyncBluetoothListener + DomObject> { promise: Option<TrustedPromise>, @@ -107,7 +160,8 @@ impl Bluetooth { fn request_bluetooth_devices(&self, p: &Rc<Promise>, filters: &Option<Vec<BluetoothLEScanFilterInit>>, - optional_services: &Option<Vec<BluetoothServiceUUID>>) { + optional_services: &Option<Vec<BluetoothServiceUUID>>, + sender: IpcSender<BluetoothResponseResult>) { // 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<Promise>) { 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<BluetoothPermissionDescriptor, Error> { + 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<Promise>, + 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<JS<BluetoothDevice>> = 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<BluetoothScanfilter> = 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<Promise>, + 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<BluetoothRemoteGATTServer> { + self.gatt.or_init(|| { + BluetoothRemoteGATTServer::new(&self.global(), self) + }) + } + fn get_context(&self) -> Root<Bluetooth> { 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<BluetoothRemoteGATTServer> { - // TODO: Step 1 - 2: Implement the Permission API. - self.gatt.or_init(|| { - BluetoothRemoteGATTServer::new(&self.global(), self) - }) + fn GetGatt(&self) -> Option<Root<BluetoothRemoteGATTServer>> { + // 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<Vec<JS<BluetoothDevice>>>, +} + +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<BluetoothPermissionResult> { + reflect_dom_object(box BluetoothPermissionResult::new_inherited(status), + global, + BluetoothPermissionResultBinding::Wrap) + } + + pub fn get_bluetooth(&self) -> Root<Bluetooth> { + self.global().as_window().Navigator().Bluetooth() + } + + pub fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> { + 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<JS<BluetoothDevice>>) { + *self.devices.borrow_mut() = devices; + } +} + +impl BluetoothPermissionResultMethods for BluetoothPermissionResult { + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothpermissionresult-devices + fn Devices(&self) -> Vec<Root<BluetoothDevice>> { + let device_vec: Vec<Root<BluetoothDevice>> = + 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<Promise>) { + 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<Promise> { 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<BluetoothDescriptorUUID>) -> Rc<Promise> { 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<Promise> { - // 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<BluetoothServiceUUID>) -> Rc<Promise> { - // 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<Promise> { 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<BluetoothCharacteristicUUID>) -> Rc<Promise> { 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<Promise> { 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<BluetoothServiceUUID>) -> Rc<Promise> { 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<void> 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<BluetoothLEScanFilterInit> filters; + sequence<BluetoothServiceUUID> 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<UUID>) allowedServices; +}; + +dictionary BluetoothPermissionData { + required sequence<AllowedBluetoothDevice> allowedDevices/* = []*/; +}; + +// [Pref="dom.bluetooth.enabled"] +interface BluetoothPermissionResult : PermissionStatus { + // attribute FrozenArray<BluetoothDevice> devices; + // Workaround until FrozenArray get implemented. + sequence<BluetoothDevice> devices(); +}; |