зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #13909 - Webbluetooth Async behaviour (from dati91:promise-queue); r=jdm
<!-- 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. --> Source-Repo: https://github.com/servo/servo Source-Revision: 1153ca9841f458daf373471f3c65295abd872271
This commit is contained in:
Родитель
7d3399c8b2
Коммит
2190c013f5
|
@ -15,10 +15,9 @@ extern crate uuid;
|
|||
|
||||
pub mod test;
|
||||
|
||||
use bluetooth_traits::{BluetoothCharacteristicMsg, BluetoothCharacteristicsMsg};
|
||||
use bluetooth_traits::{BluetoothDescriptorMsg, BluetoothDescriptorsMsg};
|
||||
use bluetooth_traits::{BluetoothDeviceMsg, BluetoothError, BluetoothMethodMsg};
|
||||
use bluetooth_traits::{BluetoothResult, BluetoothServiceMsg, BluetoothServicesMsg};
|
||||
use bluetooth_traits::{BluetoothCharacteristicMsg, BluetoothDescriptorMsg, BluetoothServiceMsg};
|
||||
use bluetooth_traits::{BluetoothDeviceMsg, BluetoothRequest, BluetoothResponse};
|
||||
use bluetooth_traits::{BluetoothError, BluetoothResponseResult, BluetoothResult};
|
||||
use bluetooth_traits::blacklist::{uuid_is_blacklisted, Blacklist};
|
||||
use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence, RequestDeviceoptions};
|
||||
use device::bluetooth::{BluetoothAdapter, BluetoothDevice, BluetoothGATTCharacteristic};
|
||||
|
@ -85,8 +84,8 @@ pub trait BluetoothThreadFactory {
|
|||
fn new() -> Self;
|
||||
}
|
||||
|
||||
impl BluetoothThreadFactory for IpcSender<BluetoothMethodMsg> {
|
||||
fn new() -> IpcSender<BluetoothMethodMsg> {
|
||||
impl BluetoothThreadFactory for IpcSender<BluetoothRequest> {
|
||||
fn new() -> IpcSender<BluetoothRequest> {
|
||||
let (sender, receiver) = ipc::channel().unwrap();
|
||||
let adapter = BluetoothAdapter::init().ok();
|
||||
spawn_named("BluetoothThread".to_owned(), move || {
|
||||
|
@ -167,7 +166,7 @@ fn is_mock_adapter(adapter: &BluetoothAdapter) -> bool {
|
|||
}
|
||||
|
||||
pub struct BluetoothManager {
|
||||
receiver: IpcReceiver<BluetoothMethodMsg>,
|
||||
receiver: IpcReceiver<BluetoothRequest>,
|
||||
adapter: Option<BluetoothAdapter>,
|
||||
address_to_id: HashMap<String, String>,
|
||||
service_to_device: HashMap<String, String>,
|
||||
|
@ -181,7 +180,7 @@ pub struct BluetoothManager {
|
|||
}
|
||||
|
||||
impl BluetoothManager {
|
||||
pub fn new(receiver: IpcReceiver<BluetoothMethodMsg>, adapter: Option<BluetoothAdapter>) -> BluetoothManager {
|
||||
pub fn new(receiver: IpcReceiver<BluetoothRequest>, adapter: Option<BluetoothAdapter>) -> BluetoothManager {
|
||||
BluetoothManager {
|
||||
receiver: receiver,
|
||||
adapter: adapter,
|
||||
|
@ -200,49 +199,49 @@ impl BluetoothManager {
|
|||
fn start(&mut self) {
|
||||
while let Ok(msg) = self.receiver.recv() {
|
||||
match msg {
|
||||
BluetoothMethodMsg::RequestDevice(options, sender) => {
|
||||
BluetoothRequest::RequestDevice(options, sender) => {
|
||||
self.request_device(options, sender)
|
||||
},
|
||||
BluetoothMethodMsg::GATTServerConnect(device_id, sender) => {
|
||||
BluetoothRequest::GATTServerConnect(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)
|
||||
},
|
||||
BluetoothMethodMsg::GetPrimaryService(device_id, uuid, sender) => {
|
||||
BluetoothRequest::GetPrimaryService(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)
|
||||
},
|
||||
BluetoothMethodMsg::GetIncludedService(service_id, uuid, sender) => {
|
||||
BluetoothRequest::GetIncludedService(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)
|
||||
},
|
||||
BluetoothMethodMsg::GetCharacteristic(service_id, uuid, sender) => {
|
||||
BluetoothRequest::GetCharacteristic(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)
|
||||
},
|
||||
BluetoothMethodMsg::GetDescriptor(characteristic_id, uuid, sender) => {
|
||||
BluetoothRequest::GetDescriptor(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)
|
||||
},
|
||||
BluetoothMethodMsg::ReadValue(id, sender) => {
|
||||
BluetoothRequest::ReadValue(id, sender) => {
|
||||
self.read_value(id, sender)
|
||||
},
|
||||
BluetoothMethodMsg::WriteValue(id, value, sender) => {
|
||||
BluetoothRequest::WriteValue(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)
|
||||
}
|
||||
BluetoothMethodMsg::Exit => {
|
||||
BluetoothRequest::Exit => {
|
||||
break
|
||||
},
|
||||
}
|
||||
|
@ -527,7 +526,7 @@ impl BluetoothManager {
|
|||
// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
|
||||
fn request_device(&mut self,
|
||||
options: RequestDeviceoptions,
|
||||
sender: IpcSender<BluetoothResult<BluetoothDeviceMsg>>) {
|
||||
sender: IpcSender<BluetoothResponseResult>) {
|
||||
let mut adapter = get_adapter_or_return_error!(self, sender);
|
||||
if let Ok(ref session) = adapter.create_discovery_session() {
|
||||
if session.start_discovery().is_ok() {
|
||||
|
@ -561,31 +560,30 @@ impl BluetoothManager {
|
|||
}
|
||||
self.allowed_services.insert(device_id.clone(), services);
|
||||
if let Some(device) = self.get_device(&mut adapter, &device_id) {
|
||||
let message = Ok(BluetoothDeviceMsg {
|
||||
id: device_id,
|
||||
name: device.get_name().ok(),
|
||||
appearance: device.get_appearance().ok(),
|
||||
tx_power: device.get_tx_power().ok().map(|p| p as i8),
|
||||
rssi: device.get_rssi().ok().map(|p| p as i8),
|
||||
});
|
||||
return drop(sender.send(message));
|
||||
let message = BluetoothDeviceMsg {
|
||||
id: device_id,
|
||||
name: device.get_name().ok(),
|
||||
appearance: device.get_appearance().ok(),
|
||||
tx_power: device.get_tx_power().ok().map(|p| p as i8),
|
||||
rssi: device.get_rssi().ok().map(|p| p as i8),
|
||||
};
|
||||
return drop(sender.send(Ok(BluetoothResponse::RequestDevice(message))));
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
match self.get_device(&mut adapter, &device_id) {
|
||||
Some(d) => {
|
||||
if d.is_connected().unwrap_or(false) {
|
||||
return drop(sender.send(Ok(true)));
|
||||
return drop(sender.send(Ok(BluetoothResponse::GATTServerConnect(true))));
|
||||
}
|
||||
let _ = d.connect();
|
||||
for _ in 0..MAXIMUM_TRANSACTION_TIME {
|
||||
match d.is_connected().unwrap_or(false) {
|
||||
true => return drop(sender.send(Ok(true))),
|
||||
true => return drop(sender.send(Ok(BluetoothResponse::GATTServerConnect(true)))),
|
||||
false => {
|
||||
if is_mock_adapter(&adapter) {
|
||||
break;
|
||||
|
@ -624,7 +622,7 @@ impl BluetoothManager {
|
|||
fn get_primary_service(&mut self,
|
||||
device_id: String,
|
||||
uuid: String,
|
||||
sender: IpcSender<BluetoothResult<BluetoothServiceMsg>>) {
|
||||
sender: IpcSender<BluetoothResponseResult>) {
|
||||
if !self.cached_devices.contains_key(&device_id) {
|
||||
return drop(sender.send(Err(BluetoothError::InvalidState)));
|
||||
}
|
||||
|
@ -639,11 +637,15 @@ impl BluetoothManager {
|
|||
for service in services {
|
||||
if service.is_primary().unwrap_or(false) {
|
||||
if let Ok(uuid) = service.get_uuid() {
|
||||
return drop(sender.send(Ok(BluetoothServiceMsg {
|
||||
uuid: uuid,
|
||||
is_primary: true,
|
||||
instance_id: service.get_id(),
|
||||
})));
|
||||
return drop(sender.send(
|
||||
Ok(BluetoothResponse::GetPrimaryService(
|
||||
BluetoothServiceMsg {
|
||||
uuid: uuid,
|
||||
is_primary: true,
|
||||
instance_id: service.get_id(),
|
||||
}
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -653,7 +655,7 @@ impl BluetoothManager {
|
|||
fn get_primary_services(&mut self,
|
||||
device_id: String,
|
||||
uuid: Option<String>,
|
||||
sender: IpcSender<BluetoothResult<BluetoothServicesMsg>>) {
|
||||
sender: IpcSender<BluetoothResponseResult>) {
|
||||
if !self.cached_devices.contains_key(&device_id) {
|
||||
return drop(sender.send(Err(BluetoothError::InvalidState)));
|
||||
}
|
||||
|
@ -674,11 +676,13 @@ impl BluetoothManager {
|
|||
for service in services {
|
||||
if service.is_primary().unwrap_or(false) {
|
||||
if let Ok(uuid) = service.get_uuid() {
|
||||
services_vec.push(BluetoothServiceMsg {
|
||||
uuid: uuid,
|
||||
is_primary: true,
|
||||
instance_id: service.get_id(),
|
||||
});
|
||||
services_vec.push(
|
||||
BluetoothServiceMsg {
|
||||
uuid: uuid,
|
||||
is_primary: true,
|
||||
instance_id: service.get_id(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -690,20 +694,17 @@ impl BluetoothManager {
|
|||
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,
|
||||
service_id: String,
|
||||
uuid: String,
|
||||
sender: IpcSender<BluetoothResult<BluetoothServiceMsg>>) {
|
||||
sender: IpcSender<BluetoothResponseResult>) {
|
||||
if !self.cached_services.contains_key(&service_id) {
|
||||
return drop(sender.send(Err(BluetoothError::InvalidState)));
|
||||
}
|
||||
let mut adapter = match self.get_or_create_adapter() {
|
||||
Some(a) => a,
|
||||
None => return drop(sender.send(Err(BluetoothError::NotFound))),
|
||||
};
|
||||
let mut adapter = get_adapter_or_return_error!(self, sender);
|
||||
let device = match self.device_from_service_id(&service_id) {
|
||||
Some(device) => device,
|
||||
None => return drop(sender.send(Err(BluetoothError::NotFound))),
|
||||
|
@ -716,11 +717,15 @@ impl BluetoothManager {
|
|||
for service in services {
|
||||
if let Ok(service_uuid) = service.get_uuid() {
|
||||
if uuid == service_uuid {
|
||||
return drop(sender.send(Ok(BluetoothServiceMsg {
|
||||
uuid: uuid,
|
||||
is_primary: service.is_primary().unwrap_or(false),
|
||||
instance_id: service.get_id(),
|
||||
})));
|
||||
return drop(sender.send(
|
||||
Ok(BluetoothResponse::GetIncludedService(
|
||||
BluetoothServiceMsg {
|
||||
uuid: uuid,
|
||||
is_primary: service.is_primary().unwrap_or(false),
|
||||
instance_id: service.get_id(),
|
||||
}
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -730,14 +735,11 @@ impl BluetoothManager {
|
|||
fn get_included_services(&mut self,
|
||||
service_id: String,
|
||||
uuid: Option<String>,
|
||||
sender: IpcSender<BluetoothResult<BluetoothServicesMsg>>) {
|
||||
sender: IpcSender<BluetoothResponseResult>) {
|
||||
if !self.cached_services.contains_key(&service_id) {
|
||||
return drop(sender.send(Err(BluetoothError::InvalidState)));
|
||||
}
|
||||
let mut adapter = match self.get_or_create_adapter() {
|
||||
Some(a) => a,
|
||||
None => return drop(sender.send(Err(BluetoothError::NotFound))),
|
||||
};
|
||||
let mut adapter = get_adapter_or_return_error!(self, sender);
|
||||
let device = match self.device_from_service_id(&service_id) {
|
||||
Some(device) => device,
|
||||
None => return drop(sender.send(Err(BluetoothError::NotFound))),
|
||||
|
@ -750,11 +752,13 @@ impl BluetoothManager {
|
|||
let mut services_vec = vec!();
|
||||
for service in services {
|
||||
if let Ok(service_uuid) = service.get_uuid() {
|
||||
services_vec.push(BluetoothServiceMsg {
|
||||
uuid: service_uuid,
|
||||
is_primary: service.is_primary().unwrap_or(false),
|
||||
instance_id: service.get_id(),
|
||||
});
|
||||
services_vec.push(
|
||||
BluetoothServiceMsg {
|
||||
uuid: service_uuid,
|
||||
is_primary: service.is_primary().unwrap_or(false),
|
||||
instance_id: service.get_id(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(uuid) = uuid {
|
||||
|
@ -765,13 +769,13 @@ impl BluetoothManager {
|
|||
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,
|
||||
service_id: String,
|
||||
uuid: String,
|
||||
sender: IpcSender<BluetoothResult<BluetoothCharacteristicMsg>>) {
|
||||
sender: IpcSender<BluetoothResponseResult>) {
|
||||
if !self.cached_services.contains_key(&service_id) {
|
||||
return drop(sender.send(Err(BluetoothError::InvalidState)));
|
||||
}
|
||||
|
@ -783,20 +787,20 @@ impl BluetoothManager {
|
|||
for characteristic in characteristics {
|
||||
if let Ok(uuid) = characteristic.get_uuid() {
|
||||
let properties = self.get_characteristic_properties(&characteristic);
|
||||
let message = Ok(BluetoothCharacteristicMsg {
|
||||
uuid: uuid,
|
||||
instance_id: characteristic.get_id(),
|
||||
broadcast: properties.contains(BROADCAST),
|
||||
read: properties.contains(READ),
|
||||
write_without_response: properties.contains(WRITE_WITHOUT_RESPONSE),
|
||||
write: properties.contains(WRITE),
|
||||
notify: properties.contains(NOTIFY),
|
||||
indicate: properties.contains(INDICATE),
|
||||
authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES),
|
||||
reliable_write: properties.contains(RELIABLE_WRITE),
|
||||
writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES),
|
||||
});
|
||||
return drop(sender.send(message));
|
||||
let message = BluetoothCharacteristicMsg {
|
||||
uuid: uuid,
|
||||
instance_id: characteristic.get_id(),
|
||||
broadcast: properties.contains(BROADCAST),
|
||||
read: properties.contains(READ),
|
||||
write_without_response: properties.contains(WRITE_WITHOUT_RESPONSE),
|
||||
write: properties.contains(WRITE),
|
||||
notify: properties.contains(NOTIFY),
|
||||
indicate: properties.contains(INDICATE),
|
||||
authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES),
|
||||
reliable_write: properties.contains(RELIABLE_WRITE),
|
||||
writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES),
|
||||
};
|
||||
return drop(sender.send(Ok(BluetoothResponse::GetCharacteristic(message))));
|
||||
}
|
||||
}
|
||||
return drop(sender.send(Err(BluetoothError::NotFound)));
|
||||
|
@ -805,7 +809,7 @@ impl BluetoothManager {
|
|||
fn get_characteristics(&mut self,
|
||||
service_id: String,
|
||||
uuid: Option<String>,
|
||||
sender: IpcSender<BluetoothResult<BluetoothCharacteristicsMsg>>) {
|
||||
sender: IpcSender<BluetoothResponseResult>) {
|
||||
if !self.cached_services.contains_key(&service_id) {
|
||||
return drop(sender.send(Err(BluetoothError::InvalidState)));
|
||||
}
|
||||
|
@ -822,19 +826,20 @@ impl BluetoothManager {
|
|||
if let Ok(uuid) = characteristic.get_uuid() {
|
||||
let properties = self.get_characteristic_properties(&characteristic);
|
||||
characteristics_vec.push(
|
||||
BluetoothCharacteristicMsg {
|
||||
uuid: uuid,
|
||||
instance_id: characteristic.get_id(),
|
||||
broadcast: properties.contains(BROADCAST),
|
||||
read: properties.contains(READ),
|
||||
write_without_response: properties.contains(WRITE_WITHOUT_RESPONSE),
|
||||
write: properties.contains(WRITE),
|
||||
notify: properties.contains(NOTIFY),
|
||||
indicate: properties.contains(INDICATE),
|
||||
authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES),
|
||||
reliable_write: properties.contains(RELIABLE_WRITE),
|
||||
writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES),
|
||||
});
|
||||
BluetoothCharacteristicMsg {
|
||||
uuid: uuid,
|
||||
instance_id: characteristic.get_id(),
|
||||
broadcast: properties.contains(BROADCAST),
|
||||
read: properties.contains(READ),
|
||||
write_without_response: properties.contains(WRITE_WITHOUT_RESPONSE),
|
||||
write: properties.contains(WRITE),
|
||||
notify: properties.contains(NOTIFY),
|
||||
indicate: properties.contains(INDICATE),
|
||||
authenticated_signed_writes: properties.contains(AUTHENTICATED_SIGNED_WRITES),
|
||||
reliable_write: properties.contains(RELIABLE_WRITE),
|
||||
writable_auxiliaries: properties.contains(WRITABLE_AUXILIARIES),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
characteristics_vec.retain(|c| !uuid_is_blacklisted(&c.uuid, Blacklist::All));
|
||||
|
@ -842,13 +847,13 @@ impl BluetoothManager {
|
|||
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,
|
||||
characteristic_id: String,
|
||||
uuid: String,
|
||||
sender: IpcSender<BluetoothResult<BluetoothDescriptorMsg>>) {
|
||||
sender: IpcSender<BluetoothResponseResult>) {
|
||||
if !self.cached_characteristics.contains_key(&characteristic_id) {
|
||||
return drop(sender.send(Err(BluetoothError::InvalidState)));
|
||||
}
|
||||
|
@ -859,10 +864,14 @@ impl BluetoothManager {
|
|||
}
|
||||
for descriptor in descriptors {
|
||||
if let Ok(uuid) = descriptor.get_uuid() {
|
||||
return drop(sender.send(Ok(BluetoothDescriptorMsg {
|
||||
uuid: uuid,
|
||||
instance_id: descriptor.get_id(),
|
||||
})));
|
||||
return drop(sender.send(
|
||||
Ok(BluetoothResponse::GetDescriptor(
|
||||
BluetoothDescriptorMsg {
|
||||
uuid: uuid,
|
||||
instance_id: descriptor.get_id(),
|
||||
}
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
return drop(sender.send(Err(BluetoothError::NotFound)));
|
||||
|
@ -871,7 +880,7 @@ impl BluetoothManager {
|
|||
fn get_descriptors(&mut self,
|
||||
characteristic_id: String,
|
||||
uuid: Option<String>,
|
||||
sender: IpcSender<BluetoothResult<BluetoothDescriptorsMsg>>) {
|
||||
sender: IpcSender<BluetoothResponseResult>) {
|
||||
if !self.cached_characteristics.contains_key(&characteristic_id) {
|
||||
return drop(sender.send(Err(BluetoothError::InvalidState)));
|
||||
}
|
||||
|
@ -886,20 +895,22 @@ impl BluetoothManager {
|
|||
let mut descriptors_vec = vec!();
|
||||
for descriptor in descriptors {
|
||||
if let Ok(uuid) = descriptor.get_uuid() {
|
||||
descriptors_vec.push(BluetoothDescriptorMsg {
|
||||
uuid: uuid,
|
||||
instance_id: descriptor.get_id(),
|
||||
});
|
||||
descriptors_vec.push(
|
||||
BluetoothDescriptorMsg {
|
||||
uuid: uuid,
|
||||
instance_id: descriptor.get_id(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
descriptors_vec.retain(|d| !uuid_is_blacklisted(&d.uuid, Blacklist::All));
|
||||
if descriptors_vec.is_empty() {
|
||||
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 value = self.get_gatt_characteristic(&mut adapter, &id)
|
||||
.map(|c| c.read_value().unwrap_or(vec![]));
|
||||
|
@ -907,10 +918,13 @@ impl BluetoothManager {
|
|||
value = self.get_gatt_descriptor(&mut adapter, &id)
|
||||
.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 result = self.get_gatt_characteristic(&mut adapter, &id)
|
||||
.map(|c| c.write_value(value.clone()));
|
||||
|
@ -918,13 +932,12 @@ impl BluetoothManager {
|
|||
result = self.get_gatt_descriptor(&mut adapter, &id)
|
||||
.map(|d| d.write_value(value.clone()));
|
||||
}
|
||||
let message = match result {
|
||||
match result {
|
||||
Some(v) => match v {
|
||||
Ok(_) => Ok(true),
|
||||
Ok(_) => return drop(sender.send(Ok(BluetoothResponse::WriteValue(value)))),
|
||||
Err(_) => return drop(sender.send(Err(BluetoothError::NotSupported))),
|
||||
},
|
||||
None => return drop(sender.send(Err(BluetoothError::InvalidState))),
|
||||
};
|
||||
let _ = sender.send(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,21 +75,43 @@ pub type BluetoothDescriptorsMsg = Vec<BluetoothDescriptorMsg>;
|
|||
|
||||
pub type BluetoothResult<T> = Result<T, BluetoothError>;
|
||||
|
||||
pub type BluetoothResponseResult = Result<BluetoothResponse, BluetoothError>;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum BluetoothMethodMsg {
|
||||
RequestDevice(RequestDeviceoptions, IpcSender<BluetoothResult<BluetoothDeviceMsg>>),
|
||||
GATTServerConnect(String, IpcSender<BluetoothResult<bool>>),
|
||||
pub enum BluetoothRequest {
|
||||
RequestDevice(RequestDeviceoptions, IpcSender<BluetoothResponseResult>),
|
||||
GATTServerConnect(String, IpcSender<BluetoothResponseResult>),
|
||||
GATTServerDisconnect(String, IpcSender<BluetoothResult<bool>>),
|
||||
GetPrimaryService(String, String, IpcSender<BluetoothResult<BluetoothServiceMsg>>),
|
||||
GetPrimaryServices(String, Option<String>, IpcSender<BluetoothResult<BluetoothServicesMsg>>),
|
||||
GetIncludedService(String, String, IpcSender<BluetoothResult<BluetoothServiceMsg>>),
|
||||
GetIncludedServices(String, Option<String>, IpcSender<BluetoothResult<BluetoothServicesMsg>>),
|
||||
GetCharacteristic(String, String, IpcSender<BluetoothResult<BluetoothCharacteristicMsg>>),
|
||||
GetCharacteristics(String, Option<String>, IpcSender<BluetoothResult<BluetoothCharacteristicsMsg>>),
|
||||
GetDescriptor(String, String, IpcSender<BluetoothResult<BluetoothDescriptorMsg>>),
|
||||
GetDescriptors(String, Option<String>, IpcSender<BluetoothResult<BluetoothDescriptorsMsg>>),
|
||||
ReadValue(String, IpcSender<BluetoothResult<Vec<u8>>>),
|
||||
WriteValue(String, Vec<u8>, IpcSender<BluetoothResult<bool>>),
|
||||
GetPrimaryService(String, String, IpcSender<BluetoothResponseResult>),
|
||||
GetPrimaryServices(String, Option<String>, IpcSender<BluetoothResponseResult>),
|
||||
GetIncludedService(String, String, IpcSender<BluetoothResponseResult>),
|
||||
GetIncludedServices(String, Option<String>, IpcSender<BluetoothResponseResult>),
|
||||
GetCharacteristic(String, String, IpcSender<BluetoothResponseResult>),
|
||||
GetCharacteristics(String, Option<String>, IpcSender<BluetoothResponseResult>),
|
||||
GetDescriptor(String, String, IpcSender<BluetoothResponseResult>),
|
||||
GetDescriptors(String, Option<String>, IpcSender<BluetoothResponseResult>),
|
||||
ReadValue(String, IpcSender<BluetoothResponseResult>),
|
||||
WriteValue(String, Vec<u8>, IpcSender<BluetoothResponseResult>),
|
||||
Test(String, IpcSender<BluetoothResult<()>>),
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
//! `LayoutThread`, and `PaintThread`.
|
||||
|
||||
use backtrace::Backtrace;
|
||||
use bluetooth_traits::BluetoothMethodMsg;
|
||||
use bluetooth_traits::BluetoothRequest;
|
||||
use canvas::canvas_paint_thread::CanvasPaintThread;
|
||||
use canvas::webgl_paint_thread::WebGLPaintThread;
|
||||
use canvas_traits::CanvasMsg;
|
||||
|
@ -121,7 +121,7 @@ pub struct Constellation<Message, LTF, STF> {
|
|||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||
|
||||
/// 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
|
||||
swmanager_chan: Option<IpcSender<ServiceWorkerMsg>>,
|
||||
|
@ -199,7 +199,7 @@ pub struct InitialConstellationState {
|
|||
/// A channel to the developer tools, if applicable.
|
||||
pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||
/// A channel to the bluetooth thread.
|
||||
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>,
|
||||
pub bluetooth_thread: IpcSender<BluetoothRequest>,
|
||||
/// A channel to the image cache thread.
|
||||
pub image_cache_thread: ImageCacheThread,
|
||||
/// A channel to the font cache thread.
|
||||
|
@ -1095,7 +1095,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 compositing::CompositionPipeline;
|
||||
use compositing::CompositorProxy;
|
||||
use compositing::compositor_thread::Msg as CompositorMsg;
|
||||
|
@ -94,7 +94,7 @@ pub struct InitialPipelineState {
|
|||
/// A channel to the developer tools, if applicable.
|
||||
pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||
/// A channel to the bluetooth thread.
|
||||
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>,
|
||||
pub bluetooth_thread: IpcSender<BluetoothRequest>,
|
||||
/// A channel to the service worker manager thread
|
||||
pub swmanager_thread: IpcSender<SWManagerMsg>,
|
||||
/// A channel to the image cache thread.
|
||||
|
@ -384,7 +384,7 @@ pub struct UnprivilegedPipelineContent {
|
|||
layout_to_constellation_chan: IpcSender<LayoutMsg>,
|
||||
scheduler_chan: IpcSender<TimerEventRequest>,
|
||||
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||
bluetooth_thread: IpcSender<BluetoothMethodMsg>,
|
||||
bluetooth_thread: IpcSender<BluetoothRequest>,
|
||||
swmanager_thread: IpcSender<SWManagerMsg>,
|
||||
image_cache_thread: ImageCacheThread,
|
||||
font_cache_thread: FontCacheThread,
|
||||
|
|
|
@ -10,6 +10,7 @@ name = "net_traits"
|
|||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
bluetooth_traits = {path = "../bluetooth_traits"}
|
||||
util = {path = "../util"}
|
||||
msg = {path = "../msg"}
|
||||
ipc-channel = "0.5"
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
extern crate bluetooth_traits;
|
||||
extern crate cookie as cookie_rs;
|
||||
extern crate heapsize;
|
||||
#[macro_use] extern crate heapsize_derive;
|
||||
|
@ -32,6 +33,7 @@ extern crate uuid;
|
|||
extern crate webrender_traits;
|
||||
extern crate websocket;
|
||||
|
||||
use bluetooth_traits::{BluetoothResponseListener, BluetoothResponseResult};
|
||||
use cookie_rs::Cookie;
|
||||
use filemanager_thread::FileManagerThreadMsg;
|
||||
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.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum LoadConsumer {
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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>>,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -38,7 +38,7 @@ extern crate url;
|
|||
mod script_msg;
|
||||
pub mod webdriver_msg;
|
||||
|
||||
use bluetooth_traits::BluetoothMethodMsg;
|
||||
use bluetooth_traits::BluetoothRequest;
|
||||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
||||
use euclid::Size2D;
|
||||
use euclid::length::Length;
|
||||
|
@ -445,7 +445,7 @@ pub struct InitialScriptState {
|
|||
/// A channel to the resource manager thread.
|
||||
pub resource_threads: ResourceThreads,
|
||||
/// A channel to the bluetooth thread.
|
||||
pub bluetooth_thread: IpcSender<BluetoothMethodMsg>,
|
||||
pub bluetooth_thread: IpcSender<BluetoothRequest>,
|
||||
/// A channel to the image cache thread.
|
||||
pub image_cache_thread: ImageCacheThread,
|
||||
/// A channel to the time profiler thread.
|
||||
|
|
|
@ -1571,6 +1571,7 @@ dependencies = [
|
|||
name = "net_traits"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bluetooth_traits 0.0.1",
|
||||
"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_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -64,7 +64,7 @@ fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
|
|||
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { }
|
||||
|
||||
use bluetooth::BluetoothThreadFactory;
|
||||
use bluetooth_traits::BluetoothMethodMsg;
|
||||
use bluetooth_traits::BluetoothRequest;
|
||||
use compositing::{CompositorProxy, IOCompositor};
|
||||
use compositing::compositor_thread::InitialCompositorState;
|
||||
use compositing::windowing::WindowEvent;
|
||||
|
@ -247,7 +247,7 @@ fn create_constellation(opts: opts::Opts,
|
|||
supports_clipboard: bool,
|
||||
webrender_api_sender: webrender_traits::RenderApiSender)
|
||||
-> (Sender<ConstellationMsg>, SWManagerSenders) {
|
||||
let bluetooth_thread: IpcSender<BluetoothMethodMsg> = BluetoothThreadFactory::new();
|
||||
let bluetooth_thread: IpcSender<BluetoothRequest> = BluetoothThreadFactory::new();
|
||||
|
||||
let (public_resource_threads, private_resource_threads) =
|
||||
new_resource_threads(opts.user_agent,
|
||||
|
|
|
@ -1447,6 +1447,7 @@ dependencies = [
|
|||
name = "net_traits"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bluetooth_traits 0.0.1",
|
||||
"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_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
Загрузка…
Ссылка в новой задаче