feat: add support for WebUSB (#36289)
* feat: add support for WebUSB
* fixup for gn check
* fixup gn check on Windows
* Apply review feedback
Co-authored-by: Charles Kerr <charles@charleskerr.com>
* chore: address review feedback
* chore: removed unneeded code
* Migrate non-default ScopedObservation<> instantiations to ScopedObservationTraits<> in chrome/browser/
4016595
Co-authored-by: Charles Kerr <charles@charleskerr.com>
This commit is contained in:
parent
2751c2b07f
commit
629c54ba36
29 changed files with 1772 additions and 23 deletions
165
shell/browser/usb/usb_chooser_controller.cc
Normal file
165
shell/browser/usb/usb_chooser_controller.cc
Normal file
|
@ -0,0 +1,165 @@
|
|||
// Copyright (c) 2022 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/usb/usb_chooser_controller.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/strings/grit/components_strings.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "gin/data_object_builder.h"
|
||||
#include "services/device/public/cpp/usb/usb_utils.h"
|
||||
#include "services/device/public/mojom/usb_enumeration_options.mojom.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/usb/usb_chooser_context_factory.h"
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
#include "shell/common/gin_converters/content_converter.h"
|
||||
#include "shell/common/gin_converters/frame_converter.h"
|
||||
#include "shell/common/gin_converters/usb_device_info_converter.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/process_util.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
using content::RenderFrameHost;
|
||||
using content::WebContents;
|
||||
|
||||
namespace electron {
|
||||
|
||||
UsbChooserController::UsbChooserController(
|
||||
RenderFrameHost* render_frame_host,
|
||||
std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
|
||||
blink::mojom::WebUsbService::GetPermissionCallback callback,
|
||||
content::WebContents* web_contents,
|
||||
base::WeakPtr<ElectronUsbDelegate> usb_delegate)
|
||||
: WebContentsObserver(web_contents),
|
||||
filters_(std::move(device_filters)),
|
||||
callback_(std::move(callback)),
|
||||
origin_(render_frame_host->GetMainFrame()->GetLastCommittedOrigin()),
|
||||
usb_delegate_(usb_delegate),
|
||||
render_frame_host_id_(render_frame_host->GetGlobalId()) {
|
||||
chooser_context_ = UsbChooserContextFactory::GetForBrowserContext(
|
||||
web_contents->GetBrowserContext())
|
||||
->AsWeakPtr();
|
||||
DCHECK(chooser_context_);
|
||||
chooser_context_->GetDevices(base::BindOnce(
|
||||
&UsbChooserController::GotUsbDeviceList, weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
UsbChooserController::~UsbChooserController() {
|
||||
RunCallback(/*device=*/nullptr);
|
||||
}
|
||||
|
||||
api::Session* UsbChooserController::GetSession() {
|
||||
if (!web_contents()) {
|
||||
return nullptr;
|
||||
}
|
||||
return api::Session::FromBrowserContext(web_contents()->GetBrowserContext());
|
||||
}
|
||||
|
||||
void UsbChooserController::OnDeviceAdded(
|
||||
const device::mojom::UsbDeviceInfo& device_info) {
|
||||
if (DisplayDevice(device_info)) {
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
session->Emit("usb-device-added", device_info.Clone(), web_contents());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UsbChooserController::OnDeviceRemoved(
|
||||
const device::mojom::UsbDeviceInfo& device_info) {
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
session->Emit("usb-device-removed", device_info.Clone(), web_contents());
|
||||
}
|
||||
}
|
||||
|
||||
void UsbChooserController::OnDeviceChosen(gin::Arguments* args) {
|
||||
std::string device_id;
|
||||
if (!args->GetNext(&device_id) || device_id.empty()) {
|
||||
RunCallback(/*device=*/nullptr);
|
||||
} else {
|
||||
auto* device_info = chooser_context_->GetDeviceInfo(device_id);
|
||||
if (device_info) {
|
||||
RunCallback(device_info->Clone());
|
||||
} else {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
node::Environment* env = node::Environment::GetCurrent(isolate);
|
||||
EmitWarning(env, "The device id " + device_id + " was not found.",
|
||||
"UnknownUsbDeviceId");
|
||||
RunCallback(/*device=*/nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UsbChooserController::OnBrowserContextShutdown() {
|
||||
observation_.Reset();
|
||||
}
|
||||
|
||||
// Get a list of devices that can be shown in the chooser bubble UI for
|
||||
// user to grant permsssion.
|
||||
void UsbChooserController::GotUsbDeviceList(
|
||||
std::vector<::device::mojom::UsbDeviceInfoPtr> devices) {
|
||||
// Listen to UsbChooserContext for OnDeviceAdded/Removed events after the
|
||||
// enumeration.
|
||||
if (chooser_context_)
|
||||
observation_.Observe(chooser_context_.get());
|
||||
|
||||
bool prevent_default = false;
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_);
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Object> details = gin::DataObjectBuilder(isolate)
|
||||
.Set("deviceList", devices)
|
||||
.Set("frame", rfh)
|
||||
.Build();
|
||||
|
||||
prevent_default =
|
||||
session->Emit("select-usb-device", details,
|
||||
base::AdaptCallbackForRepeating(
|
||||
base::BindOnce(&UsbChooserController::OnDeviceChosen,
|
||||
weak_factory_.GetWeakPtr())));
|
||||
}
|
||||
if (!prevent_default) {
|
||||
RunCallback(/*port=*/nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool UsbChooserController::DisplayDevice(
|
||||
const device::mojom::UsbDeviceInfo& device_info) const {
|
||||
if (!device::UsbDeviceFilterMatchesAny(filters_, device_info))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UsbChooserController::RenderFrameDeleted(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
if (usb_delegate_) {
|
||||
usb_delegate_->DeleteControllerForFrame(render_frame_host);
|
||||
}
|
||||
}
|
||||
|
||||
void UsbChooserController::RunCallback(
|
||||
device::mojom::UsbDeviceInfoPtr device_info) {
|
||||
if (callback_) {
|
||||
if (!chooser_context_ || !device_info) {
|
||||
std::move(callback_).Run(nullptr);
|
||||
} else {
|
||||
chooser_context_->GrantDevicePermission(origin_, *device_info);
|
||||
std::move(callback_).Run(std::move(device_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace electron
|
Loading…
Add table
Add a link
Reference in a new issue