/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use bluetooth_traits::{BluetoothRequest, BluetoothResponse}; use bluetooth_traits::blocklist::{Blocklist, uuid_is_blocklisted}; use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; use dom::bindings::error::Error::{self, Network, Security}; use dom::bindings::error::ErrorResult; use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; use dom::bluetooth::{AsyncBluetoothListener, response_async}; use dom::bluetoothdevice::BluetoothDevice; 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; // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattserver #[dom_struct] pub struct BluetoothRemoteGATTServer { reflector_: Reflector, device: MutHeap>, connected: Cell, } impl BluetoothRemoteGATTServer { pub fn new_inherited(device: &BluetoothDevice) -> BluetoothRemoteGATTServer { BluetoothRemoteGATTServer { reflector_: Reflector::new(), device: MutHeap::new(device), connected: Cell::new(false), } } pub fn new(global: &GlobalScope, device: &BluetoothDevice) -> Root { reflect_dom_object(box BluetoothRemoteGATTServer::new_inherited(device), global, BluetoothRemoteGATTServerBinding::Wrap) } fn get_bluetooth_thread(&self) -> IpcSender { self.global().as_window().bluetooth_thread() } } impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-device fn Device(&self) -> Root { self.device.get() } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connected fn Connected(&self) -> bool { self.connected.get() } #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect fn Connect(&self) -> Rc { // Step 1. let p = Promise::new(&self.global()); let sender = response_async(&p, self); // TODO: Step 2: Implement representedDevice internal slot for BluetoothDevice. // TODO: Step 3: Check if the UA is currently using the Bluetooth system. // TODO: Step 4: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. // TODO: Step 5.1 - 5.2: Implement activeAlgorithms, representedDevice internal slots // and the` garbage-collect the connection` algorithm. // Note: Steps 5.1.1 and 5.1.3 are in components/bluetooth/lib.rs in the gatt_server_connect function. // Steps 5.2.4 - 5.2.5 are in response function. self.get_bluetooth_thread().send( BluetoothRequest::GATTServerConnect(String::from(self.Device().Id()), sender)).unwrap(); // Step 5: return promise. return p; } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect fn Disconnect(&self) -> ErrorResult { // TODO: Step 1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. // Step 2. if !self.Connected() { return Ok(()); } let (sender, receiver) = ipc::channel().unwrap(); self.get_bluetooth_thread().send( BluetoothRequest::GATTServerDisconnect(String::from(self.Device().Id()), sender)).unwrap(); let server = receiver.recv().unwrap(); // TODO: Step 3: Implement the `clean up the disconnected device` algorithm. // TODO: Step 4: Implement representedDevice internal slot for BluetoothDevice. // TODO: Step 5: Implement the `garbage-collect the connection` algorithm. match server { Ok(connected) => { self.connected.set(connected); Ok(()) }, Err(error) => { Err(Error::from(error)) }, } } #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice fn GetPrimaryService(&self, service: BluetoothServiceUUID) -> Rc { // TODO: Step 1: Implement the Permission API and the allowedServices BluetoothDevice internal slot. // Subsequent steps are relative to https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); // Step 1. let uuid = match BluetoothUUID::service(service) { Ok(uuid) => uuid.to_string(), Err(e) => { p.reject_error(p_cx, e); return p; } }; // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } // TODO: Step 5: Implement representedDevice internal slot for BluetoothDevice. // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_primary_service function // and in handle_response function. 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) -> Rc { // TODO: Step 1: Implement the Permission API and the allowedServices BluetoothDevice internal slot. // Subsequent steps are relative to https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); let mut uuid: Option = None; if let Some(s) = service { // Step 1. 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 { // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } } }; // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } // TODO: Step 5: Implement representedDevice internal slot for BluetoothDevice. // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_primary_services function // and in handle_response function. 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) { let device = self.Device(); match response { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect BluetoothResponse::GATTServerConnect(connected) => { // Step 5.2.4. self.connected.set(connected); // Step 5.2.5. promise.resolve_native(promise_cx, self); }, // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren // Step 7. BluetoothResponse::GetPrimaryService(service) => { let bt_service = device.get_or_create_service(&service, &self); promise.resolve_native(promise_cx, &bt_service); }, // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren // Step 7. BluetoothResponse::GetPrimaryServices(services_vec) => { let mut services = vec!(); for service in services_vec { let bt_service = device.get_or_create_service(&service, &self); services.push(bt_service); } promise.resolve_native(promise_cx, &services); }, _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())), } } }