From 315c57f94899c4f7e7088dd482160b6f6b9f711b Mon Sep 17 00:00:00 2001 From: Zakor Gyula Date: Fri, 11 Nov 2016 13:20:19 +0100 Subject: [PATCH 1/3] Move convert_request_device_options into request_bluetooth_devices. --- components/script/dom/bluetooth.rs | 94 +++++++++++++++--------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs index 0cb9005e282..1578d12acdb 100644 --- a/components/script/dom/bluetooth.rs +++ b/components/script/dom/bluetooth.rs @@ -134,14 +134,54 @@ impl Bluetooth { optional_services: &Option>) { // TODO: Step 1: Triggered by user activation. - // Step 2. - let option = match convert_request_device_options(filters, optional_services) { - Ok(o) => o, - Err(e) => { - p.reject_error(p.global().get_cx(), e); + // Step 2.2: There is no requiredServiceUUIDS, we scan for all devices. + let mut uuid_filters = vec!(); + + if let &Some(ref filters) = filters { + // Step 2.1. + if filters.is_empty() { + p.reject_error(p.global().get_cx(), Type(FILTER_EMPTY_ERROR.to_owned())); return; } - }; + + // Step 2.3: There is no requiredServiceUUIDS, we scan for all devices. + + // Step 2.4. + for filter in filters { + // Step 2.4.8. + match canonicalize_filter(&filter) { + Ok(f) => uuid_filters.push(f), + Err(e) => { + p.reject_error(p.global().get_cx(), e); + return; + }, + } + } + } + + let mut optional_services_uuids = vec!(); + if let &Some(ref opt_services) = optional_services { + for opt_service in opt_services { + // Step 2.5 - 2.6. + let uuid = match BluetoothUUID::service(opt_service.clone()) { + Ok(u) => u.to_string(), + Err(e) => { + p.reject_error(p.global().get_cx(), e); + return; + }, + }; + + // Step 2.7. + // Note: What we are doing here is adding the not blacklisted UUIDs to the result vector, + // insted of removing them from an already filled vector. + if !uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { + optional_services_uuids.push(uuid); + } + } + } + + let option = RequestDeviceoptions::new(BluetoothScanfilterSequence::new(uuid_filters), + ServiceUUIDSequence::new(optional_services_uuids)); // TODO: Step 3-5: Implement the permission API. @@ -172,48 +212,6 @@ pub fn response_async( action_sender } -// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices -fn convert_request_device_options(filters: &Option>, - optional_services: &Option>) - -> Fallible { - // Step 2.2: There is no requiredServiceUUIDS, we scan for all devices. - let mut uuid_filters = vec!(); - - if let &Some(ref filters) = filters { - // Step 2.1. - if filters.is_empty() { - return Err(Type(FILTER_EMPTY_ERROR.to_owned())); - } - - // Step 2.3: There is no requiredServiceUUIDS, we scan for all devices. - - // Step 2.4. - for filter in filters { - // Step 2.4.8. - uuid_filters.push(try!(canonicalize_filter(&filter))); - } - } - - let mut optional_services_uuids = vec!(); - if let &Some(ref opt_services) = optional_services { - for opt_service in opt_services { - // Step 2.5 - 2.6. - let uuid = try!(BluetoothUUID::service(opt_service.clone())).to_string(); - - // Step 2.7. - // Note: What we are doing here is adding the not blocklisted UUIDs to the result vector, - // insted of removing them from an already filled vector. - if !uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { - optional_services_uuids.push(uuid); - } - } - } - - Ok(RequestDeviceoptions::new(BluetoothScanfilterSequence::new(uuid_filters), - ServiceUUIDSequence::new(optional_services_uuids))) -} - -// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible { // Step 2.4.1. if filter.services.is_none() && From 89c4219a3641a2bbf282ef38c66498e0df79adf9 Mon Sep 17 00:00:00 2001 From: Zakor Gyula Date: Fri, 11 Nov 2016 14:30:09 +0100 Subject: [PATCH 2/3] Update requestDevice step annotations. --- components/bluetooth/lib.rs | 13 ++++-- components/script/dom/bluetooth.rs | 73 +++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/components/bluetooth/lib.rs b/components/bluetooth/lib.rs index 9bc00e0c43f..574c22d1d7f 100644 --- a/components/bluetooth/lib.rs +++ b/components/bluetooth/lib.rs @@ -567,6 +567,7 @@ impl BluetoothManager { fn request_device(&mut self, options: RequestDeviceoptions, sender: IpcSender) { + // Step 6. 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() { @@ -577,18 +578,20 @@ impl BluetoothManager { let _ = session.stop_discovery(); } - // Step 6. - // Note: There is no requiredServiceUUIDS, we scan for all devices. + // Step 7. + // Note: There are no requiredServiceUUIDS, we scan for all devices. let mut matched_devices = self.get_and_cache_devices(&mut adapter); - // Step 7. + // Step 8. if !options.is_accepting_all_devices() { matched_devices = matched_devices.into_iter() .filter(|d| matches_filters(d, options.get_filters())) .collect(); } - // Step 8. + // Step 9. + // TODO: After the permission API implementation + // https://w3c.github.io/permissions/#prompt-the-user-to-choose if let Some(address) = self.select_device(matched_devices, &adapter) { let device_id = match self.address_to_id.get(&address) { Some(id) => id.clone(), @@ -610,7 +613,9 @@ impl BluetoothManager { return drop(sender.send(Ok(BluetoothResponse::RequestDevice(message)))); } } + // TODO: Step 10-11: Implement the permission API. return drop(sender.send(Err(BluetoothError::NotFound))); + // Step 12: Missing, because it is optional. } fn gatt_server_connect(&mut self, device_id: String, sender: IpcSender) { diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs index 1578d12acdb..ddd1e4d411a 100644 --- a/components/script/dom/bluetooth.rs +++ b/components/script/dom/bluetooth.rs @@ -78,6 +78,8 @@ impl BluetoothResponseListener f let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get()); match response { Ok(response) => self.receiver.root().handle_response(response, promise_cx, &promise), + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice + // Step 3-4. Err(error) => promise.reject_error(promise_cx, Error::from(error)), } } @@ -134,7 +136,7 @@ impl Bluetooth { optional_services: &Option>) { // TODO: Step 1: Triggered by user activation. - // Step 2.2: There is no requiredServiceUUIDS, we scan for all devices. + // Step 2.2: There are no requiredServiceUUIDS, we scan for all devices. let mut uuid_filters = vec!(); if let &Some(ref filters) = filters { @@ -144,18 +146,20 @@ impl Bluetooth { return; } - // Step 2.3: There is no requiredServiceUUIDS, we scan for all devices. + // Step 2.3: There are no requiredServiceUUIDS, we scan for all devices. // Step 2.4. for filter in filters { - // Step 2.4.8. + // Step 2.4.1. match canonicalize_filter(&filter) { + // Step 2.4.2. Ok(f) => uuid_filters.push(f), Err(e) => { p.reject_error(p.global().get_cx(), e); return; }, } + // Step 2.4.3: There are no requiredServiceUUIDS, we scan for all devices. } } @@ -172,8 +176,8 @@ impl Bluetooth { }; // Step 2.7. - // Note: What we are doing here is adding the not blacklisted UUIDs to the result vector, - // insted of removing them from an already filled vector. + // Note: What we are doing here, is adding the not blocklisted UUIDs to the result vector, + // instead of removing them from an already filled vector. if !uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { optional_services_uuids.push(uuid); } @@ -212,8 +216,9 @@ pub fn response_async( action_sender } +// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothlescanfilterinit-canonicalizing fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible { - // Step 2.4.1. + // Step 1. if filter.services.is_none() && filter.name.is_none() && filter.namePrefix.is_none() && @@ -222,13 +227,13 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible { - // Step 2.4.3.1. + // Step 3.1. if services.is_empty() { return Err(Type(SERVICE_ERROR.to_owned())); } @@ -236,27 +241,26 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible vec!(), }; - // Step 2.4.4. + // Step 4. let name = match filter.name { Some(ref name) => { - // Step 2.4.4.1. + // Step 4.1. // Note: DOMString::len() gives back the size in bytes. if name.len() > MAX_DEVICE_NAME_LENGTH { return Err(Type(NAME_TOO_LONG_ERROR.to_owned())); @@ -265,16 +269,16 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible None, }; - // Step 2.4.5. + // Step 5. let name_prefix = match filter.namePrefix { Some(ref name_prefix) => { - // Step 2.4.5.1. + // Step 5.1. if name_prefix.is_empty() { return Err(Type(NAME_PREFIX_ERROR.to_owned())); } @@ -285,24 +289,30 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible String::new(), }; - // Step 2.4.6 - 2.4.7 + // Step 6 -7. let manufacturer_data = match filter.manufacturerData { Some(ref manufacturer_data_map) => { + // Note: If manufacturer_data_map is empty, that means there are no key values in it. if manufacturer_data_map.is_empty() { return Err(Type(MANUFACTURER_DATA_ERROR.to_owned())); } let mut map = HashMap::new(); for (key, bdfi) in manufacturer_data_map.iter() { + // Step 7.1-7.2. let manufacturer_id = match u16::from_str(key.as_ref()) { Ok(id) => id, Err(err) => return Err(Type(format!("{} {} {}", KEY_CONVERSION_ERROR, key, err))), }; + + // Step 7.3: No need to convert to IDL values since this is only used by native code. + + // Step 7.4 -7.5. map.insert(manufacturer_id, try!(canonicalize_bluetooth_data_filter_init(bdfi))); } Some(map) @@ -310,22 +320,33 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible None, }; - // Step 2.4.8 -2.4.9 + // Step 8-9. let service_data = match filter.serviceData { Some(ref service_data_map) => { + // Note: If service_data_map is empty, that means there are no key values in it. if service_data_map.is_empty() { return Err(Type(SERVICE_DATA_ERROR.to_owned())); } let mut map = HashMap::new(); for (key, bdfi) in service_data_map.iter() { let service_name = match u32::from_str(key.as_ref()) { + // Step 9.1. Ok(number) => StringOrUnsignedLong::UnsignedLong(number), + // Step 9.2. _ => StringOrUnsignedLong::String(key.clone()) }; + + // Step 9.3-9.4. let service = try!(BluetoothUUID::service(service_name)).to_string(); + + // Step 9.5. if uuid_is_blocklisted(service.as_ref(), Blocklist::All) { return Err(Security); } + + // Step 9.6: No need to convert to IDL values since this is only used by native code. + + // Step 9.7 -9.8. map.insert(service, try!(canonicalize_bluetooth_data_filter_init(bdfi))); } Some(map) @@ -341,14 +362,17 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible Fallible<(Vec, Vec)> { // Step 1. let data_prefix = bdfi.dataPrefix.clone().unwrap_or(vec![]); + // Step 2. // If no mask present, mask will be a sequence of 0xFF bytes the same length as dataPrefix. // Masking dataPrefix with this, leaves dataPrefix untouched. let mask = bdfi.mask.clone().unwrap_or(vec![0xFF; data_prefix.len()]); + // Step 3. if mask.len() != data_prefix.len() { return Err(Type(MASK_LENGTH_ERROR.to_owned())); } + // Step 4. Ok((data_prefix, mask)) } @@ -377,9 +401,10 @@ impl BluetoothMethods for Bluetooth { p.reject_error(p.global().get_cx(), Error::Type(OPTIONS_ERROR.to_owned())); return p; } + // Step 2. self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices); - // TODO(#4282): Step 3-5: Reject and resolve promise. + //Note: Step 3-4. in response function, Step 5. in handle_response function. return p; } @@ -390,6 +415,8 @@ impl BluetoothMethods for Bluetooth { impl AsyncBluetoothListener for Bluetooth { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices + // Step 13-14. 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()) { @@ -405,6 +432,8 @@ impl AsyncBluetoothListener for Bluetooth { &ad_data, &self); device_instance_map.insert(device.id, MutHeap::new(&bt_device)); + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice + // Step 5. promise.resolve_native(promise_cx, &bt_device); }, _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())), From 8dd100f74fdabd55d5f3c179b3d5e52d2e0aa0e5 Mon Sep 17 00:00:00 2001 From: Zakor Gyula Date: Thu, 24 Nov 2016 15:35:08 +0100 Subject: [PATCH 3/3] Step annotations for WebBluetooth functions --- components/bluetooth/lib.rs | 131 +++++++++++++++--- components/script/dom/bluetooth.rs | 22 +-- components/script/dom/bluetoothdevice.rs | 1 + .../dom/bluetoothremotegattcharacteristic.rs | 96 ++++++++++++- .../dom/bluetoothremotegattdescriptor.rs | 37 +++++ .../script/dom/bluetoothremotegattserver.rs | 58 ++++++++ .../script/dom/bluetoothremotegattservice.rs | 56 ++++++++ 7 files changed, 367 insertions(+), 34 deletions(-) diff --git a/components/bluetooth/lib.rs b/components/bluetooth/lib.rs index 574c22d1d7f..5b24690ddd6 100644 --- a/components/bluetooth/lib.rs +++ b/components/bluetooth/lib.rs @@ -613,13 +613,16 @@ impl BluetoothManager { return drop(sender.send(Ok(BluetoothResponse::RequestDevice(message)))); } } - // TODO: Step 10-11: Implement the permission API. + // TODO: Step 10 - 11: Implement the permission API. return drop(sender.send(Err(BluetoothError::NotFound))); // Step 12: Missing, because it is optional. } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect fn gatt_server_connect(&mut self, device_id: String, sender: IpcSender) { let mut adapter = get_adapter_or_return_error!(self, sender); + + // Step 5.1.1. match self.get_device(&mut adapter, &device_id) { Some(d) => { if d.is_connected().unwrap_or(false) { @@ -636,18 +639,22 @@ impl BluetoothManager { thread::sleep(Duration::from_millis(CONNECTION_TIMEOUT_MS)); }, } + // TODO: Step 5.1.4: Use the exchange MTU procedure. } + // Step 5.1.3. return drop(sender.send(Err(BluetoothError::Network))); }, None => return drop(sender.send(Err(BluetoothError::NotFound))), } } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect fn gatt_server_disconnect(&mut self, device_id: String, sender: IpcSender>) { let mut adapter = get_adapter_or_return_error!(self, sender); match self.get_device(&mut adapter, &device_id) { Some(d) => { + // Step 2. if !d.is_connected().unwrap_or(true) { return drop(sender.send(Ok(false))); } @@ -664,10 +671,13 @@ impl BluetoothManager { } } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn get_primary_service(&mut self, device_id: String, uuid: String, sender: IpcSender) { + // Step 5. if !self.cached_devices.contains_key(&device_id) { return drop(sender.send(Err(BluetoothError::InvalidState))); } @@ -676,9 +686,8 @@ impl BluetoothManager { return drop(sender.send(Err(BluetoothError::Security))); } let services = self.get_gatt_services_by_uuid(&mut adapter, &device_id, &uuid); - if services.is_empty() { - return drop(sender.send(Err(BluetoothError::NotFound))); - } + + // Step 6. for service in services { if service.is_primary().unwrap_or(false) { if let Ok(uuid) = service.get_uuid() { @@ -694,13 +703,17 @@ impl BluetoothManager { } } } + // Step 7. return drop(sender.send(Err(BluetoothError::NotFound))); } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn get_primary_services(&mut self, device_id: String, uuid: Option, sender: IpcSender) { + // Step 5. if !self.cached_devices.contains_key(&device_id) { return drop(sender.send(Err(BluetoothError::InvalidState))); } @@ -714,9 +727,8 @@ impl BluetoothManager { }, None => self.get_and_cache_gatt_services(&mut adapter, &device_id), }; - if services.is_empty() { - return drop(sender.send(Err(BluetoothError::NotFound))); - } + + // Step 6. let mut services_vec = vec!(); for service in services { if service.is_primary().unwrap_or(false) { @@ -735,6 +747,8 @@ impl BluetoothManager { self.allowed_services .get(&device_id) .map_or(false, |uuids| uuids.contains(&s.uuid))); + + // Step 7. if services_vec.is_empty() { return drop(sender.send(Err(BluetoothError::NotFound))); } @@ -742,10 +756,13 @@ impl BluetoothManager { return drop(sender.send(Ok(BluetoothResponse::GetPrimaryServices(services_vec)))); } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn get_included_service(&mut self, service_id: String, uuid: String, sender: IpcSender) { + // Step 5. if !self.cached_services.contains_key(&service_id) { return drop(sender.send(Err(BluetoothError::InvalidState))); } @@ -759,6 +776,8 @@ impl BluetoothManager { None => return drop(sender.send(Err(BluetoothError::NotFound))), }; let services = primary_service.get_includes(device).unwrap_or(vec!()); + + // Step 6. for service in services { if let Ok(service_uuid) = service.get_uuid() { if uuid == service_uuid { @@ -774,13 +793,17 @@ impl BluetoothManager { } } } + // Step 7. return drop(sender.send(Err(BluetoothError::NotFound))); } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn get_included_services(&mut self, service_id: String, uuid: Option, sender: IpcSender) { + // Step 5. if !self.cached_services.contains_key(&service_id) { return drop(sender.send(Err(BluetoothError::InvalidState))); } @@ -794,6 +817,8 @@ impl BluetoothManager { None => return drop(sender.send(Err(BluetoothError::NotFound))), }; let services = primary_service.get_includes(device).unwrap_or(vec!()); + + // Step 6. let mut services_vec = vec!(); for service in services { if let Ok(service_uuid) = service.get_uuid() { @@ -810,6 +835,8 @@ impl BluetoothManager { services_vec.retain(|ref s| s.uuid == uuid); } services_vec.retain(|s| !uuid_is_blocklisted(&s.uuid, Blocklist::All)); + + // Step 7. if services_vec.is_empty() { return drop(sender.send(Err(BluetoothError::NotFound))); } @@ -817,18 +844,20 @@ impl BluetoothManager { return drop(sender.send(Ok(BluetoothResponse::GetIncludedServices(services_vec)))); } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn get_characteristic(&mut self, service_id: String, uuid: String, sender: IpcSender) { + // Step 5. if !self.cached_services.contains_key(&service_id) { return drop(sender.send(Err(BluetoothError::InvalidState))); } let mut adapter = get_adapter_or_return_error!(self, sender); let characteristics = self.get_gatt_characteristics_by_uuid(&mut adapter, &service_id, &uuid); - if characteristics.is_empty() { - return drop(sender.send(Err(BluetoothError::NotFound))); - } + + // Step 6. for characteristic in characteristics { if let Ok(uuid) = characteristic.get_uuid() { let properties = self.get_characteristic_properties(&characteristic); @@ -848,13 +877,17 @@ impl BluetoothManager { return drop(sender.send(Ok(BluetoothResponse::GetCharacteristic(message)))); } } + // Step 7. return drop(sender.send(Err(BluetoothError::NotFound))); } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn get_characteristics(&mut self, service_id: String, uuid: Option, sender: IpcSender) { + // Step 5. if !self.cached_services.contains_key(&service_id) { return drop(sender.send(Err(BluetoothError::InvalidState))); } @@ -863,9 +896,8 @@ impl BluetoothManager { Some(id) => self.get_gatt_characteristics_by_uuid(&mut adapter, &service_id, &id), None => self.get_and_cache_gatt_characteristics(&mut adapter, &service_id), }; - if characteristics.is_empty() { - return drop(sender.send(Err(BluetoothError::NotFound))); - } + + // Step 6. let mut characteristics_vec = vec!(); for characteristic in characteristics { if let Ok(uuid) = characteristic.get_uuid() { @@ -888,6 +920,8 @@ impl BluetoothManager { } } characteristics_vec.retain(|c| !uuid_is_blocklisted(&c.uuid, Blocklist::All)); + + // Step 7. if characteristics_vec.is_empty() { return drop(sender.send(Err(BluetoothError::NotFound))); } @@ -895,18 +929,20 @@ impl BluetoothManager { return drop(sender.send(Ok(BluetoothResponse::GetCharacteristics(characteristics_vec)))); } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn get_descriptor(&mut self, characteristic_id: String, uuid: String, sender: IpcSender) { + // Step 5. if !self.cached_characteristics.contains_key(&characteristic_id) { return drop(sender.send(Err(BluetoothError::InvalidState))); } let mut adapter = get_adapter_or_return_error!(self, sender); let descriptors = self.get_gatt_descriptors_by_uuid(&mut adapter, &characteristic_id, &uuid); - if descriptors.is_empty() { - return drop(sender.send(Err(BluetoothError::NotFound))); - } + + // Step 6. for descriptor in descriptors { if let Ok(uuid) = descriptor.get_uuid() { return drop(sender.send( @@ -919,13 +955,17 @@ impl BluetoothManager { )); } } + // Step 7. return drop(sender.send(Err(BluetoothError::NotFound))); } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptors + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn get_descriptors(&mut self, characteristic_id: String, uuid: Option, sender: IpcSender) { + // Step 5. if !self.cached_characteristics.contains_key(&characteristic_id) { return drop(sender.send(Err(BluetoothError::InvalidState))); } @@ -934,9 +974,8 @@ impl BluetoothManager { Some(id) => self.get_gatt_descriptors_by_uuid(&mut adapter, &characteristic_id, &id), None => self.get_and_cache_gatt_descriptors(&mut adapter, &characteristic_id), }; - if descriptors.is_empty() { - return drop(sender.send(Err(BluetoothError::NotFound))); - } + + // Step 6. let mut descriptors_vec = vec!(); for descriptor in descriptors { if let Ok(uuid) = descriptor.get_uuid() { @@ -949,60 +988,108 @@ impl BluetoothManager { } } descriptors_vec.retain(|d| !uuid_is_blocklisted(&d.uuid, Blocklist::All)); + + // Step 7. if descriptors_vec.is_empty() { return drop(sender.send(Err(BluetoothError::NotFound))); } return drop(sender.send(Ok(BluetoothResponse::GetDescriptors(descriptors_vec)))); } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue fn read_value(&mut self, id: String, sender: IpcSender) { + // (Characteristic) Step 5.2: Missing because it is optional. + // (Descriptor) Step 5.1: Missing because it is optional. let mut adapter = get_adapter_or_return_error!(self, sender); + + // (Characteristic) Step 5.3. let mut value = self.get_gatt_characteristic(&mut adapter, &id) .map(|c| c.read_value().unwrap_or(vec![])); + + // (Characteristic) TODO: Step 5.4: Handle all the errors returned from the read_value call. + + // (Descriptor) Step 5.2. if value.is_none() { value = self.get_gatt_descriptor(&mut adapter, &id) .map(|d| d.read_value().unwrap_or(vec![])); } + + // (Descriptor) TODO: Step 5.3: Handle all the errors returned from the read_value call. + match value { + // (Characteristic) Step 5.5.4. + // (Descriptor) Step 5.4.3. Some(v) => return drop(sender.send(Ok(BluetoothResponse::ReadValue(v)))), + + // (Characteristic) Step 4. + // (Descriptor) Step 4. None => return drop(sender.send(Err(BluetoothError::InvalidState))), } } + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue fn write_value(&mut self, id: String, value: Vec, sender: IpcSender) { + // (Characteristic) Step 7.2: Missing because it is optional. + // (Descriptor) Step 7.1: Missing because it is optional. let mut adapter = get_adapter_or_return_error!(self, sender); + + // (Characteristic) Step 7.3. let mut result = self.get_gatt_characteristic(&mut adapter, &id) .map(|c| c.write_value(value.clone())); + + // (Characteristic) TODO: Step 7.4: Handle all the errors returned from the write_value call. + + // (Descriptor) Step 7.2. if result.is_none() { result = self.get_gatt_descriptor(&mut adapter, &id) .map(|d| d.write_value(value.clone())); } + + // (Descriptor) TODO: Step 7.3: Handle all the errors returned from the write_value call. + match result { Some(v) => match v { + // (Characteristic) Step 7.5.3. + // (Descriptor) Step 7.4.3. Ok(_) => return drop(sender.send(Ok(BluetoothResponse::WriteValue(value)))), + + // (Characteristic) Step 7.1. Err(_) => return drop(sender.send(Err(BluetoothError::NotSupported))), }, + + // (Characteristic) Step 6. + // (Descriptor) Step 6. None => return drop(sender.send(Err(BluetoothError::InvalidState))), } } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-startnotifications + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-stopnotifications fn enable_notification(&mut self, id: String, enable: bool, sender: IpcSender) { + // (StartNotification) TODO: Step 7: Missing because it is optional. let mut adapter = get_adapter_or_return_error!(self, sender); match self.get_gatt_characteristic(&mut adapter, &id) { Some(c) => { let result = match enable { + // (StartNotification) Step 8. + // TODO: Handle all the errors returned from the start_notify call. true => c.start_notify(), + + // (StopNotification) Step 4. false => c.stop_notify(), }; match result { - // Step 11. + // (StartNotification) Step 11. + // (StopNotification) Step 5. Ok(_) => return drop(sender.send(Ok(BluetoothResponse::EnableNotification(())))), - // Step 4. + + // (StartNotification) Step 4. Err(_) => return drop(sender.send(Err(BluetoothError::NotSupported))), } }, - // Step 3. + // (StartNotification) Step 3. None => return drop(sender.send(Err(BluetoothError::InvalidState))), } } diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs index ddd1e4d411a..aa2c4c4639a 100644 --- a/components/script/dom/bluetooth.rs +++ b/components/script/dom/bluetooth.rs @@ -79,7 +79,7 @@ impl BluetoothResponseListener f match response { Ok(response) => self.receiver.root().handle_response(response, promise_cx, &promise), // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice - // Step 3-4. + // Step 3 - 4. Err(error) => promise.reject_error(promise_cx, Error::from(error)), } } @@ -187,9 +187,9 @@ impl Bluetooth { let option = RequestDeviceoptions::new(BluetoothScanfilterSequence::new(uuid_filters), ServiceUUIDSequence::new(optional_services_uuids)); - // TODO: Step 3-5: Implement the permission API. + // TODO: Step 3 - 5: Implement the permission API. - // Note: Steps 6-8 are implemented in + // Note: Steps 6 - 8 are implemented in // components/net/bluetooth_thread.rs in request_device function. let sender = response_async(p, self); self.get_bluetooth_thread().send(BluetoothRequest::RequestDevice(option, sender)).unwrap(); @@ -295,7 +295,7 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible String::new(), }; - // Step 6 -7. + // Step 6 - 7. let manufacturer_data = match filter.manufacturerData { Some(ref manufacturer_data_map) => { // Note: If manufacturer_data_map is empty, that means there are no key values in it. @@ -304,7 +304,7 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible id, Err(err) => return Err(Type(format!("{} {} {}", KEY_CONVERSION_ERROR, key, err))), @@ -312,7 +312,7 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible Fallible None, }; - // Step 8-9. + // Step 8 - 9. let service_data = match filter.serviceData { Some(ref service_data_map) => { // Note: If service_data_map is empty, that means there are no key values in it. @@ -336,7 +336,7 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible StringOrUnsignedLong::String(key.clone()) }; - // Step 9.3-9.4. + // Step 9.3 - 9.4. let service = try!(BluetoothUUID::service(service_name)).to_string(); // Step 9.5. @@ -346,7 +346,7 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible) { match response { // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices - // Step 13-14. + // Step 13 - 14. 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()) { diff --git a/components/script/dom/bluetoothdevice.rs b/components/script/dom/bluetoothdevice.rs index 4f66218dfb4..4af6b2a4f59 100644 --- a/components/script/dom/bluetoothdevice.rs +++ b/components/script/dom/bluetoothdevice.rs @@ -78,6 +78,7 @@ impl BluetoothDeviceMethods for BluetoothDevice { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-gatt fn Gatt(&self) -> Root { + // TODO: Step 1 - 2: Implement the Permission API. self.gatt.or_init(|| { BluetoothRemoteGATTServer::new(&self.global(), self) }) diff --git a/components/script/dom/bluetoothremotegattcharacteristic.rs b/components/script/dom/bluetoothremotegattcharacteristic.rs index f1c26f47c42..3c3c86001fb 100644 --- a/components/script/dom/bluetoothremotegattcharacteristic.rs +++ b/components/script/dom/bluetoothremotegattcharacteristic.rs @@ -103,9 +103,12 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetDescriptor(&self, descriptor: BluetoothDescriptorUUID) -> Rc { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. let uuid = match BluetoothUUID::descriptor(descriptor) { Ok(uuid) => uuid.to_string(), Err(e) => { @@ -113,14 +116,23 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris return p; } }; + + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } + + // Step 3 - 4. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetoothRemoteGATTService. + + // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_descriptor function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetDescriptor(self.get_instance_id(), uuid, sender)).unwrap(); @@ -129,6 +141,7 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptors + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetDescriptors(&self, descriptor: Option) -> Rc { @@ -136,6 +149,7 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris let p_cx = p.global().get_cx(); let mut uuid: Option = None; if let Some(d) = descriptor { + // Step 1. uuid = match BluetoothUUID::descriptor(d) { Ok(uuid) => Some(uuid.to_string()), Err(e) => { @@ -144,16 +158,24 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris } }; if let Some(ref uuid) = uuid { + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } } }; + + // Step 3 - 4. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetoothRemoteGATTService. + + // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_descriptors function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetDescriptors(self.get_instance_id(), uuid, sender)).unwrap(); @@ -170,18 +192,31 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris fn ReadValue(&self) -> Rc { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Reads) { p.reject_error(p_cx, Security); return p; } + + // Step 2. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 3 - 4: Implement representedCharacteristic internal slot for BluetoothRemoteGATTCharacteristic. + + // TODO: Step 5: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer. + + // Step 5.1. if !self.Properties().Read() { p.reject_error(p_cx, NotSupported); return p; } + + // Note: Remaining substeps of Step 5 are implemented in components/bluetooth/lib.rs in readValue function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::ReadValue(self.get_instance_id(), sender)).unwrap(); @@ -193,25 +228,39 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris fn WriteValue(&self, value: Vec) -> Rc { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Writes) { p.reject_error(p_cx, Security); return p; } + + // Step 2 - 3. if value.len() > MAXIMUM_ATTRIBUTE_LENGTH { p.reject_error(p_cx, InvalidModification); return p; } + + // Step 4. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + // TODO: Step 5 - 6: Implement representedCharacteristic internal slot for BluetoothRemoteGATTCharacteristic. + + // TODO: Step 7: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer. + + // Step 7.1. if !(self.Properties().Write() || self.Properties().WriteWithoutResponse() || self.Properties().AuthenticatedSignedWrites()) { p.reject_error(p_cx, NotSupported); return p; } + + // Note: Remaining substeps of Step 7 are implemented in components/bluetooth/lib.rs in writeValue function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::WriteValue(self.get_instance_id(), value, sender)).unwrap(); @@ -223,22 +272,32 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris fn StartNotifications(&self) -> Rc { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Reads) { p.reject_error(p_cx, Security); return p; } - // Step 3. + + // TODO: Step 2 - 3: Implement representedCharacteristic internal slot for BluetoothRemoteGATTCharacteristic. + + // Step 4. if !(self.Properties().Notify() || self.Properties().Indicate()) { p.reject_error(p_cx, NotSupported); return p; } + + // TODO: Step 5: Implement `active notification context set` for BluetoothRemoteGATTCharacteristic. + // Step 6. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // Note: Steps 7 - 11 are implemented in components/bluetooth/lib.rs in enable_notification function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::EnableNotification(self.get_instance_id(), @@ -252,6 +311,12 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris fn StopNotifications(&self) -> Rc { let p = Promise::new(&self.global()); let sender = response_async(&p, self); + + // TODO: Step 1 - 4: Implement representedCharacteristic internal slot and + // `active notification context set` for BluetoothRemoteGATTCharacteristic, + + // Note: Part of Step 4 and Step 5 are implemented in components/bluetooth/lib.rs in enable_notification + // function and in handle_response function. self.get_bluetooth_thread().send( BluetoothRequest::EnableNotification(self.get_instance_id(), false, @@ -266,6 +331,9 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetDescriptor(descriptor) => { let context = self.service.get().get_device().get_context(); let mut descriptor_map = context.get_descriptor_map().borrow_mut(); @@ -279,6 +347,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic { descriptor_map.insert(descriptor.instance_id, MutHeap::new(&bt_descriptor)); promise.resolve_native(promise_cx, &bt_descriptor); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptors + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetDescriptors(descriptors_vec) => { let mut descriptors = vec!(); let context = self.service.get().get_device().get_context(); @@ -300,17 +371,40 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic { } promise.resolve_native(promise_cx, &descriptors); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue BluetoothResponse::ReadValue(result) => { + // TODO: Step 5.5.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // Step 5.5.2. + // TODO(#5014): Replace ByteString with ArrayBuffer when it is implemented. let value = ByteString::new(result); *self.value.borrow_mut() = Some(value.clone()); + + // Step 5.5.3. self.upcast::().fire_bubbling_event(atom!("characteristicvaluechanged")); + + // Step 5.5.4. promise.resolve_native(promise_cx, &value); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue BluetoothResponse::WriteValue(result) => { + // TODO: Step 7.5.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // Step 7.5.2. + // TODO(#5014): Replace ByteString with an ArrayBuffer wrapped in a DataView. *self.value.borrow_mut() = Some(ByteString::new(result)); + + // Step 7.5.3. promise.resolve_native(promise_cx, &()); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-startnotifications + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-stopnotifications BluetoothResponse::EnableNotification(_result) => { + // (StartNotification) TODO: Step 10: Implement `active notification context set` + // for BluetoothRemoteGATTCharacteristic. + + // (StartNotification) Step 11. + // (StopNotification) Step 5. promise.resolve_native(promise_cx, self); }, _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())), diff --git a/components/script/dom/bluetoothremotegattdescriptor.rs b/components/script/dom/bluetoothremotegattdescriptor.rs index c5b67a44aec..8f092c6cad5 100644 --- a/components/script/dom/bluetoothremotegattdescriptor.rs +++ b/components/script/dom/bluetoothremotegattdescriptor.rs @@ -90,14 +90,24 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor { fn ReadValue(&self) -> Rc { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Reads) { p.reject_error(p_cx, Security); return p; } + + // Step 2. if !self.Characteristic().Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 3 - 4: Implement representedDescriptor internal slot for BluetoothRemoteGATTDescriptor. + + // TODO: Step 5: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer. + // Note: Substeps of Step 5 are implemented in components/bluetooth/lib.rs in readValue function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::ReadValue(self.get_instance_id(), sender)).unwrap(); @@ -109,18 +119,30 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor { fn WriteValue(&self, value: Vec) -> Rc { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Writes) { p.reject_error(p_cx, Security); return p; } + + // Step 2 - 3. if value.len() > MAXIMUM_ATTRIBUTE_LENGTH { p.reject_error(p_cx, InvalidModification); return p; } + + // Step 4. if !self.Characteristic().Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5 - 6: Implement representedCharacteristic internal slot for BluetoothRemoteGATTCharacteristic. + + // TODO: Step 7: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer. + // Note: Substeps of Step 7 are implemented in components/bluetooth/lib.rs in writeValue function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::WriteValue(self.get_instance_id(), value, sender)).unwrap(); @@ -131,13 +153,28 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor { impl AsyncBluetoothListener for BluetoothRemoteGATTDescriptor { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue BluetoothResponse::ReadValue(result) => { + // TODO: Step 5.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // Step 5.4.2. + // TODO(#5014): Replace ByteString with ArrayBuffer when it is implemented. let value = ByteString::new(result); *self.value.borrow_mut() = Some(value.clone()); + + // Step 5.4.3. promise.resolve_native(promise_cx, &value); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue BluetoothResponse::WriteValue(result) => { + // TODO: Step 7.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // Step 7.4.2. + // TODO(#5014): Replace ByteString with an ArrayBuffer wrapped in a DataView. *self.value.borrow_mut() = Some(ByteString::new(result)); + + // Step 7.4.3. + // TODO: Resolve promise with undefined instead of a value. promise.resolve_native(promise_cx, &()); }, _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())), diff --git a/components/script/dom/bluetoothremotegattserver.rs b/components/script/dom/bluetoothremotegattserver.rs index f8424ad6241..06c11f0aa21 100644 --- a/components/script/dom/bluetoothremotegattserver.rs +++ b/components/script/dom/bluetoothremotegattserver.rs @@ -65,19 +65,42 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect fn Connect(&self) -> Rc { + // Step 1. let p = Promise::new(&self.global()); let sender = response_async(&p, self); + + // TODO: Step 2: Implement representedDevice internal slot for BluetoothDevice. + + // TODO: Step 3: Check if the UA is currently using the Bluetooth system. + + // TODO: Step 4: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // TODO: Step 5.1 - 5.2: Implement activeAlgorithms, representedDevice internal slots + // and the` garbage-collect the connection` algorithm. + + // Note: Steps 5.1.1 and 5.1.3 are in components/bluetooth/lib.rs in the gatt_server_connect function. + // Steps 5.2.4 - 5.2.5 are in response function. self.get_bluetooth_thread().send( BluetoothRequest::GATTServerConnect(String::from(self.Device().Id()), sender)).unwrap(); + // Step 5: return promise. return p; } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect fn Disconnect(&self) -> ErrorResult { + // TODO: Step 1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // TODO: Step 2: Check if this.connected is false here too. let (sender, receiver) = ipc::channel().unwrap(); self.get_bluetooth_thread().send( BluetoothRequest::GATTServerDisconnect(String::from(self.Device().Id()), sender)).unwrap(); let server = receiver.recv().unwrap(); + + // TODO: Step 3: Implement the `clean up the disconnected device` algorithm. + + // TODO: Step 4: Implement representedDevice internal slot for BluetoothDevice. + + // TODO: Step 5: Implement the `garbage-collect the connection` algorithm. match server { Ok(connected) => { self.connected.set(connected); @@ -92,8 +115,12 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice fn GetPrimaryService(&self, service: BluetoothServiceUUID) -> Rc { + // TODO: Step 1: Implement the Permission API and the allowedServices BluetoothDevice internal slot. + // Subsequent steps are relative to https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. let uuid = match BluetoothUUID::service(service) { Ok(uuid) => uuid.to_string(), Err(e) => { @@ -101,14 +128,23 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { return p; } }; + + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedDevice internal slot for BluetoothDevice. + + // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_primary_service function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetPrimaryService(String::from(self.Device().Id()), uuid, sender)).unwrap(); @@ -118,10 +154,14 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices fn GetPrimaryServices(&self, service: Option) -> Rc { + // TODO: Step 1: Implement the Permission API and the allowedServices BluetoothDevice internal slot. + // Subsequent steps are relative to https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + let mut uuid: Option = None; if let Some(s) = service { + // Step 1. uuid = match BluetoothUUID::service(s) { Ok(uuid) => Some(uuid.to_string()), Err(e) => { @@ -130,16 +170,24 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { } }; if let Some(ref uuid) = uuid { + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } } }; + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedDevice internal slot for BluetoothDevice. + + // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_primary_services function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetPrimaryServices(String::from(self.Device().Id()), uuid, sender)).unwrap(); @@ -150,10 +198,17 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { impl AsyncBluetoothListener for BluetoothRemoteGATTServer { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect BluetoothResponse::GATTServerConnect(connected) => { + // Step 5.2.4. self.connected.set(connected); + + // Step 5.2.5. promise.resolve_native(promise_cx, self); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetPrimaryService(service) => { let context = self.device.get().get_context(); let mut service_map = context.get_service_map().borrow_mut(); @@ -168,6 +223,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTServer { service_map.insert(service.instance_id, MutHeap::new(&bt_service)); promise.resolve_native(promise_cx, &bt_service); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetPrimaryServices(services_vec) => { let mut services = vec!(); let context = self.device.get().get_context(); diff --git a/components/script/dom/bluetoothremotegattservice.rs b/components/script/dom/bluetoothremotegattservice.rs index 2f2206eb89a..ef922e9b385 100644 --- a/components/script/dom/bluetoothremotegattservice.rs +++ b/components/script/dom/bluetoothremotegattservice.rs @@ -95,11 +95,14 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetCharacteristic(&self, characteristic: BluetoothCharacteristicUUID) -> Rc { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. let uuid = match BluetoothUUID::characteristic(characteristic) { Ok(uuid) => uuid.to_string(), Err(e) => { @@ -107,14 +110,23 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { return p; } }; + + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetootRemoteGATTService. + + // Note: Steps 6 - 7 are implemented is components/bluetooth/lib.rs in get_characteristic function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap(); @@ -123,6 +135,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetCharacteristics(&self, characteristic: Option) -> Rc { @@ -130,6 +143,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { let p_cx = p.global().get_cx(); let mut uuid: Option = None; if let Some(c) = characteristic { + // Step 1. uuid = match BluetoothUUID::characteristic(c) { Ok(uuid) => Some(uuid.to_string()), Err(e) => { @@ -138,16 +152,24 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { } }; if let Some(ref uuid) = uuid { + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } } }; + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetootRemoteGATTService. + + // Note: Steps 6 - 7 are implemented is components/bluetooth/lib.rs in get_characteristics function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetCharacteristics(self.get_instance_id(), uuid, sender)).unwrap(); @@ -156,11 +178,14 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetIncludedService(&self, service: BluetoothServiceUUID) -> Rc { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. let uuid = match BluetoothUUID::service(service) { Ok(uuid) => uuid.to_string(), Err(e) => { @@ -168,14 +193,23 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { return p; } }; + + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetootRemoteGATTService. + + // Note: Steps 6 - 7 are implemented is components/bluetooth/lib.rs in get_included_service function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetIncludedService(self.get_instance_id(), @@ -187,6 +221,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetIncludedServices(&self, service: Option) -> Rc { @@ -194,6 +229,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { let p_cx = p.global().get_cx(); let mut uuid: Option = None; if let Some(s) = service { + // Step 1. uuid = match BluetoothUUID::service(s) { Ok(uuid) => Some(uuid.to_string()), Err(e) => { @@ -202,16 +238,24 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { } }; if let Some(ref uuid) = uuid { + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } } }; + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetootRemoteGATTService. + + // Note: Steps 6 - 7 are implemented is components/bluetooth/lib.rs in get_included_services function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetIncludedServices(self.get_instance_id(), @@ -233,6 +277,9 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { impl AsyncBluetoothListener for BluetoothRemoteGATTService { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetCharacteristic(characteristic) => { let context = self.device.get().get_context(); let mut characteristic_map = context.get_characteristic_map().borrow_mut(); @@ -258,6 +305,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTService { characteristic_map.insert(characteristic.instance_id, MutHeap::new(&bt_characteristic)); promise.resolve_native(promise_cx, &bt_characteristic); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetCharacteristics(characteristics_vec) => { let mut characteristics = vec!(); let context = self.device.get().get_context(); @@ -292,6 +342,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTService { } promise.resolve_native(promise_cx, &characteristics); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetIncludedService(service) => { let s = BluetoothRemoteGATTService::new(&self.global(), @@ -301,6 +354,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTService { service.instance_id); promise.resolve_native(promise_cx, &s); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetIncludedServices(services_vec) => { let s: Vec> = services_vec.into_iter()