Commit e19d9ff7 authored by Ovidio Henriquez's avatar Ovidio Henriquez Committed by Commit Bot

bluetooth: Implement FakeBluetoothDelegate

This change implements FakeBluetoothDelegate which fakes the behavior of
the new Web Bluetooth permissions implemented behied the
WebBluetoothNewPermissionsBackend flag. Therefore, the
web-bluetooth-new-permissions-backend virtual test suite runs the Web
Bluetooth tests with this flag enabled.

Design doc:
https://docs.google.com/document/d/1h3uAVXJARHrNWaNACUPiQhLt7XI-fFFQoARSs1WgMDM/

Bug: 577953
Change-Id: I5f81b92433009fc34a6a5e5495fdba72500db41e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2080813
Commit-Queue: Ovidio de Jesús Ruiz-Henríquez <odejesush@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarVincent Scheib <scheib@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746429}
parent 5b48a31e
......@@ -291,6 +291,13 @@ std::string BluetoothChooserContext::GetObjectName(const base::Value& object) {
return *object.FindStringKey(kDeviceNameKey);
}
// static
WebBluetoothDeviceId BluetoothChooserContext::GetObjectDeviceId(
const base::Value& object) {
std::string device_id_str = *object.FindStringKey(kWebBluetoothDeviceIdKey);
return WebBluetoothDeviceId(device_id_str);
}
bool BluetoothChooserContext::IsValidObject(const base::Value& object) {
return object.FindStringKey(kDeviceAddressKey) &&
object.FindStringKey(kDeviceNameKey) &&
......
......@@ -79,6 +79,8 @@ class BluetoothChooserContext : public ChooserContextBase {
// Returns the human readable string representing the given object.
static std::string GetObjectName(const base::Value& object);
static blink::WebBluetoothDeviceId GetObjectDeviceId(
const base::Value& object);
protected:
// ChooserContextBase implementation;
......
......@@ -158,6 +158,8 @@ jumbo_static_library("content_shell_lib") {
"browser/web_test/fake_bluetooth_chooser.h",
"browser/web_test/fake_bluetooth_chooser_factory.cc",
"browser/web_test/fake_bluetooth_chooser_factory.h",
"browser/web_test/fake_bluetooth_delegate.cc",
"browser/web_test/fake_bluetooth_delegate.h",
"browser/web_test/fake_bluetooth_scanning_prompt.cc",
"browser/web_test/fake_bluetooth_scanning_prompt.h",
"browser/web_test/leak_detector.cc",
......
......@@ -10,6 +10,7 @@
#include <algorithm>
#include <iostream>
#include <memory>
#include <queue>
#include <set>
#include <utility>
#include <vector>
......@@ -575,6 +576,7 @@ bool BlinkTestController::ResetAfterWebTest() {
WebTestContentBrowserClient::Get()->SetPopupBlockingEnabled(false);
WebTestContentBrowserClient::Get()->ResetMockClipboardHost();
WebTestContentBrowserClient::Get()->SetScreenOrientationChanged(false);
WebTestContentBrowserClient::Get()->ResetFakeBluetoothDelegate();
navigation_history_dump_ = "";
pixel_dump_.reset();
actual_pixel_hash_ = "";
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/shell/browser/web_test/fake_bluetooth_delegate.h"
#include "content/public/browser/web_contents.h"
#include "device/bluetooth/bluetooth_device.h"
#include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h"
#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"
#include "url/origin.h"
using blink::WebBluetoothDeviceId;
using device::BluetoothDevice;
using device::BluetoothUUID;
namespace content {
// public
FakeBluetoothDelegate::FakeBluetoothDelegate() = default;
FakeBluetoothDelegate::~FakeBluetoothDelegate() = default;
WebBluetoothDeviceId FakeBluetoothDelegate::GetWebBluetoothDeviceId(
RenderFrameHost* frame,
const std::string& device_address) {
auto& device_address_to_id_map = GetAddressToIdMapForOrigin(frame);
auto it = device_address_to_id_map.find(device_address);
if (it != device_address_to_id_map.end())
return it->second;
return {};
}
std::string FakeBluetoothDelegate::GetDeviceAddress(
RenderFrameHost* frame,
const WebBluetoothDeviceId& device_id) {
auto& device_address_to_id_map = GetAddressToIdMapForOrigin(frame);
for (auto& entry : device_address_to_id_map) {
if (entry.second == device_id)
return entry.first;
}
return std::string();
}
blink::WebBluetoothDeviceId FakeBluetoothDelegate::AddScannedDevice(
RenderFrameHost* frame,
const std::string& device_address) {
return GetOrCreateDeviceIdForDeviceAddress(frame, device_address);
}
WebBluetoothDeviceId FakeBluetoothDelegate::GrantServiceAccessPermission(
RenderFrameHost* frame,
const BluetoothDevice* device,
const blink::mojom::WebBluetoothRequestDeviceOptions* options) {
WebBluetoothDeviceId device_id =
GetOrCreateDeviceIdForDeviceAddress(frame, device->GetAddress());
device_id_to_name_map_[device_id] =
device->GetName() ? *device->GetName() : std::string();
GrantUnionOfServicesForDevice(device_id, options);
return device_id;
}
bool FakeBluetoothDelegate::HasDevicePermission(
RenderFrameHost* frame,
const WebBluetoothDeviceId& device_id) {
return base::Contains(device_id_to_services_map_, device_id);
}
bool FakeBluetoothDelegate::IsAllowedToAccessService(
RenderFrameHost* frame,
const WebBluetoothDeviceId& device_id,
const BluetoothUUID& service) {
auto id_to_services_it = device_id_to_services_map_.find(device_id);
if (id_to_services_it == device_id_to_services_map_.end())
return false;
return base::Contains(id_to_services_it->second, service);
}
bool FakeBluetoothDelegate::IsAllowedToAccessAtLeastOneService(
RenderFrameHost* frame,
const WebBluetoothDeviceId& device_id) {
auto id_to_services_it = device_id_to_services_map_.find(device_id);
if (id_to_services_it == device_id_to_services_map_.end())
return false;
return !id_to_services_it->second.empty();
}
WebBluetoothDeviceId FakeBluetoothDelegate::GetOrCreateDeviceIdForDeviceAddress(
RenderFrameHost* frame,
const std::string& device_address) {
WebBluetoothDeviceId device_id;
auto& device_address_to_id_map = GetAddressToIdMapForOrigin(frame);
auto it = device_address_to_id_map.find(device_address);
if (it != device_address_to_id_map.end()) {
device_id = it->second;
} else {
device_id = WebBluetoothDeviceId::Create();
device_address_to_id_map[device_address] = device_id;
}
return device_id;
}
void FakeBluetoothDelegate::GrantUnionOfServicesForDevice(
const WebBluetoothDeviceId& device_id,
const blink::mojom::WebBluetoothRequestDeviceOptions* options) {
if (!options)
return;
// Create an entry for |device_id| in |device_id_to_services_map_| to indicate
// that the site can attempt to GATT connect even if |options| does not
// contain any services.
base::flat_set<BluetoothUUID>& granted_services =
device_id_to_services_map_[device_id];
if (options->filters) {
for (const blink::mojom::WebBluetoothLeScanFilterPtr& filter :
options->filters.value()) {
if (!filter->services)
continue;
for (const BluetoothUUID& uuid : filter->services.value())
granted_services.insert(uuid);
}
}
for (const BluetoothUUID& uuid : options->optional_services)
granted_services.insert(uuid);
}
FakeBluetoothDelegate::AddressToIdMap&
FakeBluetoothDelegate::GetAddressToIdMapForOrigin(RenderFrameHost* frame) {
auto* web_contents = WebContents::FromRenderFrameHost(frame);
auto origin_pair =
std::make_pair(frame->GetLastCommittedOrigin(),
web_contents->GetMainFrame()->GetLastCommittedOrigin());
return device_address_to_id_map_for_origin_[origin_pair];
}
} // namespace content
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_SHELL_BROWSER_WEB_TEST_FAKE_BLUETOOTH_DELEGATE_H_
#define CONTENT_SHELL_BROWSER_WEB_TEST_FAKE_BLUETOOTH_DELEGATE_H_
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/flat_set.h"
#include "content/public/browser/bluetooth_delegate.h"
#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-forward.h"
namespace blink {
class WebBluetoothDeviceId;
} // namespace blink
namespace device {
class BluetoothDevice;
class BluetoothUUID;
} // namespace device
namespace url {
class Origin;
} // namespace url
namespace content {
class RenderFrameHost;
// Fakes Web Bluetooth permissions for web tests by emulating Chrome's
// implementation.
class FakeBluetoothDelegate : public BluetoothDelegate {
public:
FakeBluetoothDelegate();
~FakeBluetoothDelegate() override;
// Move-only class.
FakeBluetoothDelegate(const FakeBluetoothDelegate&) = delete;
FakeBluetoothDelegate& operator=(const FakeBluetoothDelegate&) = delete;
// BluetoothDelegate implementation:
blink::WebBluetoothDeviceId GetWebBluetoothDeviceId(
RenderFrameHost* frame,
const std::string& device_address) override;
std::string GetDeviceAddress(RenderFrameHost* frame,
const blink::WebBluetoothDeviceId&) override;
blink::WebBluetoothDeviceId AddScannedDevice(
RenderFrameHost* frame,
const std::string& device_address) override;
blink::WebBluetoothDeviceId GrantServiceAccessPermission(
RenderFrameHost* frame,
const device::BluetoothDevice* device,
const blink::mojom::WebBluetoothRequestDeviceOptions* options) override;
bool HasDevicePermission(
RenderFrameHost* frame,
const blink::WebBluetoothDeviceId& device_id) override;
bool IsAllowedToAccessService(RenderFrameHost* frame,
const blink::WebBluetoothDeviceId& device_id,
const device::BluetoothUUID& service) override;
bool IsAllowedToAccessAtLeastOneService(
RenderFrameHost* frame,
const blink::WebBluetoothDeviceId& device_id) override;
private:
using AddressToIdMap = std::map<std::string, blink::WebBluetoothDeviceId>;
using OriginPair = std::pair<url::Origin, url::Origin>;
using IdToServicesMap = std::map<blink::WebBluetoothDeviceId,
base::flat_set<device::BluetoothUUID>>;
using IdToNameMap = std::map<blink::WebBluetoothDeviceId, std::string>;
// Finds an existing WebBluetoothDeviceId for |device_address| for |frame| or
// creates a new ID for the Bluetooth device on the current frame.
blink::WebBluetoothDeviceId GetOrCreateDeviceIdForDeviceAddress(
RenderFrameHost* frame,
const std::string& device_address);
// Adds the union of |options->filters->services| and
// |options->optional_services| to the allowed services for |device_id|.
void GrantUnionOfServicesForDevice(
const blink::WebBluetoothDeviceId& device_id,
const blink::mojom::WebBluetoothRequestDeviceOptions* options);
AddressToIdMap& GetAddressToIdMapForOrigin(RenderFrameHost* frame);
// Maps origins to their own maps of device address to device ID.
// If a given origin and device address does not have an associated device ID,
// then the origin does not have permission to access the device.
std::map<OriginPair, AddressToIdMap> device_address_to_id_map_for_origin_;
// These map device IDs to their set of allowed services and device names.
// Since devices IDs are randomly generated, it is very unlikely that two
// unique devices will share the same ID. Therefore, these maps contain all of
// the service permissions and device names from all of the origins.
IdToServicesMap device_id_to_services_map_;
IdToNameMap device_id_to_name_map_;
};
} // namespace content
#endif // CONTENT_SHELL_BROWSER_WEB_TEST_FAKE_BLUETOOTH_DELEGATE_H_
......@@ -30,6 +30,7 @@
#include "content/shell/browser/web_test/blink_test_controller.h"
#include "content/shell/browser/web_test/fake_bluetooth_chooser.h"
#include "content/shell/browser/web_test/fake_bluetooth_chooser_factory.h"
#include "content/shell/browser/web_test/fake_bluetooth_delegate.h"
#include "content/shell/browser/web_test/mojo_web_test_helper.h"
#include "content/shell/browser/web_test/web_test_bluetooth_fake_adapter_setter_impl.h"
#include "content/shell/browser/web_test/web_test_browser_context.h"
......@@ -355,6 +356,16 @@ bool WebTestContentBrowserClient::CanAcceptUntrustedExchangesIfNeeded() {
switches::kRunWebTests);
}
BluetoothDelegate* WebTestContentBrowserClient::GetBluetoothDelegate() {
if (!fake_bluetooth_delegate_)
fake_bluetooth_delegate_ = std::make_unique<FakeBluetoothDelegate>();
return fake_bluetooth_delegate_.get();
}
void WebTestContentBrowserClient::ResetFakeBluetoothDelegate() {
fake_bluetooth_delegate_.reset();
}
content::TtsControllerDelegate*
WebTestContentBrowserClient::GetTtsControllerDelegate() {
return WebTestTtsControllerDelegate::GetInstance();
......
......@@ -6,6 +6,8 @@
#define CONTENT_SHELL_BROWSER_WEB_TEST_WEB_TEST_CONTENT_BROWSER_CLIENT_H_
#include <memory>
#include <string>
#include <vector>
#include "content/public/common/client_hints.mojom.h"
#include "content/shell/browser/shell_content_browser_client.h"
......@@ -20,6 +22,7 @@ namespace content {
class FakeBluetoothChooser;
class FakeBluetoothChooserFactory;
class FakeBluetoothDelegate;
class MockClipboardHost;
class MockPlatformNotificationService;
class WebTestBrowserContext;
......@@ -39,6 +42,7 @@ class WebTestContentBrowserClient : public ShellContentBrowserClient {
// Retrieves the last created FakeBluetoothChooser instance.
std::unique_ptr<FakeBluetoothChooser> GetNextFakeBluetoothChooser();
void ResetFakeBluetoothDelegate();
// ContentBrowserClient overrides.
void RenderProcessWillLaunch(RenderProcessHost* host) override;
......@@ -75,7 +79,7 @@ class WebTestContentBrowserClient : public ShellContentBrowserClient {
service_manager::BinderMapWithContext<content::RenderFrameHost*>* map)
override;
bool CanAcceptUntrustedExchangesIfNeeded() override;
BluetoothDelegate* GetBluetoothDelegate() override;
content::TtsControllerDelegate* GetTtsControllerDelegate() override;
content::TtsPlatform* GetTtsPlatform() override;
bool CanEnterFullscreenWithoutUserActivation() override;
......@@ -112,6 +116,7 @@ class WebTestContentBrowserClient : public ShellContentBrowserClient {
// Stores the FakeBluetoothChooserFactory that produces FakeBluetoothChoosers.
std::unique_ptr<FakeBluetoothChooserFactory> fake_bluetooth_chooser_factory_;
std::unique_ptr<FakeBluetoothDelegate> fake_bluetooth_delegate_;
std::unique_ptr<MockClipboardHost> mock_clipboard_host_;
};
......
......@@ -4,6 +4,8 @@
#include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h"
#include <utility>
#include "base/base64.h"
#include "base/strings/string_util.h"
#include "crypto/random.h"
......@@ -83,6 +85,11 @@ bool WebBluetoothDeviceId::operator!=(
return !(*this == device_id);
}
bool WebBluetoothDeviceId::operator<(
const WebBluetoothDeviceId& device_id) const {
return str() < device_id.str();
}
std::ostream& operator<<(std::ostream& out,
const WebBluetoothDeviceId& device_id) {
return out << device_id.str();
......
......@@ -41,6 +41,7 @@ class BLINK_COMMON_EXPORT WebBluetoothDeviceId {
bool operator==(const WebBluetoothDeviceId& device_id) const;
bool operator!=(const WebBluetoothDeviceId& device_id) const;
bool operator<(const WebBluetoothDeviceId& device_id) const;
private:
std::string device_id_;
......
......@@ -56,6 +56,8 @@ crbug.com/807686 crbug.com/24182 jquery/manipulation.html [ Timeout Pass ]
crbug.com/906791 external/wpt/fullscreen/api/element-ready-check-allowed-cross-origin-manual.sub.html [ Timeout ]
crbug.com/860713 external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html [ Failure Timeout ]
crbug.com/860713 external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.html [ Failure Timeout ]
crbug.com/860713 virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html [ Failure Timeout ]
crbug.com/860713 virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.html [ Failure Timeout ]
# The following tests would pass with User Activation Delegation.
crbug.com/928838 external/wpt/html/user-activation/activation-transfer-with-click.tentative.html [ Failure ]
......
......@@ -731,5 +731,10 @@
"prefix": "storage-access-api",
"bases": [ "external/wpt/storage-access-api" ],
"args": [ "--enable-features=StorageAccessAPI" ]
},
{
"prefix": "web-bluetooth-new-permissions-backend",
"bases": ["bluetooth", "external/wpt/bluetooth"],
"args": ["--enable-features=WebBluetoothNewPermissionsBackend"]
}
]
# Web Bluetooth New Permissions Backend
This virtual test suite runs content_shell with
`--enable-features=WebBluetoothNewPermissionsBackend`. This flag enables the
Web Bluetooth tests to use the
[`FakeBluetoothDelegate`](https://source.chromium.org/chromium/chromium/src/+/master:content/shell/browser/web_test/fake_bluetooth_delegate.h)
interface for granting and checking permissions. This class emulates the
behavior of the new Web Bluetooth permissions backend based on
[`ChooserContextBase`](https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/permissions/chooser_context_base.h).
The new permissions backend is implemented as part of the [Web Bluetooth
Persistent Permissions project](https://docs.google.com/document/d/1h3uAVXJARHrNWaNACUPiQhLt7XI-fFFQoARSs1WgMDM).
TODO(https://crbug.com/589228): Remove this virtual test suite when the
`WebBluetoothNewPermissionsBackend` flag is enabled by default.
\ No newline at end of file
This directory includes Web Bluetooth tests that use the [out of date Web
Bluetooth test API](https://webbluetoothcg.github.io/web-bluetooth/tests.html)
as well as some tests using the [redesigned test
API](https://docs.google.com/document/d/1Nhv_oVDCodd1pEH_jj9k8gF4rPGb_84VYaZ9IG8M_WY).
\ No newline at end of file
This directory includes Web Bluetooth tests that use the [redesigned Web
Bluetooth test
API](https://docs.google.com/document/d/1Nhv_oVDCodd1pEH_jj9k8gF4rPGb_84VYaZ9IG8M_WY)
and are available in
[web-platform-tests/wpt](https://github.com/web-platform-tests/wpt).
\ No newline at end of file
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