usb_chooser_controller.cc 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // Copyright (c) 2022 Microsoft, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/usb/usb_chooser_controller.h"
  5. #include <stddef.h>
  6. #include <utility>
  7. #include "base/functional/bind.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "base/strings/utf_string_conversions.h"
  10. #include "build/build_config.h"
  11. #include "components/strings/grit/components_strings.h"
  12. #include "content/public/browser/render_frame_host.h"
  13. #include "content/public/browser/web_contents.h"
  14. #include "gin/data_object_builder.h"
  15. #include "services/device/public/cpp/usb/usb_utils.h"
  16. #include "services/device/public/mojom/usb_enumeration_options.mojom.h"
  17. #include "shell/browser/javascript_environment.h"
  18. #include "shell/browser/usb/usb_chooser_context_factory.h"
  19. #include "shell/common/gin_converters/callback_converter.h"
  20. #include "shell/common/gin_converters/content_converter.h"
  21. #include "shell/common/gin_converters/frame_converter.h"
  22. #include "shell/common/gin_converters/usb_device_info_converter.h"
  23. #include "shell/common/node_includes.h"
  24. #include "shell/common/process_util.h"
  25. #include "ui/base/l10n/l10n_util.h"
  26. #include "url/gurl.h"
  27. using content::RenderFrameHost;
  28. using content::WebContents;
  29. namespace electron {
  30. UsbChooserController::UsbChooserController(
  31. RenderFrameHost* render_frame_host,
  32. std::vector<device::mojom::UsbDeviceFilterPtr> device_filters,
  33. blink::mojom::WebUsbService::GetPermissionCallback callback,
  34. content::WebContents* web_contents,
  35. base::WeakPtr<ElectronUsbDelegate> usb_delegate)
  36. : WebContentsObserver(web_contents),
  37. filters_(std::move(device_filters)),
  38. callback_(std::move(callback)),
  39. origin_(render_frame_host->GetMainFrame()->GetLastCommittedOrigin()),
  40. usb_delegate_(usb_delegate),
  41. render_frame_host_id_(render_frame_host->GetGlobalId()) {
  42. chooser_context_ = UsbChooserContextFactory::GetForBrowserContext(
  43. web_contents->GetBrowserContext())
  44. ->AsWeakPtr();
  45. DCHECK(chooser_context_);
  46. chooser_context_->GetDevices(base::BindOnce(
  47. &UsbChooserController::GotUsbDeviceList, weak_factory_.GetWeakPtr()));
  48. }
  49. UsbChooserController::~UsbChooserController() {
  50. RunCallback(/*device_info=*/nullptr);
  51. }
  52. api::Session* UsbChooserController::GetSession() {
  53. if (!web_contents()) {
  54. return nullptr;
  55. }
  56. return api::Session::FromBrowserContext(web_contents()->GetBrowserContext());
  57. }
  58. void UsbChooserController::OnDeviceAdded(
  59. const device::mojom::UsbDeviceInfo& device_info) {
  60. if (DisplayDevice(device_info)) {
  61. api::Session* session = GetSession();
  62. if (session) {
  63. session->Emit("usb-device-added", device_info.Clone(), web_contents());
  64. }
  65. }
  66. }
  67. void UsbChooserController::OnDeviceRemoved(
  68. const device::mojom::UsbDeviceInfo& device_info) {
  69. api::Session* session = GetSession();
  70. if (session) {
  71. session->Emit("usb-device-removed", device_info.Clone(), web_contents());
  72. }
  73. }
  74. void UsbChooserController::OnDeviceChosen(gin::Arguments* args) {
  75. std::string device_id;
  76. if (!args->GetNext(&device_id) || device_id.empty()) {
  77. RunCallback(/*device_info=*/nullptr);
  78. } else {
  79. auto* device_info = chooser_context_->GetDeviceInfo(device_id);
  80. if (device_info) {
  81. RunCallback(device_info->Clone());
  82. } else {
  83. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  84. node::Environment* env = node::Environment::GetCurrent(isolate);
  85. EmitWarning(env, "The device id " + device_id + " was not found.",
  86. "UnknownUsbDeviceId");
  87. RunCallback(/*device_info=*/nullptr);
  88. }
  89. }
  90. }
  91. void UsbChooserController::OnBrowserContextShutdown() {
  92. observation_.Reset();
  93. }
  94. // Get a list of devices that can be shown in the chooser bubble UI for
  95. // user to grant permsssion.
  96. void UsbChooserController::GotUsbDeviceList(
  97. std::vector<::device::mojom::UsbDeviceInfoPtr> devices) {
  98. // Listen to UsbChooserContext for OnDeviceAdded/Removed events after the
  99. // enumeration.
  100. if (chooser_context_)
  101. observation_.Observe(chooser_context_.get());
  102. bool prevent_default = false;
  103. api::Session* session = GetSession();
  104. if (session) {
  105. auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_);
  106. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  107. v8::HandleScope scope(isolate);
  108. v8::Local<v8::Object> details = gin::DataObjectBuilder(isolate)
  109. .Set("deviceList", devices)
  110. .Set("frame", rfh)
  111. .Build();
  112. prevent_default =
  113. session->Emit("select-usb-device", details,
  114. base::AdaptCallbackForRepeating(
  115. base::BindOnce(&UsbChooserController::OnDeviceChosen,
  116. weak_factory_.GetWeakPtr())));
  117. }
  118. if (!prevent_default) {
  119. RunCallback(/*device_info=*/nullptr);
  120. }
  121. }
  122. bool UsbChooserController::DisplayDevice(
  123. const device::mojom::UsbDeviceInfo& device_info) const {
  124. if (!device::UsbDeviceFilterMatchesAny(filters_, device_info))
  125. return false;
  126. return true;
  127. }
  128. void UsbChooserController::RenderFrameDeleted(
  129. content::RenderFrameHost* render_frame_host) {
  130. if (usb_delegate_) {
  131. usb_delegate_->DeleteControllerForFrame(render_frame_host);
  132. }
  133. }
  134. void UsbChooserController::RunCallback(
  135. device::mojom::UsbDeviceInfoPtr device_info) {
  136. if (callback_) {
  137. if (!chooser_context_ || !device_info) {
  138. std::move(callback_).Run(nullptr);
  139. } else {
  140. chooser_context_->GrantDevicePermission(origin_, *device_info);
  141. std::move(callback_).Run(std::move(device_info));
  142. }
  143. }
  144. }
  145. } // namespace electron