Commit cfcd3337 authored by ortuno's avatar ortuno Committed by Commit bot

bluetooth: Implement acceptAllDevices

When acceptAllDevices is true the chooser will show all devices in range.

BUG=654918

Review-Url: https://codereview.chromium.org/2510323002
Cr-Commit-Position: refs/heads/master@{#436209}
parent ba78be88
...@@ -121,7 +121,7 @@ enum BadMessageReason { ...@@ -121,7 +121,7 @@ enum BadMessageReason {
RDH_INVALID_URL = 97, RDH_INVALID_URL = 97,
BDH_CHARACTERISTIC_ALREADY_SUBSCRIBED = 98, BDH_CHARACTERISTIC_ALREADY_SUBSCRIBED = 98,
RFH_OWNER_PROPERTY = 99, RFH_OWNER_PROPERTY = 99,
BDH_EMPTY_OR_INVALID_FILTERS = 100, BDH_EMPTY_OR_INVALID_FILTERS = 100, // obsolete; no longer used
WC_CONTENT_WITH_CERT_ERRORS_BAD_SECURITY_INFO = 101, // obsolete; not used WC_CONTENT_WITH_CERT_ERRORS_BAD_SECURITY_INFO = 101, // obsolete; not used
RFMF_RENDERER_FAKED_ITS_OWN_DEATH = 102, RFMF_RENDERER_FAKED_ITS_OWN_DEATH = 102,
DWNLD_INVALID_SAVABLE_RESOURCE_LINKS_RESPONSE = 103, DWNLD_INVALID_SAVABLE_RESOURCE_LINKS_RESPONSE = 103,
...@@ -179,6 +179,7 @@ enum BadMessageReason { ...@@ -179,6 +179,7 @@ enum BadMessageReason {
MDDH_INVALID_SUBSCRIPTION_REQUEST = 155, MDDH_INVALID_SUBSCRIPTION_REQUEST = 155,
MDDH_INVALID_UNSUBSCRIPTION_REQUEST = 156, MDDH_INVALID_UNSUBSCRIPTION_REQUEST = 156,
AOAH_NONSENSE_DEVICE_ID = 157, AOAH_NONSENSE_DEVICE_ID = 157,
BDH_INVALID_OPTIONS = 158,
// Please add new elements here. The naming convention is abbreviated class // Please add new elements here. The naming convention is abbreviated class
// name (e.g. RenderFrameHost becomes RFH) plus a unique description of the // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
......
...@@ -166,15 +166,18 @@ void BluetoothAllowedDevicesMap::AddUnionOfServicesTo( ...@@ -166,15 +166,18 @@ void BluetoothAllowedDevicesMap::AddUnionOfServicesTo(
const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options, const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options,
std::unordered_set<BluetoothUUID, device::BluetoothUUIDHash>* std::unordered_set<BluetoothUUID, device::BluetoothUUIDHash>*
unionOfServices) { unionOfServices) {
for (const auto& filter : options->filters) { if (options->filters) {
if (!filter->services) { for (const auto& filter : options->filters.value()) {
continue; if (!filter->services) {
} continue;
}
for (const BluetoothUUID& uuid : filter->services.value()) {
unionOfServices->insert(uuid); for (const BluetoothUUID& uuid : filter->services.value()) {
unionOfServices->insert(uuid);
}
} }
} }
for (const BluetoothUUID& uuid : options->optional_services) { for (const BluetoothUUID& uuid : options->optional_services) {
unionOfServices->insert(uuid); unionOfServices->insert(uuid);
} }
......
...@@ -191,7 +191,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, NoPermissionForAnyService) { ...@@ -191,7 +191,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, NoPermissionForAnyService) {
blink::mojom::WebBluetoothScanFilter::New(); blink::mojom::WebBluetoothScanFilter::New();
scan_filter->name = kDeviceName; scan_filter->name = kDeviceName;
options->filters.push_back(scan_filter.Clone()); options->filters.emplace();
options->filters->push_back({scan_filter.Clone()});
// Add to map. // Add to map.
const WebBluetoothDeviceId device_id = const WebBluetoothDeviceId device_id =
...@@ -217,11 +218,12 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginOneDevice) { ...@@ -217,11 +218,12 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginOneDevice) {
scan_filter1->services.emplace(); scan_filter1->services.emplace();
scan_filter1->services->push_back(kGlucoseUUID); scan_filter1->services->push_back(kGlucoseUUID);
options->filters.push_back(scan_filter1.Clone()); options->filters.emplace();
options->filters->push_back(scan_filter1.Clone());
scan_filter2->services.emplace(); scan_filter2->services.emplace();
scan_filter2->services->push_back(kHeartRateUUID); scan_filter2->services->push_back(kHeartRateUUID);
options->filters.push_back(scan_filter2.Clone()); options->filters->push_back(scan_filter2.Clone());
options->optional_services.push_back(kBatteryServiceUUID); options->optional_services.push_back(kBatteryServiceUUID);
options->optional_services.push_back(kHeartRateUUID); options->optional_services.push_back(kHeartRateUUID);
...@@ -260,8 +262,9 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginOneDevice) { ...@@ -260,8 +262,9 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginOneDevice) {
blink::mojom::WebBluetoothRequestDeviceOptionsPtr options2 = blink::mojom::WebBluetoothRequestDeviceOptionsPtr options2 =
blink::mojom::WebBluetoothRequestDeviceOptions::New(); blink::mojom::WebBluetoothRequestDeviceOptions::New();
options2->filters.push_back(scan_filter1.Clone()); options2->filters.emplace();
options2->filters.push_back(scan_filter2.Clone()); options2->filters->push_back(scan_filter1.Clone());
options2->filters->push_back(scan_filter2.Clone());
const WebBluetoothDeviceId device_id2 = const WebBluetoothDeviceId device_id2 =
allowed_devices_map.AddDevice(kTestOrigin1, kDeviceAddress1, options2); allowed_devices_map.AddDevice(kTestOrigin1, kDeviceAddress1, options2);
...@@ -298,7 +301,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginTwoDevices) { ...@@ -298,7 +301,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginTwoDevices) {
scan_filter1->services.emplace(); scan_filter1->services.emplace();
scan_filter1->services->push_back(kGlucoseUUID); scan_filter1->services->push_back(kGlucoseUUID);
options1->filters.push_back(std::move(scan_filter1)); options1->filters.emplace();
options1->filters->push_back(std::move(scan_filter1));
options1->optional_services.push_back(kHeartRateUUID); options1->optional_services.push_back(kHeartRateUUID);
...@@ -310,7 +314,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginTwoDevices) { ...@@ -310,7 +314,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginTwoDevices) {
scan_filter2->services.emplace(); scan_filter2->services.emplace();
scan_filter2->services->push_back(kBatteryServiceUUID); scan_filter2->services->push_back(kBatteryServiceUUID);
options2->filters.push_back(std::move(scan_filter2)); options2->filters.emplace();
options2->filters->push_back(std::move(scan_filter2));
options2->optional_services.push_back(kBloodPressureUUID); options2->optional_services.push_back(kBloodPressureUUID);
...@@ -361,7 +366,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_TwoOriginsOneDevice) { ...@@ -361,7 +366,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_TwoOriginsOneDevice) {
scan_filter1->services.emplace(); scan_filter1->services.emplace();
scan_filter1->services->push_back(kGlucoseUUID); scan_filter1->services->push_back(kGlucoseUUID);
options1->filters.push_back(std::move(scan_filter1)); options1->filters.emplace();
options1->filters->push_back(std::move(scan_filter1));
options1->optional_services.push_back(kHeartRateUUID); options1->optional_services.push_back(kHeartRateUUID);
...@@ -373,7 +379,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_TwoOriginsOneDevice) { ...@@ -373,7 +379,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_TwoOriginsOneDevice) {
scan_filter2->services.emplace(); scan_filter2->services.emplace();
scan_filter2->services->push_back(kBatteryServiceUUID); scan_filter2->services->push_back(kBatteryServiceUUID);
options2->filters.push_back(std::move(scan_filter2)); options2->filters.emplace();
options2->filters->push_back(std::move(scan_filter2));
options2->optional_services.push_back(kBloodPressureUUID); options2->optional_services.push_back(kBloodPressureUUID);
...@@ -443,7 +450,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, MergeServices) { ...@@ -443,7 +450,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, MergeServices) {
scan_filter1->services.emplace(); scan_filter1->services.emplace();
scan_filter1->services->push_back(kGlucoseUUID); scan_filter1->services->push_back(kGlucoseUUID);
options1->filters.push_back(std::move(scan_filter1)); options1->filters.emplace();
options1->filters->push_back(std::move(scan_filter1));
options1->optional_services.push_back(kBatteryServiceUUID); options1->optional_services.push_back(kBatteryServiceUUID);
...@@ -459,7 +467,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, MergeServices) { ...@@ -459,7 +467,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, MergeServices) {
scan_filter2->services.emplace(); scan_filter2->services.emplace();
scan_filter2->services->push_back(kHeartRateUUID); scan_filter2->services->push_back(kHeartRateUUID);
options2->filters.push_back(std::move(scan_filter2)); options2->filters.emplace();
options2->filters->push_back(std::move(scan_filter2));
options2->optional_services.push_back(kBloodPressureUUID); options2->optional_services.push_back(kBloodPressureUUID);
...@@ -499,7 +508,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, NoFilterServices) { ...@@ -499,7 +508,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, NoFilterServices) {
blink::mojom::WebBluetoothScanFilterPtr scan_filter = blink::mojom::WebBluetoothScanFilterPtr scan_filter =
blink::mojom::WebBluetoothScanFilter::New(); blink::mojom::WebBluetoothScanFilter::New();
options->filters.push_back(std::move(scan_filter)); options->filters.emplace();
options->filters->push_back(std::move(scan_filter));
// Add to map. // Add to map.
const WebBluetoothDeviceId device_id = const WebBluetoothDeviceId device_id =
......
...@@ -61,8 +61,13 @@ constexpr int kTestScanDuration = 0; ...@@ -61,8 +61,13 @@ constexpr int kTestScanDuration = 0;
void LogRequestDeviceOptions( void LogRequestDeviceOptions(
const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) { const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) {
VLOG(1) << "requestDevice called with the following filters: "; VLOG(1) << "requestDevice called with the following filters: ";
VLOG(1) << "acceptAllDevices: " << options->accept_all_devices;
if (!options->filters)
return;
int i = 0; int i = 0;
for (const auto& filter : options->filters) { for (const auto& filter : options->filters.value()) {
VLOG(1) << "Filter #" << ++i; VLOG(1) << "Filter #" << ++i;
if (filter->name) if (filter->name)
VLOG(1) << "Name: " << filter->name.value(); VLOG(1) << "Name: " << filter->name.value();
...@@ -100,11 +105,25 @@ bool IsEmptyOrInvalidFilter( ...@@ -100,11 +105,25 @@ bool IsEmptyOrInvalidFilter(
} }
bool HasEmptyOrInvalidFilter( bool HasEmptyOrInvalidFilter(
const std::vector<blink::mojom::WebBluetoothScanFilterPtr>& filters) { const base::Optional<std::vector<blink::mojom::WebBluetoothScanFilterPtr>>&
return filters.empty() filters) {
if (!filters) {
return true;
}
return filters->empty()
? true ? true
: filters.end() != std::find_if(filters.begin(), filters.end(), : filters->end() != std::find_if(filters->begin(), filters->end(),
IsEmptyOrInvalidFilter); IsEmptyOrInvalidFilter);
}
bool IsOptionsInvalid(
const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) {
if (options->accept_all_devices) {
return options->filters.has_value();
} else {
return HasEmptyOrInvalidFilter(options->filters);
}
} }
bool MatchesFilter(const std::string* device_name, bool MatchesFilter(const std::string* device_name,
...@@ -139,9 +158,10 @@ bool MatchesFilter(const std::string* device_name, ...@@ -139,9 +158,10 @@ bool MatchesFilter(const std::string* device_name,
bool MatchesFilters( bool MatchesFilters(
const std::string* device_name, const std::string* device_name,
const UUIDSet& device_uuids, const UUIDSet& device_uuids,
const std::vector<blink::mojom::WebBluetoothScanFilterPtr>& filters) { const base::Optional<std::vector<blink::mojom::WebBluetoothScanFilterPtr>>&
filters) {
DCHECK(!HasEmptyOrInvalidFilter(filters)); DCHECK(!HasEmptyOrInvalidFilter(filters));
for (const auto& filter : filters) { for (const auto& filter : filters.value()) {
if (MatchesFilter(device_name, device_uuids, filter)) { if (MatchesFilter(device_name, device_uuids, filter)) {
return true; return true;
} }
...@@ -150,16 +170,21 @@ bool MatchesFilters( ...@@ -150,16 +170,21 @@ bool MatchesFilters(
} }
std::unique_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter( std::unique_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter(
const std::vector<blink::mojom::WebBluetoothScanFilterPtr>& filters) { const base::Optional<std::vector<blink::mojom::WebBluetoothScanFilterPtr>>&
filters) {
std::unordered_set<BluetoothUUID, device::BluetoothUUIDHash> services; std::unordered_set<BluetoothUUID, device::BluetoothUUIDHash> services;
for (const auto& filter : filters) {
if (!filter->services) { if (filters) {
continue; for (const auto& filter : filters.value()) {
} if (!filter->services) {
for (const auto& service : filter->services.value()) { continue;
services.insert(service); }
for (const auto& service : filter->services.value()) {
services.insert(service);
}
} }
} }
// There isn't much support for GATT over BR/EDR from neither platforms nor // There isn't much support for GATT over BR/EDR from neither platforms nor
// devices so performing a Dual scan will find devices that the API is not // devices so performing a Dual scan will find devices that the API is not
// able to interact with. To avoid wasting power and confusing users with // able to interact with. To avoid wasting power and confusing users with
...@@ -261,17 +286,18 @@ void BluetoothDeviceChooserController::GetDevice( ...@@ -261,17 +286,18 @@ void BluetoothDeviceChooserController::GetDevice(
success_callback_ = success_callback; success_callback_ = success_callback;
error_callback_ = error_callback; error_callback_ = error_callback;
// The renderer should never send empty filters. // The renderer should never send invalid options.
if (HasEmptyOrInvalidFilter(options->filters)) { if (IsOptionsInvalid(options)) {
web_bluetooth_service_->CrashRendererAndClosePipe( web_bluetooth_service_->CrashRendererAndClosePipe(
bad_message::BDH_EMPTY_OR_INVALID_FILTERS); bad_message::BDH_INVALID_OPTIONS);
return; return;
} }
options_ = std::move(options); options_ = std::move(options);
LogRequestDeviceOptions(options_); LogRequestDeviceOptions(options_);
// Check blocklist to reject invalid filters and adjust optional_services. // Check blocklist to reject invalid filters and adjust optional_services.
if (BluetoothBlocklist::Get().IsExcluded(options_->filters)) { if (options_->filters &&
BluetoothBlocklist::Get().IsExcluded(options_->filters.value())) {
RecordRequestDeviceOutcome( RecordRequestDeviceOutcome(
UMARequestDeviceOutcome::BLOCKLISTED_SERVICE_IN_FILTER); UMARequestDeviceOutcome::BLOCKLISTED_SERVICE_IN_FILTER);
PostErrorCallback( PostErrorCallback(
...@@ -372,15 +398,17 @@ void BluetoothDeviceChooserController::GetDevice( ...@@ -372,15 +398,17 @@ void BluetoothDeviceChooserController::GetDevice(
void BluetoothDeviceChooserController::AddFilteredDevice( void BluetoothDeviceChooserController::AddFilteredDevice(
const device::BluetoothDevice& device) { const device::BluetoothDevice& device) {
base::Optional<std::string> device_name = device.GetName(); base::Optional<std::string> device_name = device.GetName();
if (chooser_.get() && if (chooser_.get()) {
MatchesFilters(device_name ? &device_name.value() : nullptr, if (options_->accept_all_devices ||
device.GetUUIDs(), options_->filters)) { MatchesFilters(device_name ? &device_name.value() : nullptr,
base::Optional<int8_t> rssi = device.GetInquiryRSSI(); device.GetUUIDs(), options_->filters)) {
chooser_->AddOrUpdateDevice( base::Optional<int8_t> rssi = device.GetInquiryRSSI();
device.GetAddress(), !!device.GetName() /* should_update_name */, chooser_->AddOrUpdateDevice(
device.GetNameForDisplay(), device.IsGattConnected(), device.GetAddress(), !!device.GetName() /* should_update_name */,
web_bluetooth_service_->IsDevicePaired(device.GetAddress()), device.GetNameForDisplay(), device.IsGattConnected(),
rssi ? CalculateSignalStrengthLevel(rssi.value()) : -1); web_bluetooth_service_->IsDevicePaired(device.GetAddress()),
rssi ? CalculateSignalStrengthLevel(rssi.value()) : -1);
}
} }
} }
......
...@@ -101,12 +101,14 @@ static void RecordUnionOfServices( ...@@ -101,12 +101,14 @@ static void RecordUnionOfServices(
union_of_services.insert(service.canonical_value()); union_of_services.insert(service.canonical_value());
} }
for (const auto& filter : options->filters) { if (options->filters) {
if (!filter->services) { for (const auto& filter : options->filters.value()) {
continue; if (!filter->services) {
} continue;
for (const BluetoothUUID& service : filter->services.value()) { }
union_of_services.insert(service.canonical_value()); for (const BluetoothUUID& service : filter->services.value()) {
union_of_services.insert(service.canonical_value());
}
} }
} }
...@@ -124,7 +126,13 @@ static void RecordUnionOfServices( ...@@ -124,7 +126,13 @@ static void RecordUnionOfServices(
void RecordRequestDeviceOptions( void RecordRequestDeviceOptions(
const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) { const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) {
RecordRequestDeviceFilters(options->filters); UMA_HISTOGRAM_BOOLEAN("Bluetooth.Web.RequestDevice.Options.AcceptAllDevices",
options->accept_all_devices);
if (options->filters) {
RecordRequestDeviceFilters(options->filters.value());
}
RecordRequestDeviceOptionalServices(options->optional_services); RecordRequestDeviceOptionalServices(options->optional_services);
RecordUnionOfServices(options); RecordUnionOfServices(options);
} }
......
...@@ -40,14 +40,21 @@ TypeConverter<blink::mojom::WebBluetoothRequestDeviceOptionsPtr, ...@@ -40,14 +40,21 @@ TypeConverter<blink::mojom::WebBluetoothRequestDeviceOptionsPtr,
blink::mojom::WebBluetoothRequestDeviceOptionsPtr options = blink::mojom::WebBluetoothRequestDeviceOptionsPtr options =
blink::mojom::WebBluetoothRequestDeviceOptions::New(); blink::mojom::WebBluetoothRequestDeviceOptions::New();
for (const auto& filter : web_options.filters) { options->accept_all_devices = web_options.acceptAllDevices;
options->filters.push_back(blink::mojom::WebBluetoothScanFilter::From<
blink::WebBluetoothScanFilter>(filter)); if (web_options.hasFilters) {
options->filters.emplace();
for (const auto& filter : web_options.filters) {
options->filters->push_back(blink::mojom::WebBluetoothScanFilter::From<
blink::WebBluetoothScanFilter>(filter));
}
} }
for (const auto& optional_service : web_options.optionalServices) { for (const auto& optional_service : web_options.optionalServices) {
options->optional_services.push_back( options->optional_services.push_back(
device::BluetoothUUID(optional_service.utf8())); device::BluetoothUUID(optional_service.utf8()));
} }
return options; return options;
} }
......
...@@ -188,6 +188,10 @@ LayoutTestBluetoothAdapterProvider::GetBluetoothAdapter( ...@@ -188,6 +188,10 @@ LayoutTestBluetoothAdapterProvider::GetBluetoothAdapter(
return GetMissingCharacteristicHeartRateAdapter(); return GetMissingCharacteristicHeartRateAdapter();
if (fake_adapter_name == "HeartRateAdapter") if (fake_adapter_name == "HeartRateAdapter")
return GetHeartRateAdapter(); return GetHeartRateAdapter();
if (fake_adapter_name == "EmptyNameDeviceAdapter")
return GetEmptyNameDeviceAdapter();
if (fake_adapter_name == "NoNameDeviceAdapter")
return GetNoNameDeviceAdapter();
if (fake_adapter_name == "EmptyNameHeartRateAdapter") if (fake_adapter_name == "EmptyNameHeartRateAdapter")
return GetEmptyNameHeartRateAdapter(); return GetEmptyNameHeartRateAdapter();
if (fake_adapter_name == "NoNameHeartRateAdapter") if (fake_adapter_name == "NoNameHeartRateAdapter")
...@@ -713,6 +717,28 @@ LayoutTestBluetoothAdapterProvider::GetDisconnectingHealthThermometer() { ...@@ -713,6 +717,28 @@ LayoutTestBluetoothAdapterProvider::GetDisconnectingHealthThermometer() {
return adapter; return adapter;
} }
// static
scoped_refptr<NiceMockBluetoothAdapter>
LayoutTestBluetoothAdapterProvider::GetEmptyNameDeviceAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
std::unique_ptr<NiceMockBluetoothDevice> device(
GetConnectableDevice(adapter.get(), "" /* device_name */));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static
scoped_refptr<NiceMockBluetoothAdapter>
LayoutTestBluetoothAdapterProvider::GetNoNameDeviceAdapter() {
scoped_refptr<NiceMockBluetoothAdapter> adapter(GetEmptyAdapter());
std::unique_ptr<NiceMockBluetoothDevice> device(
GetConnectableDevice(adapter.get(), nullptr /* device_name */));
adapter->AddMockDevice(std::move(device));
return adapter;
}
// static // static
scoped_refptr<NiceMockBluetoothAdapter> scoped_refptr<NiceMockBluetoothAdapter>
LayoutTestBluetoothAdapterProvider::GetEmptyNameHeartRateAdapter() { LayoutTestBluetoothAdapterProvider::GetEmptyNameHeartRateAdapter() {
......
...@@ -233,6 +233,18 @@ class LayoutTestBluetoothAdapterProvider { ...@@ -233,6 +233,18 @@ class LayoutTestBluetoothAdapterProvider {
static scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> static scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>>
GetHeartRateAdapter(); GetHeartRateAdapter();
// |GetEmptyNameDeviceAdapter|
// Inherits from |EmptyAdapter|
// Contains a single device with an empty name and no UUIDs.
static scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>>
GetEmptyNameDeviceAdapter();
// |GetNoNameDeviceAdapter|
// Inherits from |EmptyAdapter|
// Contains a single device with no name and no UUIDs.
static scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>>
GetNoNameDeviceAdapter();
// |GetEmptyNameHeartRateAdapter| // |GetEmptyNameHeartRateAdapter|
// Inherits from |EmptyAdapter| // Inherits from |EmptyAdapter|
// Internal Structure: // Internal Structure:
......
...@@ -4,18 +4,12 @@ ...@@ -4,18 +4,12 @@
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script> <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<script> <script>
'use strict'; 'use strict';
promise_test(() => {
let test_specs = [ return setBluetoothFakeAdapter('EmptyNameDeviceAdapter')
{}, { .then(() => requestDeviceWithKeyDown({acceptAllDevices: true}))
optionalServices: ['wrong_service'] .then(device => {
}]; assert_equals(device.name, '');
});
promise_test(t => { }, 'Device with empty name and no UUIDs nearby. Should be found if ' +
let test_promises = Promise.resolve(); 'acceptAllDevices is true.');
test_specs.forEach(args => {
test_promises = test_promises.then(() => promise_rejects(
t, new TypeError(), requestDeviceWithKeyDown(args)));
});
return test_promises;
}, 'RequestDeviceOptions requires a |filters| member.');
</script> </script>
<!DOCTYPE html>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
promise_test(() => {
// The UnicodeDevice's name is '❤❤❤❤❤❤❤❤❤'. ❤ = \u2764
let device_name = generate_string(9, '\u2764');
return setBluetoothFakeAdapter('UnicodeDeviceAdapter')
.then(() => requestDeviceWithKeyDown({acceptAllDevices: true}))
.then(device => {
assert_equals(device.name, device_name);
});
}, 'A device with name and no UUIDs nearby. Should be found if ' +
'acceptAllDevices is true.');
</script>
<!DOCTYPE html>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
'use strict';
promise_test(() => {
return setBluetoothFakeAdapter('NoNameDeviceAdapter')
.then(() => requestDeviceWithKeyDown({acceptAllDevices: true}))
.then(device => {
assert_true(device.name === null);
});
}, 'Device with no name or UUIDs nearby. Should be found if ' +
'acceptAllDevices is true.');
</script>
<!DOCTYPE html>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
'use strict';
promise_test(() => {
return setBluetoothFakeAdapter('NoNameHeartRateAdapter')
.then(() => requestDeviceWithKeyDown({acceptAllDevices: true}))
.then(device => {
assert_true(device.name === null);
});
}, 'Device with only UUIDs nearby. Should be found if ' +
'acceptAllDevices is true.');
</script>
<!DOCTYPE html>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
'use strict';
promise_test(() => {
return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
.then(() => requestDeviceWithKeyDown({acceptAllDevices: true}))
.then(device => device.gatt.connect())
.then(gattServer => assert_promise_rejects_with_message(
gattServer.getPrimaryServices(),
new DOMException(
'Origin is not allowed to access any service. ' +
'Tip: Add the service UUID to \'optionalServices\' in ' +
'requestDevice() options. https://goo.gl/HxfxSQ',
'SecurityError')));
}, 'requestDevice called with acceptAllDevices: true and with no ' +
'optionalServices. Should not get access to any services.');
</script>
<!DOCTYPE html>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
'use strict';
promise_test(() => {
return setBluetoothFakeAdapter('TwoHeartRateServicesAdapter')
.then(() => requestDeviceWithKeyDown({
acceptAllDevices: true,
optionalServices: ['heart_rate']
}))
.then(device => device.gatt.connect())
.then(gattServer => gattServer.getPrimaryServices())
.then(services => {
assert_equals(services.length, 2);
services.forEach(service => {
assert_equals(service.uuid, BluetoothUUID.getService('heart_rate'));
});
})
}, 'requestDevice called with acceptAllDevices: true and with ' +
'optionalServices. Should get access to services.');
</script>
<!DOCTYPE html>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
'use strict';
let test_specs = [
{},
{optionalServices: ['heart_rate']},
{filters: [], acceptAllDevices: true},
{filters: [], acceptAllDevices: true, optionalServices: ['heart_rate']}
];
promise_test(t => {
let test_promises = Promise.resolve();
test_specs.forEach(args => {
test_promises = test_promises
.then(() => assert_promise_rejects_with_message(
requestDeviceWithKeyDown(args),
new TypeError(
'Failed to execute \'requestDevice\' on \'Bluetooth\': ' +
'Either \'filters\' should be present or ' +
'\'acceptAllDevices\' should be true, but not both.')));
});
return test_promises;
}, 'RequestDeviceOptions should have exactly one of \'filters\' or ' +
'\'acceptAllDevices:true\'. Reject with TypeError if not.')
</script>
...@@ -100,26 +100,37 @@ static void canonicalizeFilter(const BluetoothScanFilterInit& filter, ...@@ -100,26 +100,37 @@ static void canonicalizeFilter(const BluetoothScanFilterInit& filter,
static void convertRequestDeviceOptions(const RequestDeviceOptions& options, static void convertRequestDeviceOptions(const RequestDeviceOptions& options,
WebRequestDeviceOptions& result, WebRequestDeviceOptions& result,
ExceptionState& exceptionState) { ExceptionState& exceptionState) {
ASSERT(options.hasFilters()); if (!(options.hasFilters() ^ options.acceptAllDevices())) {
if (options.filters().isEmpty()) {
exceptionState.throwTypeError( exceptionState.throwTypeError(
"'filters' member must be non-empty to find any devices."); "Either 'filters' should be present or 'acceptAllDevices' should be "
"true, but not both.");
return;
} }
Vector<WebBluetoothScanFilter> filters; result.acceptAllDevices = options.acceptAllDevices();
for (const BluetoothScanFilterInit& filter : options.filters()) {
WebBluetoothScanFilter canonicalizedFilter = WebBluetoothScanFilter();
canonicalizeFilter(filter, canonicalizedFilter, exceptionState); result.hasFilters = options.hasFilters();
if (result.hasFilters) {
if (exceptionState.hadException()) if (options.filters().isEmpty()) {
exceptionState.throwTypeError(
"'filters' member must be non-empty to find any devices.");
return; return;
}
filters.append(canonicalizedFilter); Vector<WebBluetoothScanFilter> filters;
} for (const BluetoothScanFilterInit& filter : options.filters()) {
WebBluetoothScanFilter canonicalizedFilter = WebBluetoothScanFilter();
canonicalizeFilter(filter, canonicalizedFilter, exceptionState);
result.filters.assign(filters); if (exceptionState.hadException())
return;
filters.append(canonicalizedFilter);
}
result.filters.assign(filters);
}
if (options.hasOptionalServices()) { if (options.hasOptionalServices()) {
Vector<WebString> optionalServices; Vector<WebString> optionalServices;
...@@ -172,16 +183,16 @@ ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState, ...@@ -172,16 +183,16 @@ ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState,
ExceptionState& exceptionState) { ExceptionState& exceptionState) {
ExecutionContext* context = scriptState->getExecutionContext(); ExecutionContext* context = scriptState->getExecutionContext();
// 1. If the incumbent settings object is not a secure context, reject promise // If the incumbent settings object is not a secure context, reject promise
// with a SecurityError and abort these steps. // with a SecurityError and abort these steps.
String errorMessage; String errorMessage;
if (!context->isSecureContext(errorMessage)) { if (!context->isSecureContext(errorMessage)) {
return ScriptPromise::rejectWithDOMException( return ScriptPromise::rejectWithDOMException(
scriptState, DOMException::create(SecurityError, errorMessage)); scriptState, DOMException::create(SecurityError, errorMessage));
} }
// 2. If the algorithm is not allowed to show a popup, reject promise with a // If the algorithm is not allowed to show a popup, reject promise with a
// SecurityError and abort these steps. // SecurityError and abort these steps.
if (!UserGestureIndicator::consumeUserGesture()) { if (!UserGestureIndicator::consumeUserGesture()) {
return ScriptPromise::rejectWithDOMException( return ScriptPromise::rejectWithDOMException(
scriptState, scriptState,
...@@ -196,8 +207,8 @@ ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState, ...@@ -196,8 +207,8 @@ ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState,
return ScriptPromise::rejectWithDOMException( return ScriptPromise::rejectWithDOMException(
scriptState, DOMException::create(NotSupportedError)); scriptState, DOMException::create(NotSupportedError));
// 3. In order to convert the arguments from service names and aliases to just // In order to convert the arguments from service names and aliases to just
// UUIDs, do the following substeps: // UUIDs, do the following substeps:
WebRequestDeviceOptions webOptions; WebRequestDeviceOptions webOptions;
convertRequestDeviceOptions(options, webOptions, exceptionState); convertRequestDeviceOptions(options, webOptions, exceptionState);
if (exceptionState.hadException()) if (exceptionState.hadException())
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
// https://webbluetoothcg.github.io/web-bluetooth/#dictdef-requestdeviceoptions // https://webbluetoothcg.github.io/web-bluetooth/#dictdef-requestdeviceoptions
dictionary RequestDeviceOptions { dictionary RequestDeviceOptions {
required sequence<BluetoothScanFilterInit> filters; sequence<BluetoothScanFilterInit> filters;
sequence<BluetoothServiceUUID> optionalServices = []; sequence<BluetoothServiceUUID> optionalServices = [];
boolean acceptAllDevices = false;
}; };
...@@ -30,7 +30,9 @@ struct WebRequestDeviceOptions { ...@@ -30,7 +30,9 @@ struct WebRequestDeviceOptions {
WebRequestDeviceOptions() {} WebRequestDeviceOptions() {}
WebVector<WebBluetoothScanFilter> filters; WebVector<WebBluetoothScanFilter> filters;
bool hasFilters;
WebVector<WebString> optionalServices; WebVector<WebString> optionalServices;
bool acceptAllDevices;
}; };
} // namespace blink } // namespace blink
......
...@@ -78,8 +78,9 @@ struct WebBluetoothScanFilter { ...@@ -78,8 +78,9 @@ struct WebBluetoothScanFilter {
}; };
struct WebBluetoothRequestDeviceOptions { struct WebBluetoothRequestDeviceOptions {
array<WebBluetoothScanFilter> filters; array<WebBluetoothScanFilter>? filters;
array<bluetooth.mojom.UUID> optional_services; array<bluetooth.mojom.UUID> optional_services;
bool accept_all_devices;
}; };
// Indicates if the function will return a single or multiple // Indicates if the function will return a single or multiple
......
...@@ -4848,6 +4848,18 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. ...@@ -4848,6 +4848,18 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</summary> </summary>
</histogram> </histogram>
<histogram name="Bluetooth.Web.RequestDevice.Options.AcceptAllDevices"
enum="Boolean">
<owner>jyasskin@chromium.org</owner>
<owner>ortuno@chromium.org</owner>
<owner>scheib@chromium.org</owner>
<summary>
Records the value of 'acceptAllDevices' in RequestDeviceOptions. These
results will help us better understand the uses of the API and make changes
according to developers' behavior.
</summary>
</histogram>
<histogram name="Bluetooth.Web.RequestDevice.Outcome" <histogram name="Bluetooth.Web.RequestDevice.Outcome"
enum="WebBluetoothRequestDeviceOutcome"> enum="WebBluetoothRequestDeviceOutcome">
<owner>jyasskin@chromium.org</owner> <owner>jyasskin@chromium.org</owner>
...@@ -76456,6 +76468,7 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. ...@@ -76456,6 +76468,7 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
<int value="155" label="MDDH_INVALID_SUBSCRIPTION_REQUEST"/> <int value="155" label="MDDH_INVALID_SUBSCRIPTION_REQUEST"/>
<int value="156" label="MDDH_INVALID_UNSUBSCRIPTION_REQUEST"/> <int value="156" label="MDDH_INVALID_UNSUBSCRIPTION_REQUEST"/>
<int value="157" label="AOAH_NONSENSE_DEVICE_ID"/> <int value="157" label="AOAH_NONSENSE_DEVICE_ID"/>
<int value="158" label="BDH_INVALID_OPTIONS"/>
</enum> </enum>
<enum name="BadMessageReasonExtensions" type="int"> <enum name="BadMessageReasonExtensions" type="int">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment