Auto merge of #13909 - dati91:promise-queue, r=jdm

Webbluetooth Async behaviour

<!-- Please describe your changes on the following line: -->

Note: depends on https://github.com/servo/servo/pull/13612

The current WBT communication is synchronous. With this, it should work properly (except the disconnect function, which will need some more work, because the current implementation differ from the spec).

<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors

<!-- Either: -->
- [X] There are tests for these changes

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---

This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13909)

<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-11-08 09:05:12 -06:00 committed by GitHub
commit 1153ca9841
19 changed files with 806 additions and 729 deletions

View file

@ -15,10 +15,9 @@ extern crate uuid;
pub mod test; pub mod test;
use bluetooth_traits::{BluetoothCharacteristicMsg, BluetoothCharacteristicsMsg}; use bluetooth_traits::{BluetoothCharacteristicMsg, BluetoothDescriptorMsg, BluetoothServiceMsg};
use bluetooth_traits::{BluetoothDescriptorMsg, BluetoothDescriptorsMsg}; use bluetooth_traits::{BluetoothDeviceMsg, BluetoothRequest, BluetoothResponse};
use bluetooth_traits::{BluetoothDeviceMsg, BluetoothError, BluetoothMethodMsg}; use bluetooth_traits::{BluetoothError, BluetoothResponseResult, BluetoothResult};
use bluetooth_traits::{BluetoothResult, BluetoothServiceMsg, BluetoothServicesMsg};
use bluetooth_traits::blacklist::{uuid_is_blacklisted, Blacklist}; use bluetooth_traits::blacklist::{uuid_is_blacklisted, Blacklist};
use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence, RequestDeviceoptions}; use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence, RequestDeviceoptions};
use device::bluetooth::{BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic}; use device::bluetooth::{BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic};
@ -85,8 +84,8 @@ pub trait BluetoothThreadFactory {
fn new() -> Self; fn new() -> Self;
} }
impl BluetoothThreadFactory for IpcSender<BluetoothMethodMsg> { impl BluetoothThreadFactory for IpcSender<BluetoothRequest> {
fn new() -> IpcSender<BluetoothMethodMsg> { fn new() -> IpcSender<BluetoothRequest> {
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
let adapter = BluetoothAdapter::init().ok(); let adapter = BluetoothAdapter::init().ok();
spawn_named("BluetoothThread".to_owned(), move || { spawn_named("BluetoothThread".to_owned(), move || {
@ -167,7 +166,7 @@ fn is_mock_adapter(adapter: &BluetoothAdapter) -> bool {
} }
pub struct BluetoothManager { pub struct BluetoothManager {
receiver: IpcReceiver<BluetoothMethodMsg>, receiver: IpcReceiver<BluetoothRequest>,
adapter: Option<BluetoothAdapter>, adapter: Option<BluetoothAdapter>,
address_to_id: HashMap<String, String>, address_to_id: HashMap<String, String>,
service_to_device: HashMap<String, String>, service_to_device: HashMap<String, String>,
@ -181,7 +180,7 @@ pub struct BluetoothManager {
} }
impl BluetoothManager { impl BluetoothManager {
pub fn new(receiver: IpcReceiver<BluetoothMethodMsg>, adapter: Option<BluetoothAdapter>) -> BluetoothManager { pub fn new(receiver: IpcReceiver<BluetoothRequest>, adapter: Option<BluetoothAdapter>) -> BluetoothManager {
BluetoothManager { BluetoothManager {
receiver: receiver, receiver: receiver,
adapter: adapter, adapter: adapter,
@ -200,49 +199,49 @@ impl BluetoothManager {
fn start(&mut self) { fn start(&mut self) {
while let Ok(msg) = self.receiver.recv() { while let Ok(msg) = self.receiver.recv() {
match msg { match msg {
BluetoothMethodMsg::RequestDevice(options, sender) => { BluetoothRequest::RequestDevice(options, sender) => {
self.request_device(options, sender) self.request_device(options, sender)
}, },
BluetoothMethodMsg::GATTServerConnect(device_id, sender) => { BluetoothRequest::GATTServerConnect(device_id, sender) => {
self.gatt_server_connect(device_id, sender) self.gatt_server_connect(device_id, sender)
}, },
BluetoothMethodMsg::GATTServerDisconnect(device_id, sender) => { BluetoothRequest::GATTServerDisconnect(device_id, sender) => {
self.gatt_server_disconnect(device_id, sender) self.gatt_server_disconnect(device_id, sender)
}, },
BluetoothMethodMsg::GetPrimaryService(device_id, uuid, sender) => { BluetoothRequest::GetPrimaryService(device_id, uuid, sender) => {
self.get_primary_service(device_id, uuid, sender) self.get_primary_service(device_id, uuid, sender)
}, },
BluetoothMethodMsg::GetPrimaryServices(device_id, uuid, sender) => { BluetoothRequest::GetPrimaryServices(device_id, uuid, sender) => {
self.get_primary_services(device_id, uuid, sender) self.get_primary_services(device_id, uuid, sender)
}, },
BluetoothMethodMsg::GetIncludedService(service_id, uuid, sender) => { BluetoothRequest::GetIncludedService(service_id, uuid, sender) => {
self.get_included_service(service_id, uuid, sender) self.get_included_service(service_id, uuid, sender)
}, },
BluetoothMethodMsg::GetIncludedServices(service_id, uuid, sender) => { BluetoothRequest::GetIncludedServices(service_id, uuid, sender) => {
self.get_included_services(service_id, uuid, sender) self.get_included_services(service_id, uuid, sender)
}, },
BluetoothMethodMsg::GetCharacteristic(service_id, uuid, sender) => { BluetoothRequest::GetCharacteristic(service_id, uuid, sender) => {
self.get_characteristic(service_id, uuid, sender) self.get_characteristic(service_id, uuid, sender)
}, },
BluetoothMethodMsg::GetCharacteristics(service_id, uuid, sender) => { BluetoothRequest::GetCharacteristics(service_id, uuid, sender) => {
self.get_characteristics(service_id, uuid, sender) self.get_characteristics(service_id, uuid, sender)
}, },
BluetoothMethodMsg::GetDescriptor(characteristic_id, uuid, sender) => { BluetoothRequest::GetDescriptor(characteristic_id, uuid, sender) => {
self.get_descriptor(characteristic_id, uuid, sender) self.get_descriptor(characteristic_id, uuid, sender)
}, },
BluetoothMethodMsg::GetDescriptors(characteristic_id, uuid, sender) => { BluetoothRequest::GetDescriptors(characteristic_id, uuid, sender) => {
self.get_descriptors(characteristic_id, uuid, sender) self.get_descriptors(characteristic_id, uuid, sender)
}, },
BluetoothMethodMsg::ReadValue(id, sender) => { BluetoothRequest::ReadValue(id, sender) => {
self.read_value(id, sender) self.read_value(id, sender)
}, },
BluetoothMethodMsg::WriteValue(id, value, sender) => { BluetoothRequest::WriteValue(id, value, sender) => {
self.write_value(id, value, sender) self.write_value(id, value, sender)
}, },
BluetoothMethodMsg::Test(data_set_name, sender) => { BluetoothRequest::Test(data_set_name, sender) => {
self.test(data_set_name, sender) self.test(data_set_name, sender)
} }
BluetoothMethodMsg::Exit => { BluetoothRequest::Exit => {
break break
}, },
} }
@ -527,7 +526,7 @@ impl BluetoothManager {
// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
fn request_device(&mut self, fn request_device(&mut self,
options: RequestDeviceoptions, options: RequestDeviceoptions,
sender: IpcSender<BluetoothResult<BluetoothDeviceMsg>>) { sender: IpcSender<BluetoothResponseResult>) {
let mut adapter = get_adapter_or_return_error!(self, sender); let mut adapter = get_adapter_or_return_error!(self, sender);
if let Ok(ref session) = adapter.create_discovery_session() { if let Ok(ref session) = adapter.create_discovery_session() {
if session.start_discovery().is_ok() { if session.start_discovery().is_ok() {
@ -561,31 +560,30 @@ impl BluetoothManager {
} }
self.allowed_services.insert(device_id.clone(), services); self.allowed_services.insert(device_id.clone(), services);
if let Some(device) = self.get_device(&mut adapter, &device_id) { if let Some(device) = self.get_device(&mut adapter, &device_id) {
let message = Ok(BluetoothDeviceMsg { let message = BluetoothDeviceMsg {
id: device_id, id: device_id,
name: device.get_name().ok(), name: device.get_name().ok(),
appearance: device.get_appearance().ok(), appearance: device.get_appearance().ok(),
tx_power: device.get_tx_power().ok().map(|p| p as i8), tx_power: device.get_tx_power().ok().map(|p| p as i8),
rssi: device.get_rssi().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(Ok(BluetoothResponse::RequestDevice(message))));
} }
} }
return drop(sender.send(Err(BluetoothError::NotFound))); return drop(sender.send(Err(BluetoothError::NotFound)));
} }
fn gatt_server_connect(&mut self, device_id: String, sender: IpcSender<BluetoothResult<bool>>) { fn gatt_server_connect(&mut self, device_id: String, sender: IpcSender<BluetoothResponseResult>) {
let mut adapter = get_adapter_or_return_error!(self, sender); let mut adapter = get_adapter_or_return_error!(self, sender);
match self.get_device(&mut adapter, &device_id) { match self.get_device(&mut adapter, &device_id) {
Some(d) => { Some(d) => {
if d.is_connected().unwrap_or(false) { if d.is_connected().unwrap_or(false) {
return drop(sender.send(Ok(true))); return drop(sender.send(Ok(BluetoothResponse::GATTServerConnect(true))));
} }
let _ = d.connect(); let _ = d.connect();
for _ in 0..MAXIMUM_TRANSACTION_TIME { for _ in 0..MAXIMUM_TRANSACTION_TIME {
match d.is_connected().unwrap_or(false) { match d.is_connected().unwrap_or(false) {
true => return drop(sender.send(Ok(true))), true => return drop(sender.send(Ok(BluetoothResponse::GATTServerConnect(true)))),
false => { false => {
if is_mock_adapter(&adapter) { if is_mock_adapter(&adapter) {
break; break;
@ -624,7 +622,7 @@ impl BluetoothManager {
fn get_primary_service(&mut self, fn get_primary_service(&mut self,
device_id: String, device_id: String,
uuid: String, uuid: String,
sender: IpcSender<BluetoothResult<BluetoothServiceMsg>>) { sender: IpcSender<BluetoothResponseResult>) {
if !self.cached_devices.contains_key(&device_id) { if !self.cached_devices.contains_key(&device_id) {
return drop(sender.send(Err(BluetoothError::InvalidState))); return drop(sender.send(Err(BluetoothError::InvalidState)));
} }
@ -639,11 +637,15 @@ impl BluetoothManager {
for service in services { for service in services {
if service.is_primary().unwrap_or(false) { if service.is_primary().unwrap_or(false) {
if let Ok(uuid) = service.get_uuid() { if let Ok(uuid) = service.get_uuid() {
return drop(sender.send(Ok(BluetoothServiceMsg { return drop(sender.send(
Ok(BluetoothResponse::GetPrimaryService(
BluetoothServiceMsg {
uuid: uuid, uuid: uuid,
is_primary: true, is_primary: true,
instance_id: service.get_id(), instance_id: service.get_id(),
}))); }
))
));
} }
} }
} }
@ -653,7 +655,7 @@ impl BluetoothManager {
fn get_primary_services(&mut self, fn get_primary_services(&mut self,
device_id: String, device_id: String,
uuid: Option<String>, uuid: Option<String>,
sender: IpcSender<BluetoothResult<BluetoothServicesMsg>>) { sender: IpcSender<BluetoothResponseResult>) {
if !self.cached_devices.contains_key(&device_id) { if !self.cached_devices.contains_key(&device_id) {
return drop(sender.send(Err(BluetoothError::InvalidState))); return drop(sender.send(Err(BluetoothError::InvalidState)));
} }
@ -674,11 +676,13 @@ impl BluetoothManager {
for service in services { for service in services {
if service.is_primary().unwrap_or(false) { if service.is_primary().unwrap_or(false) {
if let Ok(uuid) = service.get_uuid() { if let Ok(uuid) = service.get_uuid() {
services_vec.push(BluetoothServiceMsg { services_vec.push(
BluetoothServiceMsg {
uuid: uuid, uuid: uuid,
is_primary: true, is_primary: true,
instance_id: service.get_id(), instance_id: service.get_id(),
}); }
);
} }
} }
} }
@ -690,20 +694,17 @@ impl BluetoothManager {
return drop(sender.send(Err(BluetoothError::NotFound))); return drop(sender.send(Err(BluetoothError::NotFound)));
} }
let _ = sender.send(Ok(services_vec)); return drop(sender.send(Ok(BluetoothResponse::GetPrimaryServices(services_vec))));
} }
fn get_included_service(&mut self, fn get_included_service(&mut self,
service_id: String, service_id: String,
uuid: String, uuid: String,
sender: IpcSender<BluetoothResult<BluetoothServiceMsg>>) { sender: IpcSender<BluetoothResponseResult>) {
if !self.cached_services.contains_key(&service_id) { if !self.cached_services.contains_key(&service_id) {
return drop(sender.send(Err(BluetoothError::InvalidState))); return drop(sender.send(Err(BluetoothError::InvalidState)));
} }
let mut adapter = match self.get_or_create_adapter() { let mut adapter = get_adapter_or_return_error!(self, sender);
Some(a) => a,
None => return drop(sender.send(Err(BluetoothError::NotFound))),
};
let device = match self.device_from_service_id(&service_id) { let device = match self.device_from_service_id(&service_id) {
Some(device) => device, Some(device) => device,
None => return drop(sender.send(Err(BluetoothError::NotFound))), None => return drop(sender.send(Err(BluetoothError::NotFound))),
@ -716,11 +717,15 @@ impl BluetoothManager {
for service in services { for service in services {
if let Ok(service_uuid) = service.get_uuid() { if let Ok(service_uuid) = service.get_uuid() {
if uuid == service_uuid { if uuid == service_uuid {
return drop(sender.send(Ok(BluetoothServiceMsg { return drop(sender.send(
Ok(BluetoothResponse::GetIncludedService(
BluetoothServiceMsg {
uuid: uuid, uuid: uuid,
is_primary: service.is_primary().unwrap_or(false), is_primary: service.is_primary().unwrap_or(false),
instance_id: service.get_id(), instance_id: service.get_id(),
}))); }
))
));
} }
} }
} }
@ -730,14 +735,11 @@ impl BluetoothManager {
fn get_included_services(&mut self, fn get_included_services(&mut self,
service_id: String, service_id: String,
uuid: Option<String>, uuid: Option<String>,
sender: IpcSender<BluetoothResult<BluetoothServicesMsg>>) { sender: IpcSender<BluetoothResponseResult>) {
if !self.cached_services.contains_key(&service_id) { if !self.cached_services.contains_key(&service_id) {
return drop(sender.send(Err(BluetoothError::InvalidState))); return drop(sender.send(Err(BluetoothError::InvalidState)));
} }
let mut adapter = match self.get_or_create_adapter() { let mut adapter = get_adapter_or_return_error!(self, sender);
Some(a) => a,
None => return drop(sender.send(Err(BluetoothError::NotFound))),
};
let device = match self.device_from_service_id(&service_id) { let device = match self.device_from_service_id(&service_id) {
Some(device) => device, Some(device) => device,
None => return drop(sender.send(Err(BluetoothError::NotFound))), None => return drop(sender.send(Err(BluetoothError::NotFound))),
@ -750,11 +752,13 @@ impl BluetoothManager {
let mut services_vec = vec!(); let mut services_vec = vec!();
for service in services { for service in services {
if let Ok(service_uuid) = service.get_uuid() { if let Ok(service_uuid) = service.get_uuid() {
services_vec.push(BluetoothServiceMsg { services_vec.push(
BluetoothServiceMsg {
uuid: service_uuid, uuid: service_uuid,
is_primary: service.is_primary().unwrap_or(false), is_primary: service.is_primary().unwrap_or(false),
instance_id: service.get_id(), instance_id: service.get_id(),
}); }
);
} }
} }
if let Some(uuid) = uuid { if let Some(uuid) = uuid {
@ -765,13 +769,13 @@ impl BluetoothManager {
return drop(sender.send(Err(BluetoothError::NotFound))); return drop(sender.send(Err(BluetoothError::NotFound)));
} }
let _ = sender.send(Ok(services_vec)); return drop(sender.send(Ok(BluetoothResponse::GetIncludedServices(services_vec))));
} }
fn get_characteristic(&mut self, fn get_characteristic(&mut self,
service_id: String, service_id: String,
uuid: String, uuid: String,
sender: IpcSender<BluetoothResult<BluetoothCharacteristicMsg>>) { sender: IpcSender<BluetoothResponseResult>) {
if !self.cached_services.contains_key(&service_id) { if !self.cached_services.contains_key(&service_id) {
return drop(sender.send(Err(BluetoothError::InvalidState))); return drop(sender.send(Err(BluetoothError::InvalidState)));
} }
@ -783,7 +787,7 @@ impl BluetoothManager {
for characteristic in characteristics { for characteristic in characteristics {
if let Ok(uuid) = characteristic.get_uuid() { if let Ok(uuid) = characteristic.get_uuid() {
let properties = self.get_characteristic_properties(&characteristic); let properties = self.get_characteristic_properties(&characteristic);
let message = Ok(BluetoothCharacteristicMsg { let message = BluetoothCharacteristicMsg {
uuid: uuid, uuid: uuid,
instance_id: characteristic.get_id(), instance_id: characteristic.get_id(),
broadcast: properties.contains(BROADCAST), broadcast: properties.contains(BROADCAST),
@ -795,8 +799,8 @@ impl BluetoothManager {
authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES), authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES),
reliable_write: properties.contains(RELIABLE_WRITE), reliable_write: properties.contains(RELIABLE_WRITE),
writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES), writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES),
}); };
return drop(sender.send(message)); return drop(sender.send(Ok(BluetoothResponse::GetCharacteristic(message))));
} }
} }
return drop(sender.send(Err(BluetoothError::NotFound))); return drop(sender.send(Err(BluetoothError::NotFound)));
@ -805,7 +809,7 @@ impl BluetoothManager {
fn get_characteristics(&mut self, fn get_characteristics(&mut self,
service_id: String, service_id: String,
uuid: Option<String>, uuid: Option<String>,
sender: IpcSender<BluetoothResult<BluetoothCharacteristicsMsg>>) { sender: IpcSender<BluetoothResponseResult>) {
if !self.cached_services.contains_key(&service_id) { if !self.cached_services.contains_key(&service_id) {
return drop(sender.send(Err(BluetoothError::InvalidState))); return drop(sender.send(Err(BluetoothError::InvalidState)));
} }
@ -834,7 +838,8 @@ impl BluetoothManager {
authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES), authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES),
reliable_write: properties.contains(RELIABLE_WRITE), reliable_write: properties.contains(RELIABLE_WRITE),
writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES), writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES),
}); }
);
} }
} }
characteristics_vec.retain(|c| !uuid_is_blacklisted(&c.uuid, Blacklist::All)); characteristics_vec.retain(|c| !uuid_is_blacklisted(&c.uuid, Blacklist::All));
@ -842,13 +847,13 @@ impl BluetoothManager {
return drop(sender.send(Err(BluetoothError::NotFound))); return drop(sender.send(Err(BluetoothError::NotFound)));
} }
let _ = sender.send(Ok(characteristics_vec)); return drop(sender.send(Ok(BluetoothResponse::GetCharacteristics(characteristics_vec))));
} }
fn get_descriptor(&mut self, fn get_descriptor(&mut self,
characteristic_id: String, characteristic_id: String,
uuid: String, uuid: String,
sender: IpcSender<BluetoothResult<BluetoothDescriptorMsg>>) { sender: IpcSender<BluetoothResponseResult>) {
if !self.cached_characteristics.contains_key(&characteristic_id) { if !self.cached_characteristics.contains_key(&characteristic_id) {
return drop(sender.send(Err(BluetoothError::InvalidState))); return drop(sender.send(Err(BluetoothError::InvalidState)));
} }
@ -859,10 +864,14 @@ impl BluetoothManager {
} }
for descriptor in descriptors { for descriptor in descriptors {
if let Ok(uuid) = descriptor.get_uuid() { if let Ok(uuid) = descriptor.get_uuid() {
return drop(sender.send(Ok(BluetoothDescriptorMsg { return drop(sender.send(
Ok(BluetoothResponse::GetDescriptor(
BluetoothDescriptorMsg {
uuid: uuid, uuid: uuid,
instance_id: descriptor.get_id(), instance_id: descriptor.get_id(),
}))); }
))
));
} }
} }
return drop(sender.send(Err(BluetoothError::NotFound))); return drop(sender.send(Err(BluetoothError::NotFound)));
@ -871,7 +880,7 @@ impl BluetoothManager {
fn get_descriptors(&mut self, fn get_descriptors(&mut self,
characteristic_id: String, characteristic_id: String,
uuid: Option<String>, uuid: Option<String>,
sender: IpcSender<BluetoothResult<BluetoothDescriptorsMsg>>) { sender: IpcSender<BluetoothResponseResult>) {
if !self.cached_characteristics.contains_key(&characteristic_id) { if !self.cached_characteristics.contains_key(&characteristic_id) {
return drop(sender.send(Err(BluetoothError::InvalidState))); return drop(sender.send(Err(BluetoothError::InvalidState)));
} }
@ -886,20 +895,22 @@ impl BluetoothManager {
let mut descriptors_vec = vec!(); let mut descriptors_vec = vec!();
for descriptor in descriptors { for descriptor in descriptors {
if let Ok(uuid) = descriptor.get_uuid() { if let Ok(uuid) = descriptor.get_uuid() {
descriptors_vec.push(BluetoothDescriptorMsg { descriptors_vec.push(
BluetoothDescriptorMsg {
uuid: uuid, uuid: uuid,
instance_id: descriptor.get_id(), instance_id: descriptor.get_id(),
}); }
);
} }
} }
descriptors_vec.retain(|d| !uuid_is_blacklisted(&d.uuid, Blacklist::All)); descriptors_vec.retain(|d| !uuid_is_blacklisted(&d.uuid, Blacklist::All));
if descriptors_vec.is_empty() { if descriptors_vec.is_empty() {
return drop(sender.send(Err(BluetoothError::NotFound))); return drop(sender.send(Err(BluetoothError::NotFound)));
} }
let _ = sender.send(Ok(descriptors_vec)); return drop(sender.send(Ok(BluetoothResponse::GetDescriptors(descriptors_vec))));
} }
fn read_value(&mut self, id: String, sender: IpcSender<BluetoothResult<Vec<u8>>>) { fn read_value(&mut self, id: String, sender: IpcSender<BluetoothResponseResult>) {
let mut adapter = get_adapter_or_return_error!(self, sender); let mut adapter = get_adapter_or_return_error!(self, sender);
let mut value = self.get_gatt_characteristic(&mut adapter, &id) let mut value = self.get_gatt_characteristic(&mut adapter, &id)
.map(|c| c.read_value().unwrap_or(vec![])); .map(|c| c.read_value().unwrap_or(vec![]));
@ -907,10 +918,13 @@ impl BluetoothManager {
value = self.get_gatt_descriptor(&mut adapter, &id) value = self.get_gatt_descriptor(&mut adapter, &id)
.map(|d| d.read_value().unwrap_or(vec![])); .map(|d| d.read_value().unwrap_or(vec![]));
} }
let _ = sender.send(value.ok_or(BluetoothError::InvalidState)); match value {
Some(v) => return drop(sender.send(Ok(BluetoothResponse::ReadValue(v)))),
None => return drop(sender.send(Err(BluetoothError::InvalidState))),
}
} }
fn write_value(&mut self, id: String, value: Vec<u8>, sender: IpcSender<BluetoothResult<bool>>) { fn write_value(&mut self, id: String, value: Vec<u8>, sender: IpcSender<BluetoothResponseResult>) {
let mut adapter = get_adapter_or_return_error!(self, sender); let mut adapter = get_adapter_or_return_error!(self, sender);
let mut result = self.get_gatt_characteristic(&mut adapter, &id) let mut result = self.get_gatt_characteristic(&mut adapter, &id)
.map(|c| c.write_value(value.clone())); .map(|c| c.write_value(value.clone()));
@ -918,13 +932,12 @@ impl BluetoothManager {
result = self.get_gatt_descriptor(&mut adapter, &id) result = self.get_gatt_descriptor(&mut adapter, &id)
.map(|d| d.write_value(value.clone())); .map(|d| d.write_value(value.clone()));
} }
let message = match result { match result {
Some(v) => match v { Some(v) => match v {
Ok(_) => Ok(true), Ok(_) => return drop(sender.send(Ok(BluetoothResponse::WriteValue(value)))),
Err(_) => return drop(sender.send(Err(BluetoothError::NotSupported))), Err(_) => return drop(sender.send(Err(BluetoothError::NotSupported))),
}, },
None => return drop(sender.send(Err(BluetoothError::InvalidState))), None => return drop(sender.send(Err(BluetoothError::InvalidState))),
}; }
let _ = sender.send(message);
} }
} }

View file

@ -75,21 +75,43 @@ pub type BluetoothDescriptorsMsg = Vec<BluetoothDescriptorMsg>;
pub type BluetoothResult<T> = Result<T, BluetoothError>; pub type BluetoothResult<T> = Result<T, BluetoothError>;
pub type BluetoothResponseResult = Result<BluetoothResponse, BluetoothError>;
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub enum BluetoothMethodMsg { pub enum BluetoothRequest {
RequestDevice(RequestDeviceoptions, IpcSender<BluetoothResult<BluetoothDeviceMsg>>), RequestDevice(RequestDeviceoptions, IpcSender<BluetoothResponseResult>),
GATTServerConnect(String, IpcSender<BluetoothResult<bool>>), GATTServerConnect(String, IpcSender<BluetoothResponseResult>),
GATTServerDisconnect(String, IpcSender<BluetoothResult<bool>>), GATTServerDisconnect(String, IpcSender<BluetoothResult<bool>>),
GetPrimaryService(String, String, IpcSender<BluetoothResult<BluetoothServiceMsg>>), GetPrimaryService(String, String, IpcSender<BluetoothResponseResult>),
GetPrimaryServices(String, Option<String>, IpcSender<BluetoothResult<BluetoothServicesMsg>>), GetPrimaryServices(String, Option<String>, IpcSender<BluetoothResponseResult>),
GetIncludedService(String, String, IpcSender<BluetoothResult<BluetoothServiceMsg>>), GetIncludedService(String, String, IpcSender<BluetoothResponseResult>),
GetIncludedServices(String, Option<String>, IpcSender<BluetoothResult<BluetoothServicesMsg>>), GetIncludedServices(String, Option<String>, IpcSender<BluetoothResponseResult>),
GetCharacteristic(String, String, IpcSender<BluetoothResult<BluetoothCharacteristicMsg>>), GetCharacteristic(String, String, IpcSender<BluetoothResponseResult>),
GetCharacteristics(String, Option<String>, IpcSender<BluetoothResult<BluetoothCharacteristicsMsg>>), GetCharacteristics(String, Option<String>, IpcSender<BluetoothResponseResult>),
GetDescriptor(String, String, IpcSender<BluetoothResult<BluetoothDescriptorMsg>>), GetDescriptor(String, String, IpcSender<BluetoothResponseResult>),
GetDescriptors(String, Option<String>, IpcSender<BluetoothResult<BluetoothDescriptorsMsg>>), GetDescriptors(String, Option<String>, IpcSender<BluetoothResponseResult>),
ReadValue(String, IpcSender<BluetoothResult<Vec<u8>>>), ReadValue(String, IpcSender<BluetoothResponseResult>),
WriteValue(String, Vec<u8>, IpcSender<BluetoothResult<bool>>), WriteValue(String, Vec<u8>, IpcSender<BluetoothResponseResult>),
Test(String, IpcSender<BluetoothResult<()>>), Test(String, IpcSender<BluetoothResult<()>>),
Exit, Exit,
} }
#[derive(Deserialize, Serialize)]
pub enum BluetoothResponse {
RequestDevice(BluetoothDeviceMsg),
GATTServerConnect(bool),
GetPrimaryService(BluetoothServiceMsg),
GetPrimaryServices(BluetoothServicesMsg),
GetIncludedService(BluetoothServiceMsg),
GetIncludedServices(BluetoothServicesMsg),
GetCharacteristic(BluetoothCharacteristicMsg),
GetCharacteristics(BluetoothCharacteristicsMsg),
GetDescriptor(BluetoothDescriptorMsg),
GetDescriptors(BluetoothDescriptorsMsg),
ReadValue(Vec<u8>),
WriteValue(Vec<u8>),
}
pub trait BluetoothResponseListener {
fn response(&mut self, response: BluetoothResponseResult);
}

View file

@ -10,7 +10,7 @@
//! `LayoutThread`, and `PaintThread`. //! `LayoutThread`, and `PaintThread`.
use backtrace::Backtrace; use backtrace::Backtrace;
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::BluetoothRequest;
use canvas::canvas_paint_thread::CanvasPaintThread; use canvas::canvas_paint_thread::CanvasPaintThread;
use canvas::webgl_paint_thread::WebGLPaintThread; use canvas::webgl_paint_thread::WebGLPaintThread;
use canvas_traits::CanvasMsg; use canvas_traits::CanvasMsg;
@ -121,7 +121,7 @@ pub struct Constellation<Message, LTF, STF> {
devtools_chan: Option<Sender<DevtoolsControlMsg>>, devtools_chan: Option<Sender<DevtoolsControlMsg>>,
/// A channel through which messages can be sent to the bluetooth thread. /// A channel through which messages can be sent to the bluetooth thread.
bluetooth_thread: IpcSender<BluetoothMethodMsg>, bluetooth_thread: IpcSender<BluetoothRequest>,
/// Sender to Service Worker Manager thread /// Sender to Service Worker Manager thread
swmanager_chan: Option<IpcSender<ServiceWorkerMsg>>, swmanager_chan: Option<IpcSender<ServiceWorkerMsg>>,
@ -199,7 +199,7 @@ pub struct InitialConstellationState {
/// A channel to the developer tools, if applicable. /// A channel to the developer tools, if applicable.
pub devtools_chan: Option<Sender<DevtoolsControlMsg>>, pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
/// A channel to the bluetooth thread. /// A channel to the bluetooth thread.
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>, pub bluetooth_thread: IpcSender<BluetoothRequest>,
/// A channel to the image cache thread. /// A channel to the image cache thread.
pub image_cache_thread: ImageCacheThread, pub image_cache_thread: ImageCacheThread,
/// A channel to the font cache thread. /// A channel to the font cache thread.
@ -1095,7 +1095,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
} }
debug!("Exiting bluetooth thread."); debug!("Exiting bluetooth thread.");
if let Err(e) = self.bluetooth_thread.send(BluetoothMethodMsg::Exit) { if let Err(e) = self.bluetooth_thread.send(BluetoothRequest::Exit) {
warn!("Exit bluetooth thread failed ({})", e); warn!("Exit bluetooth thread failed ({})", e);
} }

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::BluetoothRequest;
use compositing::CompositionPipeline; use compositing::CompositionPipeline;
use compositing::CompositorProxy; use compositing::CompositorProxy;
use compositing::compositor_thread::Msg as CompositorMsg; use compositing::compositor_thread::Msg as CompositorMsg;
@ -94,7 +94,7 @@ pub struct InitialPipelineState {
/// A channel to the developer tools, if applicable. /// A channel to the developer tools, if applicable.
pub devtools_chan: Option<Sender<DevtoolsControlMsg>>, pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
/// A channel to the bluetooth thread. /// A channel to the bluetooth thread.
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>, pub bluetooth_thread: IpcSender<BluetoothRequest>,
/// A channel to the service worker manager thread /// A channel to the service worker manager thread
pub swmanager_thread: IpcSender<SWManagerMsg>, pub swmanager_thread: IpcSender<SWManagerMsg>,
/// A channel to the image cache thread. /// A channel to the image cache thread.
@ -384,7 +384,7 @@ pub struct UnprivilegedPipelineContent {
layout_to_constellation_chan: IpcSender<LayoutMsg>, layout_to_constellation_chan: IpcSender<LayoutMsg>,
scheduler_chan: IpcSender<TimerEventRequest>, scheduler_chan: IpcSender<TimerEventRequest>,
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>, devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
bluetooth_thread: IpcSender<BluetoothMethodMsg>, bluetooth_thread: IpcSender<BluetoothRequest>,
swmanager_thread: IpcSender<SWManagerMsg>, swmanager_thread: IpcSender<SWManagerMsg>,
image_cache_thread: ImageCacheThread, image_cache_thread: ImageCacheThread,
font_cache_thread: FontCacheThread, font_cache_thread: FontCacheThread,

View file

@ -10,6 +10,7 @@ name = "net_traits"
path = "lib.rs" path = "lib.rs"
[dependencies] [dependencies]
bluetooth_traits = {path = "../bluetooth_traits"}
util = {path = "../util"} util = {path = "../util"}
msg = {path = "../msg"} msg = {path = "../msg"}
ipc-channel = "0.5" ipc-channel = "0.5"

View file

@ -9,6 +9,7 @@
#![deny(unsafe_code)] #![deny(unsafe_code)]
extern crate bluetooth_traits;
extern crate cookie as cookie_rs; extern crate cookie as cookie_rs;
extern crate heapsize; extern crate heapsize;
#[macro_use] extern crate heapsize_derive; #[macro_use] extern crate heapsize_derive;
@ -32,6 +33,7 @@ extern crate uuid;
extern crate webrender_traits; extern crate webrender_traits;
extern crate websocket; extern crate websocket;
use bluetooth_traits::{BluetoothResponseListener, BluetoothResponseResult};
use cookie_rs::Cookie; use cookie_rs::Cookie;
use filemanager_thread::FileManagerThreadMsg; use filemanager_thread::FileManagerThreadMsg;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
@ -291,6 +293,13 @@ impl<T: FetchResponseListener> Action<T> for FetchResponseMsg {
} }
} }
impl<T: BluetoothResponseListener> Action<T> for BluetoothResponseResult {
/// Execute the default action on a provided listener.
fn process(self, listener: &mut T) {
listener.response(self)
}
}
/// A wrapper for a network load that can either be channel or event-based. /// A wrapper for a network load that can either be channel or event-based.
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub enum LoadConsumer { pub enum LoadConsumer {

View file

@ -2,7 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::{BluetoothError, BluetoothMethodMsg}; use bluetooth_traits::{BluetoothError, BluetoothRequest};
use bluetooth_traits::{BluetoothResponse, BluetoothResponseListener, BluetoothResponseResult};
use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted}; use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted};
use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence}; use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence};
use bluetooth_traits::scanfilter::{RequestDeviceoptions, ServiceUUIDSequence}; use bluetooth_traits::scanfilter::{RequestDeviceoptions, ServiceUUIDSequence};
@ -13,6 +14,7 @@ use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions;
use dom::bindings::error::Error::{self, NotFound, Security, Type}; use dom::bindings::error::Error::{self, NotFound, Security, Type};
use dom::bindings::error::Fallible; use dom::bindings::error::Fallible;
use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::refcounted::{Trusted, TrustedPromise};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::bluetoothadvertisingdata::BluetoothAdvertisingData; use dom::bluetoothadvertisingdata::BluetoothAdvertisingData;
@ -24,9 +26,12 @@ use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
use dom::globalscope::GlobalScope; use dom::globalscope::GlobalScope;
use dom::promise::Promise; use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use js::conversions::ToJSValConvertible; use ipc_channel::router::ROUTER;
use js::jsapi::{JSAutoCompartment, JSContext};
use network_listener::{NetworkListener, PreInvoke};
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex};
const FILTER_EMPTY_ERROR: &'static str = "'filters' member, if present, must be nonempty to find any devices."; const FILTER_EMPTY_ERROR: &'static str = "'filters' member, if present, must be nonempty to find any devices.";
const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way."; const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way.";
@ -43,6 +48,33 @@ const SERVICE_ERROR: &'static str = "'services', if present, must contain at lea
const OPTIONS_ERROR: &'static str = "Fields of 'options' conflict with each other. const OPTIONS_ERROR: &'static str = "Fields of 'options' conflict with each other.
Either 'acceptAllDevices' member must be true, or 'filters' member must be set to a value."; Either 'acceptAllDevices' member must be true, or 'filters' member must be set to a value.";
struct BluetoothContext<T: AsyncBluetoothListener + Reflectable> {
promise: Option<TrustedPromise>,
receiver: Trusted<T>,
}
pub trait AsyncBluetoothListener {
fn handle_response(&self, result: BluetoothResponse, cx: *mut JSContext, promise: &Rc<Promise>);
}
impl<Listener: AsyncBluetoothListener + Reflectable> PreInvoke for BluetoothContext<Listener> {}
impl<Listener: AsyncBluetoothListener + Reflectable> BluetoothResponseListener for BluetoothContext<Listener> {
#[allow(unrooted_must_root)]
fn response(&mut self, response: BluetoothResponseResult) {
let promise = self.promise.take().expect("bt promise is missing").root();
let promise_cx = promise.global().get_cx();
// JSAutoCompartment needs to be manually made.
// Otherwise, Servo will crash.
let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
match response {
Ok(response) => self.receiver.root().handle_response(response, promise_cx, &promise),
Err(error) => promise.reject_error(promise_cx, Error::from(error)),
}
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth // https://webbluetoothcg.github.io/web-bluetooth/#bluetooth
#[dom_struct] #[dom_struct]
pub struct Bluetooth { pub struct Bluetooth {
@ -83,73 +115,53 @@ impl Bluetooth {
&self.descriptor_instance_map &self.descriptor_instance_map
} }
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> { fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread() self.global().as_window().bluetooth_thread()
} }
fn request_device(&self, option: &RequestDeviceOptions) -> Fallible<Root<BluetoothDevice>> {
// Step 1.
// TODO(#4282): Reject promise.
if (option.filters.is_some() && option.acceptAllDevices) ||
(option.filters.is_none() && !option.acceptAllDevices) {
return Err(Type(OPTIONS_ERROR.to_owned()));
}
// Step 2.
if !option.acceptAllDevices {
return self.request_bluetooth_devices(&option.filters, &option.optionalServices);
}
self.request_bluetooth_devices(&None, &option.optionalServices)
// TODO(#4282): Step 3-5: Reject and resolve promise.
}
// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
fn request_bluetooth_devices(&self, fn request_bluetooth_devices(&self,
p: &Rc<Promise>,
filters: &Option<Vec<BluetoothRequestDeviceFilter>>, filters: &Option<Vec<BluetoothRequestDeviceFilter>>,
optional_services: &Option<Vec<BluetoothServiceUUID>>) optional_services: &Option<Vec<BluetoothServiceUUID>>) {
-> Fallible<Root<BluetoothDevice>> {
// TODO: Step 1: Triggered by user activation. // TODO: Step 1: Triggered by user activation.
// Step 2. // Step 2.
let option = try!(convert_request_device_options(filters, optional_services)); let option = match convert_request_device_options(filters, optional_services) {
Ok(o) => o,
Err(e) => {
p.reject_error(p.global().get_cx(), e);
return;
}
};
// TODO: Step 3-5: Implement the permission API. // TODO: Step 3-5: Implement the permission API.
// Note: Steps 6-8 are implemented in // Note: Steps 6-8 are implemented in
// components/net/bluetooth_thread.rs in request_device function. // components/net/bluetooth_thread.rs in request_device function.
let (sender, receiver) = ipc::channel().unwrap(); let sender = response_async(p, self);
self.get_bluetooth_thread().send(BluetoothMethodMsg::RequestDevice(option, sender)).unwrap(); self.get_bluetooth_thread().send(BluetoothRequest::RequestDevice(option, sender)).unwrap();
let device = receiver.recv().unwrap();
// TODO: Step 9-10: Implement the permission API.
// Step 11: This step is optional.
// Step 12-13.
match device {
Ok(device) => {
let mut device_instance_map = self.device_instance_map.borrow_mut();
if let Some(existing_device) = device_instance_map.get(&device.id.clone()) {
return Ok(existing_device.get());
} }
let ad_data = BluetoothAdvertisingData::new(&self.global(),
device.appearance,
device.tx_power,
device.rssi);
let bt_device = BluetoothDevice::new(&self.global(),
DOMString::from(device.id.clone()),
device.name.map(DOMString::from),
&ad_data,
&self);
device_instance_map.insert(device.id, MutHeap::new(&bt_device));
Ok(bt_device)
},
Err(error) => {
Err(Error::from(error))
},
} }
} pub fn response_async<T: AsyncBluetoothListener + Reflectable + 'static>(
promise: &Rc<Promise>,
receiver: &T) -> IpcSender<BluetoothResponseResult> {
let (action_sender, action_receiver) = ipc::channel().unwrap();
let chan = receiver.global().networking_task_source();
let context = Arc::new(Mutex::new(BluetoothContext {
promise: Some(TrustedPromise::new(promise.clone())),
receiver: Trusted::new(receiver),
}));
let listener = NetworkListener {
context: context,
script_chan: chan,
wrapper: None,
};
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
listener.notify_response(message.to().unwrap());
});
action_sender
} }
// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
@ -300,18 +312,6 @@ fn canonicalize_filter(filter: &BluetoothRequestDeviceFilter) -> Fallible<Blueto
service_data_uuid)) service_data_uuid))
} }
#[allow(unrooted_must_root)]
pub fn result_to_promise<T: ToJSValConvertible>(global: &GlobalScope,
bluetooth_result: Fallible<T>)
-> Rc<Promise> {
let p = Promise::new(global);
match bluetooth_result {
Ok(v) => p.resolve_native(p.global().get_cx(), &v),
Err(e) => p.reject_error(p.global().get_cx(), e),
}
p
}
impl From<BluetoothError> for Error { impl From<BluetoothError> for Error {
fn from(error: BluetoothError) -> Self { fn from(error: BluetoothError) -> Self {
match error { match error {
@ -329,6 +329,45 @@ impl BluetoothMethods for Bluetooth {
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
fn RequestDevice(&self, option: &RequestDeviceOptions) -> Rc<Promise> { fn RequestDevice(&self, option: &RequestDeviceOptions) -> Rc<Promise> {
result_to_promise(&self.global(), self.request_device(option)) let p = Promise::new(&self.global());
// Step 1.
if (option.filters.is_some() && option.acceptAllDevices) ||
(option.filters.is_none() && !option.acceptAllDevices) {
p.reject_error(p.global().get_cx(), Error::Type(OPTIONS_ERROR.to_owned()));
return p;
}
// Step 2.
if !option.acceptAllDevices {
self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices);
} else {
self.request_bluetooth_devices(&p, &None, &option.optionalServices);
}
// TODO(#4282): Step 3-5: Reject and resolve promise.
return p;
}
}
impl AsyncBluetoothListener for Bluetooth {
fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) {
match response {
BluetoothResponse::RequestDevice(device) => {
let mut device_instance_map = self.device_instance_map.borrow_mut();
if let Some(existing_device) = device_instance_map.get(&device.id.clone()) {
return promise.resolve_native(promise_cx, &existing_device.get());
}
let ad_data = BluetoothAdvertisingData::new(&self.global(),
device.appearance,
device.tx_power,
device.rssi);
let bt_device = BluetoothDevice::new(&self.global(),
DOMString::from(device.id.clone()),
device.name.map(DOMString::from),
&ad_data,
&self);
device_instance_map.insert(device.id, MutHeap::new(&bt_device));
promise.resolve_native(promise_cx, &bt_device);
},
_ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())),
}
} }
} }

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::{BluetoothRequest, BluetoothResponse};
use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted}; use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BluetoothCharacteristicPropertiesBinding:: use dom::bindings::codegen::Bindings::BluetoothCharacteristicPropertiesBinding::
@ -13,19 +13,19 @@ use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::
BluetoothRemoteGATTCharacteristicMethods; BluetoothRemoteGATTCharacteristicMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::error::Error::{self, InvalidModification, Network, NotSupported, Security}; use dom::bindings::error::Error::{self, InvalidModification, Network, NotSupported, Security};
use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::{ByteString, DOMString}; use dom::bindings::str::{ByteString, DOMString};
use dom::bluetooth::result_to_promise; use dom::bluetooth::{AsyncBluetoothListener, response_async};
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties; use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor; use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor;
use dom::bluetoothremotegattservice::BluetoothRemoteGATTService; use dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
use dom::bluetoothuuid::{BluetoothDescriptorUUID, BluetoothUUID}; use dom::bluetoothuuid::{BluetoothDescriptorUUID, BluetoothUUID};
use dom::globalscope::GlobalScope; use dom::globalscope::GlobalScope;
use dom::promise::Promise; use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::IpcSender;
use js::jsapi::JSContext;
use std::rc::Rc; use std::rc::Rc;
// Maximum length of an attribute value. // Maximum length of an attribute value.
@ -73,150 +73,13 @@ impl BluetoothRemoteGATTCharacteristic {
BluetoothRemoteGATTCharacteristicBinding::Wrap) BluetoothRemoteGATTCharacteristicBinding::Wrap)
} }
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> { fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread() self.global().as_window().bluetooth_thread()
} }
fn get_instance_id(&self) -> String { fn get_instance_id(&self) -> String {
self.instance_id.clone() self.instance_id.clone()
} }
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor
fn get_descriptor(&self, descriptor: BluetoothDescriptorUUID) -> Fallible<Root<BluetoothRemoteGATTDescriptor>> {
let uuid = try!(BluetoothUUID::descriptor(descriptor)).to_string();
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
if !self.Service().Device().Gatt().Connected() {
return Err(Network)
}
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) => {
let context = self.service.get().get_device().get_context();
let mut descriptor_map = context.get_descriptor_map().borrow_mut();
if let Some(existing_descriptor) = descriptor_map.get(&descriptor.instance_id) {
return Ok(existing_descriptor.get());
}
let bt_descriptor = BluetoothRemoteGATTDescriptor::new(&self.global(),
self,
DOMString::from(descriptor.uuid),
descriptor.instance_id.clone());
descriptor_map.insert(descriptor.instance_id, MutHeap::new(&bt_descriptor));
Ok(bt_descriptor)
},
Err(error) => {
Err(Error::from(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptors
fn get_descriptors(&self,
descriptor: Option<BluetoothDescriptorUUID>)
-> Fallible<Vec<Root<BluetoothRemoteGATTDescriptor>>> {
let mut uuid: Option<String> = None;
if let Some(d) = descriptor {
uuid = Some(try!(BluetoothUUID::descriptor(d)).to_string());
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
}
};
if !self.Service().Device().Gatt().Connected() {
return Err(Network)
}
let mut descriptors = vec!();
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) => {
let context = self.service.get().get_device().get_context();
let mut descriptor_map = context.get_descriptor_map().borrow_mut();
for descriptor in descriptor_vec {
let bt_descriptor = match descriptor_map.get(&descriptor.instance_id) {
Some(existing_descriptor) => existing_descriptor.get(),
None => {
BluetoothRemoteGATTDescriptor::new(&self.global(),
self,
DOMString::from(descriptor.uuid),
descriptor.instance_id.clone())
},
};
if !descriptor_map.contains_key(&descriptor.instance_id) {
descriptor_map.insert(descriptor.instance_id, MutHeap::new(&bt_descriptor));
}
descriptors.push(bt_descriptor);
}
Ok(descriptors)
},
Err(error) => {
Err(Error::from(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue
fn read_value(&self) -> Fallible<ByteString> {
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Reads) {
return Err(Security)
}
let (sender, receiver) = ipc::channel().unwrap();
if !self.Service().Device().Gatt().Connected() {
return Err(Network)
}
if !self.Properties().Read() {
return Err(NotSupported)
}
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(Error::from(error))
},
};
*self.value.borrow_mut() = Some(value.clone());
Ok(value)
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue
fn write_value(&self, value: Vec<u8>) -> ErrorResult {
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Writes) {
return Err(Security)
}
if value.len() > MAXIMUM_ATTRIBUTE_LENGTH {
return Err(InvalidModification)
}
if !self.Service().Device().Gatt().Connected() {
return Err(Network)
}
if !(self.Properties().Write() ||
self.Properties().WriteWithoutResponse() ||
self.Properties().AuthenticatedSignedWrites()) {
return Err(NotSupported)
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::WriteValue(self.get_instance_id(), value.clone(), sender)).unwrap();
let result = receiver.recv().unwrap();
match result {
Ok(_) => Ok(*self.value.borrow_mut() = Some(ByteString::new(value))),
Err(error) => {
Err(Error::from(error))
},
}
}
} }
impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteristic { impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteristic {
@ -238,7 +101,27 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor
fn GetDescriptor(&self, descriptor: BluetoothDescriptorUUID) -> Rc<Promise> { fn GetDescriptor(&self, descriptor: BluetoothDescriptorUUID) -> Rc<Promise> {
result_to_promise(&self.global(), self.get_descriptor(descriptor)) let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
let uuid = match BluetoothUUID::descriptor(descriptor) {
Ok(uuid) => uuid.to_string(),
Err(e) => {
p.reject_error(p_cx, e);
return p;
}
};
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
p.reject_error(p_cx, Security);
return p;
}
if !self.Service().Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::GetDescriptor(self.get_instance_id(), uuid, sender)).unwrap();
return p;
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
@ -246,7 +129,32 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
fn GetDescriptors(&self, fn GetDescriptors(&self,
descriptor: Option<BluetoothDescriptorUUID>) descriptor: Option<BluetoothDescriptorUUID>)
-> Rc<Promise> { -> Rc<Promise> {
result_to_promise(&self.global(), self.get_descriptors(descriptor)) let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
let mut uuid: Option<String> = None;
if let Some(d) = descriptor {
uuid = match BluetoothUUID::descriptor(d) {
Ok(uuid) => Some(uuid.to_string()),
Err(e) => {
p.reject_error(p_cx, e);
return p;
}
};
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
p.reject_error(p_cx, Security);
return p;
}
}
};
if !self.Service().Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::GetDescriptors(self.get_instance_id(), uuid, sender)).unwrap();
return p;
} }
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-value // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-value
@ -257,12 +165,105 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue
fn ReadValue(&self) -> Rc<Promise> { fn ReadValue(&self) -> Rc<Promise> {
result_to_promise(&self.global(), self.read_value()) let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Reads) {
p.reject_error(p_cx, Security);
return p;
}
if !self.Service().Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
if !self.Properties().Read() {
p.reject_error(p_cx, NotSupported);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::ReadValue(self.get_instance_id(), sender)).unwrap();
return p;
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue
fn WriteValue(&self, value: Vec<u8>) -> Rc<Promise> { fn WriteValue(&self, value: Vec<u8>) -> Rc<Promise> {
result_to_promise(&self.global(), self.write_value(value)) let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Writes) {
p.reject_error(p_cx, Security);
return p;
}
if value.len() > MAXIMUM_ATTRIBUTE_LENGTH {
p.reject_error(p_cx, InvalidModification);
return p;
}
if !self.Service().Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
if !(self.Properties().Write() ||
self.Properties().WriteWithoutResponse() ||
self.Properties().AuthenticatedSignedWrites()) {
p.reject_error(p_cx, NotSupported);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::WriteValue(self.get_instance_id(), value, sender)).unwrap();
return p;
}
}
impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic {
fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) {
match response {
BluetoothResponse::GetDescriptor(descriptor) => {
let context = self.service.get().get_device().get_context();
let mut descriptor_map = context.get_descriptor_map().borrow_mut();
if let Some(existing_descriptor) = descriptor_map.get(&descriptor.instance_id) {
return promise.resolve_native(promise_cx, &existing_descriptor.get());
}
let bt_descriptor = BluetoothRemoteGATTDescriptor::new(&self.global(),
self,
DOMString::from(descriptor.uuid),
descriptor.instance_id.clone());
descriptor_map.insert(descriptor.instance_id, MutHeap::new(&bt_descriptor));
promise.resolve_native(promise_cx, &bt_descriptor);
},
BluetoothResponse::GetDescriptors(descriptors_vec) => {
let mut descriptors = vec!();
let context = self.service.get().get_device().get_context();
let mut descriptor_map = context.get_descriptor_map().borrow_mut();
for descriptor in descriptors_vec {
let bt_descriptor = match descriptor_map.get(&descriptor.instance_id) {
Some(existing_descriptor) => existing_descriptor.get(),
None => {
BluetoothRemoteGATTDescriptor::new(&self.global(),
self,
DOMString::from(descriptor.uuid),
descriptor.instance_id.clone())
},
};
if !descriptor_map.contains_key(&descriptor.instance_id) {
descriptor_map.insert(descriptor.instance_id, MutHeap::new(&bt_descriptor));
}
descriptors.push(bt_descriptor);
}
promise.resolve_native(promise_cx, &descriptors);
},
BluetoothResponse::ReadValue(result) => {
let value = ByteString::new(result);
*self.value.borrow_mut() = Some(value.clone());
promise.resolve_native(promise_cx, &value);
},
BluetoothResponse::WriteValue(result) => {
let value = ByteString::new(result);
*self.value.borrow_mut() = Some(value.clone());
promise.resolve_native(promise_cx, &value);
},
_ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())),
}
} }
} }

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::{BluetoothRequest, BluetoothResponse};
use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted}; use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods; use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
@ -12,16 +12,16 @@ use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::error::Error::{self, InvalidModification, Network, Security}; use dom::bindings::error::Error::{self, InvalidModification, Network, Security};
use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::{ByteString, DOMString}; use dom::bindings::str::{ByteString, DOMString};
use dom::bluetooth::result_to_promise; use dom::bluetooth::{AsyncBluetoothListener, response_async};
use dom::bluetoothremotegattcharacteristic::{BluetoothRemoteGATTCharacteristic, MAXIMUM_ATTRIBUTE_LENGTH}; use dom::bluetoothremotegattcharacteristic::{BluetoothRemoteGATTCharacteristic, MAXIMUM_ATTRIBUTE_LENGTH};
use dom::globalscope::GlobalScope; use dom::globalscope::GlobalScope;
use dom::promise::Promise; use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::IpcSender;
use js::jsapi::JSContext;
use std::rc::Rc; use std::rc::Rc;
// http://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattdescriptor // http://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattdescriptor
@ -60,60 +60,13 @@ impl BluetoothRemoteGATTDescriptor {
BluetoothRemoteGATTDescriptorBinding::Wrap) BluetoothRemoteGATTDescriptorBinding::Wrap)
} }
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> { fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread() self.global().as_window().bluetooth_thread()
} }
fn get_instance_id(&self) -> String { fn get_instance_id(&self) -> String {
self.instance_id.clone() self.instance_id.clone()
} }
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
fn read_value(&self) -> Fallible<ByteString> {
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Reads) {
return Err(Security)
}
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(Error::from(error))
},
};
*self.value.borrow_mut() = Some(value.clone());
Ok(value)
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
fn write_value(&self, value: Vec<u8>) -> ErrorResult {
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Writes) {
return Err(Security)
}
if value.len() > MAXIMUM_ATTRIBUTE_LENGTH {
return Err(InvalidModification)
}
if !self.Characteristic().Service().Device().Gatt().Connected() {
return Err(Network)
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::WriteValue(self.get_instance_id(), value.clone(), sender)).unwrap();
let result = receiver.recv().unwrap();
match result {
Ok(_) => Ok(*self.value.borrow_mut() = Some(ByteString::new(value))),
Err(error) => {
Err(Error::from(error))
},
}
}
} }
impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor { impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor {
@ -135,12 +88,60 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor {
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
fn ReadValue(&self) -> Rc<Promise> { fn ReadValue(&self) -> Rc<Promise> {
result_to_promise(&self.global(), self.read_value()) let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Reads) {
p.reject_error(p_cx, Security);
return p;
}
if !self.Characteristic().Service().Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::ReadValue(self.get_instance_id(), sender)).unwrap();
return p;
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
fn WriteValue(&self, value: Vec<u8>) -> Rc<Promise> { fn WriteValue(&self, value: Vec<u8>) -> Rc<Promise> {
result_to_promise(&self.global(), self.write_value(value)) let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
if uuid_is_blacklisted(self.uuid.as_ref(), Blacklist::Writes) {
p.reject_error(p_cx, Security);
return p;
}
if value.len() > MAXIMUM_ATTRIBUTE_LENGTH {
p.reject_error(p_cx, InvalidModification);
return p;
}
if !self.Characteristic().Service().Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::WriteValue(self.get_instance_id(), value, sender)).unwrap();
return p;
}
}
impl AsyncBluetoothListener for BluetoothRemoteGATTDescriptor {
fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) {
match response {
BluetoothResponse::ReadValue(result) => {
let value = ByteString::new(result);
*self.value.borrow_mut() = Some(value.clone());
promise.resolve_native(promise_cx, &value);
},
BluetoothResponse::WriteValue(result) => {
let value = ByteString::new(result);
*self.value.borrow_mut() = Some(value.clone());
promise.resolve_native(promise_cx, &value);
},
_ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())),
}
} }
} }

View file

@ -2,23 +2,24 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::{BluetoothRequest, BluetoothResponse};
use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted}; use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods; use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::error::Error::{self, Network, Security}; use dom::bindings::error::Error::{self, Network, Security};
use dom::bindings::error::ErrorResult;
use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::bluetooth::result_to_promise; use dom::bluetooth::{AsyncBluetoothListener, response_async};
use dom::bluetoothdevice::BluetoothDevice; use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattservice::BluetoothRemoteGATTService; use dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID}; use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
use dom::globalscope::GlobalScope; use dom::globalscope::GlobalScope;
use dom::promise::Promise; use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::JSContext;
use std::cell::Cell; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
@ -45,109 +46,9 @@ impl BluetoothRemoteGATTServer {
BluetoothRemoteGATTServerBinding::Wrap) BluetoothRemoteGATTServerBinding::Wrap)
} }
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> { fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread() self.global().as_window().bluetooth_thread()
} }
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
fn connect(&self) -> Fallible<Root<BluetoothRemoteGATTServer>> {
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(Error::from(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice
fn get_primary_service(&self, service: BluetoothServiceUUID) -> Fallible<Root<BluetoothRemoteGATTService>> {
let uuid = try!(BluetoothUUID::service(service)).to_string();
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
if !self.Device().Gatt().Connected() {
return Err(Network)
}
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) => {
let context = self.device.get().get_context();
let mut service_map = context.get_service_map().borrow_mut();
if let Some(existing_service) = service_map.get(&service.instance_id) {
return Ok(existing_service.get());
}
let bt_service = BluetoothRemoteGATTService::new(&self.global(),
&self.device.get(),
DOMString::from(service.uuid),
service.is_primary,
service.instance_id.clone());
service_map.insert(service.instance_id, MutHeap::new(&bt_service));
Ok(bt_service)
},
Err(error) => {
Err(Error::from(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices
fn get_primary_services(&self,
service: Option<BluetoothServiceUUID>)
-> Fallible<Vec<Root<BluetoothRemoteGATTService>>> {
let mut uuid: Option<String> = None;
if let Some(s) = service {
uuid = Some(try!(BluetoothUUID::service(s)).to_string());
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
}
};
if !self.Device().Gatt().Connected() {
return Err(Network)
}
let mut services = vec!();
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) => {
let context = self.device.get().get_context();
let mut service_map = context.get_service_map().borrow_mut();
for service in service_vec {
let bt_service = match service_map.get(&service.instance_id) {
Some(existing_service) => existing_service.get(),
None => {
BluetoothRemoteGATTService::new(&self.global(),
&self.device.get(),
DOMString::from(service.uuid),
service.is_primary,
service.instance_id.clone())
},
};
if !service_map.contains_key(&service.instance_id) {
service_map.insert(service.instance_id, MutHeap::new(&bt_service));
}
services.push(bt_service);
}
Ok(services)
},
Err(error) => {
Err(Error::from(error))
},
}
}
} }
impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
@ -164,14 +65,18 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
fn Connect(&self) -> Rc<Promise> { fn Connect(&self) -> Rc<Promise> {
result_to_promise(&self.global(), self.connect()) let p = Promise::new(&self.global());
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::GATTServerConnect(String::from(self.Device().Id()), sender)).unwrap();
return p;
} }
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect
fn Disconnect(&self) -> ErrorResult { fn Disconnect(&self) -> ErrorResult {
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send( self.get_bluetooth_thread().send(
BluetoothMethodMsg::GATTServerDisconnect(String::from(self.Device().Id()), sender)).unwrap(); BluetoothRequest::GATTServerDisconnect(String::from(self.Device().Id()), sender)).unwrap();
let server = receiver.recv().unwrap(); let server = receiver.recv().unwrap();
match server { match server {
Ok(connected) => { Ok(connected) => {
@ -187,14 +92,105 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice
fn GetPrimaryService(&self, service: BluetoothServiceUUID) -> Rc<Promise> { fn GetPrimaryService(&self, service: BluetoothServiceUUID) -> Rc<Promise> {
result_to_promise(&self.global(), self.get_primary_service(service)) let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
let uuid = match BluetoothUUID::service(service) {
Ok(uuid) => uuid.to_string(),
Err(e) => {
p.reject_error(p_cx, e);
return p;
}
};
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
p.reject_error(p_cx, Security);
return p;
}
if !self.Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::GetPrimaryService(String::from(self.Device().Id()), uuid, sender)).unwrap();
return p;
} }
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices
fn GetPrimaryServices(&self, fn GetPrimaryServices(&self, service: Option<BluetoothServiceUUID>) -> Rc<Promise> {
service: Option<BluetoothServiceUUID>) let p = Promise::new(&self.global());
-> Rc<Promise> { let p_cx = p.global().get_cx();
result_to_promise(&self.global(), self.get_primary_services(service)) let mut uuid: Option<String> = None;
if let Some(s) = service {
uuid = match BluetoothUUID::service(s) {
Ok(uuid) => Some(uuid.to_string()),
Err(e) => {
p.reject_error(p_cx, e);
return p;
}
};
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
p.reject_error(p_cx, Security);
return p;
}
}
};
if !self.Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::GetPrimaryServices(String::from(self.Device().Id()), uuid, sender)).unwrap();
return p;
}
}
impl AsyncBluetoothListener for BluetoothRemoteGATTServer {
fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) {
match response {
BluetoothResponse::GATTServerConnect(connected) => {
self.connected.set(connected);
promise.resolve_native(promise_cx, self);
},
BluetoothResponse::GetPrimaryService(service) => {
let context = self.device.get().get_context();
let mut service_map = context.get_service_map().borrow_mut();
if let Some(existing_service) = service_map.get(&service.instance_id) {
promise.resolve_native(promise_cx, &existing_service.get());
}
let bt_service = BluetoothRemoteGATTService::new(&self.global(),
&self.device.get(),
DOMString::from(service.uuid),
service.is_primary,
service.instance_id.clone());
service_map.insert(service.instance_id, MutHeap::new(&bt_service));
promise.resolve_native(promise_cx, &bt_service);
},
BluetoothResponse::GetPrimaryServices(services_vec) => {
let mut services = vec!();
let context = self.device.get().get_context();
let mut service_map = context.get_service_map().borrow_mut();
for service in services_vec {
let bt_service = match service_map.get(&service.instance_id) {
Some(existing_service) => existing_service.get(),
None => {
BluetoothRemoteGATTService::new(&self.global(),
&self.device.get(),
DOMString::from(service.uuid),
service.is_primary,
service.instance_id.clone())
},
};
if !service_map.contains_key(&service.instance_id) {
service_map.insert(service.instance_id, MutHeap::new(&bt_service));
}
services.push(bt_service);
}
promise.resolve_native(promise_cx, &services);
},
_ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())),
}
} }
} }

View file

@ -2,25 +2,25 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::{BluetoothRequest, BluetoothResponse};
use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted}; use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted};
use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods; use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{self, Network, Security}; use dom::bindings::error::Error::{self, Network, Security};
use dom::bindings::error::Fallible;
use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::bluetooth::result_to_promise; use dom::bluetooth::{AsyncBluetoothListener, response_async};
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties; use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothdevice::BluetoothDevice; use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic; use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothServiceUUID, BluetoothUUID}; use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothServiceUUID, BluetoothUUID};
use dom::globalscope::GlobalScope; use dom::globalscope::GlobalScope;
use dom::promise::Promise; use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc::IpcSender;
use js::jsapi::JSContext;
use std::rc::Rc; use std::rc::Rc;
// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice
@ -66,38 +66,170 @@ impl BluetoothRemoteGATTService {
self.device.get() self.device.get()
} }
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> { fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread() self.global().as_window().bluetooth_thread()
} }
fn get_instance_id(&self) -> String { fn get_instance_id(&self) -> String {
self.instance_id.clone() self.instance_id.clone()
} }
}
impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-device
fn Device(&self) -> Root<BluetoothDevice> {
self.device.get()
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-isprimary
fn IsPrimary(&self) -> bool {
self.is_primary
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-uuid
fn Uuid(&self) -> DOMString {
self.uuid.clone()
}
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic
fn get_characteristic(&self, fn GetCharacteristic(&self,
characteristic: BluetoothCharacteristicUUID) characteristic: BluetoothCharacteristicUUID)
-> Fallible<Root<BluetoothRemoteGATTCharacteristic>> { -> Rc<Promise> {
let uuid = try!(BluetoothUUID::characteristic(characteristic)).to_string(); let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
let uuid = match BluetoothUUID::characteristic(characteristic) {
Ok(uuid) => uuid.to_string(),
Err(e) => {
p.reject_error(p_cx, e);
return p;
}
};
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) { if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security) p.reject_error(p_cx, Security);
return p;
} }
if !self.Device().Gatt().Connected() { if !self.Device().Gatt().Connected() {
return Err(Network) p.reject_error(p_cx, Network);
return p;
} }
let (sender, receiver) = ipc::channel().unwrap(); let sender = response_async(&p, self);
self.get_bluetooth_thread().send( self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap(); BluetoothRequest::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap();
let characteristic = receiver.recv().unwrap(); return p;
match characteristic { }
Ok(characteristic) => {
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics
fn GetCharacteristics(&self,
characteristic: Option<BluetoothCharacteristicUUID>)
-> Rc<Promise> {
let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
let mut uuid: Option<String> = None;
if let Some(c) = characteristic {
uuid = match BluetoothUUID::characteristic(c) {
Ok(uuid) => Some(uuid.to_string()),
Err(e) => {
p.reject_error(p_cx, e);
return p;
}
};
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
p.reject_error(p_cx, Security);
return p;
}
}
};
if !self.Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::GetCharacteristics(self.get_instance_id(), uuid, sender)).unwrap();
return p;
}
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice
fn GetIncludedService(&self,
service: BluetoothServiceUUID)
-> Rc<Promise> {
let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
let uuid = match BluetoothUUID::service(service) {
Ok(uuid) => uuid.to_string(),
Err(e) => {
p.reject_error(p_cx, e);
return p;
}
};
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
p.reject_error(p_cx, Security);
return p;
}
if !self.Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::GetIncludedService(self.get_instance_id(),
uuid,
sender)).unwrap();
return p;
}
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices
fn GetIncludedServices(&self,
service: Option<BluetoothServiceUUID>)
-> Rc<Promise> {
let p = Promise::new(&self.global());
let p_cx = p.global().get_cx();
let mut uuid: Option<String> = None;
if let Some(s) = service {
uuid = match BluetoothUUID::service(s) {
Ok(uuid) => Some(uuid.to_string()),
Err(e) => {
p.reject_error(p_cx, e);
return p;
}
};
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
p.reject_error(p_cx, Security);
return p;
}
}
};
if !self.Device().Gatt().Connected() {
p.reject_error(p_cx, Network);
return p;
}
let sender = response_async(&p, self);
self.get_bluetooth_thread().send(
BluetoothRequest::GetIncludedServices(self.get_instance_id(),
uuid,
sender)).unwrap();
return p;
}
}
impl AsyncBluetoothListener for BluetoothRemoteGATTService {
fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) {
match response {
BluetoothResponse::GetCharacteristic(characteristic) => {
let context = self.device.get().get_context(); let context = self.device.get().get_context();
let mut characteristic_map = context.get_characteristic_map().borrow_mut(); let mut characteristic_map = context.get_characteristic_map().borrow_mut();
if let Some(existing_characteristic) = characteristic_map.get(&characteristic.instance_id) { if let Some(existing_characteristic) = characteristic_map.get(&characteristic.instance_id) {
return Ok(existing_characteristic.get()); return promise.resolve_native(promise_cx, &existing_characteristic.get());
} }
let global = self.global(); let properties =
let properties = BluetoothCharacteristicProperties::new(&global, BluetoothCharacteristicProperties::new(&self.global(),
characteristic.broadcast, characteristic.broadcast,
characteristic.read, characteristic.read,
characteristic.write_without_response, characteristic.write_without_response,
@ -107,46 +239,19 @@ impl BluetoothRemoteGATTService {
characteristic.authenticated_signed_writes, characteristic.authenticated_signed_writes,
characteristic.reliable_write, characteristic.reliable_write,
characteristic.writable_auxiliaries); characteristic.writable_auxiliaries);
let bt_characteristic = BluetoothRemoteGATTCharacteristic::new(&global, let bt_characteristic = BluetoothRemoteGATTCharacteristic::new(&self.global(),
self, self,
DOMString::from(characteristic.uuid), DOMString::from(characteristic.uuid),
&properties, &properties,
characteristic.instance_id.clone()); characteristic.instance_id.clone());
characteristic_map.insert(characteristic.instance_id, MutHeap::new(&bt_characteristic)); characteristic_map.insert(characteristic.instance_id, MutHeap::new(&bt_characteristic));
Ok(bt_characteristic) promise.resolve_native(promise_cx, &bt_characteristic);
}, },
Err(error) => { BluetoothResponse::GetCharacteristics(characteristics_vec) => {
Err(Error::from(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics
fn get_characteristics(&self,
characteristic: Option<BluetoothCharacteristicUUID>)
-> Fallible<Vec<Root<BluetoothRemoteGATTCharacteristic>>> {
let mut uuid: Option<String> = None;
if let Some(c) = characteristic {
uuid = Some(try!(BluetoothUUID::characteristic(c)).to_string());
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
}
};
if !self.Device().Gatt().Connected() {
return Err(Network)
}
let mut characteristics = vec!(); 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) => {
let context = self.device.get().get_context(); let context = self.device.get().get_context();
let mut characteristic_map = context.get_characteristic_map().borrow_mut(); let mut characteristic_map = context.get_characteristic_map().borrow_mut();
for characteristic in characteristic_vec { for characteristic in characteristics_vec {
let bt_characteristic = match characteristic_map.get(&characteristic.instance_id) { let bt_characteristic = match characteristic_map.get(&characteristic.instance_id) {
Some(existing_characteristic) => existing_characteristic.get(), Some(existing_characteristic) => existing_characteristic.get(),
None => { None => {
@ -174,149 +279,29 @@ impl BluetoothRemoteGATTService {
} }
characteristics.push(bt_characteristic); characteristics.push(bt_characteristic);
} }
Ok(characteristics) promise.resolve_native(promise_cx, &characteristics);
}, },
Err(error) => { BluetoothResponse::GetIncludedService(service) => {
Err(Error::from(error)) let s =
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice
fn get_included_service(&self,
service: BluetoothServiceUUID)
-> Fallible<Root<BluetoothRemoteGATTService>> {
let uuid = try!(BluetoothUUID::service(service)).to_string();
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
if !self.Device().Gatt().Connected() {
return Err(Network)
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetIncludedService(self.get_instance_id(),
uuid,
sender)).unwrap();
let service = receiver.recv().unwrap();
match service {
Ok(service) => {
let context = self.device.get().get_context();
let mut service_map = context.get_service_map().borrow_mut();
if let Some(existing_service) = service_map.get(&service.instance_id) {
return Ok(existing_service.get());
}
let bt_service = BluetoothRemoteGATTService::new(&self.global(),
&self.device.get(),
DOMString::from(service.uuid),
service.is_primary,
service.instance_id.clone());
service_map.insert(service.instance_id, MutHeap::new(&bt_service));
Ok(bt_service)
},
Err(error) => {
Err(Error::from(error))
},
}
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices
fn get_included_services(&self,
service: Option<BluetoothServiceUUID>)
-> Fallible<Vec<Root<BluetoothRemoteGATTService>>> {
let mut uuid: Option<String> = None;
if let Some(s) = service {
uuid = Some(try!(BluetoothUUID::service(s)).to_string());
if let Some(ref uuid) = uuid {
if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
return Err(Security)
}
}
};
if !self.Device().Gatt().Connected() {
return Err(Network)
}
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(
BluetoothMethodMsg::GetIncludedServices(self.get_instance_id(),
uuid,
sender)).unwrap();
let services_vec = receiver.recv().unwrap();
let mut services = vec!();
match services_vec {
Ok(service_vec) => {
let context = self.device.get().get_context();
let mut service_map = context.get_service_map().borrow_mut();
for service in service_vec {
let bt_service = match service_map.get(&service.instance_id) {
Some(existing_service) => existing_service.get(),
None => {
BluetoothRemoteGATTService::new(&self.global(), BluetoothRemoteGATTService::new(&self.global(),
&self.device.get(), &self.device.get(),
DOMString::from(service.uuid), DOMString::from(service.uuid),
service.is_primary, service.is_primary,
service.instance_id.clone()) service.instance_id);
promise.resolve_native(promise_cx, &s);
}, },
}; BluetoothResponse::GetIncludedServices(services_vec) => {
if !service_map.contains_key(&service.instance_id) { let s: Vec<Root<BluetoothRemoteGATTService>> =
service_map.insert(service.instance_id, MutHeap::new(&bt_service)); services_vec.into_iter()
} .map(|service| BluetoothRemoteGATTService::new(&self.global(),
services.push(bt_service); &self.device.get(),
} DOMString::from(service.uuid),
Ok(services) service.is_primary,
}, service.instance_id))
Err(error) => { .collect();
Err(Error::from(error)) promise.resolve_native(promise_cx, &s);
}, },
_ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())),
} }
} }
} }
impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService {
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-device
fn Device(&self) -> Root<BluetoothDevice> {
self.device.get()
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-isprimary
fn IsPrimary(&self) -> bool {
self.is_primary
}
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-uuid
fn Uuid(&self) -> DOMString {
self.uuid.clone()
}
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic
fn GetCharacteristic(&self,
characteristic: BluetoothCharacteristicUUID)
-> Rc<Promise> {
result_to_promise(&self.global(), self.get_characteristic(characteristic))
}
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics
fn GetCharacteristics(&self,
characteristic: Option<BluetoothCharacteristicUUID>)
-> Rc<Promise> {
result_to_promise(&self.global(), self.get_characteristics(characteristic))
}
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice
fn GetIncludedService(&self,
service: BluetoothServiceUUID)
-> Rc<Promise> {
result_to_promise(&self.global(), self.get_included_service(service))
}
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices
fn GetIncludedServices(&self,
service: Option<BluetoothServiceUUID>)
-> Rc<Promise> {
result_to_promise(&self.global(), self.get_included_services(service))
}
}

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::BluetoothRequest;
use dom::bindings::codegen::Bindings::TestRunnerBinding; use dom::bindings::codegen::Bindings::TestRunnerBinding;
use dom::bindings::codegen::Bindings::TestRunnerBinding::TestRunnerMethods; use dom::bindings::codegen::Bindings::TestRunnerBinding::TestRunnerMethods;
use dom::bindings::error::{Error, ErrorResult}; use dom::bindings::error::{Error, ErrorResult};
@ -31,7 +31,7 @@ impl TestRunner {
TestRunnerBinding::Wrap) TestRunnerBinding::Wrap)
} }
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> { fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread() self.global().as_window().bluetooth_thread()
} }
} }
@ -40,7 +40,7 @@ impl TestRunnerMethods for TestRunner {
// https://webbluetoothcg.github.io/web-bluetooth/tests#setBluetoothMockDataSet // https://webbluetoothcg.github.io/web-bluetooth/tests#setBluetoothMockDataSet
fn SetBluetoothMockDataSet(&self, dataSetName: DOMString) -> ErrorResult { fn SetBluetoothMockDataSet(&self, dataSetName: DOMString) -> ErrorResult {
let (sender, receiver) = ipc::channel().unwrap(); let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(BluetoothMethodMsg::Test(String::from(dataSetName), sender)).unwrap(); self.get_bluetooth_thread().send(BluetoothRequest::Test(String::from(dataSetName), sender)).unwrap();
match receiver.recv().unwrap().into() { match receiver.recv().unwrap().into() {
Ok(()) => { Ok(()) => {
Ok(()) Ok(())

View file

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au; use app_units::Au;
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::BluetoothRequest;
use cssparser::Parser; use cssparser::Parser;
use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
use dom::bindings::callback::ExceptionHandling; use dom::bindings::callback::ExceptionHandling;
@ -203,7 +203,7 @@ pub struct Window {
/// A handle for communicating messages to the bluetooth thread. /// A handle for communicating messages to the bluetooth thread.
#[ignore_heap_size_of = "channels are hard"] #[ignore_heap_size_of = "channels are hard"]
bluetooth_thread: IpcSender<BluetoothMethodMsg>, bluetooth_thread: IpcSender<BluetoothRequest>,
/// Pending scroll to fragment event, if any /// Pending scroll to fragment event, if any
fragment_name: DOMRefCell<Option<String>>, fragment_name: DOMRefCell<Option<String>>,
@ -304,7 +304,7 @@ impl Window {
self.browsing_context.get().unwrap() self.browsing_context.get().unwrap()
} }
pub fn bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> { pub fn bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.bluetooth_thread.clone() self.bluetooth_thread.clone()
} }
@ -1521,7 +1521,7 @@ impl Window {
image_cache_chan: ImageCacheChan, image_cache_chan: ImageCacheChan,
image_cache_thread: ImageCacheThread, image_cache_thread: ImageCacheThread,
resource_threads: ResourceThreads, resource_threads: ResourceThreads,
bluetooth_thread: IpcSender<BluetoothMethodMsg>, bluetooth_thread: IpcSender<BluetoothRequest>,
mem_profiler_chan: mem::ProfilerChan, mem_profiler_chan: mem::ProfilerChan,
time_profiler_chan: ProfilerChan, time_profiler_chan: ProfilerChan,
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>, devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::{BluetoothResponseListener, BluetoothResponseResult};
use net_traits::{Action, FetchResponseListener, FetchResponseMsg}; use net_traits::{Action, FetchResponseListener, FetchResponseMsg};
use script_runtime::{CommonScriptMsg, ScriptChan}; use script_runtime::{CommonScriptMsg, ScriptChan};
use script_runtime::ScriptThreadEventCategory::NetworkEvent; use script_runtime::ScriptThreadEventCategory::NetworkEvent;
@ -40,6 +41,13 @@ impl<Listener: FetchResponseListener + PreInvoke + Send + 'static> NetworkListen
} }
} }
// helps type inference
impl<Listener: BluetoothResponseListener + PreInvoke + Send + 'static> NetworkListener<Listener> {
pub fn notify_response(&self, action: BluetoothResponseResult) {
self.notify(action);
}
}
/// A gating mechanism that runs before invoking the runnable on the target thread. /// A gating mechanism that runs before invoking the runnable on the target thread.
/// If the `should_invoke` method returns false, the runnable is discarded without /// If the `should_invoke` method returns false, the runnable is discarded without
/// being invoked. /// being invoked.

View file

@ -17,7 +17,7 @@
//! a page runs its course and the script thread returns to processing events in the main event //! a page runs its course and the script thread returns to processing events in the main event
//! loop. //! loop.
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::BluetoothRequest;
use devtools; use devtools;
use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo}; use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo};
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
@ -343,7 +343,7 @@ pub struct ScriptThread {
/// there are many iframes. /// there are many iframes.
resource_threads: ResourceThreads, resource_threads: ResourceThreads,
/// A handle to the bluetooth thread. /// A handle to the bluetooth thread.
bluetooth_thread: IpcSender<BluetoothMethodMsg>, bluetooth_thread: IpcSender<BluetoothRequest>,
/// The port on which the script thread receives messages (load URL, exit, etc.) /// The port on which the script thread receives messages (load URL, exit, etc.)
port: Receiver<MainThreadScriptMsg>, port: Receiver<MainThreadScriptMsg>,

View file

@ -38,7 +38,7 @@ extern crate url;
mod script_msg; mod script_msg;
pub mod webdriver_msg; pub mod webdriver_msg;
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::BluetoothRequest;
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
use euclid::Size2D; use euclid::Size2D;
use euclid::length::Length; use euclid::length::Length;
@ -445,7 +445,7 @@ pub struct InitialScriptState {
/// A channel to the resource manager thread. /// A channel to the resource manager thread.
pub resource_threads: ResourceThreads, pub resource_threads: ResourceThreads,
/// A channel to the bluetooth thread. /// A channel to the bluetooth thread.
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>, pub bluetooth_thread: IpcSender<BluetoothRequest>,
/// A channel to the image cache thread. /// A channel to the image cache thread.
pub image_cache_thread: ImageCacheThread, pub image_cache_thread: ImageCacheThread,
/// A channel to the time profiler thread. /// A channel to the time profiler thread.

View file

@ -1571,6 +1571,7 @@ dependencies = [
name = "net_traits" name = "net_traits"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"bluetooth_traits 0.0.1",
"cookie 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -64,7 +64,7 @@ fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { } fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { }
use bluetooth::BluetoothThreadFactory; use bluetooth::BluetoothThreadFactory;
use bluetooth_traits::BluetoothMethodMsg; use bluetooth_traits::BluetoothRequest;
use compositing::{CompositorProxy, IOCompositor}; use compositing::{CompositorProxy, IOCompositor};
use compositing::compositor_thread::InitialCompositorState; use compositing::compositor_thread::InitialCompositorState;
use compositing::windowing::WindowEvent; use compositing::windowing::WindowEvent;
@ -247,7 +247,7 @@ fn create_constellation(opts: opts::Opts,
supports_clipboard: bool, supports_clipboard: bool,
webrender_api_sender: webrender_traits::RenderApiSender) webrender_api_sender: webrender_traits::RenderApiSender)
-> (Sender<ConstellationMsg>, SWManagerSenders) { -> (Sender<ConstellationMsg>, SWManagerSenders) {
let bluetooth_thread: IpcSender<BluetoothMethodMsg> = BluetoothThreadFactory::new(); let bluetooth_thread: IpcSender<BluetoothRequest> = BluetoothThreadFactory::new();
let (public_resource_threads, private_resource_threads) = let (public_resource_threads, private_resource_threads) =
new_resource_threads(opts.user_agent, new_resource_threads(opts.user_agent,

1
ports/cef/Cargo.lock generated
View file

@ -1447,6 +1447,7 @@ dependencies = [
name = "net_traits" name = "net_traits"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"bluetooth_traits 0.0.1",
"cookie 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"heapsize_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",