Skip to content

Commit

Permalink
feat: add bluetooth-pair-prompt event
Browse files Browse the repository at this point in the history
add debugging

chore: add debugging for bluetooth pairing

add more debugging

more debugging

update for review feedback

Remove debugging
  • Loading branch information
jkleinsc committed Aug 26, 2022
1 parent 8128fa6 commit ced73e4
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 3 deletions.
53 changes: 53 additions & 0 deletions docs/api/session.md
Expand Up @@ -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:
Expand Down
102 changes: 99 additions & 3 deletions shell/browser/bluetooth/electron_bluetooth_delegate.cc
Expand Up @@ -7,14 +7,21 @@
#include <memory>
#include <utility>

#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"

Expand All @@ -23,6 +30,28 @@ using content::RenderFrameHost;
using content::WebContents;
using device::BluetoothUUID;

namespace gin {

template <>
struct Converter<content::BluetoothDelegate::PairingKind> {
static v8::Local<v8::Value> 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;
Expand All @@ -43,27 +72,31 @@ std::unique_ptr<content::BluetoothScanningPrompt>
ElectronBluetoothDelegate::ShowBluetoothScanningPrompt(
content::RenderFrameHost* frame,
const content::BluetoothScanningPrompt::EventHandler& event_handler) {
LOG(INFO) << "In ElectronBluetoothDelegate::ShowBluetoothScanningPrompt";
NOTIMPLEMENTED();
return nullptr;
}

WebBluetoothDeviceId ElectronBluetoothDelegate::GetWebBluetoothDeviceId(
RenderFrameHost* frame,
const std::string& device_address) {
LOG(INFO) << "In ElectronBluetoothDelegate::GetWebBluetoothDeviceId";
NOTIMPLEMENTED();
return WebBluetoothDeviceId::Create();
}

std::string ElectronBluetoothDelegate::GetDeviceAddress(
RenderFrameHost* frame,
const WebBluetoothDeviceId& device_id) {
LOG(INFO) << "In ElectronBluetoothDelegate::GetDeviceAddress";
NOTIMPLEMENTED();
return nullptr;
}

WebBluetoothDeviceId ElectronBluetoothDelegate::AddScannedDevice(
RenderFrameHost* frame,
const std::string& device_address) {
LOG(INFO) << "In ElectronBluetoothDelegate::AddScannedDevice";
NOTIMPLEMENTED();
return WebBluetoothDeviceId::Create();
}
Expand All @@ -72,20 +105,24 @@ WebBluetoothDeviceId ElectronBluetoothDelegate::GrantServiceAccessPermission(
RenderFrameHost* frame,
const device::BluetoothDevice* device,
const blink::mojom::WebBluetoothRequestDeviceOptions* options) {
LOG(INFO) << "In ElectronBluetoothDelegate::GrantServiceAccessPermission";
NOTIMPLEMENTED();
return WebBluetoothDeviceId::Create();
}

bool ElectronBluetoothDelegate::HasDevicePermission(
RenderFrameHost* frame,
const WebBluetoothDeviceId& device_id) {
LOG(INFO) << "In ElectronBluetoothDelegate::HasDevicePermission";
NOTIMPLEMENTED();
return true;
}

void ElectronBluetoothDelegate::RevokeDevicePermissionWebInitiated(
RenderFrameHost* frame,
const WebBluetoothDeviceId& device_id) {
LOG(INFO)
<< "In ElectronBluetoothDelegate::RevokeDevicePermissionWebInitiated";
NOTIMPLEMENTED();
}

Expand All @@ -100,6 +137,8 @@ bool ElectronBluetoothDelegate::IsAllowedToAccessService(
bool ElectronBluetoothDelegate::IsAllowedToAccessAtLeastOneService(
RenderFrameHost* frame,
const WebBluetoothDeviceId& device_id) {
LOG(INFO)
<< "In ElectronBluetoothDelegate::IsAllowedToAccessAtLeastOneService";
NOTIMPLEMENTED();
return true;
}
Expand All @@ -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;
}
Expand All @@ -126,6 +167,7 @@ std::vector<blink::mojom::WebBluetoothDevicePtr>
ElectronBluetoothDelegate::GetPermittedDevices(
content::RenderFrameHost* frame) {
std::vector<blink::mojom::WebBluetoothDevicePtr> permitted_devices;
LOG(INFO) << "In ElectronBluetoothDelegate:::GetPermittedDevices";
NOTIMPLEMENTED();
return permitted_devices;
}
Expand All @@ -136,9 +178,63 @@ void ElectronBluetoothDelegate::ShowDevicePairPrompt(
PairPromptCallback callback,
PairingKind pairing_kind,
const absl::optional<std::u16string>& 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
9 changes: 9 additions & 0 deletions shell/browser/bluetooth/electron_bluetooth_delegate.h
Expand Up @@ -9,10 +9,12 @@
#include <string>
#include <vector>

#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 {
Expand Down Expand Up @@ -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<ElectronBluetoothDelegate> weak_factory_{this};
};

} // namespace electron
Expand Down

0 comments on commit ced73e4

Please sign in to comment.