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()