blob: 15a075feddfcc0f7cba226ed3a1a424b67fc1571 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/hid/chrome_hid_delegate.h"
#include <cstddef>
#include <utility>
#include "base/containers/contains.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "chrome/browser/hid/hid_chooser_context.h"
#include "chrome/browser/hid/hid_chooser_context_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/hid/hid_chooser.h"
#include "chrome/browser/ui/hid/hid_chooser_controller.h"
#include "chrome/common/chrome_features.h"
#include "components/permissions/object_permission_context_base.h"
#include "content/public/browser/render_frame_host.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/constants.h"
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
namespace {
HidChooserContext* GetChooserContext(content::BrowserContext* browser_context) {
auto* profile = Profile::FromBrowserContext(browser_context);
return profile ? HidChooserContextFactory::GetForProfile(profile) : nullptr;
}
} // namespace
// Manages the HidDelegate observers for a single browser context.
class ChromeHidDelegate::ContextObservation
: public permissions::ObjectPermissionContextBase::PermissionObserver,
public HidChooserContext::DeviceObserver {
public:
ContextObservation(ChromeHidDelegate* parent,
content::BrowserContext* browser_context)
: parent_(parent), browser_context_(browser_context) {
auto* chooser_context = GetChooserContext(browser_context_);
device_observation_.Observe(chooser_context);
permission_observation_.Observe(chooser_context);
}
ContextObservation(ContextObservation&) = delete;
ContextObservation& operator=(ContextObservation&) = delete;
~ContextObservation() override = default;
// permissions::ObjectPermissionContextBase::PermissionObserver:
void OnPermissionRevoked(const url::Origin& origin) override {
for (auto& observer : observer_list_)
observer.OnPermissionRevoked(origin);
}
// HidChooserContext::DeviceObserver:
void OnDeviceAdded(const device::mojom::HidDeviceInfo& device_info) override {
for (auto& observer : observer_list_)
observer.OnDeviceAdded(device_info);
}
void OnDeviceRemoved(
const device::mojom::HidDeviceInfo& device_info) override {
for (auto& observer : observer_list_)
observer.OnDeviceRemoved(device_info);
}
void OnDeviceChanged(
const device::mojom::HidDeviceInfo& device_info) override {
for (auto& observer : observer_list_)
observer.OnDeviceChanged(device_info);
}
void OnHidManagerConnectionError() override {
for (auto& observer : observer_list_)
observer.OnHidManagerConnectionError();
}
void OnHidChooserContextShutdown() override {
parent_->observations_.erase(browser_context_);
// Return since `this` is now deleted.
}
void AddObserver(content::HidDelegate::Observer* observer) {
observer_list_.AddObserver(observer);
}
void RemoveObserver(content::HidDelegate::Observer* observer) {
observer_list_.RemoveObserver(observer);
}
private:
// Safe because `parent_` owns `this`.
const raw_ptr<ChromeHidDelegate> parent_;
// Safe because `this` is destroyed when the context is lost.
const raw_ptr<content::BrowserContext> browser_context_;
base::ScopedObservation<HidChooserContext, HidChooserContext::DeviceObserver>
device_observation_{this};
base::ScopedObservation<
permissions::ObjectPermissionContextBase,
permissions::ObjectPermissionContextBase::PermissionObserver>
permission_observation_{this};
base::ObserverList<content::HidDelegate::Observer> observer_list_;
};
ChromeHidDelegate::ChromeHidDelegate() = default;
ChromeHidDelegate::~ChromeHidDelegate() = default;
std::unique_ptr<content::HidChooser> ChromeHidDelegate::RunChooser(
content::RenderFrameHost* render_frame_host,
std::vector<blink::mojom::HidDeviceFilterPtr> filters,
std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
content::HidChooser::Callback callback) {
DCHECK(render_frame_host);
auto* browser_context = render_frame_host->GetBrowserContext();
// Start observing HidChooserContext for permission and device events.
GetContextObserver(browser_context);
DCHECK(base::Contains(observations_, browser_context));
return std::make_unique<HidChooser>(chrome::ShowDeviceChooserDialog(
render_frame_host,
std::make_unique<HidChooserController>(
render_frame_host, std::move(filters), std::move(exclusion_filters),
std::move(callback))));
}
bool ChromeHidDelegate::CanRequestDevicePermission(
content::BrowserContext* browser_context,
const url::Origin& origin) {
return browser_context &&
GetChooserContext(browser_context)->CanRequestObjectPermission(origin);
}
bool ChromeHidDelegate::HasDevicePermission(
content::BrowserContext* browser_context,
const url::Origin& origin,
const device::mojom::HidDeviceInfo& device) {
return browser_context && GetChooserContext(browser_context)
->HasDevicePermission(origin, device);
}
void ChromeHidDelegate::RevokeDevicePermission(
content::BrowserContext* browser_context,
const url::Origin& origin,
const device::mojom::HidDeviceInfo& device) {
if (browser_context) {
GetChooserContext(browser_context)->RevokeDevicePermission(origin, device);
}
}
device::mojom::HidManager* ChromeHidDelegate::GetHidManager(
content::BrowserContext* browser_context) {
if (!browser_context) {
return nullptr;
}
return GetChooserContext(browser_context)->GetHidManager();
}
void ChromeHidDelegate::AddObserver(content::BrowserContext* browser_context,
Observer* observer) {
if (!browser_context) {
return;
}
GetContextObserver(browser_context)->AddObserver(observer);
}
void ChromeHidDelegate::RemoveObserver(
content::BrowserContext* browser_context,
content::HidDelegate::Observer* observer) {
if (!browser_context) {
return;
}
DCHECK(base::Contains(observations_, browser_context));
GetContextObserver(browser_context)->RemoveObserver(observer);
}
const device::mojom::HidDeviceInfo* ChromeHidDelegate::GetDeviceInfo(
content::BrowserContext* browser_context,
const std::string& guid) {
auto* chooser_context = GetChooserContext(browser_context);
if (!chooser_context) {
return nullptr;
}
return chooser_context->GetDeviceInfo(guid);
}
bool ChromeHidDelegate::IsFidoAllowedForOrigin(
content::BrowserContext* browser_context,
const url::Origin& origin) {
auto* chooser_context = GetChooserContext(browser_context);
return chooser_context && chooser_context->IsFidoAllowedForOrigin(origin);
}
bool ChromeHidDelegate::IsServiceWorkerAllowedForOrigin(
const url::Origin& origin) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
// WebHID is only available on extension service workers with feature flag
// enabled for now.
if (base::FeatureList::IsEnabled(
features::kEnableWebHidOnExtensionServiceWorker) &&
origin.scheme() == extensions::kExtensionScheme)
return true;
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
return false;
}
ChromeHidDelegate::ContextObservation* ChromeHidDelegate::GetContextObserver(
content::BrowserContext* browser_context) {
DCHECK(browser_context);
if (!base::Contains(observations_, browser_context)) {
observations_.emplace(browser_context, std::make_unique<ContextObservation>(
this, browser_context));
}
return observations_[browser_context].get();
}