diff --git a/components/net/bluetooth_thread.rs b/components/net/bluetooth_thread.rs index e81ed2be8b60..efdeb02d7b21 100644 --- a/components/net/bluetooth_thread.rs +++ b/components/net/bluetooth_thread.rs @@ -84,17 +84,20 @@ impl BluetoothThreadFactory for IpcSender { } } +// https://webbluetoothcg.github.io/web-bluetooth/#matches-a-filter fn matches_filter(device: &BluetoothDevice, filter: &BluetoothScanfilter) -> bool { if filter.is_empty_or_invalid() { return false; } + // Step 1. if !filter.get_name().is_empty() { if device.get_name().ok() != Some(filter.get_name().to_string()) { return false; } } + // Step 2. if !filter.get_name_prefix().is_empty() { if let Ok(device_name) = device.get_name() { if !device_name.starts_with(filter.get_name_prefix()) { @@ -105,6 +108,7 @@ fn matches_filter(device: &BluetoothDevice, filter: &BluetoothScanfilter) -> boo } } + // Step 3. if !filter.get_services().is_empty() { if let Ok(device_uuids) = device.get_uuids() { for service in filter.get_services() { @@ -115,6 +119,23 @@ fn matches_filter(device: &BluetoothDevice, filter: &BluetoothScanfilter) -> boo } } +// Step 4. +// TODO: Implement get_manufacturer_data in device crate. +// if let Some(manufacturer_id) = filter.get_manufacturer_id() { +// if !device.get_manufacturer_data().contains_key(manufacturer_id) { +// return false; +// } +// } +// +// Step 5. +// TODO: Implement get_device_data in device crate. +// if !filter.get_service_data_uuid().is_empty() { +// if !device.get_service_data().contains_key(filter.get_service_data_uuid()) { +// return false; +// } +// } + + // Step 6. true } @@ -428,6 +449,7 @@ impl BluetoothManager { // Methods + // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices fn request_device(&mut self, options: RequestDeviceoptions, sender: IpcSender>) { @@ -439,13 +461,18 @@ impl BluetoothManager { let _ = session.stop_discovery(); } + // Step 6. + // Note: There is no requiredServiceUUIDS, we scan for all devices. let mut matched_devices = self.get_and_cache_devices(&mut adapter); + + // Step 7. if !options.is_accepting_all_devices() { matched_devices = matched_devices.into_iter() .filter(|d| matches_filters(d, options.get_filters())) .collect(); } + // Step 8. if let Some(address) = self.select_device(matched_devices) { let device_id = match self.address_to_id.get(&address) { Some(id) => id.clone(), diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs index cac1cb7c7ee7..2940a211bf35 100644 --- a/components/script/dom/bluetooth.rs +++ b/components/script/dom/bluetooth.rs @@ -66,10 +66,24 @@ impl Bluetooth { filters: &Option>, optional_services: &Option>) -> Fallible> { + // TODO: Step 1: Triggered by user activation. + + // Step 2. let option = try!(convert_request_device_options(self.global().r(), filters, optional_services)); + + // TODO: Step 3-5: Implement the permission API. + + // Note: Steps 6-8 are implemented in + // components/net/bluetooth_thread.rs in request_device function. let (sender, receiver) = ipc::channel().unwrap(); self.get_bluetooth_thread().send(BluetoothMethodMsg::RequestDevice(option, sender)).unwrap(); let device = receiver.recv().unwrap(); + + // TODO: Step 9-10: Implement the permission API. + + // Step 11: This step is optional. + + // Step 12-13. match device { Ok(device) => { let ad_data = BluetoothAdvertisingData::new(self.global().r(), @@ -89,16 +103,25 @@ impl Bluetooth { } } +// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices fn convert_request_device_options(global: GlobalRef, 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, global))); } } @@ -106,8 +129,12 @@ fn convert_request_device_options(global: GlobalRef, 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::GetService(global, opt_service.clone())).to_string(); + // 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_blacklisted(uuid.as_ref(), Blacklist::All) { optional_services_uuids.push(uuid); } @@ -118,7 +145,9 @@ fn convert_request_device_options(global: GlobalRef, ServiceUUIDSequence::new(optional_services_uuids))) } +// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices fn canonicalize_filter(filter: &BluetoothRequestDeviceFilter, global: GlobalRef) -> Fallible { + // Step 2.4.1. if filter.services.is_none() && filter.name.is_none() && filter.namePrefix.is_none() && @@ -127,26 +156,41 @@ fn canonicalize_filter(filter: &BluetoothRequestDeviceFilter, global: GlobalRef) return Err(Type(FILTER_ERROR.to_owned())); } + // Step 2.4.2: There is no empty canonicalizedFilter member, + // we create a BluetoothScanfilter instance at the end of the function. + + // Step 2.4.3. let services_vec = match filter.services { Some(ref services) => { + // Step 2.4.3.1. if services.is_empty() { return Err(Type(SERVICE_ERROR.to_owned())); } + let mut services_vec = vec!(); + for service in services { + // Step 2.4.3.2 - 2.4.3.3. let uuid = try!(BluetoothUUID::GetService(global, service.clone())).to_string(); + + // Step 2.4.3.4. if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) { return Err(Security) } + services_vec.push(uuid); } + // Step 2.4.3.5. services_vec + // Step 2.4.3.6: There is no requiredServiceUUIDS, we scan for all devices. }, None => vec!(), }; + // Step 2.4.4. let name = match filter.name { Some(ref name) => { + // Step 2.4.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())); @@ -154,13 +198,17 @@ fn canonicalize_filter(filter: &BluetoothRequestDeviceFilter, global: GlobalRef) if name.len() > MAX_FILTER_NAME_LENGTH { return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned())); } + + // Step 2.4.4.2. name.to_string() }, None => String::new(), }; + // Step 2.4.5. let name_prefix = match filter.namePrefix { Some(ref name_prefix) => { + // Step 2.4.5.1. if name_prefix.is_empty() { return Err(Type(NAME_PREFIX_ERROR.to_owned())); } @@ -170,19 +218,28 @@ fn canonicalize_filter(filter: &BluetoothRequestDeviceFilter, global: GlobalRef) if name_prefix.len() > MAX_FILTER_NAME_LENGTH { return Err(Type(FILTER_NAME_TOO_LONG_ERROR.to_owned())); } + + // Step 2.4.5.2. name_prefix.to_string() }, None => String::new(), }; + // Step 2.4.6. let manufacturer_id = filter.manufacturerId; + // Step 2.4.7. let service_data_uuid = match filter.serviceDataUUID { Some(ref service_data_uuid) => { + // Step 2.4.7.1 - 2.4.7.2. let uuid = try!(BluetoothUUID::GetService(global, service_data_uuid.clone())).to_string(); + + // Step 2.4.7.3. if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) { return Err(Security) } + + // Step 2.4.7.4. uuid }, None => String::new(), @@ -210,15 +267,18 @@ impl From for Error { impl BluetoothMethods for Bluetooth { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice fn RequestDevice(&self, option: &RequestDeviceOptions) -> Fallible> { + // Step 1. + // TODO(#4282): Reject promise. if (option.filters.is_some() && option.acceptAllDevices) || (option.filters.is_none() && !option.acceptAllDevices) { return Err(Type(OPTIONS_ERROR.to_owned())); } - + // Step 2. if !option.acceptAllDevices { return self.request_bluetooth_devices(&option.filters, &option.optionalServices); } self.request_bluetooth_devices(&None, &option.optionalServices) + // TODO(#4282): Step 3-5: Reject and resolve promise. } }