diff --git a/.travis.yml b/.travis.yml index 87bad747c60..ab91737d7a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ matrix: - python-virtualenv - xorg-dev - ccache + - libdbus-glib-1-dev branches: only: diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 0b235257d1a..1e1da237e7f 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -36,6 +36,7 @@ use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId, NavigationD use msg::constellation_msg::{SubpageId, WindowSizeData, WindowSizeType}; use msg::constellation_msg::{self, ConstellationChan, PanicMsg}; use msg::webdriver_msg; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::ImageCacheThread; use net_traits::storage_thread::{StorageThread, StorageThreadMsg}; use net_traits::{self, ResourceThread}; @@ -123,6 +124,9 @@ pub struct Constellation { /// A channel through which messages can be sent to the developer tools. devtools_chan: Option>, + /// A channel through which messages can be sent to the bluetooth thread. + bluetooth_thread: IpcSender, + /// A channel through which messages can be sent to the storage thread. storage_thread: StorageThread, @@ -197,6 +201,8 @@ pub struct InitialConstellationState { pub compositor_proxy: Box, /// A channel to the developer tools, if applicable. pub devtools_chan: Option>, + /// A channel to the bluetooth thread. + pub bluetooth_thread: IpcSender, /// A channel to the image cache thread. pub image_cache_thread: ImageCacheThread, /// A channel to the font cache thread. @@ -338,6 +344,7 @@ impl Constellation panic_receiver: panic_receiver, compositor_proxy: state.compositor_proxy, devtools_chan: state.devtools_chan, + bluetooth_thread: state.bluetooth_thread, resource_thread: state.resource_thread, image_cache_thread: state.image_cache_thread, font_cache_thread: state.font_cache_thread, @@ -424,6 +431,7 @@ impl Constellation scheduler_chan: self.scheduler_chan.clone(), compositor_proxy: self.compositor_proxy.clone_compositor_proxy(), devtools_chan: self.devtools_chan.clone(), + bluetooth_thread: self.bluetooth_thread.clone(), image_cache_thread: self.image_cache_thread.clone(), font_cache_thread: self.font_cache_thread.clone(), resource_thread: self.resource_thread.clone(), @@ -840,6 +848,9 @@ impl Constellation if let Err(e) = self.storage_thread.send(StorageThreadMsg::Exit) { warn!("Exit storage thread failed ({})", e); } + if let Err(e) = self.bluetooth_thread.send(BluetoothMethodMsg::Exit) { + warn!("Exit bluetooth thread failed ({})", e); + } self.font_cache_thread.exit(); self.compositor_proxy.send(ToCompositorMsg::ShutdownComplete); } diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index 9facc11829c..ca44fa49faf 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -18,6 +18,7 @@ use msg::constellation_msg::{ConstellationChan, PanicMsg, FrameId, PipelineId, S use msg::constellation_msg::{LoadData, WindowSizeData}; use msg::constellation_msg::{PipelineNamespaceId}; use net_traits::ResourceThread; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::ImageCacheThread; use net_traits::storage_thread::StorageThread; use profile_traits::mem as profile_mem; @@ -92,6 +93,8 @@ pub struct InitialPipelineState { pub compositor_proxy: Box, /// A channel to the developer tools, if applicable. pub devtools_chan: Option>, + /// A channel to the bluetooth thread. + pub bluetooth_thread: IpcSender, /// A channel to the image cache thread. pub image_cache_thread: ImageCacheThread, /// A channel to the font cache thread. @@ -214,6 +217,7 @@ impl Pipeline { constellation_chan: state.constellation_chan, scheduler_chan: state.scheduler_chan, devtools_chan: script_to_devtools_chan, + bluetooth_thread: state.bluetooth_thread, image_cache_thread: state.image_cache_thread, font_cache_thread: state.font_cache_thread.clone(), resource_thread: state.resource_thread, @@ -390,6 +394,7 @@ pub struct UnprivilegedPipelineContent { scheduler_chan: IpcSender, devtools_chan: Option>, script_to_compositor_chan: IpcSender, + bluetooth_thread: IpcSender, image_cache_thread: ImageCacheThread, font_cache_thread: FontCacheThread, resource_thread: ResourceThread, @@ -430,6 +435,7 @@ impl UnprivilegedPipelineContent { layout_to_constellation_chan: self.layout_to_constellation_chan.clone(), scheduler_chan: self.scheduler_chan.clone(), panic_chan: self.panic_chan.clone(), + bluetooth_thread: self.bluetooth_thread.clone(), resource_thread: self.resource_thread, storage_thread: self.storage_thread.clone(), image_cache_thread: self.image_cache_thread.clone(), diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index 80437830a24..14b9914d51f 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -17,6 +17,8 @@ plugins = {path = "../plugins"} msg = {path = "../msg"} ipc-channel = {git = "https://github.com/servo/ipc-channel"} webrender_traits = {git = "https://github.com/servo/webrender_traits"} +device = {git = "https://github.com/servo/devices"} +bitflags = "0.6.0" cookie = { version = "0.2.4", features = [ "serialize-rustc" ] } flate2 = "0.2.0" hyper = { version = "0.9", features = [ "serde-serialization" ] } diff --git a/components/net/bluetooth_thread.rs b/components/net/bluetooth_thread.rs new file mode 100644 index 00000000000..14bb72f2bcf --- /dev/null +++ b/components/net/bluetooth_thread.rs @@ -0,0 +1,649 @@ +/* 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 device::bluetooth::BluetoothAdapter; +use device::bluetooth::BluetoothDevice; +use device::bluetooth::BluetoothGATTCharacteristic; +use device::bluetooth::BluetoothGATTDescriptor; +use device::bluetooth::BluetoothGATTService; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use net_traits::bluetooth_scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence, RequestDeviceoptions}; +use net_traits::bluetooth_thread::{BluetoothCharacteristicMsg, BluetoothCharacteristicsMsg}; +use net_traits::bluetooth_thread::{BluetoothDescriptorMsg, BluetoothDescriptorsMsg}; +use net_traits::bluetooth_thread::{BluetoothDeviceMsg, BluetoothMethodMsg}; +use net_traits::bluetooth_thread::{BluetoothResult, BluetoothServiceMsg, BluetoothServicesMsg}; +use std::borrow::ToOwned; +use std::collections::HashMap; +use std::string::String; +use util::thread::spawn_named; + +const ADAPTER_ERROR: &'static str = "No adapter found"; +const DEVICE_ERROR: &'static str = "No device found"; +const DEVICE_MATCH_ERROR: &'static str = "No device found, that matches the given options"; +const PRIMARY_SERVICE_ERROR: &'static str = "No primary service found"; +const CHARACTERISTIC_ERROR: &'static str = "No characteristic found"; +const DESCRIPTOR_ERROR: &'static str = "No descriptor found"; +const VALUE_ERROR: &'static str = "No characteristic or descriptor found with that id"; + +bitflags! { + flags Flags: u32 { + const BROADCAST = 0b000000001, + const READ = 0b000000010, + const WRITE_WITHOUT_RESPONSE = 0b000000100, + const WRITE = 0b000001000, + const NOTIFY = 0b000010000, + const INDICATE = 0b000100000, + const AUTHENTICATED_SIGNED_WRITES = 0b001000000, + const RELIABLE_WRITE = 0b010000000, + const WRITABLE_AUXILIARIES = 0b100000000, + } +} + +macro_rules! return_if_cached( + ($cache:expr, $key:expr) => ( + if $cache.contains_key($key) { + return $cache.get($key); + } + ); +); + +pub trait BluetoothThreadFactory { + fn new() -> Self; +} + +impl BluetoothThreadFactory for IpcSender { + fn new() -> IpcSender { + let (sender, receiver) = ipc::channel().unwrap(); + let adapter = BluetoothAdapter::init().ok(); + spawn_named("BluetoothThread".to_owned(), move || { + BluetoothManager::new(receiver, adapter).start(); + }); + sender + } +} + +fn matches_filter(device: &BluetoothDevice, filter: &BluetoothScanfilter) -> bool { + if filter.is_empty_or_invalid() { + return false; + } + + if !filter.get_name().is_empty() { + if device.get_name().ok() != Some(filter.get_name().to_string()) { + return false; + } + } + + if !filter.get_name_prefix().is_empty() { + if let Ok(device_name) = device.get_name() { + if !device_name.starts_with(filter.get_name_prefix()) { + return false; + } + } else { + return false; + } + } + + if !filter.get_services().is_empty() { + if let Ok(device_uuids) = device.get_uuids() { + for service in filter.get_services() { + if device_uuids.iter().find(|x| x == &service).is_none() { + return false; + } + } + } + } + return true; +} + +fn matches_filters(device: &BluetoothDevice, filters: &BluetoothScanfilterSequence) -> bool { + if filters.has_empty_or_invalid_filter() { + return false; + } + + return filters.iter().any(|f| matches_filter(device, f)) +} + +pub struct BluetoothManager { + receiver: IpcReceiver, + adapter: Option, + service_to_device: HashMap, + characteristic_to_service: HashMap, + descriptor_to_characteristic: HashMap, + cached_devices: HashMap, + cached_services: HashMap, + cached_characteristics: HashMap, + cached_descriptors: HashMap, +} + +impl BluetoothManager { + pub fn new (receiver: IpcReceiver, adapter: Option) -> BluetoothManager { + BluetoothManager { + receiver: receiver, + adapter: adapter, + service_to_device: HashMap::new(), + characteristic_to_service: HashMap::new(), + descriptor_to_characteristic: HashMap::new(), + cached_devices: HashMap::new(), + cached_services: HashMap::new(), + cached_characteristics: HashMap::new(), + cached_descriptors: HashMap::new(), + } + } + + fn start(&mut self) { + while let Ok(msg) = self.receiver.recv() { + match msg { + BluetoothMethodMsg::RequestDevice(options, sender) => { + self.request_device(options, sender) + }, + BluetoothMethodMsg::GATTServerConnect(device_id, sender) => { + self.gatt_server_connect(device_id, sender) + }, + BluetoothMethodMsg::GATTServerDisconnect(device_id, sender) => { + self.gatt_server_disconnect(device_id, sender) + }, + BluetoothMethodMsg::GetPrimaryService(device_id, uuid, sender) => { + self.get_primary_service(device_id, uuid, sender) + }, + BluetoothMethodMsg::GetPrimaryServices(device_id, uuid, sender) => { + self.get_primary_services(device_id, uuid, sender) + }, + BluetoothMethodMsg::GetCharacteristic(service_id, uuid, sender) => { + self.get_characteristic(service_id, uuid, sender) + }, + BluetoothMethodMsg::GetCharacteristics(service_id, uuid, sender) => { + self.get_characteristics(service_id, uuid, sender) + }, + BluetoothMethodMsg::GetDescriptor(characteristic_id, uuid, sender) => { + self.get_descriptor(characteristic_id, uuid, sender) + }, + BluetoothMethodMsg::GetDescriptors(characteristic_id, uuid, sender) => { + self.get_descriptors(characteristic_id, uuid, sender) + }, + BluetoothMethodMsg::ReadValue(id, sender) => { + self.read_value(id, sender) + }, + BluetoothMethodMsg::WriteValue(id, value, sender) => { + self.write_value(id, value, sender) + }, + BluetoothMethodMsg::Exit => { + break + }, + } + } + } + + // Adapter + + fn get_or_create_adapter(&mut self) -> Option { + let adapter_valid = self.adapter.as_ref().map_or(false, |a| a.get_address().is_ok()); + if !adapter_valid { + self.adapter = BluetoothAdapter::init().ok(); + } + self.adapter.clone() + } + + // Device + + fn get_and_cache_devices(&mut self, adapter: &mut BluetoothAdapter) -> Vec { + let devices = adapter.get_devices().unwrap_or(vec!()); + for device in &devices { + if let Ok(address) = device.get_address() { + self.cached_devices.insert(address, device.clone()); + } + } + self.cached_devices.iter().map(|(_, d)| d.clone()).collect() + } + + fn get_device(&mut self, adapter: &mut BluetoothAdapter, device_id: &str) -> Option<&BluetoothDevice> { + return_if_cached!(self.cached_devices, device_id); + self.get_and_cache_devices(adapter); + return_if_cached!(self.cached_devices, device_id); + None + } + + // Service + + fn get_and_cache_gatt_services(&mut self, + adapter: &mut BluetoothAdapter, + device_id: &str) + -> Vec { + let services = match self.get_device(adapter, device_id) { + Some(d) => d.get_gatt_services().unwrap_or(vec!()), + None => vec!(), + }; + for service in &services { + self.cached_services.insert(service.get_object_path(), service.clone()); + self.service_to_device.insert(service.get_object_path(), device_id.to_owned()); + } + services + } + + fn get_gatt_service(&mut self, adapter: &mut BluetoothAdapter, service_id: &str) -> Option<&BluetoothGATTService> { + return_if_cached!(self.cached_services, service_id); + let device_id = match self.service_to_device.get(service_id) { + Some(d) => d.clone(), + None => return None, + }; + self.get_and_cache_gatt_services(adapter, &device_id); + return_if_cached!(self.cached_services, service_id); + None + } + + fn get_gatt_services_by_uuid(&mut self, + adapter: &mut BluetoothAdapter, + device_id: &str, + service_uuid: &str) + -> Vec { + let services = self.get_and_cache_gatt_services(adapter, device_id); + services.into_iter().filter(|s| s.get_uuid().ok() == Some(service_uuid.to_string())).collect() + } + + // Characteristic + + fn get_and_cache_gatt_characteristics(&mut self, + adapter: &mut BluetoothAdapter, + service_id: &str) + -> Vec { + let characteristics = match self.get_gatt_service(adapter, service_id) { + Some(s) => s.get_gatt_characteristics().unwrap_or(vec!()), + None => vec!(), + }; + + for characteristic in &characteristics { + self.cached_characteristics.insert(characteristic.get_object_path(), characteristic.clone()); + self.characteristic_to_service.insert(characteristic.get_object_path(), service_id.to_owned()); + } + characteristics + } + + fn get_gatt_characteristic(&mut self, + adapter: &mut BluetoothAdapter, + characteristic_id: &str) + -> Option<&BluetoothGATTCharacteristic> { + return_if_cached!(self.cached_characteristics, characteristic_id); + let service_id = match self.characteristic_to_service.get(characteristic_id) { + Some(s) => s.clone(), + None => return None, + }; + self.get_and_cache_gatt_characteristics(adapter, &service_id); + return_if_cached!(self.cached_characteristics, characteristic_id); + None + } + + fn get_gatt_characteristics_by_uuid(&mut self, + adapter: &mut BluetoothAdapter, + service_id: &str, + characteristic_uuid: &str) + -> Vec { + let characteristics = self.get_and_cache_gatt_characteristics(adapter, service_id); + characteristics.into_iter() + .filter(|c| c.get_uuid().ok() == Some(characteristic_uuid.to_string())) + .collect() + } + + fn get_characteristic_properties(&self, characteristic: &BluetoothGATTCharacteristic) -> Flags { + let mut props: Flags = Flags::empty(); + let flags = characteristic.get_flags().unwrap_or(vec!()); + for flag in flags { + match flag.as_ref() { + "broadcast" => props.insert(BROADCAST), + "read" => props.insert(READ), + "write_without_response" => props.insert(WRITE_WITHOUT_RESPONSE), + "write" => props.insert(WRITE), + "notify" => props.insert(NOTIFY), + "indicate" => props.insert(INDICATE), + "authenticated_signed_writes" => props.insert(AUTHENTICATED_SIGNED_WRITES), + "reliable_write" => props.insert(RELIABLE_WRITE), + "writable_auxiliaries" => props.insert(WRITABLE_AUXILIARIES), + _ => (), + } + } + props + } + + // Descriptor + + fn get_and_cache_gatt_descriptors(&mut self, + adapter: &mut BluetoothAdapter, + characteristic_id: &str) + -> Vec { + let descriptors = match self.get_gatt_characteristic(adapter, characteristic_id) { + Some(c) => c.get_gatt_descriptors().unwrap_or(vec!()), + None => vec!(), + }; + + for descriptor in &descriptors { + self.cached_descriptors.insert(descriptor.get_object_path(), descriptor.clone()); + self.descriptor_to_characteristic.insert(descriptor.get_object_path(), characteristic_id.to_owned()); + } + descriptors + } + + fn get_gatt_descriptor(&mut self, + adapter: &mut BluetoothAdapter, + descriptor_id: &str) + -> Option<&BluetoothGATTDescriptor> { + return_if_cached!(self.cached_descriptors, descriptor_id); + let characteristic_id = match self.descriptor_to_characteristic.get(descriptor_id) { + Some(c) => c.clone(), + None => return None, + }; + self.get_and_cache_gatt_descriptors(adapter, &characteristic_id); + return_if_cached!(self.cached_descriptors, descriptor_id); + None + } + + fn get_gatt_descriptors_by_uuid(&mut self, + adapter: &mut BluetoothAdapter, + characteristic_id: &str, + descriptor_uuid: &str) + -> Vec { + let descriptors = self.get_and_cache_gatt_descriptors(adapter, characteristic_id); + descriptors.into_iter() + .filter(|d| d.get_uuid().ok() == Some(descriptor_uuid.to_string())) + .collect() + } + + // Methods + + fn request_device(&mut self, + options: RequestDeviceoptions, + sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + let devices = self.get_and_cache_devices(&mut adapter); + if devices.is_empty() { + return drop(sender.send(Err(String::from(DEVICE_ERROR)))); + } + + let matched_devices: Vec = devices.into_iter() + .filter(|d| matches_filters(d, options.get_filters())) + .collect(); + for device in matched_devices { + if let Ok(address) = device.get_address() { + let message = Ok(BluetoothDeviceMsg { + id: address, + name: device.get_name().ok(), + device_class: device.get_class().ok(), + vendor_id_source: device.get_vendor_id_source().ok(), + vendor_id: device.get_vendor_id().ok(), + product_id: device.get_product_id().ok(), + product_version: device.get_device_id().ok(), + appearance: device.get_appearance().ok(), + tx_power: device.get_tx_power().ok().map(|p| p as i8), + rssi: device.get_rssi().ok().map(|p| p as i8), + }); + return drop(sender.send(message)); + } + } + return drop(sender.send(Err(String::from(DEVICE_MATCH_ERROR)))); + } + + fn gatt_server_connect(&mut self, device_id: String, sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + + let connected = match self.get_device(&mut adapter, &device_id) { + Some(d) => { + if d.is_connected().unwrap_or(false) { + true + } else { + d.connect().is_ok() + } + }, + None => return drop(sender.send(Err(String::from(DEVICE_ERROR)))), + }; + + let _ = sender.send(Ok(connected)); + } + + fn gatt_server_disconnect(&mut self, device_id: String, sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + + let connected = match self.get_device(&mut adapter, &device_id) { + Some(d) => { + if d.is_connected().unwrap_or(false) { + d.disconnect().is_ok() + } else { + false + } + }, + None => return drop(sender.send(Err(String::from(DEVICE_ERROR)))), + }; + + let _ = sender.send(Ok(connected)); + } + + fn get_primary_service(&mut self, + device_id: String, + uuid: String, + sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + let services = self.get_gatt_services_by_uuid(&mut adapter, &device_id, &uuid); + if services.is_empty() { + return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR)))); + } + for service in services { + if service.is_primary().unwrap_or(false) { + if let Ok(uuid) = service.get_uuid() { + return drop(sender.send(Ok(BluetoothServiceMsg { + uuid: uuid, + is_primary: true, + instance_id: service.get_object_path(), + }))); + } + } + } + return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR)))); + } + + fn get_primary_services(&mut self, + device_id: String, + uuid: Option, + sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + let services = match uuid { + Some(id) => self.get_gatt_services_by_uuid(&mut adapter, &device_id, &id), + None => self.get_and_cache_gatt_services(&mut adapter, &device_id), + }; + if services.is_empty() { + return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR)))); + } + let mut services_vec = vec!(); + for service in services { + if service.is_primary().unwrap_or(false) { + if let Ok(uuid) = service.get_uuid() { + services_vec.push(BluetoothServiceMsg { + uuid: uuid, + is_primary: true, + instance_id: service.get_object_path(), + }); + } + } + } + if services_vec.is_empty() { + return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR)))); + } + + let _ = sender.send(Ok(services_vec)); + } + + fn get_characteristic(&mut self, + service_id: String, + uuid: String, + sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + let characteristics = self.get_gatt_characteristics_by_uuid(&mut adapter, &service_id, &uuid); + if characteristics.is_empty() { + return drop(sender.send(Err(String::from(CHARACTERISTIC_ERROR)))); + } + for characteristic in characteristics { + if let Ok(uuid) = characteristic.get_uuid() { + let properties = self.get_characteristic_properties(&characteristic); + let message = Ok(BluetoothCharacteristicMsg { + uuid: uuid, + instance_id: characteristic.get_object_path(), + broadcast: properties.contains(BROADCAST), + read: properties.contains(READ), + write_without_response: properties.contains(WRITE_WITHOUT_RESPONSE), + write: properties.contains(WRITE), + notify: properties.contains(NOTIFY), + indicate: properties.contains(INDICATE), + authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES), + reliable_write: properties.contains(RELIABLE_WRITE), + writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES), + }); + return drop(sender.send(message)); + } + } + return drop(sender.send(Err(String::from(CHARACTERISTIC_ERROR)))); + } + + fn get_characteristics(&mut self, + service_id: String, + uuid: Option, + sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + let characteristics = match uuid { + Some(id) => self.get_gatt_characteristics_by_uuid(&mut adapter, &service_id, &id), + None => self.get_and_cache_gatt_characteristics(&mut adapter, &service_id), + }; + if characteristics.is_empty() { + return drop(sender.send(Err(String::from(CHARACTERISTIC_ERROR)))); + } + let mut characteristics_vec = vec!(); + for characteristic in characteristics { + if let Ok(uuid) = characteristic.get_uuid() { + let properties = self.get_characteristic_properties(&characteristic); + characteristics_vec.push( + BluetoothCharacteristicMsg { + uuid: uuid, + instance_id: characteristic.get_object_path(), + broadcast: properties.contains(BROADCAST), + read: properties.contains(READ), + write_without_response: properties.contains(WRITE_WITHOUT_RESPONSE), + write: properties.contains(WRITE), + notify: properties.contains(NOTIFY), + indicate: properties.contains(INDICATE), + authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES), + reliable_write: properties.contains(RELIABLE_WRITE), + writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES), + }); + } + } + if characteristics_vec.is_empty() { + return drop(sender.send(Err(String::from(CHARACTERISTIC_ERROR)))); + } + + let _ = sender.send(Ok(characteristics_vec)); + } + + fn get_descriptor(&mut self, + characteristic_id: String, + uuid: String, + sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + let descriptors = self.get_gatt_descriptors_by_uuid(&mut adapter, &characteristic_id, &uuid); + if descriptors.is_empty() { + return drop(sender.send(Err(String::from(DESCRIPTOR_ERROR)))); + } + for descriptor in descriptors { + if let Ok(uuid) = descriptor.get_uuid() { + return drop(sender.send(Ok(BluetoothDescriptorMsg { + uuid: uuid, + instance_id: descriptor.get_object_path(), + }))); + } + } + return drop(sender.send(Err(String::from(DESCRIPTOR_ERROR)))); + } + + fn get_descriptors(&mut self, + characteristic_id: String, + uuid: Option, + sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + let descriptors = match uuid { + Some(id) => self.get_gatt_descriptors_by_uuid(&mut adapter, &characteristic_id, &id), + None => self.get_and_cache_gatt_descriptors(&mut adapter, &characteristic_id), + }; + if descriptors.is_empty() { + return drop(sender.send(Err(String::from(DESCRIPTOR_ERROR)))); + } + let mut descriptors_vec = vec!(); + for descriptor in descriptors { + if let Ok(uuid) = descriptor.get_uuid() { + descriptors_vec.push(BluetoothDescriptorMsg { + uuid: uuid, + instance_id: descriptor.get_object_path(), + }); + } + } + if descriptors_vec.is_empty() { + return drop(sender.send(Err(String::from(DESCRIPTOR_ERROR)))); + } + let _ = sender.send(Ok(descriptors_vec)); + } + + fn read_value(&mut self, id: String, sender: IpcSender>>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + let mut value = self.get_gatt_characteristic(&mut adapter, &id) + .map(|c| c.read_value().unwrap_or(vec![])); + if value.is_none() { + value = self.get_gatt_descriptor(&mut adapter, &id) + .map(|d| d.read_value().unwrap_or(vec![])); + } + let _ = sender.send(value.ok_or(String::from(VALUE_ERROR))); + } + + fn write_value(&mut self, id: String, value: Vec, sender: IpcSender>) { + let mut adapter = match self.get_or_create_adapter() { + Some(a) => a, + None => return drop(sender.send(Err(String::from(ADAPTER_ERROR)))), + }; + let mut result = self.get_gatt_characteristic(&mut adapter, &id) + .map(|c| c.write_value(value.clone())); + if result.is_none() { + result = self.get_gatt_descriptor(&mut adapter, &id) + .map(|d| d.write_value(value.clone())); + } + let message = match result { + Some(v) => match v { + Ok(_) => Ok(true), + Err(e) => return drop(sender.send(Err(e.to_string()))), + }, + None => return drop(sender.send(Err(String::from(VALUE_ERROR)))), + }; + let _ = sender.send(message); + } +} diff --git a/components/net/lib.rs b/components/net/lib.rs index 97b978f1725..27b9d8f4dae 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -11,8 +11,11 @@ #![deny(unsafe_code)] +#[macro_use] +extern crate bitflags; extern crate brotli; extern crate cookie as cookie_rs; +extern crate device; extern crate devtools_traits; extern crate flate2; extern crate hyper; @@ -39,6 +42,7 @@ extern crate webrender_traits; extern crate websocket; pub mod about_loader; +pub mod bluetooth_thread; pub mod chrome_loader; pub mod cookie; pub mod cookie_storage; diff --git a/components/net_traits/bluetooth_scanfilter.rs b/components/net_traits/bluetooth_scanfilter.rs new file mode 100644 index 00000000000..b332c1a52b2 --- /dev/null +++ b/components/net_traits/bluetooth_scanfilter.rs @@ -0,0 +1,93 @@ +/* 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 std::slice::Iter; + +// A device name can never be longer than 29 bytes. An adv packet is at most +// 31 bytes long. The length and identifier of the length field take 2 bytes. +// That leaves 29 bytes for the name. +const MAX_NAME_LENGTH: usize = 29; + +#[derive(Deserialize, Serialize)] +pub struct ServiceUUIDSequence(Vec); + +impl ServiceUUIDSequence { + pub fn new(vec: Vec) -> ServiceUUIDSequence { + ServiceUUIDSequence(vec) + } +} + +#[derive(Deserialize, Serialize)] +pub struct BluetoothScanfilter { + name: String, + name_prefix: String, + services: ServiceUUIDSequence, +} + +impl BluetoothScanfilter { + pub fn new(name: String, name_prefix: String, services: Vec) -> BluetoothScanfilter { + BluetoothScanfilter { + name: name, + name_prefix: name_prefix, + services: ServiceUUIDSequence::new(services), + } + } + + pub fn get_name(&self) -> &str { + &self.name + } + + pub fn get_name_prefix(&self) -> &str { + &self.name_prefix + } + + pub fn get_services(&self) -> &[String] { + &self.services.0 + } + + pub fn is_empty_or_invalid(&self) -> bool { + (self.name.is_empty() && self.name_prefix.is_empty() && self.get_services().is_empty()) || + self.name.len() > MAX_NAME_LENGTH || + self.name_prefix.len() > MAX_NAME_LENGTH + } +} + +#[derive(Deserialize, Serialize)] +pub struct BluetoothScanfilterSequence(Vec); + +impl BluetoothScanfilterSequence { + pub fn new(vec: Vec) -> BluetoothScanfilterSequence { + BluetoothScanfilterSequence(vec) + } + + pub fn has_empty_or_invalid_filter(&self) -> bool { + self.0.is_empty() || + self.0.iter().any(BluetoothScanfilter::is_empty_or_invalid) + } + + pub fn iter(&self) -> Iter { + self.0.iter() + } +} + +#[derive(Deserialize, Serialize)] +pub struct RequestDeviceoptions { + filters: BluetoothScanfilterSequence, + optional_services: ServiceUUIDSequence, +} + +impl RequestDeviceoptions { + pub fn new(filters: BluetoothScanfilterSequence, + services: ServiceUUIDSequence) + -> RequestDeviceoptions { + RequestDeviceoptions { + filters: filters, + optional_services: services, + } + } + + pub fn get_filters(&self) -> &BluetoothScanfilterSequence { + &self.filters + } +} diff --git a/components/net_traits/bluetooth_thread.rs b/components/net_traits/bluetooth_thread.rs new file mode 100644 index 00000000000..34019528a1e --- /dev/null +++ b/components/net_traits/bluetooth_thread.rs @@ -0,0 +1,75 @@ +/* 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_scanfilter::RequestDeviceoptions; +use ipc_channel::ipc::IpcSender; + +#[derive(Deserialize, Serialize)] +pub struct BluetoothDeviceMsg { + // Bluetooth Device properties + pub id: String, + pub name: Option, + pub device_class: Option, + pub vendor_id_source: Option, + pub vendor_id: Option, + pub product_id: Option, + pub product_version: Option, + // Advertisiong Data properties + pub appearance: Option, + pub tx_power: Option, + pub rssi: Option, +} + +#[derive(Deserialize, Serialize)] +pub struct BluetoothServiceMsg { + pub uuid: String, + pub is_primary: bool, + pub instance_id: String, +} + +#[derive(Deserialize, Serialize)] +pub struct BluetoothCharacteristicMsg { + // Characteristic + pub uuid: String, + pub instance_id: String, + // Characteristic properties + pub broadcast: bool, + pub read: bool, + pub write_without_response: bool, + pub write: bool, + pub notify: bool, + pub indicate: bool, + pub authenticated_signed_writes: bool, + pub reliable_write: bool, + pub writable_auxiliaries: bool, +} + +#[derive(Deserialize, Serialize)] +pub struct BluetoothDescriptorMsg { + pub uuid: String, + pub instance_id: String, +} + +pub type BluetoothServicesMsg = Vec; + +pub type BluetoothCharacteristicsMsg = Vec; + +pub type BluetoothDescriptorsMsg = Vec; + +pub type BluetoothResult = Result; + +#[derive(Deserialize, Serialize)] +pub enum BluetoothMethodMsg { + RequestDevice(RequestDeviceoptions, IpcSender>), + GATTServerConnect(String, IpcSender>), + GATTServerDisconnect(String, IpcSender>), + GetPrimaryService(String, String, IpcSender>), + GetPrimaryServices(String, Option, IpcSender>), + GetCharacteristic(String, String, IpcSender>), + GetCharacteristics(String, Option, IpcSender>), + GetDescriptor(String, String, IpcSender>), + GetDescriptors(String, Option, IpcSender>), + ReadValue(String, IpcSender>>), + WriteValue(String, Vec, IpcSender>), + Exit, +} diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index f4b7a85e7c5..33ef19ef3bd 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -40,6 +40,8 @@ use std::thread; use url::Url; use websocket::header; +pub mod bluetooth_scanfilter; +pub mod bluetooth_thread; pub mod hosts; pub mod image_cache_thread; pub mod net_error_list; diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs index 58d506dec45..1e1ae3b147d 100644 --- a/components/script/dom/bluetooth.rs +++ b/components/script/dom/bluetooth.rs @@ -2,12 +2,38 @@ * 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::codegen::Bindings::BluetoothBinding; -use dom::bindings::codegen::Bindings::BluetoothBinding::BluetoothMethods; +use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions; +use dom::bindings::codegen::Bindings::BluetoothBinding::{BluetoothScanFilter, BluetoothMethods}; +use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::VendorIDSource; +use dom::bindings::error::Error::Type; +use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::js::Root; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::bluetoothadvertisingdata::BluetoothAdvertisingData; use dom::bluetoothdevice::BluetoothDevice; +use dom::bluetoothuuid::BluetoothUUID; +use ipc_channel::ipc::{self, IpcSender}; +use net_traits::bluetooth_scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence}; +use net_traits::bluetooth_scanfilter::{RequestDeviceoptions, ServiceUUIDSequence}; +use net_traits::bluetooth_thread::BluetoothMethodMsg; +use util::str::DOMString; + +const FILTER_EMPTY_ERROR: &'static str = "'filters' member must be non - empty to find any devices."; +const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way."; +const FILTER_NAME_TOO_LONG_ERROR: &'static str = "A 'name' or 'namePrefix' can't be longer then 29 bytes."; +// 248 is the maximum number of UTF-8 code units in a Bluetooth Device Name. +const MAX_DEVICE_NAME_LENGTH: usize = 248; +// A device name can never be longer than 29 bytes. +// An advertising packet is at most 31 bytes long. +// The length and identifier of the length field take 2 bytes. +// That leaves 29 bytes for the name. +const MAX_FILTER_NAME_LENGTH: usize = 29; +const NAME_PREFIX_ERROR: &'static str = "'namePrefix', if present, must be non - empty."; +const NAME_TOO_LONG_ERROR: &'static str = "A device name can't be longer than 248 bytes."; +const SERVICE_ERROR: &'static str = "'services', if present, must contain at least one service."; // https://webbluetoothcg.github.io/web-bluetooth/#bluetooth #[dom_struct] @@ -27,13 +53,113 @@ impl Bluetooth { global, BluetoothBinding::Wrap) } + + fn get_bluetooth_thread(&self) -> IpcSender { + let global_root = self.global(); + let global_ref = global_root.r(); + global_ref.as_window().bluetooth_thread() + } +} + +fn canonicalize_filter(filter: &BluetoothScanFilter, global: GlobalRef) -> Fallible { + if filter.services.is_none() && filter.name.is_none() && filter.namePrefix.is_none() { + return Err(Type(FILTER_ERROR.to_owned())); + } + + let mut services_vec = vec!(); + if let Some(ref services) = filter.services { + if services.is_empty() { + return Err(Type(SERVICE_ERROR.to_owned())); + } + for service in services { + services_vec.push(try!(BluetoothUUID::GetService(global, service.clone())).to_string()); + } + } + + let mut name = String::new(); + if let Some(ref filter_name) = filter.name { + //NOTE: DOMString::len() gives back the size in bytes + if filter_name.len() > MAX_DEVICE_NAME_LENGTH { + return Err(Type(NAME_TOO_LONG_ERROR.to_owned())); + } + if filter_name.len() > MAX_FILTER_NAME_LENGTH { + return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned())); + } + name = filter_name.to_string(); + } + + let mut name_prefix = String::new(); + if let Some(ref filter_name_prefix) = filter.namePrefix { + if filter_name_prefix.is_empty() { + return Err(Type(NAME_PREFIX_ERROR.to_owned())); + } + if filter_name_prefix.len() > MAX_DEVICE_NAME_LENGTH { + return Err(Type(NAME_TOO_LONG_ERROR.to_owned())); + } + if filter_name_prefix.len() > MAX_FILTER_NAME_LENGTH { + return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned())); + } + name_prefix = filter_name_prefix.to_string(); + } + + Ok(BluetoothScanfilter::new(name, name_prefix, services_vec)) +} + +fn convert_request_device_options(options: &RequestDeviceOptions, + global: GlobalRef) + -> Fallible { + if options.filters.is_empty() { + return Err(Type(FILTER_EMPTY_ERROR.to_owned())); + } + + let mut filters = vec!(); + for filter in &options.filters { + filters.push(try!(canonicalize_filter(&filter, global))); + } + + let mut optional_services = vec!(); + if let Some(ref opt_services) = options.optionalServices { + for opt_service in opt_services { + optional_services.push(try!(BluetoothUUID::GetService(global, opt_service.clone())).to_string()); + } + } + + Ok(RequestDeviceoptions::new(BluetoothScanfilterSequence::new(filters), + ServiceUUIDSequence::new(optional_services))) } impl BluetoothMethods for Bluetooth { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice - fn RequestDevice(&self) -> Option> { - //UNIMPLEMENTED - None + fn RequestDevice(&self, option: &RequestDeviceOptions) -> Fallible> { + let (sender, receiver) = ipc::channel().unwrap(); + let option = try!(convert_request_device_options(option, self.global().r())); + self.get_bluetooth_thread().send(BluetoothMethodMsg::RequestDevice(option, sender)).unwrap(); + let device = receiver.recv().unwrap(); + match device { + Ok(device) => { + let ad_data = BluetoothAdvertisingData::new(self.global().r(), + device.appearance, + device.tx_power, + device.rssi); + let vendor_id_source = device.vendor_id_source.map(|vid| match vid.as_str() { + "bluetooth" => VendorIDSource::Bluetooth, + "usb" => VendorIDSource::Usb, + _ => VendorIDSource::Unknown, + }); + Ok(BluetoothDevice::new(self.global().r(), + DOMString::from(device.id), + device.name.map(DOMString::from), + &ad_data, + device.device_class, + vendor_id_source, + device.vendor_id, + device.product_id, + device.product_version)) + }, + Err(error) => { + Err(Type(error)) + }, + } } } diff --git a/components/script/dom/bluetoothadvertisingdata.rs b/components/script/dom/bluetoothadvertisingdata.rs index 71c1346fa3d..f6a7805acbf 100644 --- a/components/script/dom/bluetoothadvertisingdata.rs +++ b/components/script/dom/bluetoothadvertisingdata.rs @@ -12,13 +12,16 @@ use dom::bindings::reflector::{Reflector, reflect_dom_object}; #[dom_struct] pub struct BluetoothAdvertisingData { reflector_: Reflector, - appearance: u16, - txPower: i8, - rssi: i8, + appearance: Option, + txPower: Option, + rssi: Option, } impl BluetoothAdvertisingData { - pub fn new_inherited(appearance: u16, txPower: i8, rssi: i8) -> BluetoothAdvertisingData { + pub fn new_inherited(appearance: Option, + txPower: Option, + rssi: Option) + -> BluetoothAdvertisingData { BluetoothAdvertisingData { reflector_: Reflector::new(), appearance: appearance, @@ -27,7 +30,11 @@ impl BluetoothAdvertisingData { } } - pub fn new(global: GlobalRef, appearance: u16, txPower: i8, rssi: i8) -> Root { + pub fn new(global: GlobalRef, + appearance: Option, + txPower: Option, + rssi: Option) + -> Root { reflect_dom_object(box BluetoothAdvertisingData::new_inherited(appearance, txPower, rssi), @@ -39,16 +46,16 @@ impl BluetoothAdvertisingData { impl BluetoothAdvertisingDataMethods for BluetoothAdvertisingData { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothadvertisingdata-appearance fn GetAppearance(&self) -> Option { - Some(self.appearance) + self.appearance } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothadvertisingdata-txpower fn GetTxPower(&self) -> Option { - Some(self.txPower) + self.txPower } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothadvertisingdata-rssi fn GetRssi(&self) -> Option { - Some(self.rssi) + self.rssi } } diff --git a/components/script/dom/bluetoothdevice.rs b/components/script/dom/bluetoothdevice.rs index a941f89fa7e..8330ca4604a 100644 --- a/components/script/dom/bluetoothdevice.rs +++ b/components/script/dom/bluetoothdevice.rs @@ -5,8 +5,8 @@ use dom::bindings::codegen::Bindings::BluetoothDeviceBinding; use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::{BluetoothDeviceMethods, VendorIDSource}; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{JS, Root, MutHeap}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::js::{JS, Root, MutHeap, MutNullableHeap}; +use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bluetoothadvertisingdata::BluetoothAdvertisingData; use dom::bluetoothremotegattserver::BluetoothRemoteGATTServer; use util::str::DOMString; @@ -16,26 +16,25 @@ use util::str::DOMString; pub struct BluetoothDevice { reflector_: Reflector, id: DOMString, - name: DOMString, + name: Option, adData: MutHeap>, - deviceClass: u32, - vendorIDSource: VendorIDSource, - vendorID: u32, - productID: u32, - productVersion: u32, - gatt: MutHeap>, + deviceClass: Option, + vendorIDSource: Option, + vendorID: Option, + productID: Option, + productVersion: Option, + gatt: MutNullableHeap>, } impl BluetoothDevice { pub fn new_inherited(id: DOMString, - name: DOMString, + name: Option, adData: &BluetoothAdvertisingData, - deviceClass: u32, - vendorIDSource: VendorIDSource, - vendorID: u32, - productID: u32, - productVersion: u32, - gatt: &BluetoothRemoteGATTServer) + deviceClass: Option, + vendorIDSource: Option, + vendorID: Option, + productID: Option, + productVersion: Option) -> BluetoothDevice { BluetoothDevice { reflector_: Reflector::new(), @@ -47,21 +46,20 @@ impl BluetoothDevice { vendorID: vendorID, productID: productID, productVersion: productVersion, - gatt: MutHeap::new(gatt), + gatt: Default::default(), } } pub fn new(global: GlobalRef, - id: DOMString, - name: DOMString, - adData: &BluetoothAdvertisingData, - deviceClass: u32, - vendorIDSource: VendorIDSource, - vendorID: u32, - productID: u32, - productVersion: u32, - gatt: &BluetoothRemoteGATTServer) - -> Root { + id: DOMString, + name: Option, + adData: &BluetoothAdvertisingData, + deviceClass: Option, + vendorIDSource: Option, + vendorID: Option, + productID: Option, + productVersion: Option) + -> Root { reflect_dom_object(box BluetoothDevice::new_inherited(id, name, adData, @@ -69,8 +67,7 @@ impl BluetoothDevice { vendorIDSource, vendorID, productID, - productVersion, - gatt), + productVersion), global, BluetoothDeviceBinding::Wrap) } @@ -85,7 +82,7 @@ impl BluetoothDeviceMethods for BluetoothDevice { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-name fn GetName(&self) -> Option { - Some(self.name.clone()) + self.name.clone() } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-addata @@ -95,31 +92,31 @@ impl BluetoothDeviceMethods for BluetoothDevice { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-deviceclass fn GetDeviceClass(&self) -> Option { - Some(self.deviceClass) + self.deviceClass } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-vendoridsource fn GetVendorIDSource(&self) -> Option { - Some(self.vendorIDSource) + self.vendorIDSource } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-vendorid fn GetVendorID(&self) -> Option { - Some(self.vendorID) + self.vendorID } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-productid fn GetProductID(&self) -> Option { - Some(self.productID) + self.productID } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-productversion fn GetProductVersion(&self) -> Option { - Some(self.productVersion) + self.productVersion } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-gatt fn Gatt(&self) -> Root { - self.gatt.get() + self.gatt.or_init(|| BluetoothRemoteGATTServer::new(self.global().r(), self)) } } diff --git a/components/script/dom/bluetoothremotegattcharacteristic.rs b/components/script/dom/bluetoothremotegattcharacteristic.rs index 4b05b347242..9ae6c6596ba 100644 --- a/components/script/dom/bluetoothremotegattcharacteristic.rs +++ b/components/script/dom/bluetoothremotegattcharacteristic.rs @@ -2,16 +2,25 @@ * 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::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding:: BluetoothRemoteGATTCharacteristicMethods; +use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; +use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; +use dom::bindings::error::Error::{Network, Type}; +use dom::bindings::error::{Fallible, ErrorResult}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, MutHeap, Root}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::str::ByteString; use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties; use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor; use dom::bluetoothremotegattservice::BluetoothRemoteGATTService; +use dom::bluetoothuuid::{BluetoothDescriptorUUID, BluetoothUUID}; +use ipc_channel::ipc::{self, IpcSender}; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use util::str::DOMString; // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattcharacteristic @@ -21,33 +30,48 @@ pub struct BluetoothRemoteGATTCharacteristic { service: MutHeap>, uuid: DOMString, properties: MutHeap>, - value: Option, + value: DOMRefCell>, + instanceID: String, } impl BluetoothRemoteGATTCharacteristic { pub fn new_inherited(service: &BluetoothRemoteGATTService, uuid: DOMString, - properties: &BluetoothCharacteristicProperties) + properties: &BluetoothCharacteristicProperties, + instanceID: String) -> BluetoothRemoteGATTCharacteristic { BluetoothRemoteGATTCharacteristic { reflector_: Reflector::new(), service: MutHeap::new(service), uuid: uuid, properties: MutHeap::new(properties), - value: None, + value: DOMRefCell::new(None), + instanceID: instanceID, } } pub fn new(global: GlobalRef, service: &BluetoothRemoteGATTService, uuid: DOMString, - properties: &BluetoothCharacteristicProperties) + properties: &BluetoothCharacteristicProperties, + instanceID: String) -> Root { reflect_dom_object(box BluetoothRemoteGATTCharacteristic::new_inherited(service, uuid, - properties), - global, - BluetoothRemoteGATTCharacteristicBinding::Wrap) + properties, + instanceID), + global, + BluetoothRemoteGATTCharacteristicBinding::Wrap) + } + + fn get_bluetooth_thread(&self) -> IpcSender { + let global_root = self.global(); + let global_ref = global_root.r(); + global_ref.as_window().bluetooth_thread() + } + + pub fn get_instance_id(&self) -> String { + self.instanceID.clone() } } @@ -69,19 +93,89 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor - fn GetDescriptor(&self) -> Option> { - //UNIMPLEMENTED - None + fn GetDescriptor(&self, descriptor: BluetoothDescriptorUUID) -> Fallible> { + let uuid = try!(BluetoothUUID::GetDescriptor(self.global().r(), descriptor)).to_string(); + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::GetDescriptor(self.get_instance_id(), uuid, sender)).unwrap(); + let descriptor = receiver.recv().unwrap(); + match descriptor { + Ok(descriptor) => { + Ok(BluetoothRemoteGATTDescriptor::new(self.global().r(), + self, + DOMString::from(descriptor.uuid), + descriptor.instance_id)) + }, + Err(error) => { + Err(Type(error)) + }, + } + } + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptors + fn GetDescriptors(&self, + descriptor: Option) + -> Fallible>> { + let mut uuid: Option = None; + if let Some(d) = descriptor { + uuid = Some(try!(BluetoothUUID::GetDescriptor(self.global().r(), d)).to_string()) + }; + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::GetDescriptors(self.get_instance_id(), uuid, sender)).unwrap(); + let descriptors_vec = receiver.recv().unwrap(); + match descriptors_vec { + Ok(descriptor_vec) => { + Ok(descriptor_vec.into_iter() + .map(|desc| BluetoothRemoteGATTDescriptor::new(self.global().r(), + self, + DOMString::from(desc.uuid), + desc.instance_id)) + .collect()) + }, + Err(error) => { + Err(Type(error)) + }, + } } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-value fn GetValue(&self) -> Option { - self.value.clone() + self.value.borrow().clone() } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue - fn ReadValue(&self) -> ByteString { - //UNIMPLEMENTED - ByteString::new(vec!()) + fn ReadValue(&self) -> Fallible { + let (sender, receiver) = ipc::channel().unwrap(); + if !self.Service().Device().Gatt().Connected() { + return Err(Network) + } + self.get_bluetooth_thread().send( + BluetoothMethodMsg::ReadValue(self.get_instance_id(), sender)).unwrap(); + let result = receiver.recv().unwrap(); + let value = match result { + Ok(val) => { + ByteString::new(val) + }, + Err(error) => { + return Err(Type(error)) + }, + }; + *self.value.borrow_mut() = Some(value.clone()); + Ok(value) + } + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue + fn WriteValue(&self, value: Vec) -> ErrorResult { + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::WriteValue(self.get_instance_id(), value, sender)).unwrap(); + let result = receiver.recv().unwrap(); + match result { + Ok(_) => Ok(()), + Err(error) => { + Err(Type(error)) + }, + } } } diff --git a/components/script/dom/bluetoothremotegattdescriptor.rs b/components/script/dom/bluetoothremotegattdescriptor.rs index 5812a2ea832..8e5a106cd05 100644 --- a/components/script/dom/bluetoothremotegattdescriptor.rs +++ b/components/script/dom/bluetoothremotegattdescriptor.rs @@ -2,13 +2,23 @@ * 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::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods; +use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding:: + BluetoothRemoteGATTCharacteristicMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods; +use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; +use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; +use dom::bindings::error::Error::{Type, Network}; +use dom::bindings::error::{Fallible, ErrorResult}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, MutHeap, Root}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::str::ByteString; use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic; +use ipc_channel::ipc::{self, IpcSender}; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use util::str::DOMString; // http://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattdescriptor @@ -17,30 +27,45 @@ pub struct BluetoothRemoteGATTDescriptor { reflector_: Reflector, characteristic: MutHeap>, uuid: DOMString, - value: Option, + value: DOMRefCell>, + instanceID: String, } impl BluetoothRemoteGATTDescriptor { pub fn new_inherited(characteristic: &BluetoothRemoteGATTCharacteristic, - uuid: DOMString) + uuid: DOMString, + instanceID: String) -> BluetoothRemoteGATTDescriptor { BluetoothRemoteGATTDescriptor { reflector_: Reflector::new(), characteristic: MutHeap::new(characteristic), uuid: uuid, - value: None, + value: DOMRefCell::new(None), + instanceID: instanceID, } } pub fn new(global: GlobalRef, characteristic: &BluetoothRemoteGATTCharacteristic, - uuid: DOMString) + uuid: DOMString, + instanceID: String) -> Root{ reflect_dom_object(box BluetoothRemoteGATTDescriptor::new_inherited(characteristic, - uuid), + uuid, + instanceID), global, BluetoothRemoteGATTDescriptorBinding::Wrap) } + + fn get_bluetooth_thread(&self) -> IpcSender { + let global_root = self.global(); + let global_ref = global_root.r(); + global_ref.as_window().bluetooth_thread() + } + + pub fn get_instance_id(&self) -> String { + self.instanceID.clone() + } } impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor { @@ -57,12 +82,41 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-value fn GetValue(&self) -> Option { - self.value.clone() + self.value.borrow().clone() } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue - fn ReadValue(&self) -> ByteString { - //UNIMPLEMENTED - ByteString::new(vec!()) + fn ReadValue(&self) -> Fallible { + let (sender, receiver) = ipc::channel().unwrap(); + if !self.Characteristic().Service().Device().Gatt().Connected() { + return Err(Network) + } + self.get_bluetooth_thread().send( + BluetoothMethodMsg::ReadValue(self.get_instance_id(), sender)).unwrap(); + let result = receiver.recv().unwrap(); + let value = match result { + Ok(val) => { + ByteString::new(val) + }, + Err(error) => { + return Err(Type(error)) + }, + }; + *self.value.borrow_mut() = Some(value.clone()); + Ok(value) + } + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue + fn WriteValue(&self, value: Vec) -> ErrorResult { + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::WriteValue(self.get_instance_id(), value, sender)).unwrap(); + let result = receiver.recv().unwrap(); + match result { + Ok(_) => Ok(()), + Err(error) => { + Err(Type(error)) + }, + } } } diff --git a/components/script/dom/bluetoothremotegattserver.rs b/components/script/dom/bluetoothremotegattserver.rs index 7441344fe2f..e4f4b20a9dc 100644 --- a/components/script/dom/bluetoothremotegattserver.rs +++ b/components/script/dom/bluetoothremotegattserver.rs @@ -2,14 +2,21 @@ * 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::BluetoothDeviceBinding::BluetoothDeviceMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; +use dom::bindings::error::Error::Type; +use dom::bindings::error::{Fallible, ErrorResult}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, MutHeap, Root}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bluetoothdevice::BluetoothDevice; use dom::bluetoothremotegattservice::BluetoothRemoteGATTService; +use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID}; +use ipc_channel::ipc::{self, IpcSender}; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use std::cell::Cell; +use util::str::DOMString; // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattserver #[dom_struct] @@ -20,21 +27,25 @@ pub struct BluetoothRemoteGATTServer { } impl BluetoothRemoteGATTServer { - pub fn new_inherited(device: &BluetoothDevice, is_connected: bool) -> BluetoothRemoteGATTServer { + pub fn new_inherited(device: &BluetoothDevice) -> BluetoothRemoteGATTServer { BluetoothRemoteGATTServer { reflector_: Reflector::new(), device: MutHeap::new(device), - connected: Cell::new(is_connected), + connected: Cell::new(false), } } - pub fn new(global: GlobalRef, device: &BluetoothDevice, connected: bool) -> Root { - reflect_dom_object(box BluetoothRemoteGATTServer::new_inherited( - device, - connected), + pub fn new(global: GlobalRef, device: &BluetoothDevice) -> Root { + reflect_dom_object(box BluetoothRemoteGATTServer::new_inherited(device), global, BluetoothRemoteGATTServerBinding::Wrap) } + + fn get_bluetooth_thread(&self) -> IpcSender { + let global_root = self.global(); + let global_ref = global_root.r(); + global_ref.as_window().bluetooth_thread() + } } impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { @@ -50,21 +61,85 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect - fn Connect(&self) -> Root { - if !self.connected.get() { - self.connected.set(true); + fn Connect(&self) -> Fallible> { + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::GATTServerConnect(String::from(self.Device().Id()), sender)).unwrap(); + let server = receiver.recv().unwrap(); + match server { + Ok(connected) => { + self.connected.set(connected); + Ok(Root::from_ref(self)) + }, + Err(error) => { + Err(Type(error)) + }, } - Root::from_ref(self) } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect - fn Disconnect(&self) { - self.connected.set(false); + fn Disconnect(&self) -> ErrorResult { + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::GATTServerDisconnect(String::from(self.Device().Id()), sender)).unwrap(); + let server = receiver.recv().unwrap(); + match server { + Ok(connected) => { + self.connected.set(connected); + Ok(()) + }, + Err(error) => { + Err(Type(error)) + }, + } } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice - fn GetPrimaryService(&self) -> Option> { - //UNIMPLEMENTED - None + fn GetPrimaryService(&self, service: BluetoothServiceUUID) -> Fallible> { + let uuid = try!(BluetoothUUID::GetService(self.global().r(), service)).to_string(); + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::GetPrimaryService(String::from(self.Device().Id()), uuid, sender)).unwrap(); + let service = receiver.recv().unwrap(); + match service { + Ok(service) => { + Ok(BluetoothRemoteGATTService::new(self.global().r(), + &self.device.get(), + DOMString::from(service.uuid), + service.is_primary, + service.instance_id)) + }, + Err(error) => { + Err(Type(error)) + }, + } + } + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices + fn GetPrimaryServices(&self, + service: Option) + -> Fallible>> { + let mut uuid: Option = None; + if let Some(s) = service { + uuid = Some(try!(BluetoothUUID::GetService(self.global().r(), s)).to_string()) + }; + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::GetPrimaryServices(String::from(self.Device().Id()), uuid, sender)).unwrap(); + let services_vec = receiver.recv().unwrap(); + match services_vec { + Ok(service_vec) => { + Ok(service_vec.into_iter() + .map(|service| BluetoothRemoteGATTService::new(self.global().r(), + &self.device.get(), + DOMString::from(service.uuid), + service.is_primary, + service.instance_id)) + .collect()) + }, + Err(error) => { + Err(Type(error)) + }, + } } } diff --git a/components/script/dom/bluetoothremotegattservice.rs b/components/script/dom/bluetoothremotegattservice.rs index af2cd70ce7c..92a5edc2f28 100644 --- a/components/script/dom/bluetoothremotegattservice.rs +++ b/components/script/dom/bluetoothremotegattservice.rs @@ -4,11 +4,17 @@ use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; +use dom::bindings::error::Error::Type; +use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, MutHeap, Root}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties; use dom::bluetoothdevice::BluetoothDevice; use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic; +use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothUUID}; +use ipc_channel::ipc::{self, IpcSender}; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use util::str::DOMString; // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice @@ -18,32 +24,47 @@ pub struct BluetoothRemoteGATTService { device: MutHeap>, uuid: DOMString, isPrimary: bool, + instanceID: String, } impl BluetoothRemoteGATTService { pub fn new_inherited(device: &BluetoothDevice, uuid: DOMString, - isPrimary: bool) + isPrimary: bool, + instanceID: String) -> BluetoothRemoteGATTService { BluetoothRemoteGATTService { reflector_: Reflector::new(), device: MutHeap::new(device), uuid: uuid, isPrimary: isPrimary, + instanceID: instanceID, } } pub fn new(global: GlobalRef, device: &BluetoothDevice, uuid: DOMString, - isPrimary: bool) + isPrimary: bool, + instanceID: String) -> Root { reflect_dom_object(box BluetoothRemoteGATTService::new_inherited(device, uuid, - isPrimary), + isPrimary, + instanceID), global, BluetoothRemoteGATTServiceBinding::Wrap) } + + fn get_bluetooth_thread(&self) -> IpcSender { + let global_root = self.global(); + let global_ref = global_root.r(); + global_ref.as_window().bluetooth_thread() + } + + pub fn get_instance_id(&self) -> String { + self.instanceID.clone() + } } impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { @@ -63,8 +84,75 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic - fn GetCharacteristic(&self) -> Option> { - // UNIMPLEMENTED - None + fn GetCharacteristic(&self, + characteristic: BluetoothCharacteristicUUID) + -> Fallible> { + let uuid = try!(BluetoothUUID::GetCharacteristic(self.global().r(), characteristic)).to_string(); + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap(); + let characteristic = receiver.recv().unwrap(); + match characteristic { + Ok(characteristic) => { + let properties = BluetoothCharacteristicProperties::new(self.global().r(), + characteristic.broadcast, + characteristic.read, + characteristic.write_without_response, + characteristic.write, + characteristic.notify, + characteristic.indicate, + characteristic.authenticated_signed_writes, + characteristic.reliable_write, + characteristic.writable_auxiliaries); + Ok(BluetoothRemoteGATTCharacteristic::new(self.global().r(), + self, + DOMString::from(characteristic.uuid), + &properties, + characteristic.instance_id)) + }, + Err(error) => { + Err(Type(error)) + }, + } + } + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics + fn GetCharacteristics(&self, + characteristic: Option) + -> Fallible>> { + let mut uuid: Option = None; + if let Some(c) = characteristic { + uuid = Some(try!(BluetoothUUID::GetCharacteristic(self.global().r(), c)).to_string()) + }; + let mut characteristics = vec!(); + let (sender, receiver) = ipc::channel().unwrap(); + self.get_bluetooth_thread().send( + BluetoothMethodMsg::GetCharacteristics(self.get_instance_id(), uuid, sender)).unwrap(); + let characteristics_vec = receiver.recv().unwrap(); + match characteristics_vec { + Ok(characteristic_vec) => { + for characteristic in characteristic_vec { + let properties = BluetoothCharacteristicProperties::new(self.global().r(), + characteristic.broadcast, + characteristic.read, + characteristic.write_without_response, + characteristic.write, + characteristic.notify, + characteristic.indicate, + characteristic.authenticated_signed_writes, + characteristic.reliable_write, + characteristic.writable_auxiliaries); + characteristics.push(BluetoothRemoteGATTCharacteristic::new(self.global().r(), + self, + DOMString::from(characteristic.uuid), + &properties, + characteristic.instance_id)); + } + Ok(characteristics) + }, + Err(error) => { + Err(Type(error)) + }, + } } } diff --git a/components/script/dom/bluetoothuuid.rs b/components/script/dom/bluetoothuuid.rs index 33aa6d944d5..d4ed2b5e6a7 100644 --- a/components/script/dom/bluetoothuuid.rs +++ b/components/script/dom/bluetoothuuid.rs @@ -11,6 +11,9 @@ use regex::Regex; use util::str::DOMString; pub type UUID = DOMString; +pub type BluetoothServiceUUID = StringOrUnsignedLong; +pub type BluetoothCharacteristicUUID = StringOrUnsignedLong; +pub type BluetoothDescriptorUUID = StringOrUnsignedLong; // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothuuid #[dom_struct] @@ -18,27 +21,246 @@ pub struct BluetoothUUID { reflector_: Reflector, } -const BLUETOOTH_ASSIGNED_SERVICES: &'static [(&'static str, u32)] = &[ -//TODO(zakorgy) create all the services //https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx +const BLUETOOTH_ASSIGNED_SERVICES: &'static [(&'static str, u32)] = &[ ("org.bluetooth.service.alert_notification", 0x1811_u32), ("org.bluetooth.service.automation_io", 0x1815_u32), - ("org.bluetooth.service.battery_service", 0x180f_u32) + ("org.bluetooth.service.battery_service", 0x180f_u32), + ("org.bluetooth.service.blood_pressure", 0x1810_u32), + ("org.bluetooth.service.body_composition", 0x181b_u32), + ("org.bluetooth.service.bond_management", 0x181e_u32), + ("org.bluetooth.service.continuous_glucose_monitoring", 0x181f_u32), + ("org.bluetooth.service.current_time", 0x1805_u32), + ("org.bluetooth.service.cycling_power", 0x1818_u32), + ("org.bluetooth.service.cycling_speed_and_cadence", 0x1816_u32), + ("org.bluetooth.service.device_information", 0x180a_u32), + ("org.bluetooth.service.environmental_sensing", 0x181a_u32), + ("org.bluetooth.service.generic_access", 0x1800_u32), + ("org.bluetooth.service.generic_attribute", 0x1801_u32), + ("org.bluetooth.service.glucose", 0x1808_u32), + ("org.bluetooth.service.health_thermometer", 0x1809_u32), + ("org.bluetooth.service.heart_rate", 0x180d_u32), + ("org.bluetooth.service.http_proxy", 0x1823_u32), + ("org.bluetooth.service.human_interface_device", 0x1812_u32), + ("org.bluetooth.service.immediate_alert", 0x1802_u32), + ("org.bluetooth.service.indoor_positioning", 0x1821_u32), + ("org.bluetooth.service.internet_protocol_support", 0x1820_u32), + ("org.bluetooth.service.link_loss", 0x1803_u32), + ("org.bluetooth.service.location_and_navigation", 0x1819_u32), + ("org.bluetooth.service.next_dst_change", 0x1807_u32), + ("org.bluetooth.service.object_transfer", 0x1825_u32), + ("org.bluetooth.service.phone_alert_status", 0x180e_u32), + ("org.bluetooth.service.pulse_oximeter", 0x1822_u32), + ("org.bluetooth.service.reference_time_update", 0x1806_u32), + ("org.bluetooth.service.running_speed_and_cadence", 0x1814_u32), + ("org.bluetooth.service.scan_parameters", 0x1813_u32), + ("org.bluetooth.service.transport_discovery", 0x1824), + ("org.bluetooth.service.tx_power", 0x1804_u32), + ("org.bluetooth.service.user_data", 0x181c_u32), + ("org.bluetooth.service.weight_scale", 0x181d_u32), ]; -const BLUETOOTH_ASSIGNED_CHARCTERISTICS: &'static [(&'static str, u32)] = &[ -//TODO(zakorgy) create all the characteristics //https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx +const BLUETOOTH_ASSIGNED_CHARCTERISTICS: &'static [(&'static str, u32)] = &[ ("org.bluetooth.characteristic.aerobic_heart_rate_lower_limit", 0x2a7e_u32), ("org.bluetooth.characteristic.aerobic_heart_rate_upper_limit", 0x2a84_u32), - ("org.bluetooth.characteristic.battery_level", 0x2a19_u32) + ("org.bluetooth.characteristic.aerobic_threshold", 0x2a7f_u32), + ("org.bluetooth.characteristic.age", 0x2a80_u32), + ("org.bluetooth.characteristic.aggregate", 0x2a5a_u32), + ("org.bluetooth.characteristic.alert_category_id", 0x2a43_u32), + ("org.bluetooth.characteristic.alert_category_id_bit_mask", 0x2a42_u32), + ("org.bluetooth.characteristic.alert_level", 0x2a06_u32), + ("org.bluetooth.characteristic.alert_notification_control_point", 0x2a44_u32), + ("org.bluetooth.characteristic.alert_status", 0x2a3f_u32), + ("org.bluetooth.characteristic.altitude", 0x2ab3_u32), + ("org.bluetooth.characteristic.anaerobic_heart_rate_lower_limit", 0x2a81_u32), + ("org.bluetooth.characteristic.anaerobic_heart_rate_upper_limit", 0x2a82_u32), + ("org.bluetooth.characteristic.anaerobic_threshold", 0x2a83_u32), + ("org.bluetooth.characteristic.analog", 0x2a58_u32), + ("org.bluetooth.characteristic.apparent_wind_direction", 0x2a73_u32), + ("org.bluetooth.characteristic.apparent_wind_speed", 0x2a72_u32), + ("org.bluetooth.characteristic.gap.appearance", 0x2a01_u32), + ("org.bluetooth.characteristic.barometric_pressure_trend", 0x2aa3_u32), + ("org.bluetooth.characteristic.battery_level", 0x2a19_u32), + ("org.bluetooth.characteristic.blood_pressure_feature", 0x2a49_u32), + ("org.bluetooth.characteristic.blood_pressure_measurement", 0x2a35_u32), + ("org.bluetooth.characteristic.body_composition_feature", 0x2a9b_u32), + ("org.bluetooth.characteristic.body_composition_measurement", 0x2a9c_u32), + ("org.bluetooth.characteristic.body_sensor_location", 0x2a38_u32), + ("org.bluetooth.characteristic.bond_management_control_point", 0x2aa4_u32), + ("org.bluetooth.characteristic.bond_management_feature", 0x2aa5_u32), + ("org.bluetooth.characteristic.boot_keyboard_input_report", 0x2a22_u32), + ("org.bluetooth.characteristic.boot_keyboard_output_report", 0x2a32_u32), + ("org.bluetooth.characteristic.boot_mouse_input_report", 0x2a33_u32), + ("org.bluetooth.characteristic.gap.central_address_resolution_support", 0x2aa6_u32), + ("org.bluetooth.characteristic.cgm_feature", 0x2aa8_u32), + ("org.bluetooth.characteristic.cgm_measurement", 0x2aa7_u32), + ("org.bluetooth.characteristic.cgm_session_run_time", 0x2aab_u32), + ("org.bluetooth.characteristic.cgm_session_start_time", 0x2aaa_u32), + ("org.bluetooth.characteristic.cgm_specific_ops_control_point", 0x2aac_u32), + ("org.bluetooth.characteristic.cgm_status", 0x2aa9_u32), + ("org.bluetooth.characteristic.csc_feature", 0x2a5c_u32), + ("org.bluetooth.characteristic.csc_measurement", 0x2a5b_u32), + ("org.bluetooth.characteristic.current_time", 0x2a2b_u32), + ("org.bluetooth.characteristic.cycling_power_control_point", 0x2a66_u32), + ("org.bluetooth.characteristic.cycling_power_feature", 0x2a65_u32), + ("org.bluetooth.characteristic.cycling_power_measurement", 0x2a63_u32), + ("org.bluetooth.characteristic.cycling_power_vector", 0x2a64_u32), + ("org.bluetooth.characteristic.database_change_increment", 0x2a99_u32), + ("org.bluetooth.characteristic.date_of_birth", 0x2a85_u32), + ("org.bluetooth.characteristic.date_of_threshold_assessment", 0x2a86_u32), + ("org.bluetooth.characteristic.date_time", 0x2a08_u32), + ("org.bluetooth.characteristic.day_date_time", 0x2a0a_u32), + ("org.bluetooth.characteristic.day_of_week", 0x2a09_u32), + ("org.bluetooth.characteristic.descriptor_value_changed", 0x2a7d_u32), + ("org.bluetooth.characteristic.gap.device_name", 0x2a00_u32), + ("org.bluetooth.characteristic.dew_point", 0x2a7b_u32), + ("org.bluetooth.characteristic.digital", 0x2a56_u32), + ("org.bluetooth.characteristic.dst_offset", 0x2a0d_u32), + ("org.bluetooth.characteristic.elevation", 0x2a6c_u32), + ("org.bluetooth.characteristic.email_address", 0x2a87_u32), + ("org.bluetooth.characteristic.exact_time_256", 0x2a0c_u32), + ("org.bluetooth.characteristic.fat_burn_heart_rate_lower_limit", 0x2a88_u32), + ("org.bluetooth.characteristic.fat_burn_heart_rate_upper_limit", 0x2a89_u32), + ("org.bluetooth.characteristic.firmware_revision_string", 0x2a26_u32), + ("org.bluetooth.characteristic.first_name", 0x2a8a_u32), + ("org.bluetooth.characteristic.five_zone_heart_rate_limits", 0x2a8b_u32), + ("org.bluetooth.characteristic.floor_number", 0x2ab2_u32), + ("org.bluetooth.characteristic.gender", 0x2a8c_u32), + ("org.bluetooth.characteristic.glucose_feature", 0x2a51_u32), + ("org.bluetooth.characteristic.glucose_measurement", 0x2a18_u32), + ("org.bluetooth.characteristic.glucose_measurement_context", 0x2a34_u32), + ("org.bluetooth.characteristic.gust_factor", 0x2a74_u32), + ("org.bluetooth.characteristic.hardware_revision_string", 0x2a27_u32), + ("org.bluetooth.characteristic.heart_rate_control_point", 0x2a39_u32), + ("org.bluetooth.characteristic.heart_rate_max", 0x2a8d_u32), + ("org.bluetooth.characteristic.heart_rate_measurement", 0x2a37_u32), + ("org.bluetooth.characteristic.heat_index", 0x2a7a_u32), + ("org.bluetooth.characteristic.height", 0x2a8e_u32), + ("org.bluetooth.characteristic.hid_control_point", 0x2a4c_u32), + ("org.bluetooth.characteristic.hid_information", 0x2a4a_u32), + ("org.bluetooth.characteristic.hip_circumference", 0x2a8f_u32), + ("org.bluetooth.characteristic.http_control_point", 0x2aba_u32), + ("org.bluetooth.characteristic.http_entity_body", 0x2ab9_u32), + ("org.bluetooth.characteristic.http_headers", 0x2ab7_u32), + ("org.bluetooth.characteristic.http_status_code", 0x2ab8_u32), + ("org.bluetooth.characteristic.https_security", 0x2abb_u32), + ("org.bluetooth.characteristic.humidity", 0x2a6f_u32), + ("org.bluetooth.characteristic.ieee_11073-20601_regulatory_certification_data_list", 0x2a2a_u32), + ("org.bluetooth.characteristic.indoor_positioning_configuration", 0x2aad_u32), + ("org.bluetooth.characteristic.intermediate_cuff_pressure", 0x2a36_u32), + ("org.bluetooth.characteristic.intermediate_temperature", 0x2a1e_u32), + ("org.bluetooth.characteristic.irradiance", 0x2a77_u32), + ("org.bluetooth.characteristic.language", 0x2aa2_u32), + ("org.bluetooth.characteristic.last_name", 0x2a90_u32), + ("org.bluetooth.characteristic.latitude", 0x2aae_u32), + ("org.bluetooth.characteristic.ln_control_point", 0x2a6b_u32), + ("org.bluetooth.characteristic.ln_feature", 0x2a6a_u32), + ("org.bluetooth.characteristic.local_east_coordinate.xml", 0x2ab1_u32), + ("org.bluetooth.characteristic.local_north_coordinate", 0x2ab0_u32), + ("org.bluetooth.characteristic.local_time_information", 0x2a0f_u32), + ("org.bluetooth.characteristic.location_and_speed", 0x2a67_u32), + ("org.bluetooth.characteristic.location_name", 0x2ab5_u32), + ("org.bluetooth.characteristic.longitude", 0x2aaf_u32), + ("org.bluetooth.characteristic.magnetic_declination", 0x2a2c_u32), + ("org.bluetooth.characteristic.magnetic_flux_density_2d", 0x2aa0_u32), + ("org.bluetooth.characteristic.magnetic_flux_density_3d", 0x2aa1_u32), + ("org.bluetooth.characteristic.manufacturer_name_string", 0x2a29_u32), + ("org.bluetooth.characteristic.maximum_recommended_heart_rate", 0x2a91_u32), + ("org.bluetooth.characteristic.measurement_interval", 0x2a21_u32), + ("org.bluetooth.characteristic.model_number_string", 0x2a24_u32), + ("org.bluetooth.characteristic.navigation", 0x2a68_u32), + ("org.bluetooth.characteristic.new_alert", 0x2a46_u32), + ("org.bluetooth.characteristic.object_action_control_point", 0x2ac5_u32), + ("org.bluetooth.characteristic.object_changed", 0x2ac8_u32), + ("org.bluetooth.characteristic.object_first_created", 0x2ac1_u32), + ("org.bluetooth.characteristic.object_id", 0x2ac3_u32), + ("org.bluetooth.characteristic.object_last_modified", 0x2ac2_u32), + ("org.bluetooth.characteristic.object_list_control_point", 0x2ac6_u32), + ("org.bluetooth.characteristic.object_list_filter", 0x2ac7_u32), + ("org.bluetooth.characteristic.object_name", 0x2abe_u32), + ("org.bluetooth.characteristic.object_properties", 0x2ac4_u32), + ("org.bluetooth.characteristic.object_size", 0x2ac0_u32), + ("org.bluetooth.characteristic.object_type", 0x2abf_u32), + ("org.bluetooth.characteristic.ots_feature", 0x2abd_u32), + ("org.bluetooth.characteristic.gap.peripheral_preferred_connection_parameters", 0x2a04_u32), + ("org.bluetooth.characteristic.gap.peripheral_privacy_flag", 0x2a02_u32), + ("org.bluetooth.characteristic.plx_continuous_measurement", 0x2a5f_u32), + ("org.bluetooth.characteristic.plx_features", 0x2a60_u32), + ("org.bluetooth.characteristic.plx_spot_check_measurement", 0x2a5e_u32), + ("org.bluetooth.characteristic.pnp_id", 0x2a50_u32), + ("org.bluetooth.characteristic.pollen_concentration", 0x2a75_u32), + ("org.bluetooth.characteristic.position_quality", 0x2a69_u32), + ("org.bluetooth.characteristic.pressure", 0x2a6d_u32), + ("org.bluetooth.characteristic.protocol_mode", 0x2a4e_u32), + ("org.bluetooth.characteristic.rainfall", 0x2a78_u32), + ("org.bluetooth.characteristic.gap.reconnection_address", 0x2a03_u32), + ("org.bluetooth.characteristic.record_access_control_point", 0x2a52_u32), + ("org.bluetooth.characteristic.reference_time_information", 0x2a14_u32), + ("org.bluetooth.characteristic.report", 0x2a4d_u32), + ("org.bluetooth.characteristic.report_map", 0x2a4b_u32), + ("org.bluetooth.characteristic.resting_heart_rate", 0x2a92_u32), + ("org.bluetooth.characteristic.ringer_control_point", 0x2a40_u32), + ("org.bluetooth.characteristic.ringer_setting", 0x2a41_u32), + ("org.bluetooth.characteristic.rsc_feature", 0x2a54_u32), + ("org.bluetooth.characteristic.rsc_measurement", 0x2a53_u32), + ("org.bluetooth.characteristic.sc_control_point", 0x2a55_u32), + ("org.bluetooth.characteristic.scan_interval_window", 0x2a4f_u32), + ("org.bluetooth.characteristic.scan_refresh", 0x2a31_u32), + ("org.bluetooth.characteristic.sensor_location", 0x2a5d_u32), + ("org.bluetooth.characteristic.serial_number_string", 0x2a25_u32), + ("org.bluetooth.characteristic.gatt.service_changed", 0x2a05_u32), + ("org.bluetooth.characteristic.software_revision_string", 0x2a28_u32), + ("org.bluetooth.characteristic.sport_type_for_aerobic_and_anaerobic_thresholds", 0x2a93_u32), + ("org.bluetooth.characteristic.supported_new_alert_category", 0x2a47_u32), + ("org.bluetooth.characteristic.supported_unread_alert_category", 0x2a48_u32), + ("org.bluetooth.characteristic.system_id", 0x2a23_u32), + ("org.bluetooth.characteristic.tds_control_point", 0x2abc_u32), + ("org.bluetooth.characteristic.temperature", 0x2a6e_u32), + ("org.bluetooth.characteristic.temperature_measurement", 0x2a1c_u32), + ("org.bluetooth.characteristic.temperature_type", 0x2a1d_u32), + ("org.bluetooth.characteristic.three_zone_heart_rate_limits", 0x2a94_u32), + ("org.bluetooth.characteristic.time_accuracy", 0x2a12_u32), + ("org.bluetooth.characteristic.time_source", 0x2a13_u32), + ("org.bluetooth.characteristic.time_update_control_point", 0x2a16_u32), + ("org.bluetooth.characteristic.time_update_state", 0x2a17_u32), + ("org.bluetooth.characteristic.time_with_dst", 0x2a11_u32), + ("org.bluetooth.characteristic.time_zone", 0x2a0e_u32), + ("org.bluetooth.characteristic.true_wind_direction", 0x2a71_u32), + ("org.bluetooth.characteristic.true_wind_speed", 0x2a70_u32), + ("org.bluetooth.characteristic.two_zone_heart_rate_limit", 0x2a95_u32), + ("org.bluetooth.characteristic.tx_power_level", 0x2a07_u32), + ("org.bluetooth.characteristic.uncertainty", 0x2ab4_u32), + ("org.bluetooth.characteristic.unread_alert_status", 0x2a45_u32), + ("org.bluetooth.characteristic.uri", 0x2ab6_u32), + ("org.bluetooth.characteristic.user_control_point", 0x2a9f_u32), + ("org.bluetooth.characteristic.user_index", 0x2a9a_u32), + ("org.bluetooth.characteristic.uv_index", 0x2a76_u32), + ("org.bluetooth.characteristic.vo2_max", 0x2a96_u32), + ("org.bluetooth.characteristic.waist_circumference", 0x2a97_u32), + ("org.bluetooth.characteristic.weight", 0x2a98_u32), + ("org.bluetooth.characteristic.weight_measurement", 0x2a9d_u32), + ("org.bluetooth.characteristic.weight_scale_feature", 0x2a9e_u32), + ("org.bluetooth.characteristic.wind_chill", 0x2a79_u32), ]; -const BLUETOOTH_ASSIGNED_DESCRIPTORS: &'static [(&'static str, u32)] = &[ -//TODO(zakorgy) create all the descriptors //https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx +const BLUETOOTH_ASSIGNED_DESCRIPTORS: &'static [(&'static str, u32)] = &[ ("org.bluetooth.descriptor.gatt.characteristic_extended_properties", 0x2900_u32), - ("org.bluetooth.descriptor.gatt.characteristic_user_description", 0x2901_u32) + ("org.bluetooth.descriptor.gatt.characteristic_user_description", 0x2901_u32), + ("org.bluetooth.descriptor.gatt.client_characteristic_configuration", 0x2902_u32), + ("org.bluetooth.descriptor.gatt.server_characteristic_configuration", 0x2903_u32), + ("org.bluetooth.descriptor.gatt.characteristic_presentation_format", 0x2904_u32), + ("org.bluetooth.descriptor.gatt.characteristic_aggregate_format", 0x2905_u32), + ("org.bluetooth.descriptor.valid_range", 0x2906_u32), + ("org.bluetooth.descriptor.external_report_reference", 0x2907_u32), + ("org.bluetooth.descriptor.report_reference", 0x2908_u32), + ("org.bluetooth.descriptor.number_of_digitals", 0x2909_u32), + ("org.bluetooth.descriptor.value_trigger_setting", 0x290a_u32), + ("org.bluetooth.descriptor.es_configuration", 0x290b_u32), + ("org.bluetooth.descriptor.es_measurement", 0x290c_u32), + ("org.bluetooth.descriptor.es_trigger_setting", 0x290d_u32), + ("org.bluetooth.descriptor.time_trigger_setting", 0x290e_u32), ]; const BASE_UUID: &'static str = "-0000-1000-8000-00805f9b34fb"; @@ -55,9 +277,7 @@ impl BluetoothUUID { } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-getservice - pub fn GetService(globalref: GlobalRef, - name: StringOrUnsignedLong) - -> Fallible { + pub fn GetService(globalref: GlobalRef, name: BluetoothServiceUUID) -> Fallible { BluetoothUUID::resolve_uuid_name(globalref, name, BLUETOOTH_ASSIGNED_SERVICES, @@ -65,9 +285,7 @@ impl BluetoothUUID { } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-getcharacteristic - pub fn GetCharacteristic(globalref: GlobalRef, - name: StringOrUnsignedLong) - -> Fallible { + pub fn GetCharacteristic(globalref: GlobalRef, name: BluetoothCharacteristicUUID) -> Fallible { BluetoothUUID::resolve_uuid_name(globalref, name, BLUETOOTH_ASSIGNED_CHARCTERISTICS, @@ -75,9 +293,7 @@ impl BluetoothUUID { } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-getdescriptor - pub fn GetDescriptor(globalref: GlobalRef, - name: StringOrUnsignedLong) - -> Fallible { + pub fn GetDescriptor(globalref: GlobalRef, name: BluetoothDescriptorUUID) -> Fallible { BluetoothUUID::resolve_uuid_name(globalref, name, BLUETOOTH_ASSIGNED_DESCRIPTORS, @@ -92,7 +308,7 @@ impl BluetoothUUID { -> Fallible { match name { // Step 1 - StringOrUnsignedLong::UnsignedLong(unsigned32) =>{ + StringOrUnsignedLong::UnsignedLong(unsigned32) => { Ok(BluetoothUUID::CanonicalUUID(globalref, unsigned32)) }, StringOrUnsignedLong::String(dstring) => { @@ -103,8 +319,7 @@ impl BluetoothUUID { } else { // Step 3 let concatenated = format!("{}.{}", prefix, dstring); - let is_in_table = assigned_numbers_table.iter() - .find(|p| p.0 == concatenated); + let is_in_table = assigned_numbers_table.iter().find(|p| p.0 == concatenated); match is_in_table { Some(&(_, alias)) => Ok(BluetoothUUID::CanonicalUUID(globalref, alias)), None => Err(Syntax), @@ -114,3 +329,12 @@ impl BluetoothUUID { } } } + +impl Clone for StringOrUnsignedLong { + fn clone(&self) -> StringOrUnsignedLong { + match self { + &StringOrUnsignedLong::String(ref s) => StringOrUnsignedLong::String(s.clone()), + &StringOrUnsignedLong::UnsignedLong(ul) => StringOrUnsignedLong::UnsignedLong(ul), + } + } +} diff --git a/components/script/dom/webidls/Bluetooth.webidl b/components/script/dom/webidls/Bluetooth.webidl index 4199e0bc486..0d6c381e65f 100644 --- a/components/script/dom/webidls/Bluetooth.webidl +++ b/components/script/dom/webidls/Bluetooth.webidl @@ -4,10 +4,22 @@ // https://webbluetoothcg.github.io/web-bluetooth/#bluetooth +dictionary BluetoothScanFilter { + sequence services; + DOMString name; + DOMString namePrefix; +}; + +dictionary RequestDeviceOptions { + required sequence filters; + sequence optionalServices /*= []*/; +}; + [Pref="dom.bluetooth.enabled"] interface Bluetooth { // Promise requestDevice(RequestDeviceOptions options); - BluetoothDevice? requestDevice(/*RequestDeviceOptions options*/); + [Throws] + BluetoothDevice requestDevice(RequestDeviceOptions options); }; // Bluetooth implements EventTarget; diff --git a/components/script/dom/webidls/BluetoothDevice.webidl b/components/script/dom/webidls/BluetoothDevice.webidl index 3a32dc38179..e8851e6240c 100644 --- a/components/script/dom/webidls/BluetoothDevice.webidl +++ b/components/script/dom/webidls/BluetoothDevice.webidl @@ -7,7 +7,8 @@ // Allocation authorities for Vendor IDs: enum VendorIDSource { "bluetooth", - "usb" + "usb", + "unknown" }; [Pref="dom.bluetooth.enabled"] diff --git a/components/script/dom/webidls/BluetoothRemoteGATTCharacteristic.webidl b/components/script/dom/webidls/BluetoothRemoteGATTCharacteristic.webidl index e6ba9c89d2a..2f721203ee1 100644 --- a/components/script/dom/webidls/BluetoothRemoteGATTCharacteristic.webidl +++ b/components/script/dom/webidls/BluetoothRemoteGATTCharacteristic.webidl @@ -10,12 +10,18 @@ interface BluetoothRemoteGATTCharacteristic { readonly attribute DOMString uuid; readonly attribute BluetoothCharacteristicProperties properties; readonly attribute ByteString? value; - BluetoothRemoteGATTDescriptor? getDescriptor(/*BluetoothDescriptorUUID descriptor*/); + [Throws] + BluetoothRemoteGATTDescriptor getDescriptor(BluetoothDescriptorUUID descriptor); + [Throws] + sequence getDescriptors(optional BluetoothDescriptorUUID descriptor); //Promise getDescriptor(BluetoothDescriptorUUID descriptor); //Promise> //getDescriptors(optional BluetoothDescriptorUUID descriptor); - //Promise readValue(); + [Throws] ByteString readValue(); + //Promise readValue(); + [Throws] + void writeValue(sequence value); //Promise writeValue(BufferSource value); //Promise startNotifications(); //Promise stopNotifications(); diff --git a/components/script/dom/webidls/BluetoothRemoteGATTDescriptor.webidl b/components/script/dom/webidls/BluetoothRemoteGATTDescriptor.webidl index c7abe9e4859..853054f6e3a 100644 --- a/components/script/dom/webidls/BluetoothRemoteGATTDescriptor.webidl +++ b/components/script/dom/webidls/BluetoothRemoteGATTDescriptor.webidl @@ -9,8 +9,10 @@ interface BluetoothRemoteGATTDescriptor { readonly attribute BluetoothRemoteGATTCharacteristic characteristic; readonly attribute DOMString uuid; readonly attribute ByteString? value; - + [Throws] ByteString readValue(); //Promise readValue(); + [Throws] + void writeValue(sequence value); //Promise writeValue(BufferSource value); }; diff --git a/components/script/dom/webidls/BluetoothRemoteGATTServer.webidl b/components/script/dom/webidls/BluetoothRemoteGATTServer.webidl index 327542b522f..1d48a19a173 100644 --- a/components/script/dom/webidls/BluetoothRemoteGATTServer.webidl +++ b/components/script/dom/webidls/BluetoothRemoteGATTServer.webidl @@ -8,9 +8,14 @@ interface BluetoothRemoteGATTServer { readonly attribute BluetoothDevice device; readonly attribute boolean connected; + [Throws] BluetoothRemoteGATTServer connect(); + [Throws] void disconnect(); - BluetoothRemoteGATTService? getPrimaryService(); + [Throws] + BluetoothRemoteGATTService getPrimaryService(BluetoothServiceUUID service); + [Throws] + sequence getPrimaryServices(optional BluetoothServiceUUID service); //Promise getPrimaryService(BluetoothServiceUUID service); //Promise>getPrimaryServices(optional BluetoothServiceUUID service); //Promise connect(); diff --git a/components/script/dom/webidls/BluetoothRemoteGATTService.webidl b/components/script/dom/webidls/BluetoothRemoteGATTService.webidl index e8d66f6ac63..3a41865de1e 100644 --- a/components/script/dom/webidls/BluetoothRemoteGATTService.webidl +++ b/components/script/dom/webidls/BluetoothRemoteGATTService.webidl @@ -9,7 +9,11 @@ interface BluetoothRemoteGATTService { readonly attribute BluetoothDevice device; readonly attribute DOMString uuid; readonly attribute boolean isPrimary; - BluetoothRemoteGATTCharacteristic? getCharacteristic(/*DOMString characteristic*/); + [Throws] + BluetoothRemoteGATTCharacteristic getCharacteristic(BluetoothCharacteristicUUID characteristic); + [Throws] + sequence getCharacteristics + (optional BluetoothCharacteristicUUID characteristic); //PromisegetCharacteristic(BluetoothCharacteristicUUID characteristic); //Promise> //getCharacteristics(optional BluetoothCharacteristicUUID characteristic); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 6394f01ef27..aa347c048f5 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -47,6 +47,7 @@ use msg::constellation_msg::{ConstellationChan, LoadData, PipelineId, SubpageId} use msg::constellation_msg::{WindowSizeData, WindowSizeType}; use msg::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use net_traits::ResourceThread; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; use net_traits::storage_thread::{StorageThread, StorageType}; use num_traits::ToPrimitive; @@ -212,6 +213,10 @@ pub struct Window { #[ignore_heap_size_of = "channels are hard"] resource_thread: Arc, + /// A handle for communicating messages to the bluetooth thread. + #[ignore_heap_size_of = "channels are hard"] + bluetooth_thread: IpcSender, + /// A handle for communicating messages to the storage thread. #[ignore_heap_size_of = "channels are hard"] storage_thread: StorageThread, @@ -334,6 +339,10 @@ impl Window { &*self.page } + pub fn bluetooth_thread(&self) -> IpcSender { + self.bluetooth_thread.clone() + } + pub fn storage_thread(&self) -> StorageThread { self.storage_thread.clone() } @@ -1407,6 +1416,7 @@ impl Window { compositor: IpcSender, image_cache_thread: ImageCacheThread, resource_thread: Arc, + bluetooth_thread: IpcSender, storage_thread: StorageThread, mem_profiler_chan: mem::ProfilerChan, devtools_chan: Option>, @@ -1462,6 +1472,7 @@ impl Window { dom_static: GlobalStaticData::new(), js_runtime: DOMRefCell::new(Some(runtime.clone())), resource_thread: resource_thread, + bluetooth_thread: bluetooth_thread, storage_thread: storage_thread, constellation_chan: constellation_chan, page_clip_rect: Cell::new(MAX_RECT), diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 86f5a49ce6a..a46c1a28b18 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -64,6 +64,7 @@ use msg::constellation_msg::{PipelineId, PipelineNamespace}; use msg::constellation_msg::{SubpageId, WindowSizeData, WindowSizeType}; use msg::webdriver_msg::WebDriverScriptCommand; use net_traits::LoadData as NetLoadData; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; use net_traits::storage_thread::StorageThread; use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, LoadContext, Metadata, ResourceThread}; @@ -310,6 +311,8 @@ pub struct ScriptThread { /// A handle to the resource thread. This is an `Arc` to avoid running out of file descriptors if /// there are many iframes. resource_thread: Arc, + /// A handle to the bluetooth thread. + bluetooth_thread: IpcSender, /// A handle to the storage thread. storage_thread: StorageThread, @@ -554,6 +557,7 @@ impl ScriptThread { image_cache_port: image_cache_port, resource_thread: Arc::new(state.resource_thread), + bluetooth_thread: state.bluetooth_thread, storage_thread: state.storage_thread, port: port, @@ -1496,6 +1500,7 @@ impl ScriptThread { self.compositor.borrow_mut().clone(), self.image_cache_thread.clone(), self.resource_thread.clone(), + self.bluetooth_thread.clone(), self.storage_thread.clone(), self.mem_profiler_chan.clone(), self.devtools_chan.clone(), diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 2d4469ef80d..a001159acfb 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -46,6 +46,7 @@ use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData}; use msg::constellation_msg::{PipelineNamespaceId, SubpageId}; use msg::webdriver_msg::WebDriverScriptCommand; use net_traits::ResourceThread; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::image_cache_thread::ImageCacheThread; use net_traits::response::HttpsState; use net_traits::storage_thread::StorageThread; @@ -320,6 +321,8 @@ pub struct InitialScriptState { pub scheduler_chan: IpcSender, /// A channel to the resource manager thread. pub resource_thread: ResourceThread, + /// A channel to the bluetooth thread. + pub bluetooth_thread: IpcSender, /// A channel to the storage thread. pub storage_thread: StorageThread, /// A channel to the image cache thread. diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 8ef5c04b1b8..5be53b1e767 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -167,6 +167,15 @@ name = "block" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blurz" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dbus 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "brotli" version = "0.3.20" @@ -414,6 +423,15 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dbus" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "debug_unreachable" version = "0.1.1" @@ -430,6 +448,14 @@ dependencies = [ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "device" +version = "0.0.1" +source = "git+https://github.com/servo/devices#eeac5abfd548b63dcc48873261609922351551e1" +dependencies = [ + "blurz 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "devtools" version = "0.0.1" @@ -1305,8 +1331,10 @@ dependencies = [ name = "net" version = "0.0.1" dependencies = [ + "bitflags 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "brotli 0.3.20 (git+https://github.com/ende76/brotli-rs)", "cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "device 0.0.1 (git+https://github.com/servo/devices)", "devtools_traits 0.0.1", "flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 42e6706500c..c8355affa58 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -71,9 +71,11 @@ use compositing::{CompositorProxy, CompositorThread, Constellation}; use gaol::sandbox::{ChildSandbox, ChildSandboxMethods}; use gfx::font_cache_thread::FontCacheThread; use ipc_channel::ipc::{self, IpcSender}; +use net::bluetooth_thread::BluetoothThreadFactory; use net::image_cache_thread::new_image_cache_thread; use net::resource_thread::new_resource_thread; use net::storage_thread::StorageThreadFactory; +use net_traits::bluetooth_thread::BluetoothMethodMsg; use net_traits::storage_thread::StorageThread; use profile::mem as profile_mem; use profile::time as profile_time; @@ -208,6 +210,7 @@ fn create_constellation(opts: opts::Opts, devtools_chan: Option>, supports_clipboard: bool, webrender_api_sender: Option) -> Sender { + let bluetooth_thread: IpcSender = BluetoothThreadFactory::new(); let resource_thread = new_resource_thread(opts.user_agent.clone(), devtools_chan.clone()); let image_cache_thread = new_image_cache_thread(resource_thread.clone(), webrender_api_sender.as_ref().map(|wr| wr.create_api())); @@ -218,6 +221,7 @@ fn create_constellation(opts: opts::Opts, let initial_state = InitialConstellationState { compositor_proxy: compositor_proxy, devtools_chan: devtools_chan, + bluetooth_thread: bluetooth_thread, image_cache_thread: image_cache_thread, font_cache_thread: font_cache_thread, resource_thread: resource_thread, diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 09b4991b723..e6d35b12af9 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -153,6 +153,15 @@ name = "block" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blurz" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dbus 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "brotli" version = "0.3.20" @@ -385,6 +394,15 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dbus" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "debug_unreachable" version = "0.1.1" @@ -401,6 +419,14 @@ dependencies = [ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "device" +version = "0.0.1" +source = "git+https://github.com/servo/devices#eeac5abfd548b63dcc48873261609922351551e1" +dependencies = [ + "blurz 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "devtools" version = "0.0.1" @@ -1219,8 +1245,10 @@ dependencies = [ name = "net" version = "0.0.1" dependencies = [ + "bitflags 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "brotli 0.3.20 (git+https://github.com/ende76/brotli-rs)", "cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "device 0.0.1 (git+https://github.com/servo/devices)", "devtools_traits 0.0.1", "flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 7f58a20cfb6..e64d5b64eb2 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -146,6 +146,15 @@ name = "block" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "blurz" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dbus 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "brotli" version = "0.3.20" @@ -378,6 +387,15 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dbus" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "debug_unreachable" version = "0.1.1" @@ -394,6 +412,14 @@ dependencies = [ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "device" +version = "0.0.1" +source = "git+https://github.com/servo/devices#eeac5abfd548b63dcc48873261609922351551e1" +dependencies = [ + "blurz 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "devtools" version = "0.0.1" @@ -1202,8 +1228,10 @@ dependencies = [ name = "net" version = "0.0.1" dependencies = [ + "bitflags 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "brotli 0.3.20 (git+https://github.com/ende76/brotli-rs)", "cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "device 0.0.1 (git+https://github.com/servo/devices)", "devtools_traits 0.0.1", "flate2 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/tests/html/bluetooth_battery_level.html b/tests/html/bluetooth_battery_level.html new file mode 100644 index 00000000000..8618df07859 --- /dev/null +++ b/tests/html/bluetooth_battery_level.html @@ -0,0 +1,61 @@ + + +Battery Level + + + + +

+    
+
+
diff --git a/tests/html/bluetooth_characteristic_info.html b/tests/html/bluetooth_characteristic_info.html
new file mode 100644
index 00000000000..84cebd83014
--- /dev/null
+++ b/tests/html/bluetooth_characteristic_info.html
@@ -0,0 +1,69 @@
+
+
+Characteristic info
+
+    
+    
+    
+    

+    
+
+
diff --git a/tests/html/bluetooth_descriptor_info.html b/tests/html/bluetooth_descriptor_info.html
new file mode 100644
index 00000000000..c657d217697
--- /dev/null
+++ b/tests/html/bluetooth_descriptor_info.html
@@ -0,0 +1,68 @@
+
+
+Descriptor info
+
+    
+    
+    
+    
+    

+    
+
+
diff --git a/tests/html/bluetooth_device_disconnect.html b/tests/html/bluetooth_device_disconnect.html
new file mode 100644
index 00000000000..715d3d16a24
--- /dev/null
+++ b/tests/html/bluetooth_device_disconnect.html
@@ -0,0 +1,94 @@
+
+
+Device Disconnect
+
+    
+    
+    
+    
+    
+    
+    

+    
+
+
diff --git a/tests/html/bluetooth_device_info.html b/tests/html/bluetooth_device_info.html
new file mode 100644
index 00000000000..0cdc09a95ab
--- /dev/null
+++ b/tests/html/bluetooth_device_info.html
@@ -0,0 +1,59 @@
+
+
+Device Info
+
+    
+    
+    
+    
+    

+    
+
+
diff --git a/tests/html/bluetooth_primary_service_info.html b/tests/html/bluetooth_primary_service_info.html
new file mode 100644
index 00000000000..606d342c85d
--- /dev/null
+++ b/tests/html/bluetooth_primary_service_info.html
@@ -0,0 +1,57 @@
+
+
+Primary Service info
+
+    
+    
+    
+    
+    

+    
+
+