Bluetooth: Migrate some WPT tests to use Bidi SimulateAdapter
Migrate some Web Bluetooth WPT tests to use WebDriver Bidi SimulateAdapter. HeadlessBluetoothDelegate is created in order to use navigator.bluetooth.getAvailability. Change-Id: I9ee03ec00a67e95c8c0a485fa9f6a247e1f0ff59 Bug: 41484719 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6165347 Commit-Queue: Jack Hsieh <chengweih@chromium.org> Reviewed-by: Peter Kvitek <kvitekp@chromium.org> Cr-Commit-Position: refs/heads/main@{#1416319}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
586087b975
commit
8cdaf24335
@ -239,6 +239,8 @@ component("headless_non_renderer") {
|
||||
"lib/browser/command_line_handler.h",
|
||||
"lib/browser/directory_enumerator.cc",
|
||||
"lib/browser/directory_enumerator.h",
|
||||
"lib/browser/headless_bluetooth_delegate.cc",
|
||||
"lib/browser/headless_bluetooth_delegate.h",
|
||||
"lib/browser/headless_browser_context_impl.cc",
|
||||
"lib/browser/headless_browser_context_impl.h",
|
||||
"lib/browser/headless_browser_context_options.cc",
|
||||
|
@ -12,3 +12,4 @@ dvallet@chromium.org
|
||||
|
||||
per-file *.cmx=set noparent
|
||||
per-file *.cmx=file://build/fuchsia/SECURITY_OWNERS
|
||||
per-file headless_bluetooth_*=file://content/browser/bluetooth/OWNERS
|
||||
|
@ -15,6 +15,7 @@ include_rules = [
|
||||
"+third_party/skia/include",
|
||||
"+third_party/blink/public/mojom/quota",
|
||||
"+third_party/blink/public/mojom/badging",
|
||||
"+third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h",
|
||||
"+third_party/blink/public/common/client_hints/enabled_client_hints.h",
|
||||
"+third_party/blink/public/common/renderer_preferences/renderer_preferences.h",
|
||||
"+third_party/blink/public/common/user_agent/user_agent_metadata.h",
|
||||
|
120
headless/lib/browser/headless_bluetooth_delegate.cc
Normal file
120
headless/lib/browser/headless_bluetooth_delegate.cc
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2025 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "headless/lib/browser/headless_bluetooth_delegate.h"
|
||||
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h"
|
||||
|
||||
namespace headless {
|
||||
|
||||
using ::blink::WebBluetoothDeviceId;
|
||||
using ::content::BluetoothChooser;
|
||||
using ::content::BluetoothScanningPrompt;
|
||||
using ::content::RenderFrameHost;
|
||||
using ::device::BluetoothDevice;
|
||||
using ::device::BluetoothUUID;
|
||||
|
||||
HeadlessBluetoothDelegate::HeadlessBluetoothDelegate() = default;
|
||||
HeadlessBluetoothDelegate::~HeadlessBluetoothDelegate() = default;
|
||||
|
||||
std::unique_ptr<BluetoothChooser>
|
||||
HeadlessBluetoothDelegate::RunBluetoothChooser(
|
||||
RenderFrameHost* frame,
|
||||
const BluetoothChooser::EventHandler& event_handler) {
|
||||
return std::make_unique<BluetoothChooser>();
|
||||
}
|
||||
|
||||
std::unique_ptr<BluetoothScanningPrompt>
|
||||
HeadlessBluetoothDelegate::ShowBluetoothScanningPrompt(
|
||||
RenderFrameHost* frame,
|
||||
const BluetoothScanningPrompt::EventHandler& event_handler) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HeadlessBluetoothDelegate::ShowDevicePairPrompt(
|
||||
RenderFrameHost* frame,
|
||||
const std::u16string& device_identifier,
|
||||
PairPromptCallback callback,
|
||||
PairingKind pairing_kind,
|
||||
const std::optional<std::u16string>& pin) {}
|
||||
|
||||
WebBluetoothDeviceId HeadlessBluetoothDelegate::GetWebBluetoothDeviceId(
|
||||
RenderFrameHost* frame,
|
||||
const std::string& device_address) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string HeadlessBluetoothDelegate::GetDeviceAddress(
|
||||
RenderFrameHost* frame,
|
||||
const WebBluetoothDeviceId& device_id) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
WebBluetoothDeviceId HeadlessBluetoothDelegate::AddScannedDevice(
|
||||
RenderFrameHost* frame,
|
||||
const std::string& device_address) {
|
||||
return WebBluetoothDeviceId();
|
||||
}
|
||||
|
||||
WebBluetoothDeviceId HeadlessBluetoothDelegate::GrantServiceAccessPermission(
|
||||
RenderFrameHost* frame,
|
||||
const BluetoothDevice* device,
|
||||
const blink::mojom::WebBluetoothRequestDeviceOptions* options) {
|
||||
return WebBluetoothDeviceId();
|
||||
}
|
||||
|
||||
bool HeadlessBluetoothDelegate::HasDevicePermission(
|
||||
RenderFrameHost* frame,
|
||||
const WebBluetoothDeviceId& device_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void HeadlessBluetoothDelegate::RevokeDevicePermissionWebInitiated(
|
||||
RenderFrameHost* frame,
|
||||
const WebBluetoothDeviceId& device_id) {}
|
||||
|
||||
bool HeadlessBluetoothDelegate::MayUseBluetooth(RenderFrameHost* rfh) {
|
||||
// Disable any other non-default StoragePartition contexts, unless it has a
|
||||
// non-http/https scheme.
|
||||
if (rfh->GetStoragePartition() !=
|
||||
rfh->GetBrowserContext()->GetDefaultStoragePartition()) {
|
||||
return !rfh->GetLastCommittedURL().SchemeIsHTTPOrHTTPS();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HeadlessBluetoothDelegate::IsAllowedToAccessService(
|
||||
RenderFrameHost* frame,
|
||||
const WebBluetoothDeviceId& device_id,
|
||||
const BluetoothUUID& service) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HeadlessBluetoothDelegate::IsAllowedToAccessAtLeastOneService(
|
||||
RenderFrameHost* frame,
|
||||
const WebBluetoothDeviceId& device_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HeadlessBluetoothDelegate::IsAllowedToAccessManufacturerData(
|
||||
RenderFrameHost* frame,
|
||||
const WebBluetoothDeviceId& device_id,
|
||||
const uint16_t manufacturer_code) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void HeadlessBluetoothDelegate::AddFramePermissionObserver(
|
||||
FramePermissionObserver* observer) {}
|
||||
|
||||
void HeadlessBluetoothDelegate::RemoveFramePermissionObserver(
|
||||
FramePermissionObserver* observer) {}
|
||||
|
||||
std::vector<blink::mojom::WebBluetoothDevicePtr>
|
||||
HeadlessBluetoothDelegate::GetPermittedDevices(RenderFrameHost* frame) {
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace headless
|
92
headless/lib/browser/headless_bluetooth_delegate.h
Normal file
92
headless/lib/browser/headless_bluetooth_delegate.h
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright 2025 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef HEADLESS_LIB_BROWSER_HEADLESS_BLUETOOTH_DELEGATE_H_
|
||||
#define HEADLESS_LIB_BROWSER_HEADLESS_BLUETOOTH_DELEGATE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "content/public/browser/bluetooth_delegate.h"
|
||||
|
||||
namespace blink {
|
||||
class WebBluetoothDeviceId;
|
||||
} // namespace blink
|
||||
|
||||
namespace content {
|
||||
class BluetoothChooser;
|
||||
class BluetoothScanningPrompt;
|
||||
} // namespace content
|
||||
|
||||
namespace device {
|
||||
class BluetoothDevice;
|
||||
class BluetoothUUID;
|
||||
class RenderFrameHost;
|
||||
} // namespace device
|
||||
|
||||
namespace headless {
|
||||
|
||||
// A thin layer of BluetoothDelegate for Headless shell that provides a basic
|
||||
// chooser and rejects any permission of accessing a bluetooth device.
|
||||
class HeadlessBluetoothDelegate : public content::BluetoothDelegate {
|
||||
public:
|
||||
HeadlessBluetoothDelegate();
|
||||
// Not copyable or movable.
|
||||
HeadlessBluetoothDelegate(const HeadlessBluetoothDelegate&) = delete;
|
||||
HeadlessBluetoothDelegate& operator=(const HeadlessBluetoothDelegate&) =
|
||||
delete;
|
||||
~HeadlessBluetoothDelegate() override;
|
||||
|
||||
// BluetoothDelegate implementation:
|
||||
std::unique_ptr<content::BluetoothChooser> RunBluetoothChooser(
|
||||
content::RenderFrameHost* frame,
|
||||
const content::BluetoothChooser::EventHandler& event_handler) override;
|
||||
std::unique_ptr<content::BluetoothScanningPrompt> ShowBluetoothScanningPrompt(
|
||||
content::RenderFrameHost* frame,
|
||||
const content::BluetoothScanningPrompt::EventHandler& event_handler)
|
||||
override;
|
||||
void ShowDevicePairPrompt(content::RenderFrameHost* frame,
|
||||
const std::u16string& device_identifier,
|
||||
PairPromptCallback callback,
|
||||
PairingKind pairing_kind,
|
||||
const std::optional<std::u16string>& pin) override;
|
||||
blink::WebBluetoothDeviceId GetWebBluetoothDeviceId(
|
||||
content::RenderFrameHost* frame,
|
||||
const std::string& device_address) override;
|
||||
std::string GetDeviceAddress(content::RenderFrameHost* frame,
|
||||
const blink::WebBluetoothDeviceId&) override;
|
||||
blink::WebBluetoothDeviceId AddScannedDevice(
|
||||
content::RenderFrameHost* frame,
|
||||
const std::string& device_address) override;
|
||||
blink::WebBluetoothDeviceId GrantServiceAccessPermission(
|
||||
content::RenderFrameHost* frame,
|
||||
const device::BluetoothDevice* device,
|
||||
const blink::mojom::WebBluetoothRequestDeviceOptions* options) override;
|
||||
bool HasDevicePermission(
|
||||
content::RenderFrameHost* frame,
|
||||
const blink::WebBluetoothDeviceId& device_id) override;
|
||||
void RevokeDevicePermissionWebInitiated(
|
||||
content::RenderFrameHost* frame,
|
||||
const blink::WebBluetoothDeviceId& device_id) override;
|
||||
bool MayUseBluetooth(content::RenderFrameHost* rfh) override;
|
||||
bool IsAllowedToAccessService(content::RenderFrameHost* frame,
|
||||
const blink::WebBluetoothDeviceId& device_id,
|
||||
const device::BluetoothUUID& service) override;
|
||||
bool IsAllowedToAccessAtLeastOneService(
|
||||
content::RenderFrameHost* frame,
|
||||
const blink::WebBluetoothDeviceId& device_id) override;
|
||||
bool IsAllowedToAccessManufacturerData(
|
||||
content::RenderFrameHost* frame,
|
||||
const blink::WebBluetoothDeviceId& device_id,
|
||||
const uint16_t manufacturer_code) override;
|
||||
std::vector<blink::mojom::WebBluetoothDevicePtr> GetPermittedDevices(
|
||||
content::RenderFrameHost* frame) override;
|
||||
void AddFramePermissionObserver(FramePermissionObserver* observer) override;
|
||||
void RemoveFramePermissionObserver(
|
||||
FramePermissionObserver* observer) override;
|
||||
};
|
||||
|
||||
} // namespace headless
|
||||
|
||||
#endif // HEADLESS_LIB_BROWSER_HEADLESS_BLUETOOTH_DELEGATE_H_
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "headless/lib/browser/headless_content_browser_client.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
@ -31,6 +32,7 @@
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "headless/lib/browser/headless_bluetooth_delegate.h"
|
||||
#include "headless/lib/browser/headless_browser_context_impl.h"
|
||||
#include "headless/lib/browser/headless_browser_impl.h"
|
||||
#include "headless/lib/browser/headless_browser_main_parts.h"
|
||||
@ -531,4 +533,12 @@ void HeadlessContentBrowserClient::SetEncryptionKey(
|
||||
#endif
|
||||
}
|
||||
|
||||
content::BluetoothDelegate*
|
||||
HeadlessContentBrowserClient::GetBluetoothDelegate() {
|
||||
if (!bluetooth_delegate_) {
|
||||
bluetooth_delegate_ = std::make_unique<HeadlessBluetoothDelegate>();
|
||||
}
|
||||
return bluetooth_delegate_.get();
|
||||
}
|
||||
|
||||
} // namespace headless
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
namespace headless {
|
||||
|
||||
class HeadlessBluetoothDelegate;
|
||||
class HeadlessBrowserImpl;
|
||||
|
||||
class HeadlessContentBrowserClient : public content::ContentBrowserClient {
|
||||
@ -148,6 +149,8 @@ class HeadlessContentBrowserClient : public content::ContentBrowserClient {
|
||||
|
||||
bool ShouldSandboxNetworkService() override;
|
||||
|
||||
content::BluetoothDelegate* GetBluetoothDelegate() override;
|
||||
|
||||
private:
|
||||
class StubBadgeService;
|
||||
|
||||
@ -162,6 +165,8 @@ class HeadlessContentBrowserClient : public content::ContentBrowserClient {
|
||||
raw_ptr<HeadlessBrowserImpl> browser_; // Not owned.
|
||||
|
||||
std::unique_ptr<StubBadgeService> stub_badge_service_;
|
||||
|
||||
std::unique_ptr<HeadlessBluetoothDelegate> bluetooth_delegate_;
|
||||
};
|
||||
|
||||
} // namespace headless
|
||||
|
@ -1,4 +1,4 @@
|
||||
// META: script=/resources/testdriver.js
|
||||
// META: script=/resources/testdriver.js?feature=bidi
|
||||
// META: script=/resources/testdriver-vendor.js
|
||||
// META: script=/bluetooth/resources/bluetooth-test.js
|
||||
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
|
||||
@ -6,8 +6,8 @@
|
||||
const test_desc = 'getAvailability() resolves with false if the system does ' +
|
||||
'not have an adapter.';
|
||||
|
||||
bluetooth_test(async () => {
|
||||
await navigator.bluetooth.test.simulateCentral({state: 'absent'});
|
||||
bluetooth_bidi_test(async () => {
|
||||
await test_driver.bidi.bluetooth.simulate_adapter({state: "absent"});
|
||||
let availability = await navigator.bluetooth.getAvailability();
|
||||
assert_false(
|
||||
availability,
|
@ -1,4 +1,4 @@
|
||||
// META: script=/resources/testdriver.js
|
||||
// META: script=/resources/testdriver.js?feature=bidi
|
||||
// META: script=/resources/testdriver-vendor.js
|
||||
// META: script=/bluetooth/resources/bluetooth-test.js
|
||||
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
|
||||
@ -6,8 +6,8 @@
|
||||
const test_desc = 'getAvailability() resolves with true if the Bluetooth ' +
|
||||
'radio is powered off, but the platform that supports Bluetooth LE.';
|
||||
|
||||
bluetooth_test(async () => {
|
||||
await navigator.bluetooth.test.simulateCentral({state: 'powered-off'});
|
||||
bluetooth_bidi_test(async () => {
|
||||
await test_driver.bidi.bluetooth.simulate_adapter({state: "powered-off"});
|
||||
let availability = await navigator.bluetooth.getAvailability();
|
||||
assert_true(
|
||||
availability,
|
@ -1,4 +1,4 @@
|
||||
// META: script=/resources/testdriver.js
|
||||
// META: script=/resources/testdriver.js?feature=bidi
|
||||
// META: script=/resources/testdriver-vendor.js
|
||||
// META: script=/bluetooth/resources/bluetooth-test.js
|
||||
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
|
||||
@ -6,8 +6,8 @@
|
||||
const test_desc = 'getAvailability() resolves with true if the Bluetooth ' +
|
||||
'radio is powered on and the platform supports Bluetooth LE.';
|
||||
|
||||
bluetooth_test(async () => {
|
||||
await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
|
||||
bluetooth_bidi_test(async () => {
|
||||
await test_driver.bidi.bluetooth.simulate_adapter({state: "powered-on"});
|
||||
let availability = await navigator.bluetooth.getAvailability();
|
||||
assert_true(
|
||||
availability,
|
@ -1,4 +1,4 @@
|
||||
// META: script=/resources/testdriver.js
|
||||
// META: script=/resources/testdriver.js?feature=bidi
|
||||
// META: script=/resources/testdriver-vendor.js
|
||||
// META: script=/bluetooth/resources/bluetooth-test.js
|
||||
// META: script=/bluetooth/resources/bluetooth-fake-devices.js
|
||||
@ -9,8 +9,8 @@ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
|
||||
'/bluetooth/resources/health-thermometer-iframe.html'
|
||||
let iframe = document.createElement('iframe');
|
||||
|
||||
bluetooth_test(async () => {
|
||||
await navigator.bluetooth.test.simulateCentral({state: 'powered-on'});
|
||||
bluetooth_bidi_test(async () => {
|
||||
await test_driver.bidi.bluetooth.simulate_adapter({state: "powered-on"});
|
||||
await new Promise(resolve => {
|
||||
iframe.src = cross_origin_src;
|
||||
document.body.appendChild(iframe);
|
@ -89,6 +89,26 @@ function bluetooth_test(
|
||||
}, name, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* These tests rely on the User Agent providing an implementation of the
|
||||
* WebDriver-Bidi for testing Web Bluetooth
|
||||
* https://webbluetoothcg.github.io/web-bluetooth/#automated-testing
|
||||
* @param {function{*}: Promise<*>} test_function The Web Bluetooth test to run.
|
||||
* @param {string} name The name or description of the test.
|
||||
* @param {object} properties An object containing extra options for the test.
|
||||
* @param {Boolean} validate_response_consumed Whether to validate all response
|
||||
* consumed or not.
|
||||
* @returns {Promise<void>} Resolves if Web Bluetooth test ran successfully, or
|
||||
* rejects if the test failed.
|
||||
*/
|
||||
function bluetooth_bidi_test(
|
||||
test_function, name, properties, validate_response_consumed = true) {
|
||||
return promise_test(async (t) => {
|
||||
assert_implements(navigator.bluetooth, 'missing navigator.bluetooth');
|
||||
await test_function(t);
|
||||
}, name, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Helpers
|
||||
*/
|
||||
|
Reference in New Issue
Block a user