fix: navigator.bluetooth.requestDevice (#27902)
* fix: navigator.bluetooth.requestDevice * cleanup lint and add test * update bluetooth test to handle no bluetooth adapter available * update bluetooth test to handle bluetooth permission denied
This commit is contained in:
parent
bd940b2904
commit
d57fd6cef0
9 changed files with 269 additions and 2 deletions
|
@ -334,6 +334,8 @@ filenames = {
|
||||||
"shell/browser/badging/badge_manager.h",
|
"shell/browser/badging/badge_manager.h",
|
||||||
"shell/browser/badging/badge_manager_factory.cc",
|
"shell/browser/badging/badge_manager_factory.cc",
|
||||||
"shell/browser/badging/badge_manager_factory.h",
|
"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.cc",
|
||||||
"shell/browser/browser.h",
|
"shell/browser/browser.h",
|
||||||
"shell/browser/browser_observer.h",
|
"shell/browser/browser_observer.h",
|
||||||
|
|
|
@ -88,7 +88,6 @@
|
||||||
#include "shell/browser/electron_browser_main_parts.h"
|
#include "shell/browser/electron_browser_main_parts.h"
|
||||||
#include "shell/browser/electron_javascript_dialog_manager.h"
|
#include "shell/browser/electron_javascript_dialog_manager.h"
|
||||||
#include "shell/browser/electron_navigation_throttle.h"
|
#include "shell/browser/electron_navigation_throttle.h"
|
||||||
#include "shell/browser/lib/bluetooth_chooser.h"
|
|
||||||
#include "shell/browser/native_window.h"
|
#include "shell/browser/native_window.h"
|
||||||
#include "shell/browser/session_preferences.h"
|
#include "shell/browser/session_preferences.h"
|
||||||
#include "shell/browser/ui/drag_util.h"
|
#include "shell/browser/ui/drag_util.h"
|
||||||
|
|
127
shell/browser/bluetooth/electron_bluetooth_delegate.cc
Normal file
127
shell/browser/bluetooth/electron_bluetooth_delegate.cc
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronBluetoothDelegate::AddFramePermissionObserver(
|
||||||
|
FramePermissionObserver* observer) {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElectronBluetoothDelegate::RemoveFramePermissionObserver(
|
||||||
|
FramePermissionObserver* observer) {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<blink::mojom::WebBluetoothDevicePtr>
|
||||||
|
ElectronBluetoothDelegate::GetPermittedDevices(
|
||||||
|
content::RenderFrameHost* frame) {
|
||||||
|
std::vector<blink::mojom::WebBluetoothDevicePtr> permitted_devices;
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
return permitted_devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace electron
|
90
shell/browser/bluetooth/electron_bluetooth_delegate.h
Normal file
90
shell/browser/bluetooth/electron_bluetooth_delegate.h
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// 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;
|
||||||
|
void AddFramePermissionObserver(FramePermissionObserver* observer) override;
|
||||||
|
void RemoveFramePermissionObserver(
|
||||||
|
FramePermissionObserver* observer) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace electron
|
||||||
|
|
||||||
|
#endif // SHELL_BROWSER_BLUETOOTH_ELECTRON_BLUETOOTH_DELEGATE_H_
|
|
@ -1770,4 +1770,10 @@ content::SerialDelegate* ElectronBrowserClient::GetSerialDelegate() {
|
||||||
return serial_delegate_.get();
|
return serial_delegate_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content::BluetoothDelegate* ElectronBrowserClient::GetBluetoothDelegate() {
|
||||||
|
if (!bluetooth_delegate_)
|
||||||
|
bluetooth_delegate_ = std::make_unique<ElectronBluetoothDelegate>();
|
||||||
|
return bluetooth_delegate_.get();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "electron/buildflags/buildflags.h"
|
#include "electron/buildflags/buildflags.h"
|
||||||
#include "net/ssl/client_cert_identity.h"
|
#include "net/ssl/client_cert_identity.h"
|
||||||
#include "services/metrics/public/cpp/ukm_source_id.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"
|
#include "shell/browser/serial/electron_serial_delegate.h"
|
||||||
|
|
||||||
namespace content {
|
namespace content {
|
||||||
|
@ -86,6 +87,8 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||||
bool CanUseCustomSiteInstance() override;
|
bool CanUseCustomSiteInstance() override;
|
||||||
content::SerialDelegate* GetSerialDelegate() override;
|
content::SerialDelegate* GetSerialDelegate() override;
|
||||||
|
|
||||||
|
content::BluetoothDelegate* GetBluetoothDelegate() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void RenderProcessWillLaunch(content::RenderProcessHost* host) override;
|
void RenderProcessWillLaunch(content::RenderProcessHost* host) override;
|
||||||
content::SpeechRecognitionManagerDelegate*
|
content::SpeechRecognitionManagerDelegate*
|
||||||
|
@ -335,6 +338,7 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||||
uint64_t next_id_ = 0;
|
uint64_t next_id_ = 0;
|
||||||
|
|
||||||
std::unique_ptr<ElectronSerialDelegate> serial_delegate_;
|
std::unique_ptr<ElectronSerialDelegate> serial_delegate_;
|
||||||
|
std::unique_ptr<ElectronBluetoothDelegate> bluetooth_delegate_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ElectronBrowserClient);
|
DISALLOW_COPY_AND_ASSIGN(ElectronBrowserClient);
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,7 +44,9 @@ BluetoothChooser::BluetoothChooser(api::WebContents* contents,
|
||||||
const EventHandler& event_handler)
|
const EventHandler& event_handler)
|
||||||
: api_web_contents_(contents), event_handler_(event_handler) {}
|
: api_web_contents_(contents), event_handler_(event_handler) {}
|
||||||
|
|
||||||
BluetoothChooser::~BluetoothChooser() = default;
|
BluetoothChooser::~BluetoothChooser() {
|
||||||
|
event_handler_.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) {
|
void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) {
|
||||||
switch (presence) {
|
switch (presence) {
|
||||||
|
@ -65,9 +67,11 @@ void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) {
|
||||||
void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
|
void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case DiscoveryState::FAILED_TO_START:
|
case DiscoveryState::FAILED_TO_START:
|
||||||
|
refreshing_ = false;
|
||||||
event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, "");
|
event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, "");
|
||||||
break;
|
break;
|
||||||
case DiscoveryState::IDLE:
|
case DiscoveryState::IDLE:
|
||||||
|
refreshing_ = false;
|
||||||
if (device_map_.empty()) {
|
if (device_map_.empty()) {
|
||||||
auto event = ++num_retries_ > kMaxScanRetries
|
auto event = ++num_retries_ > kMaxScanRetries
|
||||||
? content::BluetoothChooserEvent::CANCELLED
|
? content::BluetoothChooserEvent::CANCELLED
|
||||||
|
@ -86,6 +90,14 @@ void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DiscoveryState::DISCOVERING:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,6 +108,11 @@ void BluetoothChooser::AddOrUpdateDevice(const std::string& device_id,
|
||||||
bool is_gatt_connected,
|
bool is_gatt_connected,
|
||||||
bool is_paired,
|
bool is_paired,
|
||||||
int signal_strength_level) {
|
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;
|
bool changed = false;
|
||||||
auto entry = device_map_.find(device_id);
|
auto entry = device_map_.find(device_id);
|
||||||
if (entry == device_map_.end()) {
|
if (entry == device_map_.end()) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ class BluetoothChooser : public content::BluetoothChooser {
|
||||||
api::WebContents* api_web_contents_;
|
api::WebContents* api_web_contents_;
|
||||||
EventHandler event_handler_;
|
EventHandler event_handler_;
|
||||||
int num_retries_ = 0;
|
int num_retries_ = 0;
|
||||||
|
bool refreshing_ = false;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(BluetoothChooser);
|
DISALLOW_COPY_AND_ASSIGN(BluetoothChooser);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1613,3 +1613,24 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se
|
||||||
expect(waitForBadgeCount(0)).to.eventually.equal(0);
|
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.']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue