// Copyright (c) 2020 Microsoft, Inc. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #include "shell/browser/bluetooth/electron_bluetooth_delegate.h" #include #include #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/public/cpp/bluetooth_uuid.h" #include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/electron_permission_manager.h" #include "shell/browser/lib/bluetooth_chooser.h" #include "shell/common/gin_converters/frame_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h" #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h" using blink::WebBluetoothDeviceId; using content::RenderFrameHost; using content::WebContents; using device::BluetoothUUID; namespace gin { template <> struct Converter { static v8::Local ToV8( v8::Isolate* isolate, content::BluetoothDelegate::PairingKind pairing_kind) { switch (pairing_kind) { case content::BluetoothDelegate::PairingKind::kConfirmOnly: return StringToV8(isolate, "confirm"); case content::BluetoothDelegate::PairingKind::kConfirmPinMatch: return StringToV8(isolate, "confirmPin"); case content::BluetoothDelegate::PairingKind::kProvidePin: return StringToV8(isolate, "providePin"); default: return StringToV8(isolate, "unknown"); } } }; } // namespace gin namespace electron { ElectronBluetoothDelegate::ElectronBluetoothDelegate() = default; ElectronBluetoothDelegate::~ElectronBluetoothDelegate() = default; std::unique_ptr ElectronBluetoothDelegate::RunBluetoothChooser( content::RenderFrameHost* frame, const content::BluetoothChooser::EventHandler& event_handler) { auto* api_web_contents = api::WebContents::From(content::WebContents::FromRenderFrameHost(frame)); return std::make_unique(api_web_contents, event_handler); } // The following methods are not currently called in Electron. std::unique_ptr ElectronBluetoothDelegate::ShowBluetoothScanningPrompt( content::RenderFrameHost* frame, const content::BluetoothScanningPrompt::EventHandler& event_handler) { NOTIMPLEMENTED(); return nullptr; } WebBluetoothDeviceId ElectronBluetoothDelegate::GetWebBluetoothDeviceId( RenderFrameHost* frame, const std::string& device_address) { NOTIMPLEMENTED(); return WebBluetoothDeviceId::Create(); } std::string ElectronBluetoothDelegate::GetDeviceAddress( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id) { NOTIMPLEMENTED(); return ""; } WebBluetoothDeviceId ElectronBluetoothDelegate::AddScannedDevice( RenderFrameHost* frame, const std::string& device_address) { NOTIMPLEMENTED(); return WebBluetoothDeviceId::Create(); } WebBluetoothDeviceId ElectronBluetoothDelegate::GrantServiceAccessPermission( RenderFrameHost* frame, const device::BluetoothDevice* device, const blink::mojom::WebBluetoothRequestDeviceOptions* options) { NOTIMPLEMENTED(); return WebBluetoothDeviceId::Create(); } bool ElectronBluetoothDelegate::HasDevicePermission( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id) { NOTIMPLEMENTED(); return true; } void ElectronBluetoothDelegate::RevokeDevicePermissionWebInitiated( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id) { NOTIMPLEMENTED(); } bool ElectronBluetoothDelegate::IsAllowedToAccessService( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id, const BluetoothUUID& service) { NOTIMPLEMENTED(); return true; } bool ElectronBluetoothDelegate::IsAllowedToAccessAtLeastOneService( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id) { NOTIMPLEMENTED(); return true; } bool ElectronBluetoothDelegate::IsAllowedToAccessManufacturerData( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id, uint16_t manufacturer_code) { NOTIMPLEMENTED(); return true; } void ElectronBluetoothDelegate::AddFramePermissionObserver( FramePermissionObserver* observer) { NOTIMPLEMENTED(); } void ElectronBluetoothDelegate::RemoveFramePermissionObserver( FramePermissionObserver* observer) { NOTIMPLEMENTED(); } std::vector ElectronBluetoothDelegate::GetPermittedDevices( content::RenderFrameHost* frame) { std::vector permitted_devices; NOTIMPLEMENTED(); return permitted_devices; } void ElectronBluetoothDelegate::ShowDevicePairPrompt( content::RenderFrameHost* frame, const std::u16string& device_identifier, PairPromptCallback callback, PairingKind pairing_kind, const std::optional& pin) { auto* web_contents = content::WebContents::FromRenderFrameHost(frame); if (web_contents) { auto* permission_manager = static_cast( web_contents->GetBrowserContext()->GetPermissionControllerDelegate()); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope scope(isolate); auto details = gin_helper::Dictionary::CreateEmpty(isolate); details.Set("deviceId", device_identifier); details.Set("pairingKind", pairing_kind); details.SetGetter("frame", frame); if (pin.has_value()) { details.Set("pin", pin.value()); } permission_manager->CheckBluetoothDevicePair( details, base::BindOnce(&ElectronBluetoothDelegate::OnDevicePairPromptResponse, weak_factory_.GetWeakPtr(), std::move(callback))); } } void ElectronBluetoothDelegate::OnDevicePairPromptResponse( PairPromptCallback callback, base::Value::Dict response) { BluetoothDelegate::PairPromptResult result; if (response.FindBool("confirmed").value_or(false)) { result.result_code = BluetoothDelegate::PairPromptStatus::kSuccess; } else { result.result_code = BluetoothDelegate::PairPromptStatus::kCancelled; } const std::string* pin = response.FindString("pin"); if (pin) { std::u16string trimmed_input = base::UTF8ToUTF16(*pin); base::TrimWhitespace(trimmed_input, base::TRIM_ALL, &trimmed_input); result.pin = base::UTF16ToUTF8(trimmed_input); } std::move(callback).Run(result); } } // namespace electron