Commit 1d7fc175 authored by Andy Paicu's avatar Andy Paicu Committed by Chromium LUCI CQ

Reland "Disconnect bluetooth device when the user revokes permission"

This is a reland of f3c62799

Original change's description:
> Reland "Disconnect bluetooth device when the user revokes permission"
>
> This is a reland of 6ef36937
>
> Original CL broke the build:
> https://ci.chromium.org/p/chromium/builders/ci/Win7%20%2832%29%20Tests/b8861845414785298224
>
> Original change's description:
> > Disconnect bluetooth device when the user revokes permission
> >
> > Bug: 1128494
> > Change-Id: I394e97f61268869557df5c64017504745a8fcd71
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2412331
> > Commit-Queue: Andy Paicu <andypaicu@chromium.org>
> > Reviewed-by: Jochen Eisinger <jochen@chromium.org>
> > Reviewed-by: Reilly Grant <reillyg@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#833671}
>
> Bug: 1128494
> Change-Id: I4480bb33f2df1ad9e9302ec94b27da9195170373
> Fixed: 1155527
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2573102
> Reviewed-by: Reilly Grant <reillyg@chromium.org>
> Reviewed-by: Ovidio de Jesús Ruiz-Henríquez <odejesush@chromium.org>
> Reviewed-by: Jochen Eisinger <jochen@chromium.org>
> Commit-Queue: Andy Paicu <andypaicu@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#845142}

TBR=reillyg@chromium.org,jochen@chromium.org,odejesush@chromium.org

Bug: 1128494
Change-Id: If80f58cf2f060c0d9cf784afdc681a00b2ef2b25
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2640614Reviewed-by: default avatarAndy Paicu <andypaicu@chromium.org>
Commit-Queue: Andy Paicu <andypaicu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845237}
parent 41bccb38
......@@ -6,6 +6,7 @@
#include <memory>
#include "base/scoped_observer.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/bluetooth/bluetooth_chooser_context.h"
......@@ -157,6 +158,26 @@ bool ChromeBluetoothDelegate::IsAllowedToAccessManufacturerData(
manufacturer_code);
}
void ChromeBluetoothDelegate::AddFramePermissionObserver(
FramePermissionObserver* observer) {
std::unique_ptr<ChooserContextPermissionObserver>& chooser_observer =
chooser_observers_[observer->GetRenderFrameHost()];
if (!chooser_observer) {
chooser_observer = std::make_unique<ChooserContextPermissionObserver>(
this, GetBluetoothChooserContext(observer->GetRenderFrameHost()));
}
chooser_observer->AddFramePermissionObserver(observer);
}
void ChromeBluetoothDelegate::RemoveFramePermissionObserver(
FramePermissionObserver* observer) {
auto it = chooser_observers_.find(observer->GetRenderFrameHost());
if (it == chooser_observers_.end())
return;
it->second->RemoveFramePermissionObserver(observer);
}
std::vector<blink::mojom::WebBluetoothDevicePtr>
ChromeBluetoothDelegate::GetPermittedDevices(content::RenderFrameHost* frame) {
auto* context = GetBluetoothChooserContext(frame);
......@@ -177,3 +198,45 @@ ChromeBluetoothDelegate::GetPermittedDevices(content::RenderFrameHost* frame) {
return permitted_devices;
}
ChromeBluetoothDelegate::ChooserContextPermissionObserver::
ChooserContextPermissionObserver(ChromeBluetoothDelegate* owning_delegate,
permissions::ChooserContextBase* context)
: owning_delegate_(owning_delegate) {
observer_.Observe(context);
}
ChromeBluetoothDelegate::ChooserContextPermissionObserver::
~ChooserContextPermissionObserver() = default;
void ChromeBluetoothDelegate::ChooserContextPermissionObserver::
OnPermissionRevoked(const url::Origin& requesting_origin,
const url::Origin& embedding_origin) {
observers_pending_removal_.clear();
is_traversing_observers_ = true;
for (auto& observer : observer_list_)
observer.OnPermissionRevoked(requesting_origin, embedding_origin);
is_traversing_observers_ = false;
for (FramePermissionObserver* observer : observers_pending_removal_)
RemoveFramePermissionObserver(observer);
}
void ChromeBluetoothDelegate::ChooserContextPermissionObserver::
AddFramePermissionObserver(FramePermissionObserver* observer) {
observer_list_.AddObserver(observer);
}
void ChromeBluetoothDelegate::ChooserContextPermissionObserver::
RemoveFramePermissionObserver(FramePermissionObserver* observer) {
if (is_traversing_observers_) {
observers_pending_removal_.emplace_back(observer);
return;
}
observer_list_.RemoveObserver(observer);
if (!observer_list_.might_have_observers())
owning_delegate_->chooser_observers_.erase(observer->GetRenderFrameHost());
// Previous call destructed this instance. Don't add code after this.
}
......@@ -8,7 +8,11 @@
#include <string>
#include <vector>
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "components/permissions/chooser_context_base.h"
#include "content/public/browser/bluetooth_delegate.h"
#include "content/public/browser/render_frame_host.h"
#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-forward.h"
namespace blink {
......@@ -72,6 +76,47 @@ class ChromeBluetoothDelegate : public content::BluetoothDelegate {
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;
private:
// Manages the FramePermissionObserver list for a particular RFH. Will
// self-delete when the last observer is removed from the |owning_delegate|'s
// |chooser_observers_| map.
class ChooserContextPermissionObserver
: public permissions::ChooserContextBase::PermissionObserver {
public:
explicit ChooserContextPermissionObserver(
ChromeBluetoothDelegate* owning_delegate,
permissions::ChooserContextBase* context);
~ChooserContextPermissionObserver() override;
ChooserContextPermissionObserver(const ChooserContextPermissionObserver&) =
delete;
ChooserContextPermissionObserver& operator=(
const ChooserContextPermissionObserver) = delete;
// permissions::ChooserContextBase::PermissionObserver:
void OnPermissionRevoked(const url::Origin& requesting_origin,
const url::Origin& embedding_origin) override;
void AddFramePermissionObserver(FramePermissionObserver* observer);
void RemoveFramePermissionObserver(FramePermissionObserver* observer);
private:
ChromeBluetoothDelegate* owning_delegate_;
base::ObserverList<FramePermissionObserver> observer_list_;
std::list<FramePermissionObserver*> observers_pending_removal_;
bool is_traversing_observers_ = false;
base::ScopedObservation<permissions::ChooserContextBase,
permissions::ChooserContextBase::PermissionObserver>
observer_{this};
};
std::map<content::RenderFrameHost*,
std::unique_ptr<ChooserContextPermissionObserver>>
chooser_observers_;
};
#endif // CHROME_BROWSER_BLUETOOTH_CHROME_BLUETOOTH_DELEGATE_H_
......@@ -8,7 +8,11 @@
#include "base/callback.h"
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/optional.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/bluetooth/bluetooth_chooser_context.h"
#include "chrome/browser/bluetooth/bluetooth_chooser_context_factory.h"
#include "chrome/browser/bluetooth/chrome_bluetooth_delegate.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/ui/browser.h"
......@@ -43,6 +47,7 @@
namespace {
constexpr char kDeviceAddress[] = "00:00:00:00:00:00";
constexpr char kDeviceAddress2[] = "00:00:00:00:00:01";
constexpr char kHeartRateUUIDString[] = "0000180d-0000-1000-8000-00805f9b34fb";
const device::BluetoothUUID kHeartRateUUID(kHeartRateUUIDString);
......@@ -58,11 +63,12 @@ class FakeBluetoothAdapter
void SetIsPresent(bool is_present) { is_present_ = is_present; }
void SimulateDeviceAdvertisementReceived(
const std::string& device_address) const {
const std::string& device_address,
const base::Optional<std::string>& advertisement_name =
base::nullopt) const {
for (auto& observer : observers_) {
observer.DeviceAdvertisementReceived(
device_address, /*device_name=*/base::nullopt,
/*advertisement_name=*/base::nullopt,
device_address, /*device_name=*/base::nullopt, advertisement_name,
/*rssi=*/base::nullopt, /*tx_power=*/base::nullopt,
/*appearance=*/base::nullopt,
/*advertised_uuids=*/{}, /*service_data_map=*/{},
......@@ -712,4 +718,146 @@ IN_PROC_BROWSER_TEST_F(WebBluetoothTestWithNewPermissionsBackendEnabled,
})())"));
}
IN_PROC_BROWSER_TEST_F(WebBluetoothTestWithNewPermissionsBackendEnabled,
RevokingPermissionDisconnectsTheDevice) {
AddFakeDevice(kDeviceAddress);
SetDeviceToSelect(kDeviceAddress);
// Connect to heart rate device and ensure the GATT service is connected.
EXPECT_EQ(kHeartRateUUIDString, content::EvalJs(web_contents_, R"(
var gatt;
var gattserverdisconnectedPromise;
(async() => {
try {
let device = await navigator.bluetooth.requestDevice({
filters: [{name: 'Test Device', services: ['heart_rate']}]});
gatt = await device.gatt.connect();
gattserverdisconnectedPromise = new Promise(resolve => {
device.addEventListener('gattserverdisconnected', _ => {
resolve("event fired");
});
});
let service = await gatt.getPrimaryService('heart_rate');
return service.uuid;
} catch(e) {
return `${e.name}: ${e.message}`;
}
})()
)"));
BluetoothChooserContext* context =
BluetoothChooserContextFactory::GetForProfile(browser()->profile());
url::Origin origin =
url::Origin::Create(web_contents_->GetLastCommittedURL());
// Revoke the permission.
const auto objects = context->GetGrantedObjects(origin, origin);
EXPECT_EQ(1ul, objects.size());
context->RevokeObjectPermission(origin, origin, objects.at(0)->value);
// Wait for gattserverdisconnect event.
EXPECT_EQ("event fired",
content::EvalJs(web_contents_, "gattserverdisconnectedPromise "));
// Ensure the service is disconnected.
EXPECT_THAT(content::EvalJs(web_contents_, R"((async() => {
try {
let service = await gatt.getPrimaryService('heart_rate');
return service.uuid;
} catch(e) {
return `${e.name}: ${e.message}`;
}
})())")
.ExtractString(),
::testing::HasSubstr("GATT Server is disconnected."));
}
IN_PROC_BROWSER_TEST_F(WebBluetoothTestWithNewPermissionsBackendEnabled,
RevokingPermissionStopsAdvertisements) {
// Setup the fake device.
AddFakeDevice(kDeviceAddress);
SetDeviceToSelect(kDeviceAddress);
// Request device and watch for advertisements. Record the last seen
// advertisement's name.
EXPECT_EQ("", content::EvalJs(web_contents_, R"(
var events_seen = "";
var first_device_promise;
(async() => {
try {
let device = await navigator.bluetooth.requestDevice({
filters: [{name: 'Test Device', services: ['heart_rate']}]});
device.watchAdvertisements();
first_device_promise = new Promise(resolve => {
device.addEventListener('advertisementreceived', event => {
events_seen += event.name + "|";
resolve(events_seen);
});
});
return "";
} catch(e) {
return `${e.name}: ${e.message}`;
}
})()
)"));
// Add a second listener on a different device which is used purely as an
// indicator of how much to wait until we can be reasonably sure that the
// second advertisement will not arrive.
AddFakeDevice(kDeviceAddress2);
SetDeviceToSelect(kDeviceAddress2);
EXPECT_EQ("", content::EvalJs(web_contents_, R"(
var second_device_promise;
(async() => {
try {
let device = await navigator.bluetooth.requestDevice({
filters: [{name: 'Test Device', services: ['heart_rate']}]});
device.watchAdvertisements();
second_device_promise = new Promise(resolve => {
device.addEventListener('advertisementreceived', event => {
events_seen += 'second_device_' + event.name;
resolve(events_seen);
});
});
return "";
} catch(e) {
return `${e.name}: ${e.message}`;
}
})()
)"));
// Number of granted objects should be 2.
url::Origin origin =
url::Origin::Create(web_contents_->GetLastCommittedURL());
BluetoothChooserContext* context =
BluetoothChooserContextFactory::GetForProfile(browser()->profile());
const auto objects = context->GetGrantedObjects(origin, origin);
EXPECT_EQ(2u, objects.size());
// Send first advertisement and wait for the event to be resolved.
adapter_->SimulateDeviceAdvertisementReceived(kDeviceAddress,
"advertisement_name1");
EXPECT_EQ("advertisement_name1|",
content::EvalJs(web_contents_, "first_device_promise"));
// Revoke the permission.
context->RevokeObjectPermission(origin, origin, objects.at(0)->value);
EXPECT_EQ(1ul, context->GetGrantedObjects(origin, origin).size());
// Send another advertisement after the permission was revoked, this
// advertisement event should not be received. Also send an advertisement
// to the second device which, when received, will indicate that we have
// waited enough.
adapter_->SimulateDeviceAdvertisementReceived(kDeviceAddress,
"advertisement_name2");
adapter_->SimulateDeviceAdvertisementReceived(kDeviceAddress2,
"advertisement_name2");
EXPECT_EQ("advertisement_name1|second_device_advertisement_name2",
content::EvalJs(web_contents_, "second_device_promise"));
}
} // namespace
......@@ -10,6 +10,7 @@
#include "content/public/browser/web_contents.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h"
#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"
namespace content {
......@@ -80,6 +81,7 @@ void FrameConnectedBluetoothDevices::CloseConnectionToDeviceWithId(
}
CHECK(device_address_to_id_map_.erase(
connection_iter->second->gatt_connection->GetDeviceAddress()));
connection_iter->second->server_client->GATTServerDisconnected();
device_id_to_connection_map_.erase(connection_iter);
DecrementDevicesConnectedCount();
}
......@@ -101,6 +103,19 @@ FrameConnectedBluetoothDevices::CloseConnectionToDeviceWithAddress(
return base::make_optional(device_id);
}
void FrameConnectedBluetoothDevices::CloseConnectionsToDevicesNotInList(
const std::set<blink::WebBluetoothDeviceId>& permitted_ids) {
std::set<blink::WebBluetoothDeviceId> ids_to_delete;
for (const auto& device_pair : device_id_to_connection_map_) {
if (!base::Contains(permitted_ids, device_pair.first))
ids_to_delete.insert(device_pair.first);
}
for (const auto& device_id : ids_to_delete)
CloseConnectionToDeviceWithId(device_id);
}
void FrameConnectedBluetoothDevices::IncrementDevicesConnectedCount() {
web_contents_impl_->IncrementBluetoothConnectedDeviceCount();
}
......
......@@ -61,6 +61,12 @@ class CONTENT_EXPORT FrameConnectedBluetoothDevices final {
base::Optional<blink::WebBluetoothDeviceId>
CloseConnectionToDeviceWithAddress(const std::string& device_address);
// Deletes all connections that are NOT in the list of |permitted_ids| and
// decrements the WebContents count of connected devices for each device that
// had a connection.
void CloseConnectionsToDevicesNotInList(
const std::set<blink::WebBluetoothDeviceId>& permitted_ids);
private:
// Increments the Connected Devices count of the frame's WebContents.
void IncrementDevicesConnectedCount();
......
......@@ -453,6 +453,15 @@ WebBluetoothServiceImpl::WebBluetoothServiceImpl(
receiver_(this, std::move(receiver)) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CHECK(web_contents());
if (base::FeatureList::IsEnabled(
features::kWebBluetoothNewPermissionsBackend)) {
BluetoothDelegate* delegate =
GetContentClient()->browser()->GetBluetoothDelegate();
if (delegate) {
observer_.Observe(delegate);
}
}
}
WebBluetoothServiceImpl::~WebBluetoothServiceImpl() {
......@@ -565,6 +574,38 @@ void WebBluetoothServiceImpl::OnBluetoothScanningPromptEvent(
}
}
void WebBluetoothServiceImpl::OnPermissionRevoked(
const url::Origin& requesting_origin,
const url::Origin& embedding_origin) {
if (render_frame_host_->GetLastCommittedOrigin() != requesting_origin ||
render_frame_host_->GetMainFrame()->GetLastCommittedOrigin() !=
embedding_origin) {
return;
}
BluetoothDelegate* delegate =
GetContentClient()->browser()->GetBluetoothDelegate();
if (!delegate)
return;
std::set<blink::WebBluetoothDeviceId> permitted_ids;
for (const auto& device : delegate->GetPermittedDevices(render_frame_host_))
permitted_ids.insert(device->id);
connected_devices_->CloseConnectionsToDevicesNotInList(permitted_ids);
base::EraseIf(watch_advertisements_clients_,
[&](const std::unique_ptr<WatchAdvertisementsClient>& client) {
return !base::Contains(permitted_ids, client->device_id());
});
MaybeStopDiscovery();
}
content::RenderFrameHost* WebBluetoothServiceImpl::GetRenderFrameHost() {
return render_frame_host_;
}
void WebBluetoothServiceImpl::DidFinishNavigation(
NavigationHandle* navigation_handle) {
if (navigation_handle->HasCommitted() &&
......@@ -1668,18 +1709,45 @@ void WebBluetoothServiceImpl::OnStartDiscoverySessionForWatchAdvertisements(
DCHECK(!watch_advertisements_discovery_session_);
watch_advertisements_discovery_session_ = std::move(session);
BluetoothDelegate* delegate =
GetContentClient()->browser()->GetBluetoothDelegate();
for (auto& callback_and_client :
watch_advertisements_callbacks_and_clients_) {
if (callback_and_client.second->is_connected()) {
watch_advertisements_clients_.push_back(
std::move(callback_and_client.second));
if (!callback_and_client.second->is_connected()) {
std::move(callback_and_client.first)
.Run(blink::mojom::WebBluetoothResult::SUCCESS);
.Run(blink::mojom::WebBluetoothResult::WATCH_ADVERTISEMENTS_ABORTED);
continue;
}
// If the new permissions backend is enabled, verify the permission using
// the delegate.
if (base::FeatureList::IsEnabled(
features::kWebBluetoothNewPermissionsBackend) &&
(!delegate ||
!delegate->HasDevicePermission(
render_frame_host_, callback_and_client.second->device_id()))) {
std::move(callback_and_client.first)
.Run(blink::mojom::WebBluetoothResult::WATCH_ADVERTISEMENTS_ABORTED);
.Run(blink::mojom::WebBluetoothResult::
NOT_ALLOWED_TO_ACCESS_ANY_SERVICE);
continue;
}
// Otherwise verify it via |allowed_devices|.
if (!base::FeatureList::IsEnabled(
features::kWebBluetoothNewPermissionsBackend) &&
!allowed_devices().IsAllowedToGATTConnect(
callback_and_client.second->device_id())) {
std::move(callback_and_client.first)
.Run(blink::mojom::WebBluetoothResult::
NOT_ALLOWED_TO_ACCESS_ANY_SERVICE);
continue;
}
watch_advertisements_clients_.push_back(
std::move(callback_and_client.second));
std::move(callback_and_client.first)
.Run(blink::mojom::WebBluetoothResult::SUCCESS);
}
watch_advertisements_callbacks_and_clients_.clear();
......
......@@ -14,9 +14,11 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/scoped_observation.h"
#include "content/browser/bad_message.h"
#include "content/browser/bluetooth/bluetooth_allowed_devices.h"
#include "content/common/content_export.h"
#include "content/public/browser/bluetooth_delegate.h"
#include "content/public/browser/bluetooth_scanning_prompt.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/web_contents_observer.h"
......@@ -63,7 +65,8 @@ bool HasValidFilter(
class CONTENT_EXPORT WebBluetoothServiceImpl
: public blink::mojom::WebBluetoothService,
public WebContentsObserver,
public device::BluetoothAdapter::Observer {
public device::BluetoothAdapter::Observer,
public BluetoothDelegate::FramePermissionObserver {
public:
// |render_frame_host|: The RFH that owns this instance.
// |receiver|: The instance will be bound to this receiver's pipe.
......@@ -92,6 +95,11 @@ class CONTENT_EXPORT WebBluetoothServiceImpl
BluetoothScanningPrompt::Event event,
BluetoothDeviceScanningPromptController* prompt_controller);
// BluetoothDelegate::FramePermissionObserverimplementation:
void OnPermissionRevoked(const url::Origin& requesting_origin,
const url::Origin& embedding_origin) override;
content::RenderFrameHost* GetRenderFrameHost() override;
private:
FRIEND_TEST_ALL_PREFIXES(WebBluetoothServiceImplTest,
ClearStateDuringRequestDevice);
......@@ -457,6 +465,12 @@ class CONTENT_EXPORT WebBluetoothServiceImpl
// the service on pipe connection errors.
mojo::Receiver<blink::mojom::WebBluetoothService> receiver_;
base::ScopedObservation<BluetoothDelegate,
BluetoothDelegate::FramePermissionObserver,
&BluetoothDelegate::AddFramePermissionObserver,
&BluetoothDelegate::RemoveFramePermissionObserver>
observer_{this};
base::WeakPtrFactory<WebBluetoothServiceImpl> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(WebBluetoothServiceImpl);
......
......@@ -151,6 +151,10 @@ class TestBluetoothDelegate : public BluetoothDelegate {
prompt_->RunPromptEventCallback(event);
}
void AddFramePermissionObserver(FramePermissionObserver* observer) override {}
void RemoveFramePermissionObserver(
FramePermissionObserver* observer) override {}
private:
FakeBluetoothScanningPrompt* prompt_ = nullptr;
};
......
......@@ -9,10 +9,12 @@
#include <vector>
#include "base/containers/flat_set.h"
#include "base/observer_list_types.h"
#include "content/common/content_export.h"
#include "content/public/browser/bluetooth_chooser.h"
#include "content/public/browser/bluetooth_scanning_prompt.h"
#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-forward.h"
#include "url/origin.h"
namespace blink {
class WebBluetoothDeviceId;
......@@ -35,6 +37,18 @@ class RenderFrameHost;
// class.
class CONTENT_EXPORT BluetoothDelegate {
public:
// An observer used to track permission revocation events for a particular
// render frame host.
class CONTENT_EXPORT FramePermissionObserver : public base::CheckedObserver {
public:
// Notify observer that an object permission was revoked for
// |requesting_origin| and |embedding_origin|.
virtual void OnPermissionRevoked(const url::Origin& requesting_origin,
const url::Origin& embedding_origin) = 0;
// Returns the frame that the observer wishes to watch.
virtual RenderFrameHost* GetRenderFrameHost() = 0;
};
virtual ~BluetoothDelegate() = default;
// Shows a chooser for the user to select a nearby Bluetooth device. The
......@@ -117,6 +131,15 @@ class CONTENT_EXPORT BluetoothDelegate {
// JavaScript objects.
virtual std::vector<blink::mojom::WebBluetoothDevicePtr> GetPermittedDevices(
RenderFrameHost* frame) = 0;
// Add a permission observer to allow observing permission revocation effects
// for a particular frame.
virtual void AddFramePermissionObserver(
FramePermissionObserver* observer) = 0;
// Remove a previously added permission observer.
virtual void RemoveFramePermissionObserver(
FramePermissionObserver* observer) = 0;
};
} // namespace content
......
......@@ -125,6 +125,12 @@ bool FakeBluetoothDelegate::IsAllowedToAccessManufacturerData(
return base::Contains(id_to_manufacturer_data_it->second, manufacturer_code);
}
void FakeBluetoothDelegate::AddFramePermissionObserver(
FramePermissionObserver* observer) {}
void FakeBluetoothDelegate::RemoveFramePermissionObserver(
FramePermissionObserver* observer) {}
std::vector<blink::mojom::WebBluetoothDevicePtr>
FakeBluetoothDelegate::GetPermittedDevices(RenderFrameHost* frame) {
std::vector<blink::mojom::WebBluetoothDevicePtr> permitted_devices;
......
......@@ -77,6 +77,9 @@ class FakeBluetoothDelegate : public BluetoothDelegate {
const uint16_t manufacturer_code) override;
std::vector<blink::mojom::WebBluetoothDevicePtr> GetPermittedDevices(
RenderFrameHost* frame) override;
void AddFramePermissionObserver(FramePermissionObserver* observer) override;
void RemoveFramePermissionObserver(
FramePermissionObserver* observer) override;
private:
using AddressToIdMap =
......
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