 05dfd14913
			
		
	
	
	
	
	05dfd14913* refactor: add EmitWarning(Isolate*, ...) warning * chore: remove EmitWarning(node::Environment*, ...) * chore: add code comments * fixup! refactor: add EmitWarning(Isolate*, ...) warning * chore: remove unused node #includes
		
			
				
	
	
		
			176 lines
		
	
	
	
		
			5.8 KiB
			
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
	
		
			5.8 KiB
			
		
	
	
	
		
			C++
		
	
	
	
	
	
| // 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 <algorithm>
 | |
| #include <cstddef>
 | |
| #include <utility>
 | |
| 
 | |
| #include "base/functional/bind.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/api/electron_api_session.h"
 | |
| #include "shell/browser/javascript_environment.h"
 | |
| #include "shell/browser/usb/electron_usb_delegate.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_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,
 | |
|     blink::mojom::WebUsbRequestDeviceOptionsPtr options,
 | |
|     blink::mojom::WebUsbService::GetPermissionCallback callback,
 | |
|     content::WebContents* web_contents,
 | |
|     base::WeakPtr<ElectronUsbDelegate> usb_delegate)
 | |
|     : WebContentsObserver(web_contents),
 | |
|       options_(std::move(options)),
 | |
|       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_info=*/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_info=*/nullptr);
 | |
|   } else {
 | |
|     auto* device_info = chooser_context_->GetDeviceInfo(device_id);
 | |
|     if (device_info) {
 | |
|       RunCallback(device_info->Clone());
 | |
|     } else {
 | |
|       util::EmitWarning(
 | |
|           base::StrCat({"The device id ", device_id, " was not found."}),
 | |
|           "UnknownUsbDeviceId");
 | |
|       RunCallback(/*device_info=*/nullptr);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void UsbChooserController::OnBrowserContextShutdown() {
 | |
|   observation_.Reset();
 | |
| }
 | |
| 
 | |
| // Get a list of devices that can be shown in the chooser bubble UI for
 | |
| // user to grant permission.
 | |
| 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 handle_scope{isolate};
 | |
| 
 | |
|     // "select-usb-device" should respect |filters|.
 | |
|     std::erase_if(devices, [this](const auto& device_info) {
 | |
|       return !DisplayDevice(*device_info);
 | |
|     });
 | |
| 
 | |
|     v8::Local<v8::Object> details = gin::DataObjectBuilder(isolate)
 | |
|                                         .Set("deviceList", devices)
 | |
|                                         .Set("frame", rfh)
 | |
|                                         .Build();
 | |
| 
 | |
|     prevent_default =
 | |
|         session->Emit("select-usb-device", details,
 | |
|                       base::BindRepeating(&UsbChooserController::OnDeviceChosen,
 | |
|                                           weak_factory_.GetWeakPtr()));
 | |
|   }
 | |
|   if (!prevent_default) {
 | |
|     RunCallback(/*device_info=*/nullptr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool UsbChooserController::DisplayDevice(
 | |
|     const device::mojom::UsbDeviceInfo& device_info) const {
 | |
|   if (!device::UsbDeviceFilterMatchesAny(options_->filters, device_info)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (std::ranges::any_of(
 | |
|           options_->exclusion_filters, [&device_info](const auto& filter) {
 | |
|             return device::UsbDeviceFilterMatches(*filter, 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
 |