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 {
RDH_INVALID_URL = 97,
BDH_CHARACTERISTIC_ALREADY_SUBSCRIBED = 98,
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
RFMF_RENDERER_FAKED_ITS_OWN_DEATH = 102,
DWNLD_INVALID_SAVABLE_RESOURCE_LINKS_RESPONSE = 103,
......@@ -179,6 +179,7 @@ enum BadMessageReason {
MDDH_INVALID_SUBSCRIPTION_REQUEST = 155,
MDDH_INVALID_UNSUBSCRIPTION_REQUEST = 156,
AOAH_NONSENSE_DEVICE_ID = 157,
BDH_INVALID_OPTIONS = 158,
// Please add new elements here. The naming convention is abbreviated class
// name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
......
......@@ -166,15 +166,18 @@ void BluetoothAllowedDevicesMap::AddUnionOfServicesTo(
const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options,
std::unordered_set<BluetoothUUID, device::BluetoothUUIDHash>*
unionOfServices) {
for (const auto& filter : options->filters) {
if (!filter->services) {
continue;
}
for (const BluetoothUUID& uuid : filter->services.value()) {
unionOfServices->insert(uuid);
if (options->filters) {
for (const auto& filter : options->filters.value()) {
if (!filter->services) {
continue;
}
for (const BluetoothUUID& uuid : filter->services.value()) {
unionOfServices->insert(uuid);
}
}
}
for (const BluetoothUUID& uuid : options->optional_services) {
unionOfServices->insert(uuid);
}
......
......@@ -191,7 +191,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, NoPermissionForAnyService) {
blink::mojom::WebBluetoothScanFilter::New();
scan_filter->name = kDeviceName;
options->filters.push_back(scan_filter.Clone());
options->filters.emplace();
options->filters->push_back({scan_filter.Clone()});
// Add to map.
const WebBluetoothDeviceId device_id =
......@@ -217,11 +218,12 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginOneDevice) {
scan_filter1->services.emplace();
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->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(kHeartRateUUID);
......@@ -260,8 +262,9 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginOneDevice) {
blink::mojom::WebBluetoothRequestDeviceOptionsPtr options2 =
blink::mojom::WebBluetoothRequestDeviceOptions::New();
options2->filters.push_back(scan_filter1.Clone());
options2->filters.push_back(scan_filter2.Clone());
options2->filters.emplace();
options2->filters->push_back(scan_filter1.Clone());
options2->filters->push_back(scan_filter2.Clone());
const WebBluetoothDeviceId device_id2 =
allowed_devices_map.AddDevice(kTestOrigin1, kDeviceAddress1, options2);
......@@ -298,7 +301,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginTwoDevices) {
scan_filter1->services.emplace();
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);
......@@ -310,7 +314,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_OneOriginTwoDevices) {
scan_filter2->services.emplace();
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);
......@@ -361,7 +366,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_TwoOriginsOneDevice) {
scan_filter1->services.emplace();
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);
......@@ -373,7 +379,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, AllowedServices_TwoOriginsOneDevice) {
scan_filter2->services.emplace();
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);
......@@ -443,7 +450,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, MergeServices) {
scan_filter1->services.emplace();
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);
......@@ -459,7 +467,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, MergeServices) {
scan_filter2->services.emplace();
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);
......@@ -499,7 +508,8 @@ TEST_F(BluetoothAllowedDevicesMapTest, NoFilterServices) {
blink::mojom::WebBluetoothScanFilterPtr scan_filter =
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.
const WebBluetoothDeviceId device_id =
......
......@@ -61,8 +61,13 @@ constexpr int kTestScanDuration = 0;
void LogRequestDeviceOptions(
const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) {
VLOG(1) << "requestDevice called with the following filters: ";
VLOG(1) << "acceptAllDevices: " << options->accept_all_devices;
if (!options->filters)
return;
int i = 0;
for (const auto& filter : options->filters) {
for (const auto& filter : options->filters.value()) {
VLOG(1) << "Filter #" << ++i;
if (filter->name)
VLOG(1) << "Name: " << filter->name.value();
......@@ -100,11 +105,25 @@ bool IsEmptyOrInvalidFilter(
}
bool HasEmptyOrInvalidFilter(
const std::vector<blink::mojom::WebBluetoothScanFilterPtr>& filters) {
return filters.empty()
const base::Optional<std::vector<blink::mojom::WebBluetoothScanFilterPtr>>&
filters) {
if (!filters) {
return true;
}
return filters->empty()
? true
: filters.end() != std::find_if(filters.begin(), filters.end(),
IsEmptyOrInvalidFilter);
: filters->end() != std::find_if(filters->begin(), filters->end(),
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,
......@@ -139,9 +158,10 @@ bool MatchesFilter(const std::string* device_name,
bool MatchesFilters(
const std::string* device_name,
const UUIDSet& device_uuids,
const std::vector<blink::mojom::WebBluetoothScanFilterPtr>& filters) {
const base::Optional<std::vector<blink::mojom::WebBluetoothScanFilterPtr>>&
filters) {
DCHECK(!HasEmptyOrInvalidFilter(filters));
for (const auto& filter : filters) {
for (const auto& filter : filters.value()) {
if (MatchesFilter(device_name, device_uuids, filter)) {
return true;
}
......@@ -150,16 +170,21 @@ bool MatchesFilters(
}
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;
for (const auto& filter : filters) {
if (!filter->services) {
continue;
}
for (const auto& service : filter->services.value()) {
services.insert(service);
if (filters) {
for (const auto& filter : filters.value()) {
if (!filter->services) {
continue;
}
for (const auto& service : filter->services.value()) {
services.insert(service);
}
}
}
// 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
// able to interact with. To avoid wasting power and confusing users with
......@@ -261,17 +286,18 @@ void BluetoothDeviceChooserController::GetDevice(
success_callback_ = success_callback;
error_callback_ = error_callback;
// The renderer should never send empty filters.
if (HasEmptyOrInvalidFilter(options->filters)) {
// The renderer should never send invalid options.
if (IsOptionsInvalid(options)) {
web_bluetooth_service_->CrashRendererAndClosePipe(
bad_message::BDH_EMPTY_OR_INVALID_FILTERS);
bad_message::BDH_INVALID_OPTIONS);
return;
}
options_ = std::move(options);
LogRequestDeviceOptions(options_);
// 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(
UMARequestDeviceOutcome::BLOCKLISTED_SERVICE_IN_FILTER);
PostErrorCallback(
......@@ -372,15 +398,17 @@ void BluetoothDeviceChooserController::GetDevice(
void BluetoothDeviceChooserController::AddFilteredDevice(
const device::BluetoothDevice& device) {
base::Optional<std::string> device_name = device.GetName();
if (chooser_.get() &&
MatchesFilters(device_name ? &device_name.value() : nullptr,
device.GetUUIDs(), options_->filters)) {
base::Optional<int8_t> rssi = device.GetInquiryRSSI();
chooser_->AddOrUpdateDevice(
device.GetAddress(), !!device.GetName() /* should_update_name */,
device.GetNameForDisplay(), device.IsGattConnected(),
web_bluetooth_service_->IsDevicePaired(device.GetAddress()),
rssi ? CalculateSignalStrengthLevel(rssi.value()) : -1);
if (chooser_.get()) {
if (options_->accept_all_devices ||
MatchesFilters(device_name ? &device_name.value() : nullptr,
device.GetUUIDs(), options_->filters)) {
base::Optional<int8_t> rssi = device.GetInquiryRSSI();
chooser_->AddOrUpdateDevice(
device.GetAddress(), !!device.GetName() /* should_update_name */,
device.GetNameForDisplay(), device.IsGattConnected(),
web_bluetooth_service_->IsDevicePaired(device.GetAddress()),
rssi ? CalculateSignalStrengthLevel(rssi.value()) : -1);
}
}
}
......
......@@ -101,12 +101,14 @@ static void RecordUnionOfServices(
union_of_services.insert(service.canonical_value());
}
for (const auto& filter : options->filters) {
if (!filter->services) {
continue;
}
for (const BluetoothUUID& service : filter->services.value()) {
union_of_services.insert(service.canonical_value());
if (options->filters) {
for (const auto& filter : options->filters.value()) {
if (!filter->services) {
continue;
}
for (const BluetoothUUID& service : filter->services.value()) {
union_of_services.insert(service.canonical_value());
}
}
}
......@@ -124,7 +126,13 @@ static void RecordUnionOfServices(
void RecordRequestDeviceOptions(
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);
RecordUnionOfServices(options);
}
......
......@@ -40,14 +40,21 @@ TypeConverter<blink::mojom::WebBluetoothRequestDeviceOptionsPtr,
blink::mojom::WebBluetoothRequestDeviceOptionsPtr options =
blink::mojom::WebBluetoothRequestDeviceOptions::New();
for (const auto& filter : web_options.filters) {
options->filters.push_back(blink::mojom::WebBluetoothScanFilter::From<
blink::WebBluetoothScanFilter>(filter));
options->accept_all_devices = web_options.acceptAllDevices;
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) {
options->optional_services.push_back(
device::BluetoothUUID(optional_service.utf8()));
}
return options;
}
......
......@@ -188,6 +188,10 @@ LayoutTestBluetoothAdapterProvider::GetBluetoothAdapter(
return GetMissingCharacteristicHeartRateAdapter();
if (fake_adapter_name == "HeartRateAdapter")
return GetHeartRateAdapter();
if (fake_adapter_name == "EmptyNameDeviceAdapter")
return GetEmptyNameDeviceAdapter();
if (fake_adapter_name == "NoNameDeviceAdapter")
return GetNoNameDeviceAdapter();
if (fake_adapter_name == "EmptyNameHeartRateAdapter")
return GetEmptyNameHeartRateAdapter();
if (fake_adapter_name == "NoNameHeartRateAdapter")
......@@ -713,6 +717,28 @@ LayoutTestBluetoothAdapterProvider::GetDisconnectingHealthThermometer() {
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
scoped_refptr<NiceMockBluetoothAdapter>
LayoutTestBluetoothAdapterProvider::GetEmptyNameHeartRateAdapter() {
......
......@@ -233,6 +233,18 @@ class LayoutTestBluetoothAdapterProvider {
static scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>>
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|
// Inherits from |EmptyAdapter|
// Internal Structure:
......
......@@ -4,18 +4,12 @@
<script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
<script>
'use strict';
let test_specs = [
{}, {
optionalServices: ['wrong_service']
}];
promise_test(t => {
let test_promises = Promise.resolve();
test_specs.forEach(args => {
test_promises = test_promises.then(() => promise_rejects(
t, new TypeError(), requestDeviceWithKeyDown(args)));
});
return test_promises;
}, 'RequestDeviceOptions requires a |filters| member.');
promise_test(() => {
return setBluetoothFakeAdapter('EmptyNameDeviceAdapter')
.then(() => requestDeviceWithKeyDown({acceptAllDevices: true}))
.then(device => {
assert_equals(device.name, '');
});
}, 'Device with empty 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>
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,
static void convertRequestDeviceOptions(const RequestDeviceOptions& options,
WebRequestDeviceOptions& result,
ExceptionState& exceptionState) {
ASSERT(options.hasFilters());
if (options.filters().isEmpty()) {
if (!(options.hasFilters() ^ options.acceptAllDevices())) {
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;
for (const BluetoothScanFilterInit& filter : options.filters()) {
WebBluetoothScanFilter canonicalizedFilter = WebBluetoothScanFilter();
result.acceptAllDevices = options.acceptAllDevices();
canonicalizeFilter(filter, canonicalizedFilter, exceptionState);
if (exceptionState.hadException())
result.hasFilters = options.hasFilters();
if (result.hasFilters) {
if (options.filters().isEmpty()) {
exceptionState.throwTypeError(
"'filters' member must be non-empty to find any devices.");
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()) {
Vector<WebString> optionalServices;
......@@ -172,16 +183,16 @@ ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState,
ExceptionState& exceptionState) {
ExecutionContext* context = scriptState->getExecutionContext();
// 1. If the incumbent settings object is not a secure context, reject promise
// with a SecurityError and abort these steps.
// If the incumbent settings object is not a secure context, reject promise
// with a SecurityError and abort these steps.
String errorMessage;
if (!context->isSecureContext(errorMessage)) {
return ScriptPromise::rejectWithDOMException(
scriptState, DOMException::create(SecurityError, errorMessage));
}
// 2. If the algorithm is not allowed to show a popup, reject promise with a
// SecurityError and abort these steps.
// If the algorithm is not allowed to show a popup, reject promise with a
// SecurityError and abort these steps.
if (!UserGestureIndicator::consumeUserGesture()) {
return ScriptPromise::rejectWithDOMException(
scriptState,
......@@ -196,8 +207,8 @@ ScriptPromise Bluetooth::requestDevice(ScriptState* scriptState,
return ScriptPromise::rejectWithDOMException(
scriptState, DOMException::create(NotSupportedError));
// 3. In order to convert the arguments from service names and aliases to just
// UUIDs, do the following substeps:
// In order to convert the arguments from service names and aliases to just
// UUIDs, do the following substeps:
WebRequestDeviceOptions webOptions;
convertRequestDeviceOptions(options, webOptions, exceptionState);
if (exceptionState.hadException())
......
......@@ -5,6 +5,7 @@
// https://webbluetoothcg.github.io/web-bluetooth/#dictdef-requestdeviceoptions
dictionary RequestDeviceOptions {
required sequence<BluetoothScanFilterInit> filters;
sequence<BluetoothScanFilterInit> filters;
sequence<BluetoothServiceUUID> optionalServices = [];
boolean acceptAllDevices = false;
};
......@@ -30,7 +30,9 @@ struct WebRequestDeviceOptions {
WebRequestDeviceOptions() {}
WebVector<WebBluetoothScanFilter> filters;
bool hasFilters;
WebVector<WebString> optionalServices;
bool acceptAllDevices;
};
} // namespace blink
......
......@@ -78,8 +78,9 @@ struct WebBluetoothScanFilter {
};
struct WebBluetoothRequestDeviceOptions {
array<WebBluetoothScanFilter> filters;
array<WebBluetoothScanFilter>? filters;
array<bluetooth.mojom.UUID> optional_services;
bool accept_all_devices;
};
// 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.
</summary>
</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"
enum="WebBluetoothRequestDeviceOutcome">
<owner>jyasskin@chromium.org</owner>
......@@ -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="156" label="MDDH_INVALID_UNSUBSCRIPTION_REQUEST"/>
<int value="157" label="AOAH_NONSENSE_DEVICE_ID"/>
<int value="158" label="BDH_INVALID_OPTIONS"/>
</enum>
<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