Skip to content

Commit d9e1cf3

Browse files
committed
Webbluetooth Async behaviour
1 parent 1c26f44 commit d9e1cf3

File tree

18 files changed

+660
-582
lines changed

18 files changed

+660
-582
lines changed

components/bluetooth/lib.rs

Lines changed: 81 additions & 75 deletions
Large diffs are not rendered by default.

components/bluetooth_traits/lib.rs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,43 @@ pub type BluetoothDescriptorsMsg = Vec<BluetoothDescriptorMsg>;
7171

7272
pub type BluetoothResult<T> = Result<T, BluetoothError>;
7373

74+
pub type BluetoothResponseResult = Result<BluetoothResponse, BluetoothError>;
75+
7476
#[derive(Deserialize, Serialize)]
75-
pub enum BluetoothMethodMsg {
76-
RequestDevice(RequestDeviceoptions, IpcSender<BluetoothResult<BluetoothDeviceMsg>>),
77-
GATTServerConnect(String, IpcSender<BluetoothResult<bool>>),
77+
pub enum BluetoothRequest {
78+
RequestDevice(RequestDeviceoptions, IpcSender<BluetoothResponseResult>),
79+
GATTServerConnect(String, IpcSender<BluetoothResponseResult>),
7880
GATTServerDisconnect(String, IpcSender<BluetoothResult<bool>>),
79-
GetPrimaryService(String, String, IpcSender<BluetoothResult<BluetoothServiceMsg>>),
80-
GetPrimaryServices(String, Option<String>, IpcSender<BluetoothResult<BluetoothServicesMsg>>),
81-
GetIncludedService(String, String, IpcSender<BluetoothResult<BluetoothServiceMsg>>),
82-
GetIncludedServices(String, Option<String>, IpcSender<BluetoothResult<BluetoothServicesMsg>>),
83-
GetCharacteristic(String, String, IpcSender<BluetoothResult<BluetoothCharacteristicMsg>>),
84-
GetCharacteristics(String, Option<String>, IpcSender<BluetoothResult<BluetoothCharacteristicsMsg>>),
85-
GetDescriptor(String, String, IpcSender<BluetoothResult<BluetoothDescriptorMsg>>),
86-
GetDescriptors(String, Option<String>, IpcSender<BluetoothResult<BluetoothDescriptorsMsg>>),
87-
ReadValue(String, IpcSender<BluetoothResult<Vec<u8>>>),
88-
WriteValue(String, Vec<u8>, IpcSender<BluetoothResult<bool>>),
81+
GetPrimaryService(String, String, IpcSender<BluetoothResponseResult>),
82+
GetPrimaryServices(String, Option<String>, IpcSender<BluetoothResponseResult>),
83+
GetIncludedService(String, String, IpcSender<BluetoothResponseResult>),
84+
GetIncludedServices(String, Option<String>, IpcSender<BluetoothResponseResult>),
85+
GetCharacteristic(String, String, IpcSender<BluetoothResponseResult>),
86+
GetCharacteristics(String, Option<String>, IpcSender<BluetoothResponseResult>),
87+
GetDescriptor(String, String, IpcSender<BluetoothResponseResult>),
88+
GetDescriptors(String, Option<String>, IpcSender<BluetoothResponseResult>),
89+
ReadValue(String, IpcSender<BluetoothResponseResult>),
90+
WriteValue(String, Vec<u8>, IpcSender<BluetoothResponseResult>),
8991
Test(String, IpcSender<BluetoothResult<()>>),
9092
Exit,
9193
}
94+
95+
#[derive(Deserialize, Serialize)]
96+
pub enum BluetoothResponse {
97+
RequestDevice(BluetoothDeviceMsg),
98+
GATTServerConnect(bool),
99+
GetPrimaryService(BluetoothServiceMsg),
100+
GetPrimaryServices(BluetoothServicesMsg),
101+
GetIncludedService(BluetoothServiceMsg),
102+
GetIncludedServices(BluetoothServicesMsg),
103+
GetCharacteristic(BluetoothCharacteristicMsg),
104+
GetCharacteristics(BluetoothCharacteristicsMsg),
105+
GetDescriptor(BluetoothDescriptorMsg),
106+
GetDescriptors(BluetoothDescriptorsMsg),
107+
ReadValue(Vec<u8>),
108+
WriteValue(()),
109+
}
110+
111+
pub trait BluetoothResponseListener {
112+
fn response(&mut self, response: BluetoothResponseResult);
113+
}

components/constellation/constellation.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//! `LayoutThread`, and `PaintThread`.
1111
1212
use backtrace::Backtrace;
13-
use bluetooth_traits::BluetoothMethodMsg;
13+
use bluetooth_traits::BluetoothRequest;
1414
use canvas::canvas_paint_thread::CanvasPaintThread;
1515
use canvas::webgl_paint_thread::WebGLPaintThread;
1616
use canvas_traits::CanvasMsg;
@@ -117,7 +117,7 @@ pub struct Constellation<Message, LTF, STF> {
117117
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
118118

119119
/// A channel through which messages can be sent to the bluetooth thread.
120-
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
120+
bluetooth_thread: IpcSender<BluetoothRequest>,
121121

122122
/// Sender to Service Worker Manager thread
123123
swmanager_chan: Option<IpcSender<ServiceWorkerMsg>>,
@@ -193,7 +193,7 @@ pub struct InitialConstellationState {
193193
/// A channel to the developer tools, if applicable.
194194
pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
195195
/// A channel to the bluetooth thread.
196-
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>,
196+
pub bluetooth_thread: IpcSender<BluetoothRequest>,
197197
/// A channel to the image cache thread.
198198
pub image_cache_thread: ImageCacheThread,
199199
/// A channel to the font cache thread.
@@ -1084,7 +1084,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
10841084
}
10851085

10861086
debug!("Exiting bluetooth thread.");
1087-
if let Err(e) = self.bluetooth_thread.send(BluetoothMethodMsg::Exit) {
1087+
if let Err(e) = self.bluetooth_thread.send(BluetoothRequest::Exit) {
10881088
warn!("Exit bluetooth thread failed ({})", e);
10891089
}
10901090

components/constellation/pipeline.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5-
use bluetooth_traits::BluetoothMethodMsg;
5+
use bluetooth_traits::BluetoothRequest;
66
use compositing::CompositionPipeline;
77
use compositing::CompositorProxy;
88
use compositing::compositor_thread::Msg as CompositorMsg;
@@ -94,7 +94,7 @@ pub struct InitialPipelineState {
9494
/// A channel to the developer tools, if applicable.
9595
pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
9696
/// A channel to the bluetooth thread.
97-
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>,
97+
pub bluetooth_thread: IpcSender<BluetoothRequest>,
9898
/// A channel to the service worker manager thread
9999
pub swmanager_thread: IpcSender<SWManagerMsg>,
100100
/// A channel to the image cache thread.
@@ -384,7 +384,7 @@ pub struct UnprivilegedPipelineContent {
384384
layout_to_constellation_chan: IpcSender<LayoutMsg>,
385385
scheduler_chan: IpcSender<TimerEventRequest>,
386386
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
387-
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
387+
bluetooth_thread: IpcSender<BluetoothRequest>,
388388
swmanager_thread: IpcSender<SWManagerMsg>,
389389
image_cache_thread: ImageCacheThread,
390390
font_cache_thread: FontCacheThread,

components/net_traits/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ name = "net_traits"
1010
path = "lib.rs"
1111

1212
[dependencies]
13+
bluetooth_traits = {path = "../bluetooth_traits"}
1314
util = {path = "../util"}
1415
msg = {path = "../msg"}
1516
ipc-channel = "0.5"

components/net_traits/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#![deny(unsafe_code)]
1111

12+
extern crate bluetooth_traits;
1213
extern crate cookie as cookie_rs;
1314
extern crate heapsize;
1415
#[macro_use] extern crate heapsize_derive;
@@ -32,6 +33,7 @@ extern crate uuid;
3233
extern crate webrender_traits;
3334
extern crate websocket;
3435

36+
use bluetooth_traits::{BluetoothResponseListener, BluetoothResponseResult};
3537
use cookie_rs::Cookie;
3638
use filemanager_thread::FileManagerThreadMsg;
3739
use heapsize::HeapSizeOf;
@@ -287,6 +289,13 @@ impl<T: FetchResponseListener> Action<T> for FetchResponseMsg {
287289
}
288290
}
289291

292+
impl<T: BluetoothResponseListener> Action<T> for BluetoothResponseResult {
293+
/// Execute the default action on a provided listener.
294+
fn process(self, listener: &mut T) {
295+
listener.response(self)
296+
}
297+
}
298+
290299
/// A wrapper for a network load that can either be channel or event-based.
291300
#[derive(Deserialize, Serialize)]
292301
pub enum LoadConsumer {

components/script/dom/bluetooth.rs

Lines changed: 100 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted};
6-
use bluetooth_traits::{BluetoothError, BluetoothMethodMsg};
6+
use bluetooth_traits::{BluetoothError, BluetoothRequest};
7+
use bluetooth_traits::{BluetoothResponse, BluetoothResponseListener, BluetoothResponseResult};
78
use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence};
89
use bluetooth_traits::scanfilter::{RequestDeviceoptions, ServiceUUIDSequence};
910
use core::clone::Clone;
@@ -12,6 +13,7 @@ use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions;
1213
use dom::bindings::error::Error::{self, Security, Type};
1314
use dom::bindings::error::Fallible;
1415
use dom::bindings::js::Root;
16+
use dom::bindings::refcounted::{Trusted, TrustedPromise};
1517
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
1618
use dom::bindings::str::DOMString;
1719
use dom::bluetoothadvertisingdata::BluetoothAdvertisingData;
@@ -20,8 +22,11 @@ use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
2022
use dom::globalscope::GlobalScope;
2123
use dom::promise::Promise;
2224
use ipc_channel::ipc::{self, IpcSender};
23-
use js::conversions::ToJSValConvertible;
25+
use ipc_channel::router::ROUTER;
26+
use js::jsapi::{JSAutoCompartment, JSContext};
27+
use network_listener::{NetworkListener, PreInvoke};
2428
use std::rc::Rc;
29+
use std::sync::{Arc, Mutex};
2530

2631
const FILTER_EMPTY_ERROR: &'static str = "'filters' member, if present, must be nonempty to find any devices.";
2732
const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way.";
@@ -39,6 +44,33 @@ const SERVICE_ERROR: &'static str = "'services', if present, must contain at lea
3944
const OPTIONS_ERROR: &'static str = "Fields of 'options' conflict with each other.
4045
Either 'acceptAllDevices' member must be true, or 'filters' member must be set to a value.";
4146

47+
struct BluetoothContext<T: AsyncBluetoothListener + Reflectable> {
48+
promise: Option<TrustedPromise>,
49+
receiver: Trusted<T>,
50+
}
51+
52+
pub trait AsyncBluetoothListener {
53+
fn handle_response(&self, result: BluetoothResponse, cx: *mut JSContext, promise: &Rc<Promise>);
54+
}
55+
56+
impl<Listener: AsyncBluetoothListener + Reflectable> PreInvoke for BluetoothContext<Listener> {}
57+
58+
impl<Listener: AsyncBluetoothListener + Reflectable> BluetoothResponseListener for BluetoothContext<Listener> {
59+
#[allow(unrooted_must_root)]
60+
fn response(&mut self, response: BluetoothResponseResult) {
61+
let promise = self.promise.take().expect("bt promise is missing").root();
62+
let promise_cx = promise.global().get_cx();
63+
64+
// JSAutoCompartment needs to be manually made.
65+
// Otherwise, Servo will crash.
66+
let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
67+
match response {
68+
Ok(response) => self.receiver.root().handle_response(response, promise_cx, &promise),
69+
Err(error) => promise.reject_error(promise_cx, Error::from(error)),
70+
}
71+
}
72+
}
73+
4274
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth
4375
#[dom_struct]
4476
pub struct Bluetooth {
@@ -58,69 +90,55 @@ impl Bluetooth {
5890
BluetoothBinding::Wrap)
5991
}
6092

61-
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
93+
fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
6294
self.global().as_window().bluetooth_thread()
6395
}
6496

65-
fn request_device(&self, option: &RequestDeviceOptions) -> Fallible<Root<BluetoothDevice>> {
66-
// Step 1.
67-
// TODO(#4282): Reject promise.
68-
if (option.filters.is_some() && option.acceptAllDevices) ||
69-
(option.filters.is_none() && !option.acceptAllDevices) {
70-
return Err(Type(OPTIONS_ERROR.to_owned()));
71-
}
72-
// Step 2.
73-
if !option.acceptAllDevices {
74-
return self.request_bluetooth_devices(&option.filters, &option.optionalServices);
75-
}
76-
77-
self.request_bluetooth_devices(&None, &option.optionalServices)
78-
// TODO(#4282): Step 3-5: Reject and resolve promise.
79-
}
80-
8197
// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
8298
fn request_bluetooth_devices(&self,
99+
p: &Rc<Promise>,
83100
filters: &Option<Vec<BluetoothRequestDeviceFilter>>,
84-
optional_services: &Option<Vec<BluetoothServiceUUID>>)
85-
-> Fallible<Root<BluetoothDevice>> {
101+
optional_services: &Option<Vec<BluetoothServiceUUID>>) {
86102
// TODO: Step 1: Triggered by user activation.
87103

88104
// Step 2.
89-
let option = try!(convert_request_device_options(filters, optional_services));
105+
let option = match convert_request_device_options(filters, optional_services) {
106+
Ok(o) => o,
107+
Err(e) => {
108+
p.reject_error(p.global().get_cx(), e);
109+
return;
110+
}
111+
};
90112

91113
// TODO: Step 3-5: Implement the permission API.
92114

93115
// Note: Steps 6-8 are implemented in
94116
// components/net/bluetooth_thread.rs in request_device function.
95-
let (sender, receiver) = ipc::channel().unwrap();
96-
self.get_bluetooth_thread().send(BluetoothMethodMsg::RequestDevice(option, sender)).unwrap();
97-
let device = receiver.recv().unwrap();
98-
99-
// TODO: Step 9-10: Implement the permission API.
100-
101-
// Step 11: This step is optional.
102-
103-
// Step 12-13.
104-
match device {
105-
Ok(device) => {
106-
let global = self.global();
107-
let ad_data = BluetoothAdvertisingData::new(&global,
108-
device.appearance,
109-
device.tx_power,
110-
device.rssi);
111-
Ok(BluetoothDevice::new(&global,
112-
DOMString::from(device.id),
113-
device.name.map(DOMString::from),
114-
&ad_data))
115-
},
116-
Err(error) => {
117-
Err(Error::from(error))
118-
},
119-
}
120-
117+
let sender = response_async(p, self);
118+
self.get_bluetooth_thread().send(BluetoothRequest::RequestDevice(option, sender)).unwrap();
121119
}
122120
}
123121

122+
pub fn response_async<T: AsyncBluetoothListener + Reflectable + 'static>(
123+
promise: &Rc<Promise>,
124+
receiver: &T) -> IpcSender<BluetoothResponseResult> {
125+
let (action_sender, action_receiver) = ipc::channel().unwrap();
126+
let chan = receiver.global().networking_task_source();
127+
let context = Arc::new(Mutex::new(BluetoothContext {
128+
promise: Some(TrustedPromise::new(promise.clone())),
129+
receiver: Trusted::new(receiver),
130+
}));
131+
let listener = NetworkListener {
132+
context: context,
133+
script_chan: chan,
134+
wrapper: None,
135+
};
136+
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
137+
listener.notify_response(message.to().unwrap());
138+
});
139+
action_sender
140+
}
141+
124142
// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
125143
fn convert_request_device_options(filters: &Option<Vec<BluetoothRequestDeviceFilter>>,
126144
optional_services: &Option<Vec<BluetoothServiceUUID>>)
@@ -269,18 +287,6 @@ fn canonicalize_filter(filter: &BluetoothRequestDeviceFilter) -> Fallible<Blueto
269287
service_data_uuid))
270288
}
271289

272-
#[allow(unrooted_must_root)]
273-
pub fn result_to_promise<T: ToJSValConvertible>(global: &GlobalScope,
274-
bluetooth_result: Fallible<T>)
275-
-> Rc<Promise> {
276-
let p = Promise::new(global);
277-
match bluetooth_result {
278-
Ok(v) => p.resolve_native(p.global().get_cx(), &v),
279-
Err(e) => p.reject_error(p.global().get_cx(), e),
280-
}
281-
p
282-
}
283-
284290
impl From<BluetoothError> for Error {
285291
fn from(error: BluetoothError) -> Self {
286292
match error {
@@ -297,6 +303,39 @@ impl BluetoothMethods for Bluetooth {
297303
#[allow(unrooted_must_root)]
298304
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
299305
fn RequestDevice(&self, option: &RequestDeviceOptions) -> Rc<Promise> {
300-
result_to_promise(&self.global(), self.request_device(option))
306+
let p = Promise::new(&self.global());
307+
// Step 1.
308+
if (option.filters.is_some() && option.acceptAllDevices) ||
309+
(option.filters.is_none() && !option.acceptAllDevices) {
310+
p.reject_error(p.global().get_cx(), Error::Type(OPTIONS_ERROR.to_owned()));
311+
return p;
312+
}
313+
// Step 2.
314+
if !option.acceptAllDevices {
315+
self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices);
316+
} else {
317+
self.request_bluetooth_devices(&p, &None, &option.optionalServices);
318+
}
319+
// TODO(#4282): Step 3-5: Reject and resolve promise.
320+
return p;
321+
}
322+
}
323+
324+
impl AsyncBluetoothListener for Bluetooth {
325+
fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) {
326+
match response {
327+
BluetoothResponse::RequestDevice(device) => {
328+
let ad_data = BluetoothAdvertisingData::new(&self.global(),
329+
device.appearance,
330+
device.tx_power,
331+
device.rssi);
332+
let dev = BluetoothDevice::new(&self.global(),
333+
DOMString::from(device.id),
334+
device.name.map(DOMString::from),
335+
&ad_data);
336+
promise.resolve_native(promise_cx, &dev);
337+
},
338+
_ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())),
339+
}
301340
}
302341
}

0 commit comments

Comments
 (0)