Skip to content

Commit

Permalink
fix: navigator.bluetooth.requestDevice (#27927)
Browse files Browse the repository at this point in the history
* fix: navigator.bluetooth.requestDevice

* remove permission observer methods not implemented in 12-x-y

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
  • Loading branch information
VerteDinde and jkleinsc committed Mar 1, 2021
1 parent e65e989 commit 0baf999
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 2 deletions.
2 changes: 2 additions & 0 deletions filenames.gni
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ filenames = {
"shell/browser/badging/badge_manager.h",
"shell/browser/badging/badge_manager_factory.cc",
"shell/browser/badging/badge_manager_factory.h",
"shell/browser/bluetooth/electron_bluetooth_delegate.cc",
"shell/browser/bluetooth/electron_bluetooth_delegate.h",
"shell/browser/browser.cc",
"shell/browser/browser.h",
"shell/browser/browser_observer.h",
Expand Down
1 change: 0 additions & 1 deletion shell/browser/api/electron_api_web_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@
#include "shell/browser/electron_browser_main_parts.h"
#include "shell/browser/electron_javascript_dialog_manager.h"
#include "shell/browser/electron_navigation_throttle.h"
#include "shell/browser/lib/bluetooth_chooser.h"
#include "shell/browser/native_window.h"
#include "shell/browser/session_preferences.h"
#include "shell/browser/ui/drag_util.h"
Expand Down
117 changes: 117 additions & 0 deletions shell/browser/bluetooth/electron_bluetooth_delegate.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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 <memory>

#include "base/scoped_observer.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/lib/bluetooth_chooser.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 electron {

ElectronBluetoothDelegate::ElectronBluetoothDelegate() = default;

ElectronBluetoothDelegate::~ElectronBluetoothDelegate() = default;

std::unique_ptr<content::BluetoothChooser>
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<BluetoothChooser>(api_web_contents, event_handler);
}

// The following methods are not currently called in Electron.
std::unique_ptr<content::BluetoothScanningPrompt>
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 nullptr;
}

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;
}

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;
}

std::vector<blink::mojom::WebBluetoothDevicePtr>
ElectronBluetoothDelegate::GetPermittedDevices(
content::RenderFrameHost* frame) {
std::vector<blink::mojom::WebBluetoothDevicePtr> permitted_devices;
NOTIMPLEMENTED();
return permitted_devices;
}

} // namespace electron
87 changes: 87 additions & 0 deletions shell/browser/bluetooth/electron_bluetooth_delegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) 2020 Microsoft, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef SHELL_BROWSER_BLUETOOTH_ELECTRON_BLUETOOTH_DELEGATE_H_
#define SHELL_BROWSER_BLUETOOTH_ELECTRON_BLUETOOTH_DELEGATE_H_

#include <memory>
#include <string>
#include <vector>

#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 "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-forward.h"

namespace blink {
class WebBluetoothDeviceId;
} // namespace blink

namespace content {
class RenderFrameHost;
} // namespace content

namespace device {
class BluetoothDevice;
class BluetoothUUID;
} // namespace device

namespace electron {

// Provides an interface for managing device permissions for Web Bluetooth and
// Web Bluetooth Scanning API. This is the Electron-specific implementation of
// the BluetoothDelegate.
class ElectronBluetoothDelegate : public content::BluetoothDelegate {
public:
ElectronBluetoothDelegate();
~ElectronBluetoothDelegate() override;

// Move-only class.
ElectronBluetoothDelegate(const ElectronBluetoothDelegate&) = delete;
ElectronBluetoothDelegate& operator=(const ElectronBluetoothDelegate&) =
delete;

// BluetoothDelegate implementation:
std::unique_ptr<content::BluetoothChooser> RunBluetoothChooser(
content::RenderFrameHost* frame,
const content::BluetoothChooser::EventHandler& event_handler) override;

std::unique_ptr<content::BluetoothScanningPrompt> ShowBluetoothScanningPrompt(
content::RenderFrameHost* frame,
const content::BluetoothScanningPrompt::EventHandler& event_handler)
override;
blink::WebBluetoothDeviceId GetWebBluetoothDeviceId(
content::RenderFrameHost* frame,
const std::string& device_address) override;
std::string GetDeviceAddress(
content::RenderFrameHost* frame,
const blink::WebBluetoothDeviceId& device_id) override;
blink::WebBluetoothDeviceId AddScannedDevice(
content::RenderFrameHost* frame,
const std::string& device_address) override;
blink::WebBluetoothDeviceId GrantServiceAccessPermission(
content::RenderFrameHost* frame,
const device::BluetoothDevice* device,
const blink::mojom::WebBluetoothRequestDeviceOptions* options) override;
bool HasDevicePermission(
content::RenderFrameHost* frame,
const blink::WebBluetoothDeviceId& device_id) override;
bool IsAllowedToAccessService(content::RenderFrameHost* frame,
const blink::WebBluetoothDeviceId& device_id,
const device::BluetoothUUID& service) override;
bool IsAllowedToAccessAtLeastOneService(
content::RenderFrameHost* frame,
const blink::WebBluetoothDeviceId& device_id) override;
bool IsAllowedToAccessManufacturerData(
content::RenderFrameHost* frame,
const blink::WebBluetoothDeviceId& device_id,
uint16_t manufacturer_code) override;
std::vector<blink::mojom::WebBluetoothDevicePtr> GetPermittedDevices(
content::RenderFrameHost* frame) override;
};

} // namespace electron

#endif // SHELL_BROWSER_BLUETOOTH_ELECTRON_BLUETOOTH_DELEGATE_H_
6 changes: 6 additions & 0 deletions shell/browser/electron_browser_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1775,4 +1775,10 @@ content::SerialDelegate* ElectronBrowserClient::GetSerialDelegate() {
return serial_delegate_.get();
}

content::BluetoothDelegate* ElectronBrowserClient::GetBluetoothDelegate() {
if (!bluetooth_delegate_)
bluetooth_delegate_ = std::make_unique<ElectronBluetoothDelegate>();
return bluetooth_delegate_.get();
}

} // namespace electron
4 changes: 4 additions & 0 deletions shell/browser/electron_browser_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "electron/buildflags/buildflags.h"
#include "net/ssl/client_cert_identity.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "shell/browser/bluetooth/electron_bluetooth_delegate.h"
#include "shell/browser/serial/electron_serial_delegate.h"

namespace content {
Expand Down Expand Up @@ -86,6 +87,8 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
bool CanUseCustomSiteInstance() override;
content::SerialDelegate* GetSerialDelegate() override;

content::BluetoothDelegate* GetBluetoothDelegate() override;

protected:
void RenderProcessWillLaunch(content::RenderProcessHost* host) override;
content::SpeechRecognitionManagerDelegate*
Expand Down Expand Up @@ -336,6 +339,7 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
uint64_t next_id_ = 0;

std::unique_ptr<ElectronSerialDelegate> serial_delegate_;
std::unique_ptr<ElectronBluetoothDelegate> bluetooth_delegate_;

DISALLOW_COPY_AND_ASSIGN(ElectronBrowserClient);
};
Expand Down
19 changes: 18 additions & 1 deletion shell/browser/lib/bluetooth_chooser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ BluetoothChooser::BluetoothChooser(api::WebContents* contents,
const EventHandler& event_handler)
: api_web_contents_(contents), event_handler_(event_handler) {}

BluetoothChooser::~BluetoothChooser() = default;
BluetoothChooser::~BluetoothChooser() {
event_handler_.Reset();
}

void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) {
switch (presence) {
Expand All @@ -60,9 +62,11 @@ void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) {
void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
switch (state) {
case DiscoveryState::FAILED_TO_START:
refreshing_ = false;
event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, "");
break;
case DiscoveryState::IDLE:
refreshing_ = false;
if (device_map_.empty()) {
auto event = ++num_retries_ > kMaxScanRetries
? content::BluetoothChooserEvent::CANCELLED
Expand All @@ -81,6 +85,14 @@ void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
}
break;
case DiscoveryState::DISCOVERING:
// The first time this state fires is due to a rescan triggering so set a
// flag to ignore devices
if (!refreshing_) {
refreshing_ = true;
} else {
// The second time this state fires we are now safe to pick a device
refreshing_ = false;
}
break;
}
}
Expand All @@ -91,6 +103,11 @@ void BluetoothChooser::AddOrUpdateDevice(const std::string& device_id,
bool is_gatt_connected,
bool is_paired,
int signal_strength_level) {
if (refreshing_) {
// If the list of bluetooth devices is currently being generated don't fire
// an event
return;
}
bool changed = false;
auto entry = device_map_.find(device_id);
if (entry == device_map_.end()) {
Expand Down
1 change: 1 addition & 0 deletions shell/browser/lib/bluetooth_chooser.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class BluetoothChooser : public content::BluetoothChooser {
api::WebContents* api_web_contents_;
EventHandler event_handler_;
int num_retries_ = 0;
bool refreshing_ = false;

DISALLOW_COPY_AND_ASSIGN(BluetoothChooser);
};
Expand Down
21 changes: 21 additions & 0 deletions spec-main/chromium-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1611,3 +1611,24 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se
expect(waitForBadgeCount(0)).to.eventually.equal(0);
});
});

describe('navigator.bluetooth', () => {
let w: BrowserWindow;
before(async () => {
w = new BrowserWindow({
show: false,
webPreferences: {
enableBlinkFeatures: 'WebBluetooth'
}
});
await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html'));
});

after(closeAllWindows);

it('can request bluetooth devices', async () => {
const bluetooth = await w.webContents.executeJavaScript(`
navigator.bluetooth.requestDevice({ acceptAllDevices: true}).then(device => "Found a device!").catch(err => err.message);`, true);
expect(bluetooth).to.be.oneOf(['Found a device!', 'Bluetooth adapter not available.', 'User cancelled the requestDevice() chooser.']);
});
});

0 comments on commit 0baf999

Please sign in to comment.