diff --git a/docs/api/session.md b/docs/api/session.md index d80e2831b0f26..f61b412693629 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -69,6 +69,59 @@ console.log(ses.getUserAgent()) The following events are available on instances of `Session`: +#### Event: 'bluetooth-pair-prompt' _Windows_ _Linux_ + +Returns: + +* `event` Event +* `details` Object + * `deviceId` string + * `pairingKind` string - The type of pairing prompt being requested. + Value will be one of `confirm`, `confirmPin`, or `providePin`. + * `confirm` + This prompt is requesting confirmation that the bluetooth device should + be paired. + * `confirmPin` + This prompt is requesting confirmation that the provided pin matches the + pin displayed on the device. + * `providePin` + This prompt is requesting that a pin be provided for the device. + * `frame` [WebFrameMain](web-frame-main.md) + * `pin` string - If the `pairingKind` is `confirmPin`, this value will be + the pin value to verify. +* `callback` Function + * `confirmed` boolean - `false` should be passed in if the dialog is canceled. + If the `pairingKind` is `confirm` or `confirmPin`, this value should indicate + if the pairing is confirmed. If the `pairingKind` is `providePin` the value + should be `true` when a value is provided. + * `pin` string | null (optional) - When the `pairingKind` is `providePin` + this value should be the required pin for the bluetooth device. + +Emitted when a bluetooth device requires a response to pairing. This event +allows developers to handle devices that require additional validation for +pairing. When handling this event, `event.preventDefault()` should be called +to prevent the default behavior of cancelling this prompt. + +```javascript +const { session } = require('electron') +session.defaultSession.on('bluetooth-pair-prompt', (event, details, callback) => { + event.preventDefault() + switch (details.pairingKind) { + case 'confirm': { + callback(confirm(`Do you want to connect to device ${details.deviceId}`)) + break + } + case 'confirmPin': { + callback(confirm(`Does the pin ${details.pin} match the pin displayed on device ${details.deviceId}?`)) + break + } + case 'providePin': { + callback(true, prompt(`Please provide a pin for ${details.deviceId}`)) + } + } +}) +``` + #### Event: 'will-download' Returns: diff --git a/shell/browser/bluetooth/electron_bluetooth_delegate.cc b/shell/browser/bluetooth/electron_bluetooth_delegate.cc index 340cb5a4a35f2..b2d4bd01912d1 100644 --- a/shell/browser/bluetooth/electron_bluetooth_delegate.cc +++ b/shell/browser/bluetooth/electron_bluetooth_delegate.cc @@ -7,14 +7,21 @@ #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 "gin/converter.h" +#include "shell/browser/api/electron_api_session.h" #include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/lib/bluetooth_chooser.h" +#include "shell/common/gin_converters/callback_converter.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" @@ -23,6 +30,28 @@ using content::RenderFrameHost; using content::WebContents; using device::BluetoothUUID; +namespace gin { + +template <> +struct Converter { + static v8::Local ToV8( + v8::Isolate* isolate, + content::BluetoothDelegate::PairingKind paring_kind) { + switch (paring_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; @@ -43,6 +72,7 @@ std::unique_ptr ElectronBluetoothDelegate::ShowBluetoothScanningPrompt( content::RenderFrameHost* frame, const content::BluetoothScanningPrompt::EventHandler& event_handler) { + LOG(INFO) << "In ElectronBluetoothDelegate::ShowBluetoothScanningPrompt"; NOTIMPLEMENTED(); return nullptr; } @@ -50,6 +80,7 @@ ElectronBluetoothDelegate::ShowBluetoothScanningPrompt( WebBluetoothDeviceId ElectronBluetoothDelegate::GetWebBluetoothDeviceId( RenderFrameHost* frame, const std::string& device_address) { + LOG(INFO) << "In ElectronBluetoothDelegate::GetWebBluetoothDeviceId"; NOTIMPLEMENTED(); return WebBluetoothDeviceId::Create(); } @@ -57,6 +88,7 @@ WebBluetoothDeviceId ElectronBluetoothDelegate::GetWebBluetoothDeviceId( std::string ElectronBluetoothDelegate::GetDeviceAddress( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id) { + LOG(INFO) << "In ElectronBluetoothDelegate::GetDeviceAddress"; NOTIMPLEMENTED(); return nullptr; } @@ -64,6 +96,7 @@ std::string ElectronBluetoothDelegate::GetDeviceAddress( WebBluetoothDeviceId ElectronBluetoothDelegate::AddScannedDevice( RenderFrameHost* frame, const std::string& device_address) { + LOG(INFO) << "In ElectronBluetoothDelegate::AddScannedDevice"; NOTIMPLEMENTED(); return WebBluetoothDeviceId::Create(); } @@ -72,6 +105,7 @@ WebBluetoothDeviceId ElectronBluetoothDelegate::GrantServiceAccessPermission( RenderFrameHost* frame, const device::BluetoothDevice* device, const blink::mojom::WebBluetoothRequestDeviceOptions* options) { + LOG(INFO) << "In ElectronBluetoothDelegate::GrantServiceAccessPermission"; NOTIMPLEMENTED(); return WebBluetoothDeviceId::Create(); } @@ -79,6 +113,7 @@ WebBluetoothDeviceId ElectronBluetoothDelegate::GrantServiceAccessPermission( bool ElectronBluetoothDelegate::HasDevicePermission( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id) { + LOG(INFO) << "In ElectronBluetoothDelegate::HasDevicePermission"; NOTIMPLEMENTED(); return true; } @@ -86,6 +121,8 @@ bool ElectronBluetoothDelegate::HasDevicePermission( void ElectronBluetoothDelegate::RevokeDevicePermissionWebInitiated( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id) { + LOG(INFO) + << "In ElectronBluetoothDelegate::RevokeDevicePermissionWebInitiated"; NOTIMPLEMENTED(); } @@ -100,6 +137,8 @@ bool ElectronBluetoothDelegate::IsAllowedToAccessService( bool ElectronBluetoothDelegate::IsAllowedToAccessAtLeastOneService( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id) { + LOG(INFO) + << "In ElectronBluetoothDelegate::IsAllowedToAccessAtLeastOneService"; NOTIMPLEMENTED(); return true; } @@ -108,6 +147,8 @@ bool ElectronBluetoothDelegate::IsAllowedToAccessManufacturerData( RenderFrameHost* frame, const WebBluetoothDeviceId& device_id, uint16_t manufacturer_code) { + LOG(INFO) + << "In ElectronBluetoothDelegate::IsAllowedToAccessManufacturerData"; NOTIMPLEMENTED(); return true; } @@ -126,6 +167,7 @@ std::vector ElectronBluetoothDelegate::GetPermittedDevices( content::RenderFrameHost* frame) { std::vector permitted_devices; + LOG(INFO) << "In ElectronBluetoothDelegate:::GetPermittedDevices"; NOTIMPLEMENTED(); return permitted_devices; } @@ -136,9 +178,63 @@ void ElectronBluetoothDelegate::ShowDevicePairPrompt( PairPromptCallback callback, PairingKind pairing_kind, const absl::optional& pin) { - NOTIMPLEMENTED(); - std::move(callback).Run(BluetoothDelegate::PairPromptResult( - BluetoothDelegate::PairPromptStatus::kCancelled)); + LOG(INFO) << "In ElectronBluetoothDelegate::ShowDevicePairPrompt"; + pair_prompt_callback_ = std::move(callback); + + bool prevent_default = false; + + auto* web_contents = content::WebContents::FromRenderFrameHost(frame); + if (web_contents) { + LOG(INFO) << "In ElectronBluetoothDelegate::ShowDevicePairPrompt, got web " + "contents; now get session"; + api::Session* session = + api::Session::FromBrowserContext(web_contents->GetBrowserContext()); + v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); + v8::HandleScope scope(isolate); + gin_helper::Dictionary 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()); + } + LOG(INFO) << "In ElectronBluetoothDelegate::ShowDevicePairPrompt, emitting " + "'bluetooth-pair-prompt'"; + prevent_default = session->Emit( + "bluetooth-pair-prompt", details, + base::AdaptCallbackForRepeating(base::BindOnce( + &ElectronBluetoothDelegate::OnDevicePairPromptResponse, + weak_factory_.GetWeakPtr()))); + + if (!prevent_default) { + LOG(INFO) << "In ElectronBluetoothDelegate::ShowDevicePairPrompt, " + "prevent_default is false so cancel the pair prompt"; + std::move(pair_prompt_callback_) + .Run(BluetoothDelegate::PairPromptResult( + BluetoothDelegate::PairPromptStatus::kCancelled)); + } + } +} + +void ElectronBluetoothDelegate::OnDevicePairPromptResponse( + gin::Arguments* args) { + BluetoothDelegate::PairPromptResult result; + LOG(INFO) << "In ElectronBluetoothDelegate::OnDevicePairPromptResponse(, " + "prevent_default is false so cancel the pair prompt"; + bool confirmed; + std::u16string pin; + if (args->GetNext(&confirmed) && confirmed) { + result.result_code = BluetoothDelegate::PairPromptStatus::kSuccess; + } else { + result.result_code = BluetoothDelegate::PairPromptStatus::kCancelled; + } + if (args->GetNext(&pin) && !pin.empty()) { + std::u16string trimmed_input; + base::TrimWhitespace(pin, base::TRIM_ALL, &trimmed_input); + result.pin = base::UTF16ToUTF8(trimmed_input); + } + std::move(pair_prompt_callback_).Run(result); } } // namespace electron diff --git a/shell/browser/bluetooth/electron_bluetooth_delegate.h b/shell/browser/bluetooth/electron_bluetooth_delegate.h index df4f07703e815..079ed3ac737a1 100644 --- a/shell/browser/bluetooth/electron_bluetooth_delegate.h +++ b/shell/browser/bluetooth/electron_bluetooth_delegate.h @@ -9,10 +9,12 @@ #include #include +#include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/scoped_observation.h" #include "content/public/browser/bluetooth_delegate.h" #include "content/public/browser/render_frame_host.h" +#include "gin/arguments.h" #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-forward.h" namespace blink { @@ -91,6 +93,13 @@ class ElectronBluetoothDelegate : public content::BluetoothDelegate { void AddFramePermissionObserver(FramePermissionObserver* observer) override; void RemoveFramePermissionObserver( FramePermissionObserver* observer) override; + + private: + void OnDevicePairPromptResponse(gin::Arguments* args); + + PairPromptCallback pair_prompt_callback_; + + base::WeakPtrFactory weak_factory_{this}; }; } // namespace electron