Compare commits

...

3 Commits

Author SHA1 Message Date
Samuel Attard
8bf80ccdb8 tests 2024-08-31 01:01:39 -07:00
Samuel Attard
d7ff2bf7b7 update origin source 2024-08-31 00:34:26 -07:00
Samuel Attard
ca221f6d80 feat: new permissions API 2024-08-30 23:37:14 -04:00
44 changed files with 684 additions and 199 deletions

View File

@@ -276,7 +276,7 @@ Emitted when a HID device needs to be selected when a call to
`navigator.hid.requestDevice` is made. `callback` should be called with
`deviceId` to be selected; passing no arguments to `callback` will
cancel the request. Additionally, permissioning on `navigator.hid` can
be further managed by using [`ses.setPermissionCheckHandler(handler)`](#sessetpermissioncheckhandlerhandler)
be further managed by using [`ses.setPermissionCheckHandler(handler)`](#sessetpermissioncheckhandlerhandler-deprecated)
and [`ses.setDevicePermissionHandler(handler)`](#sessetdevicepermissionhandlerhandler).
```js @ts-type={fetchGrantedDevices:()=>(Array<Electron.DevicePermissionHandlerHandlerDetails['device']>)}
@@ -382,7 +382,7 @@ Emitted when a serial port needs to be selected when a call to
`navigator.serial.requestPort` is made. `callback` should be called with
`portId` to be selected, passing an empty string to `callback` will
cancel the request. Additionally, permissioning on `navigator.serial` can
be managed by using [ses.setPermissionCheckHandler(handler)](#sessetpermissioncheckhandlerhandler)
be managed by using [ses.setPermissionCheckHandler(handler)](#sessetpermissioncheckhandlerhandler-deprecated)
with the `serial` permission.
```js @ts-type={fetchGrantedDevices:()=>(Array<Electron.DevicePermissionHandlerHandlerDetails['device']>)}
@@ -525,7 +525,7 @@ Emitted when a USB device needs to be selected when a call to
`navigator.usb.requestDevice` is made. `callback` should be called with
`deviceId` to be selected; passing no arguments to `callback` will
cancel the request. Additionally, permissioning on `navigator.usb` can
be further managed by using [`ses.setPermissionCheckHandler(handler)`](#sessetpermissioncheckhandlerhandler)
be further managed by using [`ses.setPermissionCheckHandler(handler)`](#sessetpermissioncheckhandlerhandler-deprecated)
and [`ses.setDevicePermissionHandler(handler)`](#sessetdevicepermissionhandlerhandler).
```js @ts-type={fetchGrantedDevices:()=>(Array<Electron.DevicePermissionHandlerHandlerDetails['device']>)} @ts-type={updateGrantedDevices:(devices:Array<Electron.DevicePermissionHandlerHandlerDetails['device']>)=>void}
@@ -858,10 +858,107 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
> **NOTE:** The result of this procedure is cached by the network service.
#### `ses.setPermissionRequestHandler(handler)`
#### `ses.setPermissionHandlers(permissionHandlers)`
* `permissionHandlers` Object | null
* `isGranted` Function\<[PermissionCheckResult](structures/permission-check-result.md)>
* `permission` string - Type of permission check.
* `clipboard-read` - Request access to read from the clipboard.
* `clipboard-sanitized-write` - Request access to write to the clipboard.
* `geolocation` - Access the user's geolocation data via the [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API)
* `fullscreen` - Control of the app's fullscreen state via the [Fullscreen API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API).
* `hid` - Access the HID protocol to manipulate HID devices via the [WebHID API](https://developer.mozilla.org/en-US/docs/Web/API/WebHID_API).
* `idle-detection` - Access the user's idle state via the [IdleDetector API](https://developer.mozilla.org/en-US/docs/Web/API/IdleDetector).
* `media` - Access to media devices such as camera, microphone and speakers.
* `mediaKeySystem` - Access to DRM protected content.
* `midi` - Enable MIDI access in the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API).
* `midiSysex` - Use system exclusive messages in the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API).
* `notifications` - Configure and display desktop notifications to the user with the [Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/notification).
* `openExternal` - Open links in external applications.
* `pointerLock` - Directly interpret mouse movements as an input method via the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API). These requests always appear to originate from the main frame.
* `serial` - Read from and write to serial devices with the [Web Serial API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API).
* `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
* `usb` - Expose non-standard Universal Serial Bus (USB) compatible devices services to the web with the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API).
* `effectiveOrigin` string - The origin of the URL of the permission check, you should use this origin to perform security checks on whether to accept or deny this permission check. You may augment checks with additional information, but this is the primary source of truth you should rely on.
* `permissionCheckDetails` Object - Some properties are only available on certain permission types.
* `embeddingOrigin` string (optional) - The origin of the frame embedding the frame that made the permission check. Only set for cross-origin sub frames making permission checks.
* `securityOrigin` string (optional) _Deprecated_ - The security origin of the `media` check. This value is identical to `effectiveOrigin`, use that value instead.
* `mediaType` string (optional) - The type of media access being requested, can be `video`, `audio` or `unknown`
* `isMainFrame` boolean (optional) - Whether the frame making the request is the main frame. This value is `undefined` in cases where the request is coming from a background worker and therefore is not related to a specific frame.
* `onRequest` Function\<Promise\<[PermissionRequestResponse](structures/permission-request-response.md)\>\>
* `permission` string - The type of requested permission.
* `clipboard-read` - Request access to read from the clipboard.
* `clipboard-sanitized-write` - Request access to write to the clipboard.
* `display-capture` - Request access to capture the screen via the [Screen Capture API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capture_API).
* `fullscreen` - Request control of the app's fullscreen state via the [Fullscreen API](https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API).
* `geolocation` - Request access to the user's location via the [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API)
* `idle-detection` - Request access to the user's idle state via the [IdleDetector API](https://developer.mozilla.org/en-US/docs/Web/API/IdleDetector).
* `media` - Request access to media devices such as camera, microphone and speakers.
* `mediaKeySystem` - Request access to DRM protected content.
* `midi` - Request MIDI access in the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API).
* `midiSysex` - Request the use of system exclusive messages in the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API).
* `notifications` - Request notification creation and the ability to display them in the user's system tray using the [Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/notification)
* `pointerLock` - Request to directly interpret mouse movements as an input method via the [Pointer Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API). These requests always appear to originate from the main frame.
* `keyboardLock` - Request capture of keypresses for any or all of the keys on the physical keyboard via the [Keyboard Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/lock). These requests always appear to originate from the main frame.
* `openExternal` - Request to open links in external applications.
* `speaker-selection` - Request to enumerate and select audio output devices via the [speaker-selection permissions policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy/speaker-selection).
* `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
* `window-management` - Request access to enumerate screens using the [`getScreenDetails`](https://developer.chrome.com/en/articles/multi-screen-window-placement/) API.
* `unknown` - An unrecognized permission request.
* `fileSystem` - Request access to read, write, and file management capabilities using the [File System API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API).
* `effectiveOrigin` String - The origin of the URL of the permission check, you should use this origin to perform security checks on whether to accept or deny this permission check. You may augment checks with additional information, but this is the primary source of truth you should rely on.
* `permissionRequestDetails` [PermissionRequest](structures/permission-request.md) | [FilesystemPermissionRequest](structures/filesystem-permission-request.md) | [MediaAccessPermissionRequest](structures/media-access-permission-request.md) | [OpenExternalPermissionRequest](structures/open-external-permission-request.md) - Additional information about the permission being requested.
<!--
STUFF
Sets the handler which can be used to respond to permission requests for the `session`.
Calling `callback(true)` will allow the permission and `callback(false)` will reject it.
To clear the handler, call `setPermissionRequestHandler(null)`. Please note that
you must also implement `setPermissionCheckHandler` to get complete permission handling.
Most web APIs do a permission check and then make a permission request if the check is denied.
```js
const { session } = require('electron')
session.fromPartition('some-partition').setPermissionRequestHandler((webContents, permission, callback) => {
if (webContents.getURL() === 'some-host' && permission === 'notifications') {
return callback(false) // denied.
}
callback(true)
})
```
#### `ses.setPermissionCheckHandler(handler)`
* `handler`
Sets the handler which can be used to respond to permission checks for the `session`.
Returning `true` will allow the permission and `false` will reject it. Please note that
you must also implement `setPermissionRequestHandler` to get complete permission handling.
Most web APIs do a permission check and then make a permission request if the check is denied.
To clear the handler, call `setPermissionCheckHandler(null)`.
```js
const { session } = require('electron')
const url = require('url')
session.fromPartition('some-partition').setPermissionCheckHandler((webContents, permission, requestingOrigin) => {
if (new URL(requestingOrigin).hostname === 'some-host' && permission === 'notifications') {
return true // granted
}
return false // denied
})
```
-->
#### `ses.setPermissionRequestHandler(handler)` _Deprecated_
* `handler` Function | null
* `webContents` [WebContents](web-contents.md) - WebContents requesting the permission. Please note that if the request comes from a subframe you should use `requestingUrl` to check the request origin.
* `webContents` [WebContents](web-contents.md) - WebContents requesting the permission. Please note that if the request comes from a subframe you should use `effectiveOrigin` to check the request origin.
* `permission` string - The type of requested permission.
* `clipboard-read` - Request access to read from the clipboard.
* `clipboard-sanitized-write` - Request access to write to the clipboard.
@@ -886,6 +983,7 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
* `callback` Function
* `permissionGranted` boolean - Allow or deny the permission.
* `details` [PermissionRequest](structures/permission-request.md) | [FilesystemPermissionRequest](structures/filesystem-permission-request.md) | [MediaAccessPermissionRequest](structures/media-access-permission-request.md) | [OpenExternalPermissionRequest](structures/open-external-permission-request.md) - Additional information about the permission being requested.
* `effectiveOrigin` string - The origin of the URL of the permission check, you should use this origin to perform security checks on whether to accept or deny this permission check. You may augment checks with additional information, but this is the primary source of truth you should rely on.
Sets the handler which can be used to respond to permission requests for the `session`.
Calling `callback(true)` will allow the permission and `callback(false)` will reject it.
@@ -904,10 +1002,10 @@ session.fromPartition('some-partition').setPermissionRequestHandler((webContents
})
```
#### `ses.setPermissionCheckHandler(handler)`
#### `ses.setPermissionCheckHandler(handler)` _Deprecated_
* `handler` Function\<boolean> | null
* `webContents` ([WebContents](web-contents.md) | null) - WebContents checking the permission. Please note that if the request comes from a subframe you should use `requestingUrl` to check the request origin. All cross origin sub frames making permission checks will pass a `null` webContents to this handler, while certain other permission checks such as `notifications` checks will always pass `null`. You should use `embeddingOrigin` and `requestingOrigin` to determine what origin the owning frame and the requesting frame are on respectively.
* `webContents` ([WebContents](web-contents.md) | null) - WebContents checking the permission. Please note that if the request comes from a subframe you should use `effectiveOrigin` to check the request origin. All cross origin sub frames making permission checks will pass a `null` webContents to this handler, while certain other permission checks such as `notifications` checks will always pass `null`. You should use `embeddingOrigin` and `effectiveOrigin` to determine what origin the owning frame and the requesting frame are on respectively.
* `permission` string - Type of permission check.
* `clipboard-read` - Request access to read from the clipboard.
* `clipboard-sanitized-write` - Request access to write to the clipboard.
@@ -926,7 +1024,7 @@ session.fromPartition('some-partition').setPermissionRequestHandler((webContents
* `storage-access` - Allows content loaded in a third-party context to request access to third-party cookies using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
* `top-level-storage-access` - Allow top-level sites to request third-party cookie access on behalf of embedded content originating from another site in the same related website set using the [Storage Access API](https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API).
* `usb` - Expose non-standard Universal Serial Bus (USB) compatible devices services to the web with the [WebUSB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API).
* `requestingOrigin` string - The origin URL of the permission check
* `effectiveOrigin` string - The origin URL of the permission check
* `details` Object - Some properties are only available on certain permission types.
* `embeddingOrigin` string (optional) - The origin of the frame embedding the frame that made the permission check. Only set for cross-origin sub frames making permission checks.
* `securityOrigin` string (optional) - The security origin of the `media` check.

View File

@@ -0,0 +1,5 @@
# PermissionCheckResult Object
* `status` string - Can be `granted`, `denied` or `ask`. Controls whether the permission check should be approved. Granted and Denied have their implied effects, `ask` will result in a permission request being fired to your permission request handler.
Note: For media permission checks `ask` is equivilant to `granted`.

View File

@@ -0,0 +1,3 @@
# PermissionRequestResponse Object
* `status` string - Can be `granted` or `denied`. Controls whether the permission should be granted.

View File

@@ -2034,9 +2034,9 @@ ipcRenderer.on('port', (e, msg) => {
})
```
#### `contents.enableDeviceEmulation(parameters)`
#### `contents.enableDeviceEmulation(deviceEmulationParameters)`
* `parameters` Object
* `deviceEmulationParameters` Object
* `screenPosition` string - Specify the screen type to emulate
(default: `desktop`):
* `desktop` - Desktop screen type.

View File

@@ -52,7 +52,7 @@ the WebHID API:
needed, a developer can store granted device permissions (eg when handling
the `select-hid-device` event) and then read from that storage with
`setDevicePermissionHandler`.
* [`ses.setPermissionCheckHandler(handler)`](../api/session.md#sessetpermissioncheckhandlerhandler)
* [`ses.setPermissionCheckHandler(handler)`](../api/session.md#sessetpermissioncheckhandlerhandler-deprecated)
can be used to disable HID access for specific origins.
### Blocklist
@@ -101,7 +101,7 @@ There are several additional APIs for working with the Web Serial API:
needed, a developer can store granted device permissions (eg when handling
the `select-serial-port` event) and then read from that storage with
`setDevicePermissionHandler`.
* [`ses.setPermissionCheckHandler(handler)`](../api/session.md#sessetpermissioncheckhandlerhandler)
* [`ses.setPermissionCheckHandler(handler)`](../api/session.md#sessetpermissioncheckhandlerhandler-deprecated)
can be used to disable serial access for specific origins.
### Example
@@ -140,7 +140,7 @@ Electron provides several APIs for working with the WebUSB API:
needed, a developer can store granted device permissions (eg when handling
the `select-usb-device` event) and then read from that storage with
`setDevicePermissionHandler`.
* [`ses.setPermissionCheckHandler(handler)`](../api/session.md#sessetpermissioncheckhandlerhandler)
* [`ses.setPermissionCheckHandler(handler)`](../api/session.md#sessetpermissioncheckhandlerhandler-deprecated)
can be used to disable USB access for specific origins.
* [`ses.setUSBProtectedClassesHandler](../api/session.md#sessetusbprotectedclasseshandlerhandler)
can be used to allow usage of [protected USB classes](https://wicg.github.io/webusb/#usbinterface-interface) that are not available by default.

View File

@@ -600,7 +600,7 @@ filenames = {
"shell/common/gin_converters/gfx_converter.cc",
"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/url_converters.h",
"shell/common/gin_converters/hid_device_info_converter.h",
"shell/common/gin_converters/image_converter.cc",
"shell/common/gin_converters/image_converter.h",

View File

@@ -6,6 +6,44 @@ Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) {
return fetchWithSession(input, init, this, net.request);
};
Session.prototype.setPermissionCheckHandler = function (handler) {
if (!handler) return this._setPermissionCheckHandler(handler);
return this._setPermissionCheckHandler((...args) => {
if (handler(...args)) return 'granted';
return 'denied';
});
};
Session.prototype.setPermissionRequestHandler = function (handler) {
if (!handler) return this._setPermissionRequestHandler(handler);
return this._setPermissionRequestHandler((wc, perm, cb: any, d, eo) => {
return handler(wc, perm, (granted) => cb(granted ? 'granted' : 'denied'), d, eo);
});
};
Session.prototype.setPermissionHandlers = function (handlers) {
if (!handlers) {
this._setPermissionCheckHandler(null);
this._setPermissionRequestHandler(null);
return;
}
this._setPermissionCheckHandler((_, permission, effectiveOrigin, details) => {
return handlers.isGranted(permission, effectiveOrigin, details).status;
});
this._setPermissionRequestHandler((_, permission, callback, details, effectiveOrigin) => {
Promise.resolve(handlers.onRequest(permission, effectiveOrigin, details))
.then((result) => callback(result.status === 'granted'))
.catch((err) => {
this.emit('error', err);
callback(false);
});
});
};
export default {
fromPartition,
fromPath,

View File

@@ -67,7 +67,7 @@
#include "shell/common/gin_converters/blink_converter.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/image_converter.h"
#include "shell/common/gin_converters/login_item_settings_converter.h"
#include "shell/common/gin_converters/net_converter.h"

View File

@@ -25,7 +25,7 @@
#include "shell/browser/cookie_change_notifier.h"
#include "shell/browser/electron_browser_context.h"
#include "shell/browser/javascript_environment.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"

View File

@@ -12,7 +12,7 @@
#include "shell/browser/electron_browser_main_parts.h"
#include "shell/common/gin_converters/file_dialog_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"

View File

@@ -14,7 +14,7 @@
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/content_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/image_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"

View File

@@ -16,7 +16,7 @@
#include "gin/object_template_builder.h"
#include "shell/browser/electron_browser_context.h"
#include "shell/browser/javascript_environment.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/node_includes.h"

View File

@@ -77,9 +77,10 @@
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/content_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/media_converter.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_converters/optional_converter.h"
#include "shell/common/gin_converters/usb_protected_classes_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
@@ -846,36 +847,37 @@ void Session::SetPermissionRequestHandler(v8::Local<v8::Value> val,
browser_context()->GetPermissionControllerDelegate());
if (val->IsNull()) {
permission_manager->SetPermissionRequestHandler(
ElectronPermissionManager::RequestHandler());
ElectronPermissionManager::OnRequestHandler());
return;
}
auto handler = std::make_unique<ElectronPermissionManager::RequestHandler>();
auto handler = std::make_unique<ElectronPermissionManager::OnRequestHandler>();
if (!gin::ConvertFromV8(args->isolate(), val, handler.get())) {
args->ThrowTypeError("Must pass null or function");
return;
}
permission_manager->SetPermissionRequestHandler(base::BindRepeating(
[](ElectronPermissionManager::RequestHandler* handler,
[](ElectronPermissionManager::OnRequestHandler* handler,
content::WebContents* web_contents,
blink::PermissionType permission_type,
ElectronPermissionManager::StatusCallback callback,
const base::Value& details) {
const base::Value& details,
const url::Origin& effective_origin) {
handler->Run(web_contents, permission_type, std::move(callback),
details);
details, effective_origin);
},
base::Owned(std::move(handler))));
}
void Session::SetPermissionCheckHandler(v8::Local<v8::Value> val,
void Session::SetPermissionIsGrantedHandler(v8::Local<v8::Value> val,
gin::Arguments* args) {
ElectronPermissionManager::CheckHandler handler;
ElectronPermissionManager::IsGrantedHandler handler;
if (!(val->IsNull() || gin::ConvertFromV8(args->isolate(), val, &handler))) {
args->ThrowTypeError("Must pass null or function");
return;
}
auto* permission_manager = static_cast<ElectronPermissionManager*>(
browser_context()->GetPermissionControllerDelegate());
permission_manager->SetPermissionCheckHandler(handler);
permission_manager->SetPermissionIsGrantedHandler(handler);
}
void Session::SetDisplayMediaRequestHandler(v8::Isolate* isolate,
@@ -1603,10 +1605,10 @@ void Session::FillObjectTemplate(v8::Isolate* isolate,
.SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation)
.SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation)
.SetMethod("setCertificateVerifyProc", &Session::SetCertVerifyProc)
.SetMethod("setPermissionRequestHandler",
.SetMethod("_setPermissionRequestHandler",
&Session::SetPermissionRequestHandler)
.SetMethod("setPermissionCheckHandler",
&Session::SetPermissionCheckHandler)
.SetMethod("_setPermissionCheckHandler",
&Session::SetPermissionIsGrantedHandler)
.SetMethod("setDisplayMediaRequestHandler",
&Session::SetDisplayMediaRequestHandler)
.SetMethod("setDevicePermissionHandler",

View File

@@ -119,7 +119,7 @@ class Session : public gin::Wrappable<Session>,
void SetCertVerifyProc(v8::Local<v8::Value> proc, gin::Arguments* args);
void SetPermissionRequestHandler(v8::Local<v8::Value> val,
gin::Arguments* args);
void SetPermissionCheckHandler(v8::Local<v8::Value> val,
void SetPermissionIsGrantedHandler(v8::Local<v8::Value> val,
gin::Arguments* args);
void SetDevicePermissionHandler(v8::Local<v8::Value> val,
gin::Arguments* args);

View File

@@ -22,7 +22,7 @@
#include "shell/browser/mac/dict_util.h"
#include "shell/browser/mac/electron_application.h"
#include "shell/common/color_util.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/node_includes.h"

View File

@@ -117,7 +117,7 @@
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/frame_converter.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/image_converter.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_converters/optional_converter.h"
@@ -1466,12 +1466,13 @@ void WebContents::OnRequestPointerLock(content::WebContents* web_contents,
}
void WebContents::RequestPointerLock(content::WebContents* web_contents,
content::RenderFrameHost* frame,
bool user_gesture,
bool last_unlocked_by_target) {
auto* permission_helper =
WebContentsPermissionHelper::FromWebContents(web_contents);
permission_helper->RequestPointerLockPermission(
user_gesture, last_unlocked_by_target,
frame, user_gesture, last_unlocked_by_target,
base::BindOnce(&WebContents::OnRequestPointerLock,
base::Unretained(this)));
}
@@ -1493,11 +1494,12 @@ void WebContents::OnRequestKeyboardLock(content::WebContents* web_contents,
}
void WebContents::RequestKeyboardLock(content::WebContents* web_contents,
content::RenderFrameHost* frame,
bool esc_key_locked) {
auto* permission_helper =
WebContentsPermissionHelper::FromWebContents(web_contents);
permission_helper->RequestKeyboardLockPermission(
esc_key_locked, base::BindOnce(&WebContents::OnRequestKeyboardLock,
frame, esc_key_locked, base::BindOnce(&WebContents::OnRequestKeyboardLock,
base::Unretained(this)));
}
@@ -1515,7 +1517,7 @@ bool WebContents::CheckMediaAccessPermission(
content::WebContents::FromRenderFrameHost(render_frame_host);
auto* permission_helper =
WebContentsPermissionHelper::FromWebContents(web_contents);
return permission_helper->CheckMediaAccessPermission(security_origin, type);
return permission_helper->CheckMediaAccessPermission(render_frame_host, security_origin, type);
}
void WebContents::RequestMediaAccessPermission(

View File

@@ -614,6 +614,7 @@ class WebContents : public ExclusiveAccessContext,
bool last_unlocked_by_target,
bool allowed);
void RequestPointerLock(content::WebContents* web_contents,
content::RenderFrameHost* requesting_frame,
bool user_gesture,
bool last_unlocked_by_target) override;
void LostPointerLock() override;
@@ -621,6 +622,7 @@ class WebContents : public ExclusiveAccessContext,
bool esc_key_locked,
bool allowed);
void RequestKeyboardLock(content::WebContents* web_contents,
content::RenderFrameHost* requesting_frame,
bool esc_key_locked) override;
void CancelKeyboardLockRequest(content::WebContents* web_contents) override;
bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host,

View File

@@ -23,7 +23,7 @@
#include "shell/browser/javascript_environment.h"
#include "shell/common/gin_converters/blink_converter.h"
#include "shell/common/gin_converters/frame_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/error_thrower.h"

View File

@@ -29,7 +29,7 @@
#include "shell/browser/javascript_environment.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/frame_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_converters/std_converter.h"
#include "shell/common/gin_converters/value_converter.h"

View File

@@ -107,7 +107,7 @@ ElectronPermissionManager::ElectronPermissionManager() = default;
ElectronPermissionManager::~ElectronPermissionManager() = default;
void ElectronPermissionManager::SetPermissionRequestHandler(
const RequestHandler& handler) {
const OnRequestHandler& handler) {
if (handler.is_null() && !pending_requests_.IsEmpty()) {
for (PendingRequestsMap::iterator iter(&pending_requests_); !iter.IsAtEnd();
iter.Advance()) {
@@ -117,12 +117,12 @@ void ElectronPermissionManager::SetPermissionRequestHandler(
}
pending_requests_.Clear();
}
request_handler_ = handler;
on_request_handler_ = handler;
}
void ElectronPermissionManager::SetPermissionCheckHandler(
const CheckHandler& handler) {
check_handler_ = handler;
void ElectronPermissionManager::SetPermissionIsGrantedHandler(
const IsGrantedHandler& handler) {
is_granted_handler_ = handler;
}
void ElectronPermissionManager::SetDevicePermissionHandler(
@@ -143,7 +143,7 @@ void ElectronPermissionManager::SetBluetoothPairingHandler(
void ElectronPermissionManager::RequestPermissionWithDetails(
blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const url::Origin& effective_origin,
bool user_gesture,
base::Value::Dict details,
StatusCallback response_callback) {
@@ -155,7 +155,7 @@ void ElectronPermissionManager::RequestPermissionWithDetails(
RequestPermissionsWithDetails(
render_frame_host,
content::PermissionRequestDescription(permission, user_gesture,
requesting_origin),
effective_origin.GetURL()),
std::move(details),
base::BindOnce(PermissionRequestResponseCallbackWrapper,
std::move(response_callback)));
@@ -187,7 +187,7 @@ void ElectronPermissionManager::RequestPermissionsWithDetails(
return;
}
if (request_handler_.is_null()) {
if (on_request_handler_.is_null()) {
std::vector<blink::mojom::PermissionStatus> statuses;
for (auto& permission : permissions) {
if (permission == blink::PermissionType::MIDI_SYSEX) {
@@ -205,8 +205,6 @@ void ElectronPermissionManager::RequestPermissionsWithDetails(
return;
}
auto* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
int request_id = pending_requests_.Add(std::make_unique<PendingRequest>(
render_frame_host, permissions, std::move(response_callback)));
@@ -214,12 +212,15 @@ void ElectronPermissionManager::RequestPermissionsWithDetails(
details.Set("isMainFrame", render_frame_host->GetParent() == nullptr);
base::Value dict_value(std::move(details));
url::Origin origin =
url::Origin::Create(request_description.requesting_origin);
for (size_t i = 0; i < permissions.size(); ++i) {
auto permission = permissions[i];
const auto callback =
base::BindRepeating(&ElectronPermissionManager::OnPermissionResponse,
base::Unretained(this), request_id, i);
request_handler_.Run(web_contents, permission, callback, dict_value);
on_request_handler_.Run(nullptr, permission, callback, dict_value, origin);
}
}
@@ -240,7 +241,7 @@ void ElectronPermissionManager::OnPermissionResponse(
void ElectronPermissionManager::ResetPermission(
blink::PermissionType permission,
const GURL& requesting_origin,
const GURL& effective_origin,
const GURL& embedding_origin) {}
void ElectronPermissionManager::RequestPermissionsFromCurrentDocument(
@@ -261,23 +262,21 @@ void ElectronPermissionManager::RequestPermissionsFromCurrentDocument(
blink::mojom::PermissionStatus ElectronPermissionManager::GetPermissionStatus(
blink::PermissionType permission,
const GURL& requesting_origin,
const GURL& effective_origin,
const GURL& embedding_origin) {
base::Value::Dict details;
details.Set("embeddingOrigin", embedding_origin.spec());
bool granted = CheckPermissionWithDetails(permission, {}, requesting_origin,
std::move(details));
return granted ? blink::mojom::PermissionStatus::GRANTED
: blink::mojom::PermissionStatus::DENIED;
return CheckPermissionWithDetails(
permission, url::Origin::Create(effective_origin), std::move(details));
}
content::PermissionResult
ElectronPermissionManager::GetPermissionResultForOriginWithoutContext(
blink::PermissionType permission,
const url::Origin& requesting_origin,
const url::Origin& effective_origin,
const url::Origin& embedding_origin) {
blink::mojom::PermissionStatus status = GetPermissionStatus(
permission, requesting_origin.GetURL(), embedding_origin.GetURL());
permission, effective_origin.GetURL(), embedding_origin.GetURL());
return content::PermissionResult(
status, content::PermissionStatusSource::UNSPECIFIED);
}
@@ -294,24 +293,14 @@ void ElectronPermissionManager::CheckBluetoothDevicePair(
}
}
bool ElectronPermissionManager::CheckPermissionWithDetails(
blink::mojom::PermissionStatus
ElectronPermissionManager::CheckPermissionWithDetails(
blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const url::Origin& effective_origin,
base::Value::Dict details) const {
if (check_handler_.is_null())
return true;
if (is_granted_handler_.is_null())
return blink::mojom::PermissionStatus::GRANTED;
auto* web_contents =
render_frame_host
? content::WebContents::FromRenderFrameHost(render_frame_host)
: nullptr;
if (render_frame_host) {
details.Set("requestingUrl",
render_frame_host->GetLastCommittedURL().spec());
}
details.Set("isMainFrame",
render_frame_host && render_frame_host->GetParent() == nullptr);
switch (permission) {
case blink::PermissionType::AUDIO_CAPTURE:
details.Set("mediaType", "audio");
@@ -322,8 +311,25 @@ bool ElectronPermissionManager::CheckPermissionWithDetails(
default:
break;
}
return check_handler_.Run(web_contents, permission, requesting_origin,
base::Value(std::move(details)));
return is_granted_handler_
.Run(nullptr, permission, effective_origin,
base::Value(std::move(details)))
.value_or(blink::mojom::PermissionStatus::DENIED);
}
blink::mojom::PermissionStatus
ElectronPermissionManager::CheckPermissionWithDetailsAndFrame(
blink::PermissionType permission,
const url::Origin& effective_origin,
content::RenderFrameHost* requesting_frame,
base::Value::Dict details) const {
details.Set("isMainFrame", requesting_frame->GetParent() == nullptr);
details.Set("embeddingOrigin",
content::PermissionUtil::GetLastCommittedOriginAsURL(
requesting_frame->GetMainFrame())
.spec());
return CheckPermissionWithDetails(permission, effective_origin,
std::move(details));
}
bool ElectronPermissionManager::CheckDevicePermission(
@@ -384,15 +390,9 @@ ElectronPermissionManager::GetPermissionStatusForCurrentDocument(
return blink::mojom::PermissionStatus::DENIED;
base::Value::Dict details;
details.Set("embeddingOrigin",
content::PermissionUtil::GetLastCommittedOriginAsURL(
render_frame_host->GetMainFrame())
.spec());
bool granted = CheckPermissionWithDetails(
permission, render_frame_host,
render_frame_host->GetLastCommittedOrigin().GetURL(), std::move(details));
return granted ? blink::mojom::PermissionStatus::GRANTED
: blink::mojom::PermissionStatus::DENIED;
return CheckPermissionWithDetailsAndFrame(
permission, render_frame_host->GetLastCommittedOrigin(),
render_frame_host, std::move(details));
}
blink::mojom::PermissionStatus
@@ -411,9 +411,9 @@ ElectronPermissionManager::GetPermissionStatusForEmbeddedRequester(
if (render_frame_host->IsNestedWithinFencedFrame())
return blink::mojom::PermissionStatus::DENIED;
return GetPermissionStatus(
permission, overridden_origin.GetURL(),
render_frame_host->GetLastCommittedOrigin().GetURL());
base::Value::Dict details;
return CheckPermissionWithDetailsAndFrame(
permission, overridden_origin, render_frame_host, std::move(details));
}
ElectronPermissionManager::SubscriptionId
@@ -421,7 +421,7 @@ ElectronPermissionManager::SubscribeToPermissionStatusChange(
blink::PermissionType permission,
content::RenderProcessHost* render_process_host,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& effective_origin,
bool should_include_device_status,
base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback) {
return SubscriptionId();

View File

@@ -48,14 +48,15 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate {
using StatusesCallback = base::OnceCallback<void(
const std::vector<blink::mojom::PermissionStatus>&)>;
using PairCallback = base::OnceCallback<void(base::Value::Dict)>;
using RequestHandler = base::RepeatingCallback<void(content::WebContents*,
using OnRequestHandler = base::RepeatingCallback<void(content::WebContents*,
blink::PermissionType,
StatusCallback,
const base::Value&)>;
using CheckHandler =
base::RepeatingCallback<bool(content::WebContents*,
const base::Value&,
const url::Origin& effective_origin)>;
using IsGrantedHandler =
base::RepeatingCallback<std::optional<blink::mojom::PermissionStatus>(content::WebContents*,
blink::PermissionType,
const GURL& requesting_origin,
const url::Origin& effective_origin,
const base::Value&)>;
using DeviceCheckHandler =
@@ -67,46 +68,52 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate {
using BluetoothPairingHandler =
base::RepeatingCallback<void(gin_helper::Dictionary, PairCallback)>;
void RequestPermissionWithDetails(blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
bool user_gesture,
base::Value::Dict details,
StatusCallback response_callback);
// Handler to dispatch permission requests in JS.
void SetPermissionRequestHandler(const RequestHandler& handler);
void SetPermissionCheckHandler(const CheckHandler& handler);
// Handlers to dispatch permission requests in JS.
void SetPermissionRequestHandler(const OnRequestHandler& handler);
void SetPermissionIsGrantedHandler(const IsGrantedHandler& handler);
void SetDevicePermissionHandler(const DeviceCheckHandler& handler);
void SetProtectedUSBHandler(const ProtectedUSBHandler& handler);
void SetBluetoothPairingHandler(const BluetoothPairingHandler& handler);
// Bluetooth permissions, maps to session.setBluetoothPairingHandler()
void CheckBluetoothDevicePair(gin_helper::Dictionary details,
PairCallback pair_callback) const;
bool CheckPermissionWithDetails(blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
base::Value::Dict details) const;
// Device permissions, maps to session.setDevicePermissionHandler()
bool CheckDevicePermission(blink::PermissionType permission,
const url::Origin& origin,
const base::Value& object,
ElectronBrowserContext* browser_context) const;
void GrantDevicePermission(blink::PermissionType permission,
const url::Origin& origin,
const base::Value& object,
ElectronBrowserContext* browser_context) const;
void RevokeDevicePermission(blink::PermissionType permission,
const url::Origin& origin,
const base::Value& object,
ElectronBrowserContext* browser_context) const;
// USB permissions, maps to session.setUSBProtectedClassesHandler()
USBProtectedClasses CheckProtectedUSBClasses(
const USBProtectedClasses& classes) const;
// Permission granted checks, maps to session.setPermissionHandlers({ isGranted })
blink::mojom::PermissionStatus CheckPermissionWithDetails(blink::PermissionType permission,
const url::Origin& effective_origin,
base::Value::Dict details) const;
blink::mojom::PermissionStatus CheckPermissionWithDetailsAndFrame(blink::PermissionType permission,
const url::Origin& effective_origin,
content::RenderFrameHost* requesting_frame,
base::Value::Dict details) const;
// Permission requests, maps to session.setPermissionHandlers({ onRequest })
void RequestPermissionWithDetails(blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
const url::Origin& effective_origin,
bool user_gesture,
base::Value::Dict details,
StatusCallback response_callback);
protected:
void OnPermissionResponse(int request_id,
int permission_id,
@@ -118,11 +125,11 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate {
const content::PermissionRequestDescription& request_description,
StatusesCallback callback) override;
void ResetPermission(blink::PermissionType permission,
const GURL& requesting_origin,
const GURL& effective_origin,
const GURL& embedding_origin) override;
blink::mojom::PermissionStatus GetPermissionStatus(
blink::PermissionType permission,
const GURL& requesting_origin,
const GURL& effective_origin,
const GURL& embedding_origin) override;
void RequestPermissionsFromCurrentDocument(
content::RenderFrameHost* render_frame_host,
@@ -132,7 +139,7 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate {
override;
content::PermissionResult GetPermissionResultForOriginWithoutContext(
blink::PermissionType permission,
const url::Origin& requesting_origin,
const url::Origin& effective_origin,
const url::Origin& embedding_origin) override;
blink::mojom::PermissionStatus GetPermissionStatusForCurrentDocument(
blink::PermissionType permission,
@@ -145,12 +152,12 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate {
blink::mojom::PermissionStatus GetPermissionStatusForEmbeddedRequester(
blink::PermissionType permission,
content::RenderFrameHost* render_frame_host,
const url::Origin& requesting_origin) override;
const url::Origin& effective_origin) override;
SubscriptionId SubscribeToPermissionStatusChange(
blink::PermissionType permission,
content::RenderProcessHost* render_process_host,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& effective_origin,
bool should_include_device_status,
base::RepeatingCallback<void(blink::mojom::PermissionStatus)> callback)
override;
@@ -166,8 +173,8 @@ class ElectronPermissionManager : public content::PermissionControllerDelegate {
base::Value::Dict details,
StatusesCallback callback);
RequestHandler request_handler_;
CheckHandler check_handler_;
OnRequestHandler on_request_handler_;
IsGrantedHandler is_granted_handler_;
DeviceCheckHandler device_permission_handler_;
ProtectedUSBHandler protected_usb_handler_;
BluetoothPairingHandler bluetooth_pairing_handler_;

View File

@@ -295,8 +295,8 @@ class FileSystemAccessPermissionContext::PermissionGrantImpl
return;
}
auto origin = rfh->GetLastCommittedOrigin().GetURL();
if (url::Origin::Create(origin) != origin_) {
auto origin = rfh->GetLastCommittedOrigin();
if (origin != origin_) {
// Third party iframes are not allowed to request more permissions.
std::move(callback).Run(PermissionRequestOutcome::kThirdPartyContext);
return;

View File

@@ -136,17 +136,10 @@ std::unique_ptr<content::HidChooser> ElectronHidDelegate::RunChooser(
bool ElectronHidDelegate::CanRequestDevicePermission(
content::BrowserContext* browser_context,
const url::Origin& origin) {
if (!browser_context)
return false;
base::Value::Dict details;
details.Set("securityOrigin", origin.GetURL().spec());
auto* permission_manager = static_cast<ElectronPermissionManager*>(
browser_context->GetPermissionControllerDelegate());
return permission_manager->CheckPermissionWithDetails(
static_cast<blink::PermissionType>(
WebContentsPermissionHelper::PermissionType::HID),
nullptr, origin.GetURL(), std::move(details));
// Returning true here does not grant the permission, rather it indicates
// that the given session is _allowed_ to _request_ the permission.
// This will then later hit the session permission request handler.
return true;
}
bool ElectronHidDelegate::HasDevicePermission(

View File

@@ -13,7 +13,7 @@
#include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/javascript_environment.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_converters/value_converter.h"

View File

@@ -30,7 +30,7 @@
#include "shell/browser/net/url_pipe_loader.h"
#include "shell/common/electron_constants.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"

View File

@@ -12,7 +12,7 @@
#include "content/public/browser/render_process_host.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "shell/browser/api/electron_api_session.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "v8/include/v8.h"
NetworkHintsHandlerImpl::NetworkHintsHandlerImpl(

View File

@@ -48,11 +48,10 @@ std::unique_ptr<content::SerialChooser> ElectronSerialDelegate::RunChooser(
bool ElectronSerialDelegate::CanRequestPortPermission(
content::RenderFrameHost* frame) {
auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
auto* permission_helper =
WebContentsPermissionHelper::FromWebContents(web_contents);
return permission_helper->CheckSerialAccessPermission(
web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin());
// Returning true here does not grant the permission, rather it indicates
// that the given session is _allowed_ to _request_ the permission.
// This will then later hit the session permission request handler.
return true;
}
bool ElectronSerialDelegate::HasPortPermission(

View File

@@ -175,17 +175,10 @@ std::unique_ptr<content::UsbChooser> ElectronUsbDelegate::RunChooser(
bool ElectronUsbDelegate::CanRequestDevicePermission(
content::BrowserContext* browser_context,
const url::Origin& origin) {
if (!browser_context)
return false;
base::Value::Dict details;
details.Set("securityOrigin", origin.GetURL().spec());
auto* permission_manager = static_cast<ElectronPermissionManager*>(
browser_context->GetPermissionControllerDelegate());
return permission_manager->CheckPermissionWithDetails(
static_cast<blink::PermissionType>(
WebContentsPermissionHelper::PermissionType::USB),
nullptr, origin.GetURL(), std::move(details));
// Returning true here does not grant the permission, rather it indicates
// that the given session is _allowed_ to _request_ the permission.
// This will then later hit the session permission request handler.
return true;
}
void ElectronUsbDelegate::RevokeDevicePermissionWebInitiated(

View File

@@ -206,34 +206,24 @@ WebContentsPermissionHelper::~WebContentsPermissionHelper() = default;
void WebContentsPermissionHelper::RequestPermission(
content::RenderFrameHost* requesting_frame,
const url::Origin& origin,
blink::PermissionType permission,
base::OnceCallback<void(bool)> callback,
bool user_gesture,
base::Value::Dict details) {
auto* permission_manager = static_cast<ElectronPermissionManager*>(
web_contents_->GetBrowserContext()->GetPermissionControllerDelegate());
auto origin = web_contents_->GetLastCommittedURL();
permission_manager->RequestPermissionWithDetails(
permission, requesting_frame, origin, false, std::move(details),
base::BindOnce(&OnPermissionResponse, std::move(callback)));
}
bool WebContentsPermissionHelper::CheckPermission(
blink::PermissionType permission,
base::Value::Dict details) const {
auto* rfh = web_contents_->GetPrimaryMainFrame();
auto* permission_manager = static_cast<ElectronPermissionManager*>(
web_contents_->GetBrowserContext()->GetPermissionControllerDelegate());
auto origin = web_contents_->GetLastCommittedURL();
return permission_manager->CheckPermissionWithDetails(permission, rfh, origin,
std::move(details));
}
void WebContentsPermissionHelper::RequestFullscreenPermission(
content::RenderFrameHost* requesting_frame,
base::OnceCallback<void(bool)> callback) {
RequestPermission(
requesting_frame,
requesting_frame->GetLastCommittedOrigin(),
static_cast<blink::PermissionType>(PermissionType::FULLSCREEN),
std::move(callback));
}
@@ -259,8 +249,9 @@ void WebContentsPermissionHelper::RequestMediaAccessPermission(
// The permission type doesn't matter here, AUDIO_CAPTURE/VIDEO_CAPTURE
// are presented as same type in content_converter.h.
RequestPermission(content::RenderFrameHost::FromID(request.render_process_id,
request.render_frame_id),
auto* frame = content::RenderFrameHost::FromID(request.render_process_id,
request.render_frame_id);
RequestPermission(frame, request.url_origin,
blink::PermissionType::AUDIO_CAPTURE, std::move(callback),
false, std::move(details));
}
@@ -268,16 +259,18 @@ void WebContentsPermissionHelper::RequestMediaAccessPermission(
void WebContentsPermissionHelper::RequestWebNotificationPermission(
content::RenderFrameHost* requesting_frame,
base::OnceCallback<void(bool)> callback) {
RequestPermission(requesting_frame, blink::PermissionType::NOTIFICATIONS,
RequestPermission(requesting_frame, requesting_frame->GetLastCommittedOrigin(), blink::PermissionType::NOTIFICATIONS,
std::move(callback));
}
void WebContentsPermissionHelper::RequestPointerLockPermission(
content::RenderFrameHost* frame,
bool user_gesture,
bool last_unlocked_by_target,
base::OnceCallback<void(content::WebContents*, bool, bool, bool)>
callback) {
RequestPermission(web_contents_->GetPrimaryMainFrame(),
RequestPermission(frame,
frame->GetLastCommittedOrigin(),
blink::PermissionType::POINTER_LOCK,
base::BindOnce(std::move(callback), web_contents_,
user_gesture, last_unlocked_by_target),
@@ -285,10 +278,12 @@ void WebContentsPermissionHelper::RequestPointerLockPermission(
}
void WebContentsPermissionHelper::RequestKeyboardLockPermission(
content::RenderFrameHost* requesting_frame,
bool esc_key_locked,
base::OnceCallback<void(content::WebContents*, bool, bool)> callback) {
RequestPermission(
web_contents_->GetPrimaryMainFrame(),
requesting_frame,
requesting_frame->GetLastCommittedOrigin(),
blink::PermissionType::KEYBOARD_LOCK,
base::BindOnce(std::move(callback), web_contents_, esc_key_locked));
}
@@ -302,29 +297,27 @@ void WebContentsPermissionHelper::RequestOpenExternalPermission(
details.Set("externalURL", url.spec());
RequestPermission(
requesting_frame,
requesting_frame->GetLastCommittedOrigin(),
static_cast<blink::PermissionType>(PermissionType::OPEN_EXTERNAL),
std::move(callback), user_gesture, std::move(details));
}
bool WebContentsPermissionHelper::CheckMediaAccessPermission(
content::RenderFrameHost* requesting_frame,
const url::Origin& security_origin,
blink::mojom::MediaStreamType type) const {
base::Value::Dict details;
// Deprecated
details.Set("securityOrigin", security_origin.GetURL().spec());
details.Set("mediaType", MediaStreamTypeToString(type));
auto blink_type = type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE
? blink::PermissionType::AUDIO_CAPTURE
: blink::PermissionType::VIDEO_CAPTURE;
return CheckPermission(blink_type, std::move(details));
}
bool WebContentsPermissionHelper::CheckSerialAccessPermission(
const url::Origin& embedding_origin) const {
base::Value::Dict details;
details.Set("securityOrigin", embedding_origin.GetURL().spec());
return CheckPermission(
static_cast<blink::PermissionType>(PermissionType::SERIAL),
std::move(details));
auto* permission_manager = static_cast<ElectronPermissionManager*>(
web_contents_->GetBrowserContext()->GetPermissionControllerDelegate());
return permission_manager->CheckPermissionWithDetailsAndFrame(blink_type, security_origin, requesting_frame,
std::move(details)) != blink::mojom::PermissionStatus::DENIED;
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsPermissionHelper);

View File

@@ -41,11 +41,13 @@ class WebContentsPermissionHelper
void RequestMediaAccessPermission(const content::MediaStreamRequest& request,
content::MediaResponseCallback callback);
void RequestPointerLockPermission(
content::RenderFrameHost* frame,
bool user_gesture,
bool last_unlocked_by_target,
base::OnceCallback<void(content::WebContents*, bool, bool, bool)>
callback);
void RequestKeyboardLockPermission(
content::RenderFrameHost* requesting_frame,
bool esc_key_locked,
base::OnceCallback<void(content::WebContents*, bool, bool)> callback);
void RequestWebNotificationPermission(
@@ -57,23 +59,21 @@ class WebContentsPermissionHelper
const GURL& url);
// Synchronous Checks
bool CheckMediaAccessPermission(const url::Origin& security_origin,
bool CheckMediaAccessPermission(content::RenderFrameHost* requesting_frame,
const url::Origin& security_origin,
blink::mojom::MediaStreamType type) const;
bool CheckSerialAccessPermission(const url::Origin& embedding_origin) const;
private:
explicit WebContentsPermissionHelper(content::WebContents* web_contents);
friend class content::WebContentsUserData<WebContentsPermissionHelper>;
void RequestPermission(content::RenderFrameHost* requesting_frame,
const url::Origin& origin,
blink::PermissionType permission,
base::OnceCallback<void(bool)> callback,
bool user_gesture = false,
base::Value::Dict details = {});
bool CheckPermission(blink::PermissionType permission,
base::Value::Dict details) const;
// TODO(clavin): refactor to use the WebContents provided by the
// WebContentsUserData base class instead of storing a duplicate ref
raw_ptr<content::WebContents> web_contents_;

View File

@@ -23,7 +23,7 @@
#include "shell/common/asar/asar_util.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/error_thrower.h"

View File

@@ -13,7 +13,7 @@
#include "shell/browser/net/resolve_host_function.h"
#include "shell/common/api/electron_api_url_loader.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/error_thrower.h"

View File

@@ -7,7 +7,7 @@
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/guid_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/error_thrower.h"
#include "shell/common/gin_helper/promise.h"

View File

@@ -40,7 +40,7 @@
#include "shell/browser/net/proxying_url_loader_factory.h"
#include "shell/browser/protocol_registry.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"

View File

@@ -8,7 +8,7 @@
#include "base/run_loop.h"
#include "electron/buildflags/buildflags.h"
#include "shell/common/gin_converters/content_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/std_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/node_includes.h"

View File

@@ -16,7 +16,7 @@
#include "gin/converter.h"
#include "gin/data_object_builder.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/std_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"

View File

@@ -17,7 +17,7 @@
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/frame_converter.h"
#include "shell/common/gin_converters/gfx_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_helper/dictionary.h"
#include "third_party/blink/public/common/context_menu_data/untrustworthy_context_menu_params.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
@@ -127,6 +127,20 @@ bool Converter<blink::mojom::PermissionStatus>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
blink::mojom::PermissionStatus* out) {
std::string str_result;
if (ConvertFromV8(isolate, val, &str_result)) {
if (str_result == "granted") {
*out = blink::mojom::PermissionStatus::GRANTED;
} else if (str_result == "denied") {
*out = blink::mojom::PermissionStatus::DENIED;
} else if (str_result == "ask") {
*out = blink::mojom::PermissionStatus::ASK;
} else {
return false;
}
return true;
}
bool result;
if (!ConvertFromV8(isolate, val, &result))
return false;

View File

@@ -7,7 +7,7 @@
#include "extensions/common/extension.h"
#include "gin/dictionary.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/value_converter.h"
namespace gin {

View File

@@ -8,7 +8,7 @@
#include "content/public/browser/render_frame_host.h"
#include "gin/data_object_builder.h"
#include "shell/common/gin_converters/frame_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
namespace gin {

View File

@@ -28,7 +28,7 @@
#include "services/network/public/cpp/resource_request_body.h"
#include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h"
#include "shell/browser/api/electron_api_data_pipe_holder.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/url_converters.h"
#include "shell/common/gin_converters/std_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/promise.h"

View File

@@ -9,6 +9,7 @@
#include "gin/converter.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace gin {
@@ -30,6 +31,13 @@ struct Converter<GURL> {
}
};
template <>
struct Converter<url::Origin> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const url::Origin& val) {
return ConvertToV8(isolate, val.Serialize());
}
};
} // namespace gin
#endif // ELECTRON_SHELL_COMMON_GIN_CONVERTERS_GURL_CONVERTER_H_

View File

@@ -1358,6 +1358,320 @@ describe('session module', () => {
});
});
describe('ses.setPermissionHandlers(handlers)', () => {
describe('onRequest', () => {
afterEach(closeAllWindows);
// These tests are done on an http server because navigator.userAgentData
// requires a secure context.
let server: http.Server;
let serverUrl: string;
before(async () => {
server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
res.end('');
});
serverUrl = (await listen(server)).url;
});
after(() => {
server.close();
});
it('cancels any pending requests when cleared', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
partition: 'very-temp-permission-handler',
nodeIntegration: true,
contextIsolation: false
}
});
const ses = w.webContents.session;
ses.setPermissionHandlers({
onRequest: () => ses.setPermissionHandlers(null) as any,
isGranted: () => ({ status: 'ask' })
});
ses.protocol.interceptStringProtocol('https', (req, cb) => {
cb(`<html><script>(${remote})()</script></html>`);
});
const result = once(require('electron').ipcMain, 'message');
function remote () {
(navigator as any).requestMIDIAccess({ sysex: true }).then(() => {}, (err: any) => {
require('electron').ipcRenderer.send('message', err.name);
});
}
w.loadURL('https://myfakesite');
const [, name] = await result;
expect(name).to.deep.equal('SecurityError');
});
it('cancels any requests when resolved with invalid status', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
partition: 'very-temp-permission-handler',
nodeIntegration: true,
contextIsolation: false
}
});
const ses = w.webContents.session;
ses.setPermissionHandlers({
onRequest: async () => ({ status: 'nope?' as any }),
isGranted: () => ({ status: 'ask' })
});
ses.protocol.interceptStringProtocol('https', (req, cb) => {
cb(`<html><script>(${remote})()</script></html>`);
});
const result = once(require('electron').ipcMain, 'message');
function remote () {
(navigator as any).requestMIDIAccess({ sysex: true }).then(() => {}, (err: any) => {
require('electron').ipcRenderer.send('message', err.name);
});
}
w.loadURL('https://myfakesite');
const [, name] = await result;
expect(name).to.deep.equal('SecurityError');
});
it('successfully resolves when calling legacy getUserMedia', async () => {
const ses = session.fromPartition('' + Math.random());
ses.setPermissionHandlers(
{
onRequest: async () => ({ status: 'granted' }),
isGranted: () => ({ status: 'ask' })
}
);
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
new Promise((resolve, reject) => navigator.getUserMedia({
video: true,
audio: true,
}, x => resolve({ok: x instanceof MediaStream}), e => reject({ok: false, message: e.message})))
`);
expect(ok).to.be.true(message);
});
it('successfully rejects when calling legacy getUserMedia', async () => {
const ses = session.fromPartition('' + Math.random());
ses.setPermissionHandlers(
{
onRequest: async () => ({ status: 'denied' }),
isGranted: () => ({ status: 'ask' })
}
);
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
await expect(w.webContents.executeJavaScript(`
new Promise((resolve, reject) => navigator.getUserMedia({
video: true,
audio: true,
}, x => resolve({ok: x instanceof MediaStream}), e => reject({ok: false, message: e.message})))
`)).to.eventually.be.rejectedWith('Permission denied');
});
});
describe('isGranted', () => {
afterEach(closeAllWindows);
it('provides valid effectiveOrigin for main frame requests', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
partition: 'very-temp-permission-handler'
}
});
const ses = w.webContents.session;
const loadUrl = 'https://myfakesite/oh-hi';
let effectiveOrigin: string = '';
ses.protocol.interceptStringProtocol('https', (req, cb) => {
cb('<html><script>console.log(\'test\');</script></html>');
});
ses.setPermissionHandlers(
{
onRequest: async () => ({ status: 'denied' }),
isGranted: (permission, origin) => {
effectiveOrigin = origin;
return { status: 'granted' };
}
}
);
const readClipboardPermission: any = () => {
return w.webContents.executeJavaScript(`
navigator.permissions.query({name: 'clipboard-read'})
.then(permission => permission.state).catch(err => err.message);
`, true);
};
await w.loadURL(loadUrl);
const state = await readClipboardPermission();
expect(state).to.equal('granted');
expect(effectiveOrigin).to.equal('https://myfakesite');
});
it('provides valid effectiveOrigin for cross origin subframes', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
partition: 'very-temp-permission-handler'
}
});
const ses = w.webContents.session;
const loadUrl = 'https://myfakesite/foo';
let effectiveOrigin: string = '';
let handlerDetails: Electron.PermissionCheckDetails;
ses.protocol.interceptStringProtocol('https', (req, cb) => {
cb('<html><script>console.log(\'test\');</script></html>');
});
ses.setPermissionHandlers(
{
onRequest: async () => ({ status: 'denied' }),
isGranted: (permission, origin, details) => {
effectiveOrigin = origin;
handlerDetails = details;
return { status: 'granted' };
}
}
);
const readClipboardPermission: any = (frame: WebFrameMain) => {
return frame.executeJavaScript(`
navigator.permissions.query({name: 'clipboard-read'})
.then(permission => permission.state).catch(err => err.message);
`, true);
};
await w.loadFile(path.join(fixtures, 'api', 'blank.html'));
w.webContents.executeJavaScript(`
var iframe = document.createElement('iframe');
iframe.src = '${loadUrl}';
iframe.allow = 'clipboard-read';
document.body.appendChild(iframe);
null;
`);
const [,, frameProcessId, frameRoutingId] = await once(w.webContents, 'did-frame-finish-load');
const state = await readClipboardPermission(webFrameMain.fromId(frameProcessId, frameRoutingId));
expect(state).to.equal('granted');
expect(effectiveOrigin).to.equal('https://myfakesite');
expect(handlerDetails!.isMainFrame).to.be.false();
expect(handlerDetails!.embeddingOrigin).to.equal('file:///');
});
it('allows providing "default" / "ask" value', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
partition: 'very-temp-permission-handler'
}
});
const ses = w.webContents.session;
const loadUrl = 'https://myfakesite/oh-hi';
ses.protocol.interceptStringProtocol('https', (req, cb) => {
cb('<html><script>console.log(\'test\');</script></html>');
});
ses.setPermissionHandlers(
{
onRequest: async () => ({ status: 'denied' }),
isGranted: () => ({ status: 'ask' })
}
);
const readNotificationPermission = () => {
return w.webContents.executeJavaScript(`
Notification.permission
`, true);
};
await w.loadURL(loadUrl);
const state = await readNotificationPermission();
expect(state).to.equal('default');
});
it('allows providing "granted" value', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
partition: 'very-temp-permission-handler'
}
});
const ses = w.webContents.session;
const loadUrl = 'https://myfakesite/oh-hi';
ses.protocol.interceptStringProtocol('https', (req, cb) => {
cb('<html><script>console.log(\'test\');</script></html>');
});
ses.setPermissionHandlers(
{
onRequest: async () => ({ status: 'denied' }),
isGranted: () => ({ status: 'granted' })
}
);
const readNotificationPermission = () => {
return w.webContents.executeJavaScript(`
Notification.permission
`, true);
};
await w.loadURL(loadUrl);
const state = await readNotificationPermission();
expect(state).to.equal('granted');
});
it('allows providing "denied" value', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
partition: 'very-temp-permission-handler'
}
});
const ses = w.webContents.session;
const loadUrl = 'https://myfakesite/oh-hi';
ses.protocol.interceptStringProtocol('https', (req, cb) => {
cb('<html><script>console.log(\'test\');</script></html>');
});
ses.setPermissionHandlers(
{
onRequest: async () => ({ status: 'denied' }),
isGranted: () => ({ status: 'denied' })
}
);
const readNotificationPermission = () => {
return w.webContents.executeJavaScript(`
Notification.permission
`, true);
};
await w.loadURL(loadUrl);
const state = await readNotificationPermission();
expect(state).to.equal('denied');
});
});
});
describe('ses.setPermissionRequestHandler(handler)', () => {
afterEach(closeAllWindows);
// These tests are done on an http server because navigator.userAgentData
@@ -1448,7 +1762,7 @@ describe('session module', () => {
describe('ses.setPermissionCheckHandler(handler)', () => {
afterEach(closeAllWindows);
it('details provides requestingURL for mainFrame', async () => {
it.skip('details provides requestingURL for mainFrame', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
@@ -1484,7 +1798,7 @@ describe('session module', () => {
expect(handlerDetails!.requestingUrl).to.equal(loadUrl);
});
it('details provides requestingURL for cross origin subFrame', async () => {
it.skip('details provides requestingURL for cross origin subFrame', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {

View File

@@ -1243,15 +1243,24 @@ session.defaultSession.setCertificateVerifyProc((request, callback) => {
}
});
session.defaultSession.setPermissionRequestHandler(function (webContents, permission, callback) {
if (webContents.getURL() === 'github.com') {
if (permission === 'notifications') {
callback(false);
return;
session.defaultSession.setPermissionHandlers({
isGranted (permission, effectiveOrigin) {
if (effectiveOrigin === 'https://github.com') {
if (permission === 'notifications') {
return { status: 'granted' };
}
}
return { status: 'denied' };
},
onRequest: async function (permission, effectiveOrigin) {
if (effectiveOrigin === 'https://github.com') {
if (permission === 'notifications') {
return { status: 'denied' };
}
}
}
callback(true);
return { status: 'granted' };
}
});
// consider any url ending with `example.com`, `foobar.com`, `baz`

View File

@@ -67,6 +67,11 @@ declare namespace Electron {
}
}
interface Session {
_setPermissionRequestHandler: (...args: Parameters<Electron.Session['setPermissionRequestHandler']>) => void;
_setPermissionCheckHandler: (fn: ((...args: Parameters<NonNullable<Parameters<Electron.Session['setPermissionCheckHandler']>[0]>>) => Electron.PermissionCheckResult['status']) | null) => void;
}
interface TouchBar {
_removeFromWindow: (win: BaseWindow) => void;
}