Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for HIDDevice.forget() #34210

Merged
merged 7 commits into from May 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/api/session.md
Expand Up @@ -274,6 +274,19 @@ from `select-hid-device` is called. This event is intended for use when using
a UI to ask users to pick a device so that the UI can be updated to remove the
specified device.

#### Event: 'hid-device-revoked'

Returns:

* `event` Event
* `details` Object
* `device` [HIDDevice[]](structures/hid-device.md)
* `frame` [WebFrameMain](web-frame-main.md)

Emitted after `HIDDevice.forget()` has been called. This event can be used
to help maintain persistent storage of permissions when
`setDevicePermissionHandler` is used.

#### Event: 'select-serial-port'

Returns:
Expand Down
1 change: 1 addition & 0 deletions filenames.gni
Expand Up @@ -566,6 +566,7 @@ filenames = {
"shell/common/gin_converters/gfx_converter.h",
"shell/common/gin_converters/guid_converter.h",
"shell/common/gin_converters/gurl_converter.h",
"shell/common/gin_converters/hid_device_info_converter.h",
"shell/common/gin_converters/image_converter.cc",
"shell/common/gin_converters/image_converter.h",
"shell/common/gin_converters/message_box_converter.cc",
Expand Down
114 changes: 102 additions & 12 deletions shell/browser/api/electron_api_web_contents.cc
Expand Up @@ -3445,37 +3445,127 @@ v8::Local<v8::Promise> WebContents::TakeHeapSnapshot(
void WebContents::GrantDevicePermission(
const url::Origin& origin,
const base::Value* device,
blink::PermissionType permissionType,
blink::PermissionType permission_type,
content::RenderFrameHost* render_frame_host) {
granted_devices_[render_frame_host->GetFrameTreeNodeId()][permissionType]
granted_devices_[render_frame_host->GetFrameTreeNodeId()][permission_type]
[origin]
.push_back(
std::make_unique<base::Value>(device->Clone()));
}

std::vector<base::Value> WebContents::GetGrantedDevices(
void WebContents::RevokeDevicePermission(
const url::Origin& origin,
blink::PermissionType permissionType,
const base::Value* device,
blink::PermissionType permission_type,
content::RenderFrameHost* render_frame_host) {
const auto& devices_for_frame_host_it =
granted_devices_.find(render_frame_host->GetFrameTreeNodeId());
if (devices_for_frame_host_it == granted_devices_.end())
return;

const auto& current_devices_it =
devices_for_frame_host_it->second.find(permission_type);
if (current_devices_it == devices_for_frame_host_it->second.end())
return;

const auto& origin_devices_it = current_devices_it->second.find(origin);
if (origin_devices_it == current_devices_it->second.end())
return;

for (auto it = origin_devices_it->second.begin();
it != origin_devices_it->second.end();) {
if (DoesDeviceMatch(device, it->get(), permission_type)) {
it = origin_devices_it->second.erase(it);
} else {
++it;
}
}
}

bool WebContents::DoesDeviceMatch(const base::Value* device,
const base::Value* device_to_compare,
blink::PermissionType permission_type) {
if (permission_type ==
static_cast<blink::PermissionType>(
WebContentsPermissionHelper::PermissionType::HID)) {
if (device->GetDict().FindInt(kHidVendorIdKey) !=
device_to_compare->GetDict().FindInt(kHidVendorIdKey) ||
device->GetDict().FindInt(kHidProductIdKey) !=
device_to_compare->GetDict().FindInt(kHidProductIdKey)) {
return false;
}

const auto* serial_number =
device_to_compare->GetDict().FindString(kHidSerialNumberKey);
const auto* device_serial_number =
device->GetDict().FindString(kHidSerialNumberKey);

if (serial_number && device_serial_number &&
*device_serial_number == *serial_number)
return true;
} else if (permission_type ==
static_cast<blink::PermissionType>(
WebContentsPermissionHelper::PermissionType::SERIAL)) {
#if BUILDFLAG(IS_WIN)
const auto* instance_id =
device->GetDict().FindString(kDeviceInstanceIdKey);
const auto* port_instance_id =
device_to_compare->GetDict().FindString(kDeviceInstanceIdKey);
if (instance_id && port_instance_id && *instance_id == *port_instance_id)
return true;
#else
const auto* serial_number = device->GetDict().FindString(kSerialNumberKey);
const auto* port_serial_number =
device_to_compare->GetDict().FindString(kSerialNumberKey);
if (device->GetDict().FindInt(kVendorIdKey) !=
device_to_compare->GetDict().FindInt(kVendorIdKey) ||
device->GetDict().FindInt(kProductIdKey) !=
device_to_compare->GetDict().FindInt(kProductIdKey) ||
(serial_number && port_serial_number &&
*port_serial_number != *serial_number)) {
return false;
}

#if BUILDFLAG(IS_MAC)
const auto* usb_driver_key = device->GetDict().FindString(kUsbDriverKey);
const auto* port_usb_driver_key =
device_to_compare->GetDict().FindString(kUsbDriverKey);
if (usb_driver_key && port_usb_driver_key &&
*usb_driver_key != *port_usb_driver_key) {
return false;
}
#endif // BUILDFLAG(IS_MAC)
return true;
#endif // BUILDFLAG(IS_WIN)
}
return false;
ckerr marked this conversation as resolved.
Show resolved Hide resolved
}

bool WebContents::CheckDevicePermission(
const url::Origin& origin,
const base::Value* device,
blink::PermissionType permission_type,
content::RenderFrameHost* render_frame_host) {
const auto& devices_for_frame_host_it =
granted_devices_.find(render_frame_host->GetFrameTreeNodeId());
if (devices_for_frame_host_it == granted_devices_.end())
return {};
return false;

const auto& current_devices_it =
devices_for_frame_host_it->second.find(permissionType);
devices_for_frame_host_it->second.find(permission_type);
if (current_devices_it == devices_for_frame_host_it->second.end())
return {};
return false;

const auto& origin_devices_it = current_devices_it->second.find(origin);
if (origin_devices_it == current_devices_it->second.end())
return {};
return false;

std::vector<base::Value> results;
for (const auto& object : origin_devices_it->second)
results.push_back(object->Clone());
for (const auto& device_to_compare : origin_devices_it->second) {
if (DoesDeviceMatch(device, device_to_compare.get(), permission_type))
return true;
}

return results;
return false;
}

void WebContents::UpdatePreferredSize(content::WebContents* web_contents,
Expand Down
19 changes: 15 additions & 4 deletions shell/browser/api/electron_api_web_contents.h
Expand Up @@ -443,13 +443,20 @@ class WebContents : public ExclusiveAccessContext,
blink::PermissionType permissionType,
content::RenderFrameHost* render_frame_host);

// Revokes |origin| access to |device|.
// To be used in place of ObjectPermissionContextBase::RevokeObjectPermission.
void RevokeDevicePermission(const url::Origin& origin,
const base::Value* device,
blink::PermissionType permission_type,
content::RenderFrameHost* render_frame_host);

// Returns the list of devices that |origin| has been granted permission to
// access. To be used in place of
// ObjectPermissionContextBase::GetGrantedObjects.
std::vector<base::Value> GetGrantedDevices(
const url::Origin& origin,
blink::PermissionType permissionType,
content::RenderFrameHost* render_frame_host);
bool CheckDevicePermission(const url::Origin& origin,
const base::Value* device,
blink::PermissionType permissionType,
content::RenderFrameHost* render_frame_host);

// disable copy
WebContents(const WebContents&) = delete;
Expand Down Expand Up @@ -746,6 +753,10 @@ class WebContents : public ExclusiveAccessContext,
// Update the html fullscreen flag in both browser and renderer.
void UpdateHtmlApiFullscreen(bool fullscreen);

bool DoesDeviceMatch(const base::Value* device,
const base::Value* device_to_compare,
blink::PermissionType permission_type);

v8::Global<v8::Value> session_;
v8::Global<v8::Value> devtools_web_contents_;
v8::Global<v8::Value> debugger_;
Expand Down
77 changes: 15 additions & 62 deletions shell/browser/electron_permission_manager.cc
Expand Up @@ -21,8 +21,6 @@
#include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/electron_browser_client.h"
#include "shell/browser/electron_browser_main_parts.h"
#include "shell/browser/hid/hid_chooser_context.h"
#include "shell/browser/serial/serial_chooser_context.h"
#include "shell/browser/web_contents_permission_helper.h"
#include "shell/browser/web_contents_preferences.h"
#include "shell/common/gin_converters/content_converter.h"
Expand Down Expand Up @@ -308,66 +306,8 @@ bool ElectronPermissionManager::CheckDevicePermission(
api::WebContents* api_web_contents = api::WebContents::From(web_contents);
if (device_permission_handler_.is_null()) {
if (api_web_contents) {
std::vector<base::Value> granted_devices =
api_web_contents->GetGrantedDevices(origin, permission,
render_frame_host);

for (const auto& granted_device : granted_devices) {
if (permission ==
static_cast<blink::PermissionType>(
WebContentsPermissionHelper::PermissionType::HID)) {
if (device->FindIntKey(kHidVendorIdKey) !=
granted_device.FindIntKey(kHidVendorIdKey) ||
device->FindIntKey(kHidProductIdKey) !=
granted_device.FindIntKey(kHidProductIdKey)) {
continue;
}

const auto* serial_number =
granted_device.FindStringKey(kHidSerialNumberKey);
const auto* device_serial_number =
device->FindStringKey(kHidSerialNumberKey);

if (serial_number && device_serial_number &&
*device_serial_number == *serial_number)
return true;
} else if (permission ==
static_cast<blink::PermissionType>(
WebContentsPermissionHelper::PermissionType::SERIAL)) {
#if BUILDFLAG(IS_WIN)
const auto* instance_id = device->FindStringKey(kDeviceInstanceIdKey);
const auto* port_instance_id =
granted_device.FindStringKey(kDeviceInstanceIdKey);
if (instance_id && port_instance_id &&
*instance_id == *port_instance_id)
return true;
#else
const auto* serial_number =
granted_device.FindStringKey(kSerialNumberKey);
const auto* port_serial_number =
device->FindStringKey(kSerialNumberKey);
if (device->FindIntKey(kVendorIdKey) !=
granted_device.FindIntKey(kVendorIdKey) ||
device->FindIntKey(kProductIdKey) !=
granted_device.FindIntKey(kProductIdKey) ||
(serial_number && port_serial_number &&
*port_serial_number != *serial_number)) {
continue;
}

#if BUILDFLAG(IS_MAC)
const auto* usb_driver_key = device->FindStringKey(kUsbDriverKey);
const auto* port_usb_driver_key =
granted_device.FindStringKey(kUsbDriverKey);
if (usb_driver_key && port_usb_driver_key &&
*usb_driver_key != *port_usb_driver_key) {
continue;
}
#endif // BUILDFLAG(IS_MAC)
return true;
#endif // BUILDFLAG(IS_WIN)
}
}
return api_web_contents->CheckDevicePermission(origin, device, permission,
render_frame_host);
}
return false;
} else {
Expand Down Expand Up @@ -398,6 +338,19 @@ void ElectronPermissionManager::GrantDevicePermission(
}
}

void ElectronPermissionManager::RevokeDevicePermission(
blink::PermissionType permission,
const url::Origin& origin,
const base::Value* device,
content::RenderFrameHost* render_frame_host) const {
auto* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
api::WebContents* api_web_contents = api::WebContents::From(web_contents);
if (api_web_contents)
api_web_contents->RevokeDevicePermission(origin, device, permission,
render_frame_host);
}

blink::mojom::PermissionStatus
ElectronPermissionManager::GetPermissionStatusForFrame(
blink::PermissionType permission,
Expand Down
6 changes: 6 additions & 0 deletions shell/browser/electron_permission_manager.h
Expand Up @@ -103,6 +103,12 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate {
const base::Value* object,
content::RenderFrameHost* render_frame_host) const;

void RevokeDevicePermission(
blink::PermissionType permission,
const url::Origin& origin,
const base::Value* object,
content::RenderFrameHost* render_frame_host) const;

protected:
void OnPermissionResponse(int request_id,
int permission_id,
Expand Down
7 changes: 5 additions & 2 deletions shell/browser/hid/electron_hid_delegate.cc
Expand Up @@ -80,8 +80,11 @@ bool ElectronHidDelegate::HasDevicePermission(
void ElectronHidDelegate::RevokeDevicePermission(
content::RenderFrameHost* render_frame_host,
const device::mojom::HidDeviceInfo& device) {
// TODO(jkleinsc) implement this for
// https://chromium-review.googlesource.com/c/chromium/src/+/3297868
auto* chooser_context = GetChooserContext(render_frame_host);
const auto& origin =
render_frame_host->GetMainFrame()->GetLastCommittedOrigin();
return chooser_context->RevokeDevicePermission(origin, device,
render_frame_host);
}

device::mojom::HidManager* ElectronHidDelegate::GetHidManager(
Expand Down