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

@ -2,7 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::{BluetoothError, BluetoothMethodMsg};
use bluetooth_traits::{BluetoothError, BluetoothRequest};
use bluetooth_traits::{BluetoothResponse, BluetoothResponseListener, BluetoothResponseResult};
use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted};
use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence};
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::Fallible;
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::str::DOMString;
use dom::bluetoothadvertisingdata::BluetoothAdvertisingData;
@ -24,9 +26,12 @@ use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
use dom::globalscope::GlobalScope;
use dom::promise::Promise;
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::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_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.
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
#[dom_struct]
pub struct Bluetooth {
@ -83,75 +115,55 @@ impl Bluetooth {
&self.descriptor_instance_map
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
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
fn request_bluetooth_devices(&self,
p: &Rc<Promise>,
filters: &Option<Vec<BluetoothRequestDeviceFilter>>,
optional_services: &Option<Vec<BluetoothServiceUUID>>)
-> Fallible<Root<BluetoothDevice>> {
optional_services: &Option<Vec<BluetoothServiceUUID>>) {
// TODO: Step 1: Triggered by user activation.
// 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.
// Note: Steps 6-8 are implemented in
// components/net/bluetooth_thread.rs in request_device function.
let (sender, receiver) = ipc::channel().unwrap();
self.get_bluetooth_thread().send(BluetoothMethodMsg::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))
},
}
let sender = response_async(p, self);
self.get_bluetooth_thread().send(BluetoothRequest::RequestDevice(option, sender)).unwrap();
}
}
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
fn convert_request_device_options(filters: &Option<Vec<BluetoothRequestDeviceFilter>>,
optional_services: &Option<Vec<BluetoothServiceUUID>>)
@ -300,18 +312,6 @@ fn canonicalize_filter(filter: &BluetoothRequestDeviceFilter) -> Fallible<Blueto
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 {
fn from(error: BluetoothError) -> Self {
match error {
@ -329,6 +329,45 @@ impl BluetoothMethods for Bluetooth {
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
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
* 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 dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BluetoothCharacteristicPropertiesBinding::
@ -13,19 +13,19 @@ use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding::
BluetoothRemoteGATTCharacteristicMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::error::Error::{self, InvalidModification, Network, NotSupported, Security};
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::{ByteString, DOMString};
use dom::bluetooth::result_to_promise;
use dom::bluetooth::{AsyncBluetoothListener, response_async};
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor;
use dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
use dom::bluetoothuuid::{BluetoothDescriptorUUID, BluetoothUUID};
use dom::globalscope::GlobalScope;
use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::ipc::IpcSender;
use js::jsapi::JSContext;
use std::rc::Rc;
// Maximum length of an attribute value.
@ -73,150 +73,13 @@ impl BluetoothRemoteGATTCharacteristic {
BluetoothRemoteGATTCharacteristicBinding::Wrap)
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread()
}
fn get_instance_id(&self) -> String {
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 {
@ -238,7 +101,27 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor
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)]
@ -246,7 +129,32 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
fn GetDescriptors(&self,
descriptor: Option<BluetoothDescriptorUUID>)
-> 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
@ -257,12 +165,105 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue
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)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue
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
* 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 dom::bindings::cell::DOMRefCell;
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::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
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::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
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::globalscope::GlobalScope;
use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::ipc::IpcSender;
use js::jsapi::JSContext;
use std::rc::Rc;
// http://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattdescriptor
@ -60,60 +60,13 @@ impl BluetoothRemoteGATTDescriptor {
BluetoothRemoteGATTDescriptorBinding::Wrap)
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread()
}
fn get_instance_id(&self) -> String {
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 {
@ -135,12 +88,60 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor {
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue
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)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue
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
* 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 dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding;
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::ErrorResult;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetooth::result_to_promise;
use dom::bluetooth::{AsyncBluetoothListener, response_async};
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
use dom::globalscope::GlobalScope;
use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::JSContext;
use std::cell::Cell;
use std::rc::Rc;
@ -45,109 +46,9 @@ impl BluetoothRemoteGATTServer {
BluetoothRemoteGATTServerBinding::Wrap)
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
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 {
@ -164,14 +65,18 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect
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
fn Disconnect(&self) -> ErrorResult {
let (sender, receiver) = ipc::channel().unwrap();
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();
match server {
Ok(connected) => {
@ -187,14 +92,105 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer {
#[allow(unrooted_must_root)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice
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)]
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices
fn GetPrimaryServices(&self,
service: Option<BluetoothServiceUUID>)
-> Rc<Promise> {
result_to_promise(&self.global(), self.get_primary_services(service))
fn GetPrimaryServices(&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::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
* 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 dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding;
use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods;
use dom::bindings::error::Error::{self, Network, Security};
use dom::bindings::error::Fallible;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetooth::result_to_promise;
use dom::bluetooth::{AsyncBluetoothListener, response_async};
use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothServiceUUID, BluetoothUUID};
use dom::globalscope::GlobalScope;
use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::ipc::IpcSender;
use js::jsapi::JSContext;
use std::rc::Rc;
// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice
@ -66,87 +66,192 @@ impl BluetoothRemoteGATTService {
self.device.get()
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread()
}
fn get_instance_id(&self) -> String {
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
fn get_characteristic(&self,
characteristic: BluetoothCharacteristicUUID)
-> Fallible<Root<BluetoothRemoteGATTCharacteristic>> {
let uuid = try!(BluetoothUUID::characteristic(characteristic)).to_string();
fn GetCharacteristic(&self,
characteristic: BluetoothCharacteristicUUID)
-> Rc<Promise> {
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) {
return Err(Security)
p.reject_error(p_cx, Security);
return p;
}
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(
BluetoothMethodMsg::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap();
let characteristic = receiver.recv().unwrap();
match characteristic {
Ok(characteristic) => {
BluetoothRequest::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap();
return p;
}
#[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 mut characteristic_map = context.get_characteristic_map().borrow_mut();
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 = BluetoothCharacteristicProperties::new(&global,
characteristic.broadcast,
characteristic.read,
characteristic.write_without_response,
characteristic.write,
characteristic.notify,
characteristic.indicate,
characteristic.authenticated_signed_writes,
characteristic.reliable_write,
characteristic.writable_auxiliaries);
let bt_characteristic = BluetoothRemoteGATTCharacteristic::new(&global,
let properties =
BluetoothCharacteristicProperties::new(&self.global(),
characteristic.broadcast,
characteristic.read,
characteristic.write_without_response,
characteristic.write,
characteristic.notify,
characteristic.indicate,
characteristic.authenticated_signed_writes,
characteristic.reliable_write,
characteristic.writable_auxiliaries);
let bt_characteristic = BluetoothRemoteGATTCharacteristic::new(&self.global(),
self,
DOMString::from(characteristic.uuid),
&properties,
characteristic.instance_id.clone());
characteristic_map.insert(characteristic.instance_id, MutHeap::new(&bt_characteristic));
Ok(bt_characteristic)
promise.resolve_native(promise_cx, &bt_characteristic);
},
Err(error) => {
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 (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) => {
BluetoothResponse::GetCharacteristics(characteristics_vec) => {
let mut characteristics = vec!();
let context = self.device.get().get_context();
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) {
Some(existing_characteristic) => existing_characteristic.get(),
None => {
@ -174,149 +279,29 @@ impl BluetoothRemoteGATTService {
}
characteristics.push(bt_characteristic);
}
Ok(characteristics)
promise.resolve_native(promise_cx, &characteristics);
},
Err(error) => {
Err(Error::from(error))
BluetoothResponse::GetIncludedService(service) => {
let s =
BluetoothRemoteGATTService::new(&self.global(),
&self.device.get(),
DOMString::from(service.uuid),
service.is_primary,
service.instance_id);
promise.resolve_native(promise_cx, &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(),
&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))
BluetoothResponse::GetIncludedServices(services_vec) => {
let s: Vec<Root<BluetoothRemoteGATTService>> =
services_vec.into_iter()
.map(|service| BluetoothRemoteGATTService::new(&self.global(),
&self.device.get(),
DOMString::from(service.uuid),
service.is_primary,
service.instance_id))
.collect();
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
* 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::TestRunnerMethods;
use dom::bindings::error::{Error, ErrorResult};
@ -31,7 +31,7 @@ impl TestRunner {
TestRunnerBinding::Wrap)
}
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.global().as_window().bluetooth_thread()
}
}
@ -40,7 +40,7 @@ impl TestRunnerMethods for TestRunner {
// https://webbluetoothcg.github.io/web-bluetooth/tests#setBluetoothMockDataSet
fn SetBluetoothMockDataSet(&self, dataSetName: DOMString) -> ErrorResult {
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() {
Ok(()) => {
Ok(())

View file

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au;
use bluetooth_traits::BluetoothMethodMsg;
use bluetooth_traits::BluetoothRequest;
use cssparser::Parser;
use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
use dom::bindings::callback::ExceptionHandling;
@ -203,7 +203,7 @@ pub struct Window {
/// A handle for communicating messages to the bluetooth thread.
#[ignore_heap_size_of = "channels are hard"]
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
bluetooth_thread: IpcSender<BluetoothRequest>,
/// Pending scroll to fragment event, if any
fragment_name: DOMRefCell<Option<String>>,
@ -304,7 +304,7 @@ impl Window {
self.browsing_context.get().unwrap()
}
pub fn bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
pub fn bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
self.bluetooth_thread.clone()
}
@ -1521,7 +1521,7 @@ impl Window {
image_cache_chan: ImageCacheChan,
image_cache_thread: ImageCacheThread,
resource_threads: ResourceThreads,
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
bluetooth_thread: IpcSender<BluetoothRequest>,
mem_profiler_chan: mem::ProfilerChan,
time_profiler_chan: ProfilerChan,
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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use bluetooth_traits::{BluetoothResponseListener, BluetoothResponseResult};
use net_traits::{Action, FetchResponseListener, FetchResponseMsg};
use script_runtime::{CommonScriptMsg, ScriptChan};
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.
/// If the `should_invoke` method returns false, the runnable is discarded without
/// 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
//! loop.
use bluetooth_traits::BluetoothMethodMsg;
use bluetooth_traits::BluetoothRequest;
use devtools;
use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo};
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
@ -343,7 +343,7 @@ pub struct ScriptThread {
/// there are many iframes.
resource_threads: ResourceThreads,
/// 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.)
port: Receiver<MainThreadScriptMsg>,