mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
78 Commits
v27.0.0-be
...
v27.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1eb0d77b46 | ||
|
|
03acfc9bed | ||
|
|
8472c6a3d7 | ||
|
|
efeaabc62a | ||
|
|
99fb3d1922 | ||
|
|
fc2d896b8e | ||
|
|
206364dd8d | ||
|
|
f9702ca0da | ||
|
|
3148a2a874 | ||
|
|
272b371be8 | ||
|
|
bbc12f6688 | ||
|
|
a6a35f12b0 | ||
|
|
0beed97bb0 | ||
|
|
a22f6118dd | ||
|
|
4487ff5368 | ||
|
|
6b87170c24 | ||
|
|
1e24b130bb | ||
|
|
ef2745ff29 | ||
|
|
8f90a408c8 | ||
|
|
e43beaeec0 | ||
|
|
7ac2e49d2e | ||
|
|
301f3da15d | ||
|
|
416c6dcb39 | ||
|
|
79876b72b1 | ||
|
|
54d9958a49 | ||
|
|
db99feb7c3 | ||
|
|
64318df509 | ||
|
|
4ceb644723 | ||
|
|
ae212f41ab | ||
|
|
1f64b7869c | ||
|
|
f282bdac65 | ||
|
|
c0ad9e154f | ||
|
|
6304ea53ef | ||
|
|
4c1e53dfa6 | ||
|
|
96f9cc5157 | ||
|
|
8890ab5119 | ||
|
|
4bb80df378 | ||
|
|
3d10512399 | ||
|
|
30305e4f2b | ||
|
|
40c21e02da | ||
|
|
6de96fb83b | ||
|
|
cf0e974ce1 | ||
|
|
7e2073226a | ||
|
|
8f370cd2e3 | ||
|
|
82d4e9427f | ||
|
|
f0b742f2d6 | ||
|
|
2bc07b098b | ||
|
|
d95f28c194 | ||
|
|
22ea11175b | ||
|
|
5b44e82eb4 | ||
|
|
4c4a3e7594 | ||
|
|
bf7474d3f4 | ||
|
|
3f142ad039 | ||
|
|
10db23828e | ||
|
|
43ba54e61f | ||
|
|
3fdfcbb5dd | ||
|
|
9e8cba981a | ||
|
|
a85c6cf0e5 | ||
|
|
7e536dfd8e | ||
|
|
7d21d113d2 | ||
|
|
7ed33db12e | ||
|
|
66c4f9cb6f | ||
|
|
a00155dcfc | ||
|
|
3e4adb91c5 | ||
|
|
f63f02fbb2 | ||
|
|
a3a6e8ff26 | ||
|
|
e70a25d5a3 | ||
|
|
8e2cf54f29 | ||
|
|
a3614983de | ||
|
|
686adf9d15 | ||
|
|
25dccaa1b1 | ||
|
|
175cd9111e | ||
|
|
70236258cf | ||
|
|
d0d9011fc9 | ||
|
|
8423a014ed | ||
|
|
4ba1b219a5 | ||
|
|
e721b683bf | ||
|
|
271be9c8e4 |
@@ -660,6 +660,7 @@ step-nodejs-headers-build: &step-nodejs-headers-build
|
||||
step-electron-publish: &step-electron-publish
|
||||
run:
|
||||
name: Publish Electron Dist
|
||||
no_output_timeout: 30m
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
rm -rf src/out/Default/obj
|
||||
@@ -754,8 +755,8 @@ step-show-goma-stats: &step-show-goma-stats
|
||||
command: |
|
||||
set +e
|
||||
set +o pipefail
|
||||
$GOMA_DIR/goma_ctl.py stat
|
||||
$GOMA_DIR/diagnose_goma_log.py
|
||||
python3 $GOMA_DIR/goma_ctl.py stat
|
||||
python3 $GOMA_DIR/diagnose_goma_log.py
|
||||
true
|
||||
when: always
|
||||
background: true
|
||||
@@ -1074,7 +1075,7 @@ commands:
|
||||
condition:
|
||||
or:
|
||||
- equal: [<< parameters.could-be-aks >>, false]
|
||||
- equal: [<< pipeline.parameters.large-linux-executor >>, xlarge]
|
||||
- equal: [<< pipeline.parameters.large-linux-executor >>, 2xlarge]
|
||||
steps: << parameters.circle >>
|
||||
- when:
|
||||
condition:
|
||||
@@ -1914,7 +1915,8 @@ jobs:
|
||||
steps:
|
||||
- electron-build:
|
||||
persist: true
|
||||
checkout: true
|
||||
checkout: false
|
||||
checkout-and-assume-cache: true
|
||||
build-nonproprietary-ffmpeg: false
|
||||
artifact-key: 'linux-x64-asan'
|
||||
build-type: 'Linux'
|
||||
|
||||
29
BUILD.gn
29
BUILD.gn
@@ -103,14 +103,26 @@ branding = read_file("shell/app/BRANDING.json", "json")
|
||||
electron_project_name = branding.project_name
|
||||
electron_product_name = branding.product_name
|
||||
electron_mac_bundle_id = branding.mac_bundle_id
|
||||
electron_version = exec_script("script/print-version.py",
|
||||
[],
|
||||
"trim string",
|
||||
[
|
||||
".git/packed-refs",
|
||||
".git/HEAD",
|
||||
"script/lib/get-version.js",
|
||||
])
|
||||
|
||||
if (override_electron_version != "") {
|
||||
electron_version = override_electron_version
|
||||
} else {
|
||||
# When building from source code tarball there is no git tag available and
|
||||
# builders must explicitly pass override_electron_version in gn args.
|
||||
# This read_file call will assert if there is no git information, without it
|
||||
# gn will generate a malformed build configuration and ninja will get into
|
||||
# infinite loop.
|
||||
read_file(".git/packed-refs", "string")
|
||||
|
||||
# Set electron version from git tag.
|
||||
electron_version = exec_script("script/get-git-version.py",
|
||||
[],
|
||||
"trim string",
|
||||
[
|
||||
".git/packed-refs",
|
||||
".git/HEAD",
|
||||
])
|
||||
}
|
||||
|
||||
if (is_mas_build) {
|
||||
assert(is_mac,
|
||||
@@ -787,6 +799,7 @@ if (is_mac) {
|
||||
"buildflags",
|
||||
"shell/common/api:mojo",
|
||||
"//base",
|
||||
"//content/public/browser",
|
||||
"//skia",
|
||||
"//third_party/electron_node:node_lib",
|
||||
"//third_party/webrtc_overrides:webrtc_component",
|
||||
|
||||
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'118.0.5993.11',
|
||||
'118.0.5993.144',
|
||||
'node_version':
|
||||
'v18.17.1',
|
||||
'nan_version':
|
||||
|
||||
@@ -18,4 +18,9 @@ declare_args() {
|
||||
|
||||
# Enable Spellchecker support
|
||||
enable_builtin_spellchecker = true
|
||||
|
||||
# The version of Electron.
|
||||
# Packagers and vendor builders should set this in gn args to avoid running
|
||||
# the script that reads git tag.
|
||||
override_electron_version = ""
|
||||
}
|
||||
|
||||
@@ -202,6 +202,10 @@ static_library("chrome") {
|
||||
"//chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc",
|
||||
"//chrome/browser/ui/views/status_icons/status_icon_linux_dbus.h",
|
||||
]
|
||||
sources += [
|
||||
"//chrome/browser/ui/views/dark_mode_manager_linux.cc",
|
||||
"//chrome/browser/ui/views/dark_mode_manager_linux.h",
|
||||
]
|
||||
public_deps += [
|
||||
"//components/dbus/menu",
|
||||
"//components/dbus/thread_linux",
|
||||
@@ -319,6 +323,34 @@ static_library("chrome") {
|
||||
"//components/pdf/renderer",
|
||||
]
|
||||
}
|
||||
} else {
|
||||
# These are required by the webRequest module.
|
||||
sources += [
|
||||
"//extensions/browser/api/declarative_net_request/request_action.cc",
|
||||
"//extensions/browser/api/declarative_net_request/request_action.h",
|
||||
"//extensions/browser/api/web_request/form_data_parser.cc",
|
||||
"//extensions/browser/api/web_request/form_data_parser.h",
|
||||
"//extensions/browser/api/web_request/upload_data_presenter.cc",
|
||||
"//extensions/browser/api/web_request/upload_data_presenter.h",
|
||||
"//extensions/browser/api/web_request/web_request_api_constants.cc",
|
||||
"//extensions/browser/api/web_request/web_request_api_constants.h",
|
||||
"//extensions/browser/api/web_request/web_request_info.cc",
|
||||
"//extensions/browser/api/web_request/web_request_info.h",
|
||||
"//extensions/browser/api/web_request/web_request_resource_type.cc",
|
||||
"//extensions/browser/api/web_request/web_request_resource_type.h",
|
||||
"//extensions/browser/extension_api_frame_id_map.cc",
|
||||
"//extensions/browser/extension_api_frame_id_map.h",
|
||||
"//extensions/browser/extension_navigation_ui_data.cc",
|
||||
"//extensions/browser/extension_navigation_ui_data.h",
|
||||
"//extensions/browser/extensions_browser_client.cc",
|
||||
"//extensions/browser/extensions_browser_client.h",
|
||||
"//extensions/browser/guest_view/web_view/web_view_renderer_state.cc",
|
||||
"//extensions/browser/guest_view/web_view/web_view_renderer_state.h",
|
||||
]
|
||||
|
||||
public_deps += [
|
||||
"//extensions/browser/api/declarative_net_request/flat:extension_ruleset",
|
||||
]
|
||||
}
|
||||
|
||||
if (!is_mas_build) {
|
||||
@@ -353,6 +385,7 @@ if (is_mac) {
|
||||
|
||||
deps = [
|
||||
"//base",
|
||||
"//content/public/browser",
|
||||
"//skia",
|
||||
"//third_party/electron_node:node_lib",
|
||||
"//third_party/webrtc_overrides:webrtc_component",
|
||||
|
||||
@@ -1134,11 +1134,11 @@ indicates success while any other value indicates failure according to Chromium
|
||||
resolver will attempt to use the system's DNS settings to do DNS lookups
|
||||
itself. Enabled by default on macOS, disabled by default on Windows and
|
||||
Linux.
|
||||
* `secureDnsMode` string (optional) - Can be "off", "automatic" or "secure".
|
||||
Configures the DNS-over-HTTP mode. When "off", no DoH lookups will be
|
||||
performed. When "automatic", DoH lookups will be performed first if DoH is
|
||||
* `secureDnsMode` string (optional) - Can be 'off', 'automatic' or 'secure'.
|
||||
Configures the DNS-over-HTTP mode. When 'off', no DoH lookups will be
|
||||
performed. When 'automatic', DoH lookups will be performed first if DoH is
|
||||
available, and insecure DNS lookups will be performed as a fallback. When
|
||||
"secure", only DoH lookups will be performed. Defaults to "automatic".
|
||||
'secure', only DoH lookups will be performed. Defaults to 'automatic'.
|
||||
* `secureDnsServers` string[] (optional) - A list of DNS-over-HTTP
|
||||
server templates. See [RFC8484 § 3][] for details on the template format.
|
||||
Most servers support the POST method; the template for such servers is
|
||||
|
||||
@@ -505,6 +505,10 @@ events.
|
||||
|
||||
A `Integer` property representing the unique ID of the window. Each ID is unique among all `BrowserWindow` instances of the entire Electron application.
|
||||
|
||||
#### `win.tabbingIdentifier` _macOS_ _Readonly_
|
||||
|
||||
A `string` (optional) property that is equal to the `tabbingIdentifier` passed to the `BrowserWindow` constructor or `undefined` if none was set.
|
||||
|
||||
#### `win.autoHideMenuBar`
|
||||
|
||||
A `boolean` property that determines whether the window menu bar should hide itself automatically. Once set, the menu bar will only show when users press the single `Alt` key.
|
||||
|
||||
@@ -85,6 +85,8 @@ Emitted when the notification is closed by manual intervention from the user.
|
||||
This event is not guaranteed to be emitted in all cases where the notification
|
||||
is closed.
|
||||
|
||||
On Windows, the `close` event can be emitted in one of three ways: programmatic dismissal with `notification.close()`, by the user closing the notification, or via system timeout. If a notification is in the Action Center after the initial `close` event is emitted, a call to `notification.close()` will remove the notification from the action center but the `close` event will not be emitted again.
|
||||
|
||||
#### Event: 'reply' _macOS_
|
||||
|
||||
Returns:
|
||||
@@ -127,6 +129,8 @@ shown notification and create a new one with identical properties.
|
||||
|
||||
Dismisses the notification.
|
||||
|
||||
On Windows, calling `notification.close()` while the notification is visible on screen will dismiss the notification and remove it from the Action Center. If `notification.close()` is called after the notification is no longer visible on screen, calling `notification.close()` will try remove it from the Action Center.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
#### `notification.title`
|
||||
|
||||
@@ -266,7 +266,7 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
* `details` Object
|
||||
* `device` [HIDDevice[]](structures/hid-device.md)
|
||||
* `device` [HIDDevice](structures/hid-device.md)
|
||||
* `frame` [WebFrameMain](web-frame-main.md)
|
||||
|
||||
Emitted after `navigator.hid.requestDevice` has been called and
|
||||
@@ -281,7 +281,7 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
* `details` Object
|
||||
* `device` [HIDDevice[]](structures/hid-device.md)
|
||||
* `device` [HIDDevice](structures/hid-device.md)
|
||||
* `frame` [WebFrameMain](web-frame-main.md)
|
||||
|
||||
Emitted after `navigator.hid.requestDevice` has been called and
|
||||
@@ -296,7 +296,7 @@ Returns:
|
||||
|
||||
* `event` Event
|
||||
* `details` Object
|
||||
* `device` [HIDDevice[]](structures/hid-device.md)
|
||||
* `device` [HIDDevice](structures/hid-device.md)
|
||||
* `origin` string (optional) - The origin that the device has been revoked from.
|
||||
|
||||
Emitted after `HIDDevice.forget()` has been called. This event can be used
|
||||
@@ -785,7 +785,7 @@ Returns `Promise<void>` - Resolves when all connections are closed.
|
||||
#### `ses.fetch(input[, init])`
|
||||
|
||||
* `input` string | [GlobalRequest](https://nodejs.org/api/globals.html#request)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) (optional)
|
||||
* `init` [RequestInit](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) & { bypassCustomProtocolHandlers?: boolean } (optional)
|
||||
|
||||
Returns `Promise<GlobalResponse>` - see [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response).
|
||||
|
||||
@@ -901,6 +901,7 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||
* `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.
|
||||
* `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.
|
||||
|
||||
@@ -273,7 +273,6 @@ This API is only available on macOS 10.14 Mojave or newer.
|
||||
* `window-frame` - Window frame.
|
||||
* `window-text` - Text in windows.
|
||||
* On **macOS**
|
||||
* `alternate-selected-control-text` - The text on a selected surface in a list or table. _Deprecated_
|
||||
* `control-background` - The background of a large interface element, such as a browser or table.
|
||||
* `control` - The surface of a control.
|
||||
* `control-text` -The text of a control that isn’t disabled.
|
||||
@@ -339,21 +338,6 @@ Returns `string` - Can be `dark`, `light` or `unknown`.
|
||||
Gets the macOS appearance setting that is currently applied to your application,
|
||||
maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc)
|
||||
|
||||
### `systemPreferences.getAppLevelAppearance()` _macOS_ _Deprecated_
|
||||
|
||||
Returns `string` | `null` - Can be `dark`, `light` or `unknown`.
|
||||
|
||||
Gets the macOS appearance setting that you have declared you want for
|
||||
your application, maps to [NSApplication.appearance](https://developer.apple.com/documentation/appkit/nsapplication/2967170-appearance?language=objc).
|
||||
You can use the `setAppLevelAppearance` API to set this value.
|
||||
|
||||
### `systemPreferences.setAppLevelAppearance(appearance)` _macOS_ _Deprecated_
|
||||
|
||||
* `appearance` string | null - Can be `dark` or `light`
|
||||
|
||||
Sets the appearance setting for your application, this should override the
|
||||
system default and override the value of `getEffectiveAppearance`.
|
||||
|
||||
### `systemPreferences.canPromptTouchID()` _macOS_
|
||||
|
||||
Returns `boolean` - whether or not this device has the ability to use Touch ID.
|
||||
@@ -417,15 +401,9 @@ Returns an object with system animation settings.
|
||||
|
||||
## Properties
|
||||
|
||||
### `systemPreferences.appLevelAppearance` _macOS_ _Deprecated_
|
||||
### `systemPreferences.accessibilityDisplayShouldReduceTransparency()` _macOS_
|
||||
|
||||
A `string` property that can be `dark`, `light` or `unknown`. It determines the macOS appearance setting for
|
||||
your application. This maps to values in: [NSApplication.appearance](https://developer.apple.com/documentation/appkit/nsapplication/2967170-appearance?language=objc). Setting this will override the
|
||||
system default as well as the value of `getEffectiveAppearance`.
|
||||
|
||||
Possible values that can be set are `dark` and `light`, and possible return values are `dark`, `light`, and `unknown`.
|
||||
|
||||
This property is only available on macOS 10.14 Mojave or newer.
|
||||
A `boolean` property which determines whether the app avoids using semitransparent backgrounds. This maps to [NSWorkspace.accessibilityDisplayShouldReduceTransparency](https://developer.apple.com/documentation/appkit/nsworkspace/1533006-accessibilitydisplayshouldreduce)
|
||||
|
||||
### `systemPreferences.effectiveAppearance` _macOS_ _Readonly_
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Process: [Main](../glossary.md#main-process)<br />
|
||||
* `inherit`: equivalent to \['ignore', 'inherit', 'inherit']
|
||||
* `serviceName` string (optional) - Name of the process that will appear in `name` property of
|
||||
[`child-process-gone` event of `app`](app.md#event-child-process-gone).
|
||||
Default is `node.mojom.NodeService`.
|
||||
Default is `Node Utility Process`.
|
||||
* `allowLoadingUnsignedLibraries` boolean (optional) _macOS_ - With this flag, the utility process will be
|
||||
launched via the `Electron Helper (Plugin).app` helper executable on macOS, which can be
|
||||
codesigned with `com.apple.security.cs.disable-library-validation` and
|
||||
|
||||
@@ -1211,7 +1211,7 @@ Returns `string` - The user agent for this web page.
|
||||
|
||||
* `css` string
|
||||
* `options` Object (optional)
|
||||
* `cssOrigin` string (optional) - Can be either 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
* `cssOrigin` string (optional) - Can be 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
|
||||
Returns `Promise<string>` - A promise that resolves with a key for the inserted CSS that can later be used to remove the CSS via `contents.removeInsertedCSS(key)`.
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ webFrame.setSpellCheckProvider('en-US', {
|
||||
|
||||
* `css` string
|
||||
* `options` Object (optional)
|
||||
* `cssOrigin` string (optional) - Can be either 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
* `cssOrigin` string (optional) - Can be 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
|
||||
Returns `string` - A key for the inserted CSS that can later be used to remove
|
||||
the CSS via `webFrame.removeInsertedCSS(key)`.
|
||||
|
||||
@@ -61,6 +61,41 @@ w.webContents.getPrintersAsync().then((printers) => {
|
||||
})
|
||||
```
|
||||
|
||||
### Removed: `systemPreferences.{get,set}AppLevelAppearance` and `systemPreferences.appLevelAppearance`
|
||||
|
||||
The `systemPreferences.getAppLevelAppearance` and `systemPreferences.setAppLevelAppearance`
|
||||
methods have been removed, as well as the `systemPreferences.appLevelAppearance` property.
|
||||
Use the `nativeTheme` module instead.
|
||||
|
||||
```js
|
||||
// Removed
|
||||
systemPreferences.getAppLevelAppearance()
|
||||
// Replace with
|
||||
nativeTheme.shouldUseDarkColors
|
||||
|
||||
// Removed
|
||||
systemPreferences.appLevelAppearance
|
||||
// Replace with
|
||||
nativeTheme.shouldUseDarkColors
|
||||
|
||||
// Removed
|
||||
systemPreferences.setAppLevelAppearance('dark')
|
||||
// Replace with
|
||||
nativeTheme.themeSource = 'dark'
|
||||
```
|
||||
|
||||
### Removed: `alternate-selected-control-text` value for `systemPreferences.getColor`
|
||||
|
||||
The `alternate-selected-control-text` value for `systemPreferences.getColor`
|
||||
has been removed. Use `selected-content-background` instead.
|
||||
|
||||
```js
|
||||
// Removed
|
||||
systemPreferences.getColor('alternate-selected-control-text')
|
||||
// Replace with
|
||||
systemPreferences.getColor('selected-content-background')
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (26.0)
|
||||
|
||||
### Deprecated: `webContents.getPrinters`
|
||||
@@ -635,6 +670,18 @@ to open synchronously scriptable child windows, among other incompatibilities.
|
||||
See the documentation for [window.open in Electron](api/window-open.md)
|
||||
for more details.
|
||||
|
||||
### Deprecated: `app.runningUnderRosettaTranslation`
|
||||
|
||||
The `app.runningUnderRosettaTranslation` property has been deprecated.
|
||||
Use `app.runningUnderARM64Translation` instead.
|
||||
|
||||
```js
|
||||
// Deprecated
|
||||
console.log(app.runningUnderRosettaTranslation)
|
||||
// Replace with
|
||||
console.log(app.runningUnderARM64Translation)
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (14.0)
|
||||
|
||||
### Removed: `remote` module
|
||||
|
||||
@@ -7,14 +7,20 @@ function createWindow () {
|
||||
height: 600
|
||||
})
|
||||
|
||||
win.setRepresentedFilename(os.homedir())
|
||||
win.setDocumentEdited(true)
|
||||
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const win = new BrowserWindow()
|
||||
createWindow()
|
||||
|
||||
win.setRepresentedFilename(os.homedir())
|
||||
win.setDocumentEdited(true)
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
@@ -22,9 +28,3 @@ app.on('window-all-closed', () => {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -22,34 +22,101 @@ There are a few ways that you can set up testing using WebDriver.
|
||||
Node.js package for testing with WebDriver. Its ecosystem also includes various plugins
|
||||
(e.g. reporter and services) that can help you put together your test setup.
|
||||
|
||||
If you already have an existing WebdriverIO setup, it is recommended to update your dependencies and validate your existing configuration with how it is [outlined in the docs](https://webdriver.io/docs/desktop-testing/electron#configuration).
|
||||
|
||||
#### Install the test runner
|
||||
|
||||
First you need to run the WebdriverIO starter toolkit in your project root directory:
|
||||
If you don't use WebdriverIO in your project yet, you can add it by running the starter toolkit in your project root directory:
|
||||
|
||||
```sh npm2yarn
|
||||
npx wdio . --yes
|
||||
npm init wdio@latest ./
|
||||
```
|
||||
|
||||
This installs all necessary packages for you and generates a `wdio.conf.js` configuration file.
|
||||
This starts a configuration wizard that helps you put together the right setup, installs all necessary packages, and generates a `wdio.conf.js` configuration file. Make sure to select _"Desktop Testing - of Electron Applications"_ on one of the first questions asking _"What type of testing would you like to do?"_.
|
||||
|
||||
#### Connect WDIO to your Electron app
|
||||
|
||||
Update the capabilities in your configuration file to point to your Electron app binary:
|
||||
After running the configuration wizard, your `wdio.conf.js` should include roughly the following content:
|
||||
|
||||
```javascript title='wdio.conf.js'
|
||||
exports.config = {
|
||||
```js title='wdio.conf.js' @ts-nocheck
|
||||
export const config = {
|
||||
// ...
|
||||
services: ['electron'],
|
||||
capabilities: [{
|
||||
browserName: 'chrome',
|
||||
'goog:chromeOptions': {
|
||||
binary: '/path/to/your/electron/binary', // Path to your Electron binary.
|
||||
args: [/* cli arguments */] // Optional, perhaps 'app=' + /path/to/your/app/
|
||||
browserName: 'electron',
|
||||
'wdio:electronServiceOptions': {
|
||||
// WebdriverIO can automatically find your bundled application
|
||||
// if you use Electron Forge or electron-builder, otherwise you
|
||||
// can define it here, e.g.:
|
||||
// appBinaryPath: './path/to/bundled/application.exe',
|
||||
appArgs: ['foo', 'bar=baz']
|
||||
}
|
||||
}]
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Write your tests
|
||||
|
||||
Use the [WebdriverIO API](https://webdriver.io/docs/api) to interact with elements on the screen. The framework provides custom "matchers" that make asserting the state of your application easy, e.g.:
|
||||
|
||||
```js @ts-nocheck
|
||||
import { browser, $, expect } from '@wdio/globals'
|
||||
|
||||
describe('keyboard input', () => {
|
||||
it('should detect keyboard input', async () => {
|
||||
await browser.keys(['y', 'o'])
|
||||
await expect($('keypress-count')).toHaveText('YO')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
Furthermore, WebdriverIO allows you to access Electron APIs to get static information about your application:
|
||||
|
||||
```js @ts-nocheck
|
||||
import { browser, $, expect } from '@wdio/globals'
|
||||
|
||||
describe('when the make smaller button is clicked', () => {
|
||||
it('should decrease the window height and width by 10 pixels', async () => {
|
||||
const boundsBefore = await browser.electron.browserWindow('getBounds')
|
||||
expect(boundsBefore.width).toEqual(210)
|
||||
expect(boundsBefore.height).toEqual(310)
|
||||
|
||||
await $('.make-smaller').click()
|
||||
const boundsAfter = await browser.electron.browserWindow('getBounds')
|
||||
expect(boundsAfter.width).toEqual(200)
|
||||
expect(boundsAfter.height).toEqual(300)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
or to retrieve other Electron process information:
|
||||
|
||||
```js @ts-nocheck
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { browser, expect } from '@wdio/globals'
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), { encoding: 'utf-8' }))
|
||||
const { name, version } = packageJson
|
||||
|
||||
describe('electron APIs', () => {
|
||||
it('should retrieve app metadata through the electron API', async () => {
|
||||
const appName = await browser.electron.app('getName')
|
||||
expect(appName).toEqual(name)
|
||||
const appVersion = await browser.electron.app('getVersion')
|
||||
expect(appVersion).toEqual(version)
|
||||
})
|
||||
|
||||
it('should pass args through to the launched application', async () => {
|
||||
// custom args are set in the wdio.conf.js file as they need to be set before WDIO starts
|
||||
const argv = await browser.electron.mainProcess('argv')
|
||||
expect(argv).toContain('--foo')
|
||||
expect(argv).toContain('--bar=baz')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### Run your tests
|
||||
|
||||
To run your tests:
|
||||
@@ -58,6 +125,12 @@ To run your tests:
|
||||
$ npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
WebdriverIO helps launch and shut down the application for you.
|
||||
|
||||
#### More documentation
|
||||
|
||||
Find more documentation on Mocking Electron APIs and other useful resources in the [official WebdriverIO documentation](https://webdriver.io/docs/desktop-testing/electron).
|
||||
|
||||
### With Selenium
|
||||
|
||||
[Selenium](https://www.selenium.dev/) is a web automation framework that
|
||||
|
||||
@@ -9,12 +9,13 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
|
||||
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
|
||||
| 27.0.0 | 2023-Aug-17 | 2023-Sep-13 | 2023-Oct-10 | TBD | M118 | TBD | ✅ |
|
||||
| 28.0.0 | 2023-Oct-11 | 2023-Nov-6 | 2023-Dec-5 | TBD | M120 | TBD | ✅ |
|
||||
| 27.0.0 | 2023-Aug-17 | 2023-Sep-13 | 2023-Oct-10 | 2024-Apr-16 | M118 | v18.17 | ✅ |
|
||||
| 26.0.0 | 2023-Jun-01 | 2023-Jun-27 | 2023-Aug-15 | 2024-Feb-27 | M116 | v18.16 | ✅ |
|
||||
| 25.0.0 | 2023-Apr-10 | 2023-May-02 | 2023-May-30 | 2024-Jan-02 | M114 | v18.15 | ✅ |
|
||||
| 24.0.0 | 2022-Feb-09 | 2023-Mar-07 | 2023-Apr-04 | 2023-Oct-10 | M112 | v18.14 | ✅ |
|
||||
| 24.0.0 | 2023-Feb-09 | 2023-Mar-07 | 2023-Apr-04 | 2023-Oct-10 | M112 | v18.14 | 🚫 |
|
||||
| 23.0.0 | 2022-Dec-01 | 2023-Jan-10 | 2023-Feb-07 | 2023-Aug-15 | M110 | v18.12 | 🚫 |
|
||||
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | 2023-Oct-10 | M108 | v16.17 | ✅ |
|
||||
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | 2023-Oct-10 | M108 | v16.17 | 🚫 |
|
||||
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | 2023-Apr-04 | M106 | v16.16 | 🚫 |
|
||||
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | 2023-Feb-07 | M104 | v16.15 | 🚫 |
|
||||
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | 2022-Nov-29 | M102 | v16.14 | 🚫 |
|
||||
@@ -48,12 +49,6 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
* Since Electron 6, Electron major versions have been targeting every other Chromium major version. Each Electron stable should happen on the same day as Chrome stable ([see blog post](https://www.electronjs.org/blog/12-week-cadence)).
|
||||
* Since Electron 16, Electron has been releasing major versions on an 8-week cadence in accordance to Chrome's change to a 4-week release cadence ([see blog post](https://www.electronjs.org/blog/8-week-cadence)).
|
||||
|
||||
:::info Chrome release dates
|
||||
|
||||
Chromium has the own public release schedule [here](https://chromiumdash.appspot.com/schedule).
|
||||
|
||||
:::
|
||||
|
||||
## Version support policy
|
||||
|
||||
:::info
|
||||
@@ -78,6 +73,38 @@ and the version prior to that receives the vast majority of those fixes
|
||||
as time and bandwidth warrants. The oldest supported release line will receive
|
||||
only security fixes directly.
|
||||
|
||||
### Chromium version support
|
||||
|
||||
:::info Chromium release schedule
|
||||
|
||||
Chromium's public release schedule is [here](https://chromiumdash.appspot.com/schedule).
|
||||
|
||||
:::
|
||||
|
||||
Electron targets Chromium even-number versions, releasing every 8 weeks in concert
|
||||
with Chromium's 4-week release schedule. For example, Electron 26 uses Chromium 116, while Electron 27 uses Chromium 118.
|
||||
|
||||
### Node.js version support
|
||||
|
||||
Electron upgrades its `main` branch to even-number versions of Node.js when they enter Active LTS. The schedule
|
||||
is as follows:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/nodejs/Release/main/schedule.svg?sanitize=true" alt="Releases">
|
||||
|
||||
As a rule, stable branches of Electron do not receive Node.js upgrades after they have been cut.
|
||||
If Electron has recently updated its `main` branch to a new major version of Node.js, the next stable
|
||||
branch to be cut will be released with the new version.
|
||||
|
||||
Patch upgrades of Node that contain significant security or bug fixes, and are submitted
|
||||
more than 2 weeks prior to a stable release date, will be accepted into an Electron alpha
|
||||
or beta release branch.
|
||||
|
||||
Minor upgrades of Node that contain significant security or bug fixes, and are submitted
|
||||
more than 2 weeks prior to a stable release date may be accepted into an Electron alpha or
|
||||
beta release branch on a case-by-case basis. These requests will be reviewed and voted on
|
||||
by the [Releases Working Group](https://github.com/electron/governance/tree/main/wg-releases),
|
||||
to ensure minimal disruption for developers who may be consuming alpha or beta releases.
|
||||
|
||||
### Breaking API changes
|
||||
|
||||
When an API is changed or removed in a way that breaks existing functionality, the
|
||||
|
||||
@@ -228,6 +228,23 @@ channel with a renderer process using [`MessagePort`][]s. An Electron app can
|
||||
always prefer the [UtilityProcess][] API over Node.js [`child_process.fork`][] API when
|
||||
there is need to fork a child process from the main process.
|
||||
|
||||
## Process-specific module aliases (TypeScript)
|
||||
|
||||
Electron's npm package also exports subpaths that contain a subset of
|
||||
Electron's TypeScript type definitions.
|
||||
|
||||
- `electron/main` includes types for all main process modules.
|
||||
- `electron/renderer` includes types for all renderer process modules.
|
||||
- `electron/common` includes types for modules that can run in main and renderer processes.
|
||||
|
||||
These aliases have no impact on runtime, but can be used for typechecking
|
||||
and autocomplete.
|
||||
|
||||
```js title="Usage example"
|
||||
const { app } = require('electron/main')
|
||||
const { shell } = require('electron/common')
|
||||
```
|
||||
|
||||
[window-mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window
|
||||
[`MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
|
||||
[`child_process.fork`]: https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html#child_processforkmodulepath-args-options
|
||||
|
||||
@@ -91,14 +91,14 @@ version: '0.1'
|
||||
summary: Hello World Electron app
|
||||
description: |
|
||||
Simple Hello World Electron app as an example
|
||||
base: core18
|
||||
base: core22
|
||||
confinement: strict
|
||||
grade: stable
|
||||
|
||||
apps:
|
||||
electron-packager-hello-world:
|
||||
command: electron-quick-start/electron-quick-start --no-sandbox
|
||||
extensions: [gnome-3-34]
|
||||
extensions: [gnome]
|
||||
plugs:
|
||||
- browser-support
|
||||
- network
|
||||
@@ -237,6 +237,34 @@ apps:
|
||||
desktop: usr/share/applications/desktop.desktop
|
||||
```
|
||||
|
||||
## Optional: Enabling desktop capture
|
||||
|
||||
Capturing the desktop requires PipeWire library in some Linux configurations that use
|
||||
the Wayland protocol. To bundle PipeWire with your application, ensure that the base
|
||||
snap is set to `core22` or newer. Next, create a part called `pipewire` and add it to
|
||||
the `after` section of your application:
|
||||
|
||||
```yaml
|
||||
pipewire:
|
||||
plugin: nil
|
||||
build-packages: [libpipewire-0.3-dev]
|
||||
stage-packages: [pipewire]
|
||||
prime:
|
||||
- usr/lib/*/pipewire-*
|
||||
- usr/lib/*/spa-*
|
||||
- usr/lib/*/libpipewire*.so*
|
||||
- usr/share/pipewire
|
||||
```
|
||||
|
||||
Finally, configure your application's environment for PipeWire:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
SPA_PLUGIN_DIR: $SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/spa-0.2
|
||||
PIPEWIRE_CONFIG_NAME: $SNAP/usr/share/pipewire/pipewire.conf
|
||||
PIPEWIRE_MODULE_DIR: $SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/pipewire-0.3
|
||||
```
|
||||
|
||||
[snapcraft-syntax]: https://docs.snapcraft.io/build-snaps/syntax
|
||||
[electron-packager]: https://github.com/electron/electron-packager
|
||||
[electron-forge]: https://github.com/electron/forge
|
||||
|
||||
@@ -222,14 +222,26 @@ with CommonJS module syntax:
|
||||
- [app][app], which controls your application's event lifecycle.
|
||||
- [BrowserWindow][browser-window], which creates and manages app windows.
|
||||
|
||||
:::info Capitalization conventions
|
||||
<details><summary>Module capitalization conventions</summary>
|
||||
|
||||
You might have noticed the capitalization difference between the **a**pp
|
||||
and **B**rowser**W**indow modules. Electron follows typical JavaScript conventions here,
|
||||
where PascalCase modules are instantiable class constructors (e.g. BrowserWindow, Tray,
|
||||
Notification) whereas camelCase modules are not instantiable (e.g. app, ipcRenderer, webContents).
|
||||
|
||||
:::
|
||||
</details>
|
||||
|
||||
<details><summary>Typed import aliases</summary>
|
||||
|
||||
For better type checking when writing TypeScript code, you can choose to import
|
||||
main process modules from <code>electron/main</code>.
|
||||
|
||||
```js
|
||||
const { app, BrowserWindow } = require('electron/main')
|
||||
```
|
||||
|
||||
For more information, see the [Process Model docs](../tutorial/process-model.md#process-specific-module-aliases-typescript).
|
||||
</details>
|
||||
|
||||
:::warning ES Modules in Electron
|
||||
|
||||
|
||||
@@ -676,6 +676,8 @@ filenames = {
|
||||
]
|
||||
|
||||
lib_sources_extensions = [
|
||||
"shell/browser/extensions/api/extension_action/extension_action_api.cc",
|
||||
"shell/browser/extensions/api/extension_action/extension_action_api.h",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.cc",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.h",
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.cc",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Buffer } from 'buffer';
|
||||
import { constants } from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
import type * as Crypto from 'crypto';
|
||||
@@ -67,18 +68,22 @@ const gid = process.getgid?.() ?? 0;
|
||||
|
||||
const fakeTime = new Date();
|
||||
|
||||
enum AsarFileType {
|
||||
kFile = (constants as any).UV_DIRENT_FILE,
|
||||
kDirectory = (constants as any).UV_DIRENT_DIR,
|
||||
kLink = (constants as any).UV_DIRENT_LINK,
|
||||
}
|
||||
|
||||
const fileTypeToMode = new Map<AsarFileType, number>([
|
||||
[AsarFileType.kFile, constants.S_IFREG],
|
||||
[AsarFileType.kDirectory, constants.S_IFDIR],
|
||||
[AsarFileType.kLink, constants.S_IFLNK]
|
||||
]);
|
||||
|
||||
const asarStatsToFsStats = function (stats: NodeJS.AsarFileStat) {
|
||||
const { Stats, constants } = require('fs');
|
||||
const { Stats } = require('fs');
|
||||
|
||||
let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR;
|
||||
|
||||
if (stats.isFile) {
|
||||
mode ^= constants.S_IFREG;
|
||||
} else if (stats.isDirectory) {
|
||||
mode ^= constants.S_IFDIR;
|
||||
} else if (stats.isLink) {
|
||||
mode ^= constants.S_IFLNK;
|
||||
}
|
||||
const mode = constants.S_IROTH | constants.S_IRGRP | constants.S_IRUSR | constants.S_IWUSR | fileTypeToMode.get(stats.type)!;
|
||||
|
||||
return new Stats(
|
||||
1, // dev
|
||||
@@ -241,7 +246,6 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
const logASARAccess = (asarPath: string, filePath: string, offset: number) => {
|
||||
if (!process.env.ELECTRON_LOG_ASAR_READS) return;
|
||||
if (!logFDs.has(asarPath)) {
|
||||
const path = require('path');
|
||||
const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt`;
|
||||
const logPath = path.join(require('os').tmpdir(), logFilename);
|
||||
logFDs.set(asarPath, fs.openSync(logPath, 'a'));
|
||||
@@ -657,13 +661,7 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
nextTick(callback!, [error]);
|
||||
return;
|
||||
}
|
||||
if (stats.isFile) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
|
||||
} else if (stats.isDirectory) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
|
||||
} else if (stats.isLink) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
|
||||
}
|
||||
dirents.push(new fs.Dirent(file, stats.type));
|
||||
}
|
||||
nextTick(callback!, [null, dirents]);
|
||||
return;
|
||||
@@ -700,13 +698,7 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
if (!stats) {
|
||||
throw createError(AsarError.NOT_FOUND, { asarPath, filePath: childPath });
|
||||
}
|
||||
if (stats.isFile) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_FILE));
|
||||
} else if (stats.isDirectory) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_DIR));
|
||||
} else if (stats.isLink) {
|
||||
dirents.push(new fs.Dirent(file, fs.constants.UV_DIRENT_LINK));
|
||||
}
|
||||
dirents.push(new fs.Dirent(file, stats.type));
|
||||
}
|
||||
return dirents;
|
||||
}
|
||||
@@ -757,7 +749,7 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) return -34;
|
||||
|
||||
return (stats.isDirectory) ? 1 : 0;
|
||||
return (stats.type === AsarFileType.kDirectory) ? 1 : 0;
|
||||
};
|
||||
|
||||
// Calling mkdir for directory inside asar archive should throw ENOTDIR
|
||||
|
||||
@@ -114,5 +114,11 @@ for (const name of events) {
|
||||
}
|
||||
|
||||
// Deprecation.
|
||||
deprecate.event(app, 'gpu-process-crashed', 'child-process-gone');
|
||||
deprecate.event(app, 'renderer-process-crashed', 'render-process-gone');
|
||||
deprecate.event(app, 'gpu-process-crashed', 'child-process-gone', () => {
|
||||
// the old event is still emitted by App::OnGpuProcessCrashed()
|
||||
return undefined;
|
||||
});
|
||||
|
||||
deprecate.event(app, 'renderer-process-crashed', 'render-process-gone', (event: Electron.Event, webContents: Electron.WebContents, details: Electron.RenderProcessGoneDetails) => {
|
||||
return [event, webContents, details.reason === 'killed'];
|
||||
});
|
||||
|
||||
@@ -1,33 +1,5 @@
|
||||
import * as deprecate from '@electron/internal/common/deprecate';
|
||||
|
||||
const { systemPreferences } = process._linkedBinding('electron_browser_system_preferences');
|
||||
|
||||
if ('getAppLevelAppearance' in systemPreferences) {
|
||||
const nativeALAGetter = systemPreferences.getAppLevelAppearance;
|
||||
const nativeALASetter = systemPreferences.setAppLevelAppearance;
|
||||
const warnALA = deprecate.warnOnce('appLevelAppearance');
|
||||
const warnALAGetter = deprecate.warnOnce('getAppLevelAppearance function');
|
||||
const warnALASetter = deprecate.warnOnce('setAppLevelAppearance function');
|
||||
Object.defineProperty(systemPreferences, 'appLevelAppearance', {
|
||||
get: () => {
|
||||
warnALA();
|
||||
return nativeALAGetter.call(systemPreferences);
|
||||
},
|
||||
set: (appearance) => {
|
||||
warnALA();
|
||||
nativeALASetter.call(systemPreferences, appearance);
|
||||
}
|
||||
});
|
||||
systemPreferences.getAppLevelAppearance = () => {
|
||||
warnALAGetter();
|
||||
return nativeALAGetter.call(systemPreferences);
|
||||
};
|
||||
systemPreferences.setAppLevelAppearance = (appearance) => {
|
||||
warnALASetter();
|
||||
nativeALASetter.call(systemPreferences, appearance);
|
||||
};
|
||||
}
|
||||
|
||||
if ('getEffectiveAppearance' in systemPreferences) {
|
||||
const nativeEAGetter = systemPreferences.getEffectiveAppearance;
|
||||
Object.defineProperty(systemPreferences, 'effectiveAppearance', {
|
||||
|
||||
@@ -337,49 +337,53 @@ WebContents.prototype.printToPDF = async function (options) {
|
||||
// TODO(codebytere): deduplicate argument sanitization by moving rest of
|
||||
// print param logic into new file shared between printToPDF and print
|
||||
WebContents.prototype.print = function (options: ElectronInternal.WebContentsPrintOptions, callback) {
|
||||
if (typeof options === 'object') {
|
||||
const pageSize = options.pageSize ?? 'A4';
|
||||
if (typeof pageSize === 'object') {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
if (typeof options !== 'object') {
|
||||
throw new Error('webContents.print(): Invalid print settings specified.');
|
||||
}
|
||||
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
const height = Math.ceil(pageSize.height);
|
||||
const width = Math.ceil(pageSize.width);
|
||||
if (!isValidCustomPageSize(width, height)) {
|
||||
throw new Error('height and width properties must be minimum 352 microns.');
|
||||
}
|
||||
const printSettings: Record<string, any> = { ...options };
|
||||
|
||||
options.mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: height,
|
||||
width_microns: width,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: width,
|
||||
imageable_area_top_microns: height
|
||||
};
|
||||
} else if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) {
|
||||
const mediaSize = PDFPageSizes[pageSize];
|
||||
options.mediaSize = {
|
||||
...mediaSize,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: mediaSize.width_microns,
|
||||
imageable_area_top_microns: mediaSize.height_microns
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
const pageSize = options.pageSize ?? 'A4';
|
||||
if (typeof pageSize === 'object') {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
const height = Math.ceil(pageSize.height);
|
||||
const width = Math.ceil(pageSize.width);
|
||||
if (!isValidCustomPageSize(width, height)) {
|
||||
throw new Error('height and width properties must be minimum 352 microns.');
|
||||
}
|
||||
|
||||
printSettings.mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: height,
|
||||
width_microns: width,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: width,
|
||||
imageable_area_top_microns: height
|
||||
};
|
||||
} else if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) {
|
||||
const mediaSize = PDFPageSizes[pageSize];
|
||||
printSettings.mediaSize = {
|
||||
...mediaSize,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: mediaSize.width_microns,
|
||||
imageable_area_top_microns: mediaSize.height_microns
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
}
|
||||
|
||||
if (this._print) {
|
||||
if (callback) {
|
||||
this._print(options, callback);
|
||||
this._print(printSettings, callback);
|
||||
} else {
|
||||
this._print(options);
|
||||
this._print(printSettings);
|
||||
}
|
||||
} else {
|
||||
console.error('Error: Printing feature is disabled.');
|
||||
@@ -653,8 +657,8 @@ WebContents.prototype._init = function () {
|
||||
ipcMain.emit(channel, event, message);
|
||||
});
|
||||
|
||||
this.on('crashed', (event, ...args) => {
|
||||
app.emit('renderer-process-crashed', event, this, ...args);
|
||||
deprecate.event(this, 'crashed', 'render-process-gone', (event: Electron.Event, details: Electron.RenderProcessGoneDetails) => {
|
||||
return [event, details.reason === 'killed'];
|
||||
});
|
||||
|
||||
this.on('render-process-gone', (event, details) => {
|
||||
|
||||
@@ -66,14 +66,17 @@ export function renameFunction<T extends Function> (fn: T, newName: string): T {
|
||||
}
|
||||
|
||||
// change the name of an event
|
||||
export function event (emitter: NodeJS.EventEmitter, oldName: string, newName: string) {
|
||||
export function event (emitter: NodeJS.EventEmitter, oldName: string, newName: string, transformer: (...args: any[]) => any[] | undefined = (...args) => args) {
|
||||
const warn = newName.startsWith('-') /* internal event */
|
||||
? warnOnce(`${oldName} event`)
|
||||
: warnOnce(`${oldName} event`, `${newName} event`);
|
||||
return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) {
|
||||
if (this.listenerCount(oldName) !== 0) {
|
||||
warn();
|
||||
this.emit(oldName, ...args);
|
||||
const transformedArgs = transformer(...args);
|
||||
if (transformedArgs) {
|
||||
this.emit(oldName, ...transformedArgs);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -131,3 +131,5 @@ build_remove_ent_content_analysis_assert.patch
|
||||
fix_activate_background_material_on_windows.patch
|
||||
fix_move_autopipsettingshelper_behind_branding_buildflag.patch
|
||||
revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch
|
||||
revert_same_party_cookie_attribute_removal.patch
|
||||
crash_gpu_process_and_clear_shader_cache_when_skia_reports.patch
|
||||
|
||||
@@ -33,10 +33,10 @@ index 41ce32113ec2679b76d5a4fd69a7109c832ac7a1..1cd35794bf78f3d92b42634d9494c85a
|
||||
"//base",
|
||||
"//build:branding_buildflags",
|
||||
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
|
||||
index 139ade66fbaf20f7c41275f5f84acf81afcc03f7..f7272dea8af27026a9094bdc9a3c5a3b10eee42b 100644
|
||||
index 31bf3d854d9a672e535f747fb8734d66ac0c37e9..cad2387217a73ec5a7533fc121ea7b77cd6995ae 100644
|
||||
--- a/chrome/browser/BUILD.gn
|
||||
+++ b/chrome/browser/BUILD.gn
|
||||
@@ -4724,7 +4724,7 @@ static_library("browser") {
|
||||
@@ -4726,7 +4726,7 @@ static_library("browser") {
|
||||
|
||||
# On Windows, the hashes are embedded in //chrome:chrome_initial rather
|
||||
# than here in :chrome_dll.
|
||||
@@ -46,10 +46,10 @@ index 139ade66fbaf20f7c41275f5f84acf81afcc03f7..f7272dea8af27026a9094bdc9a3c5a3b
|
||||
sources += [ "certificate_viewer_stub.cc" ]
|
||||
}
|
||||
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
|
||||
index fff07e866add0af2c395412ad80fb6a447ac801e..2dc1c74310fae6324e9997023c7f5ba5d7a32ca2 100644
|
||||
index a315f817a8977d36e52b8f7c81d9e6778d3dcca2..dc046a004d6f4b2e9f1ec4b9423f293cfecaff8d 100644
|
||||
--- a/chrome/test/BUILD.gn
|
||||
+++ b/chrome/test/BUILD.gn
|
||||
@@ -6839,7 +6839,6 @@ test("unit_tests") {
|
||||
@@ -6841,7 +6841,6 @@ test("unit_tests") {
|
||||
|
||||
deps += [
|
||||
"//chrome:other_version",
|
||||
@@ -57,7 +57,7 @@ index fff07e866add0af2c395412ad80fb6a447ac801e..2dc1c74310fae6324e9997023c7f5ba5
|
||||
"//chrome//services/util_win:unit_tests",
|
||||
"//chrome/app:chrome_dll_resources",
|
||||
"//chrome/app:win_unit_tests",
|
||||
@@ -6865,6 +6864,10 @@ test("unit_tests") {
|
||||
@@ -6867,6 +6866,10 @@ test("unit_tests") {
|
||||
"//ui/resources",
|
||||
]
|
||||
|
||||
@@ -68,7 +68,7 @@ index fff07e866add0af2c395412ad80fb6a447ac801e..2dc1c74310fae6324e9997023c7f5ba5
|
||||
ldflags = [
|
||||
"/DELAYLOAD:api-ms-win-core-winrt-error-l1-1-0.dll",
|
||||
"/DELAYLOAD:api-ms-win-core-winrt-l1-1-0.dll",
|
||||
@@ -7832,7 +7835,6 @@ test("unit_tests") {
|
||||
@@ -7834,7 +7837,6 @@ test("unit_tests") {
|
||||
}
|
||||
|
||||
deps += [
|
||||
@@ -76,7 +76,7 @@ index fff07e866add0af2c395412ad80fb6a447ac801e..2dc1c74310fae6324e9997023c7f5ba5
|
||||
"//chrome/browser/apps:icon_standardizer",
|
||||
"//chrome/browser/apps/app_service",
|
||||
"//chrome/browser/apps/app_service:app_registry_cache_waiter",
|
||||
@@ -7919,6 +7921,10 @@ test("unit_tests") {
|
||||
@@ -7921,6 +7923,10 @@ test("unit_tests") {
|
||||
"//ui/webui/resources/js/browser_command:mojo_bindings",
|
||||
]
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ potentially prevent a window from being created.
|
||||
TODO(loc): this patch is currently broken.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index 50d873e238b04eb6d461dc929d98dfe171cc491c..586aa87098bd3db10440fe865a02c05e7b3be14f 100644
|
||||
index af16b1053e1f42887bf0fde97bba30a377857872..ef79bf0e27bd0c5eb9fe5f0aef1cf6c6a2900e13 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -8206,6 +8206,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
@@ -8224,6 +8224,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
last_committed_origin_, params->window_container_type,
|
||||
params->target_url, params->referrer.To<Referrer>(),
|
||||
params->frame_name, params->disposition, *params->features,
|
||||
|
||||
@@ -14,7 +14,7 @@ This change patches it out to prevent the DCHECK.
|
||||
It can be removed once/if we see a better solution to the problem.
|
||||
|
||||
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
|
||||
index a478ec236b193a42296aa30be384b54973f2ff89..e65c53f1ad3f0b79bd820970436788ae4e5b61fe 100644
|
||||
index 36cead8ff426f915afbdb0e4650effb4454edb64..cecabbaa8aa49a887b4f2b26c2d7d92ceeee34e9 100644
|
||||
--- a/content/browser/site_instance_impl.cc
|
||||
+++ b/content/browser/site_instance_impl.cc
|
||||
@@ -205,7 +205,7 @@ scoped_refptr<SiteInstanceImpl> SiteInstanceImpl::CreateForGuest(
|
||||
|
||||
@@ -6,7 +6,7 @@ Subject: chore: patch out Profile methods in titlebar_config
|
||||
Make this code linkable in Electron by removing Profile references.
|
||||
|
||||
diff --git a/chrome/browser/win/titlebar_config.cc b/chrome/browser/win/titlebar_config.cc
|
||||
index 92d0e8165a264c7ef2701a66e0f7179f0d080f47..b0c91778399f811a5d1b0f208488667cb38459e1 100644
|
||||
index f088a7071b1e0e1e05aee5637484b1dea3e2a6fa..8d411e550f6a8ca1a4070bf5d5719703f90b3dfa 100644
|
||||
--- a/chrome/browser/win/titlebar_config.cc
|
||||
+++ b/chrome/browser/win/titlebar_config.cc
|
||||
@@ -19,8 +19,10 @@ BASE_FEATURE(kWindows11MicaTitlebar,
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Le Hoang Quyen <lehoangquyen@chromium.org>
|
||||
Date: Tue, 31 Oct 2023 08:52:25 +0000
|
||||
Subject: Crash GPU process and clear shader cache when skia reports
|
||||
compileError.
|
||||
|
||||
Sometimes Skia failed to compile the cached GLSL because the driver had
|
||||
been changed but GL_RENDERER was still the same. In this case, we better
|
||||
crash the GPU process and signal the browser to clear the cache & let
|
||||
Skia regenerate the GLSL in the next run.
|
||||
|
||||
Even if the compile failure wasn't caused by the cached GLSL. It's still
|
||||
better to crash the GPU process and let the shaders be re-generated.
|
||||
Because a failed compilation would have resulted in wrong rendering
|
||||
even if we had allowed the process to continue its execution.
|
||||
|
||||
Bug: 1442633
|
||||
Change-Id: Ia0e36bd4674877de5be451a6ea9c4e7fa5e34e8e
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4988290
|
||||
Reviewed-by: Sunny Sachanandani <sunnyps@chromium.org>
|
||||
Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1217461}
|
||||
|
||||
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
|
||||
index f675b3b4fb0c1892f7ddbec7aa4e2284ff3dfcba..f5d1c60ee99b84641a46fbefa9915eb409690098 100644
|
||||
--- a/gpu/command_buffer/service/shared_context_state.cc
|
||||
+++ b/gpu/command_buffer/service/shared_context_state.cc
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "gpu/command_buffer/service/shared_context_state.h"
|
||||
|
||||
+#include "base/immediate_crash.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/system/sys_info.h"
|
||||
@@ -99,6 +100,13 @@ void SharedContextState::compileError(const char* shader, const char* errors) {
|
||||
<< "------------------------\n"
|
||||
<< shader << "\nErrors:\n"
|
||||
<< errors;
|
||||
+
|
||||
+ // Increase shader cache shm count and crash the GPU process so that the
|
||||
+ // browser process would clear the cache.
|
||||
+ GpuProcessShmCount::ScopedIncrement increment(
|
||||
+ use_shader_cache_shm_count_.get());
|
||||
+
|
||||
+ base::ImmediateCrash();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,6 +302,7 @@ bool SharedContextState::InitializeGanesh(
|
||||
gl::ProgressReporter* progress_reporter) {
|
||||
progress_reporter_ = progress_reporter;
|
||||
gr_shader_cache_ = cache;
|
||||
+ use_shader_cache_shm_count_ = use_shader_cache_shm_count;
|
||||
|
||||
size_t max_resource_cache_bytes;
|
||||
size_t glyph_cache_max_texture_bytes;
|
||||
diff --git a/gpu/command_buffer/service/shared_context_state.h b/gpu/command_buffer/service/shared_context_state.h
|
||||
index 00aab5227f30a3f56b2b5c63ee5f627aa2a6c03b..4b9cf8ac797f6d5f9146fb1be43ff78a756d4ee5 100644
|
||||
--- a/gpu/command_buffer/service/shared_context_state.h
|
||||
+++ b/gpu/command_buffer/service/shared_context_state.h
|
||||
@@ -391,6 +391,8 @@ class GPU_GLES2_EXPORT SharedContextState
|
||||
std::vector<uint8_t> scratch_deserialization_buffer_;
|
||||
raw_ptr<gpu::raster::GrShaderCache, DanglingUntriaged> gr_shader_cache_ =
|
||||
nullptr;
|
||||
+ raw_ptr<GpuProcessShmCount, DanglingUntriaged> use_shader_cache_shm_count_ =
|
||||
+ nullptr;
|
||||
|
||||
// |need_context_state_reset| is set whenever Skia may have altered the
|
||||
// driver's GL state.
|
||||
@@ -6,10 +6,10 @@ Subject: disable_hidden.patch
|
||||
Electron uses this to disable background throttling for hidden windows.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
index 9e946e02bca276a877defacf6cf6cb54f882ceba..149ead7bbf52ad127747c5f80f0e35a13203502a 100644
|
||||
index 916c3f324ed2161d1f9ad1a023cc0318a1b72628..2d946e29c94a07a1aa4f4b6018ab1b564440f55d 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
@@ -801,6 +801,9 @@ void RenderWidgetHostImpl::WasHidden() {
|
||||
@@ -807,6 +807,9 @@ void RenderWidgetHostImpl::WasHidden() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ uses internally for things like menus and devtools.
|
||||
We can remove this patch once it has in some shape been upstreamed.
|
||||
|
||||
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
|
||||
index f6556e141a76b0766f672bfa5819256e635c60af..06c7ac178cefc149732294ed790f2b7cbb328d34 100644
|
||||
index af49c133fb2b176139263553d2afff33e96a94b1..699c38b2046e267619eef103c83eff258887e0c0 100644
|
||||
--- a/ui/native_theme/native_theme.cc
|
||||
+++ b/ui/native_theme/native_theme.cc
|
||||
@@ -153,6 +153,8 @@ NativeTheme::NativeTheme(bool should_use_dark_colors,
|
||||
@@ -156,6 +156,8 @@ NativeTheme::NativeTheme(bool should_use_dark_colors,
|
||||
NativeTheme::~NativeTheme() = default;
|
||||
|
||||
bool NativeTheme::ShouldUseDarkColors() const {
|
||||
@@ -26,7 +26,7 @@ index f6556e141a76b0766f672bfa5819256e635c60af..06c7ac178cefc149732294ed790f2b7c
|
||||
}
|
||||
|
||||
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
|
||||
index 6f51a8ffec973d6c2acacabb951c7cc4e36b639c..889e655fccc115faae932236cc28c6386cf19c1e 100644
|
||||
index 341248a6b15b280c382c965009540fbdb64b0978..c9bead90658ac0e2d503c90c6ce380a318fee457 100644
|
||||
--- a/ui/native_theme/native_theme.h
|
||||
+++ b/ui/native_theme/native_theme.h
|
||||
@@ -433,6 +433,23 @@ class NATIVE_THEME_EXPORT NativeTheme {
|
||||
@@ -53,7 +53,7 @@ index 6f51a8ffec973d6c2acacabb951c7cc4e36b639c..889e655fccc115faae932236cc28c638
|
||||
// Returns a shared instance of the native theme that should be used for web
|
||||
// rendering. Do not use it in a normal application context (i.e. browser).
|
||||
// The returned object should not be deleted by the caller. This function is
|
||||
@@ -643,6 +660,7 @@ class NATIVE_THEME_EXPORT NativeTheme {
|
||||
@@ -654,6 +671,7 @@ class NATIVE_THEME_EXPORT NativeTheme {
|
||||
bool inverted_colors_ = false;
|
||||
PreferredColorScheme preferred_color_scheme_ = PreferredColorScheme::kLight;
|
||||
PreferredContrast preferred_contrast_ = PreferredContrast::kNoPreference;
|
||||
@@ -62,10 +62,10 @@ index 6f51a8ffec973d6c2acacabb951c7cc4e36b639c..889e655fccc115faae932236cc28c638
|
||||
SEQUENCE_CHECKER(sequence_checker_);
|
||||
};
|
||||
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
|
||||
index faa15be0b3e08360c702b32968d3b1633038d776..7a6c284143e873b408333fa2052e05863935cb89 100644
|
||||
index 595b7185832126dd3d8ca85c445b835e5969adcb..58c64b98a0f94b0be7e7819999cf81dedf9bbe08 100644
|
||||
--- a/ui/native_theme/native_theme_win.cc
|
||||
+++ b/ui/native_theme/native_theme_win.cc
|
||||
@@ -658,6 +658,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const {
|
||||
@@ -663,6 +663,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const {
|
||||
// ...unless --force-dark-mode was specified in which case caveat emptor.
|
||||
if (InForcedColorsMode() && !IsForcedDarkMode())
|
||||
return false;
|
||||
|
||||
@@ -514,7 +514,7 @@ index 796ae2688436eb07f19909641d1620dd02f10cdb..c9e0eee0b329caf46669b419b1cd10cf
|
||||
waiting_on_draw_ack_ = true;
|
||||
|
||||
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
|
||||
index c70b0af70a68386c5d36833e621dd1b5cdc1522a..17e5c96cbb4ef2f575fd5ba7bd8b0c7bd58da5e7 100644
|
||||
index 1c59eb0cdf1786a19f74f165acc3fb071dcf2fa1..ddb2389f548a8558f79d7cf32ffd6ce0a7751553 100644
|
||||
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
|
||||
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
|
||||
@@ -97,7 +97,8 @@ RootCompositorFrameSinkImpl::Create(
|
||||
|
||||
@@ -17,7 +17,7 @@ policy->CanCommitOriginAndUrl.
|
||||
Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/3856266.
|
||||
|
||||
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
|
||||
index e91fb6b203b55d4937e9f4b4ed8fd5e8efb5aa10..855cb90260de04b90b8bbf4a8733e0869cda551d 100644
|
||||
index 5cf177fc73dd2b3038754b11d23ad637e7709f88..72103151867a9c1154b83da83b52a5b3b7b988ed 100644
|
||||
--- a/content/browser/renderer_host/navigation_request.cc
|
||||
+++ b/content/browser/renderer_host/navigation_request.cc
|
||||
@@ -7543,10 +7543,11 @@ NavigationRequest::GetOriginForURLLoaderFactoryAfterResponseWithDebugInfo() {
|
||||
|
||||
@@ -25,10 +25,10 @@ index c9a13aee1e64f359cd1d5c2e440b4df204420997..7790c311400fc84bfda6c99e1ebd8da7
|
||||
// Returns true if duplex mode is set.
|
||||
bool SetDuplexModeInPrintSettings(mojom::DuplexMode mode);
|
||||
diff --git a/printing/printing_context_mac.mm b/printing/printing_context_mac.mm
|
||||
index ee900c7541af99c61844920433ae39e2648ae90b..59137008aeddc03d60c58a463206dc91a0ef5c1f 100644
|
||||
index 7c9204db9b4c8f36e4c7405e3bd9c8b311753f04..4bff296ea2c55175416aaa778856e6182866664f 100644
|
||||
--- a/printing/printing_context_mac.mm
|
||||
+++ b/printing/printing_context_mac.mm
|
||||
@@ -464,7 +464,8 @@ void ApplySystemPrintDialogData(
|
||||
@@ -473,7 +473,8 @@ void ApplySystemPrintDialogData(
|
||||
!SetCollateInPrintSettings(settings_->collate()) ||
|
||||
!SetDuplexModeInPrintSettings(settings_->duplex_mode()) ||
|
||||
!SetOutputColor(static_cast<int>(settings_->color())) ||
|
||||
@@ -38,7 +38,7 @@ index ee900c7541af99c61844920433ae39e2648ae90b..59137008aeddc03d60c58a463206dc91
|
||||
return OnError();
|
||||
}
|
||||
}
|
||||
@@ -617,6 +618,22 @@ void ApplySystemPrintDialogData(
|
||||
@@ -626,6 +627,22 @@ void ApplySystemPrintDialogData(
|
||||
return PMSetCopies(print_settings, copies, false) == noErr;
|
||||
}
|
||||
|
||||
@@ -100,10 +100,10 @@ index 07847521e7217c78480205812a73cc89503c00d2..586e866ca7ec0eb0b573d23e3bd95792
|
||||
} else {
|
||||
// No need to bother, we don't know how many pages are available.
|
||||
diff --git a/ui/gtk/printing/print_dialog_gtk.cc b/ui/gtk/printing/print_dialog_gtk.cc
|
||||
index e270fa36d775333aaa30f1ff0104062c544d1058..e4a6213e19e4f23834802784a43db22c9d02d891 100644
|
||||
index dcabfdab6cc5a0f0a79f797d3660118b780bd110..bcbfeef775e40c1b0c1bbac49dd520e602bc4cb9 100644
|
||||
--- a/ui/gtk/printing/print_dialog_gtk.cc
|
||||
+++ b/ui/gtk/printing/print_dialog_gtk.cc
|
||||
@@ -245,6 +245,24 @@ void PrintDialogGtk::UpdateSettings(
|
||||
@@ -248,6 +248,24 @@ void PrintDialogGtk::UpdateSettings(
|
||||
|
||||
gtk_print_settings_set_n_copies(gtk_settings_, settings->copies());
|
||||
gtk_print_settings_set_collate(gtk_settings_, settings->collate());
|
||||
|
||||
@@ -75,7 +75,7 @@ index 3a815ebf505bd95fa7f6b61ba433d98fbfe20225..dbbebbdc1735bc14224dfcde0b7fe3a6
|
||||
+
|
||||
@end
|
||||
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
|
||||
index 94ac4fa8db42cb70fa5032a0c1eeb10ad94b8d9a..048cfe7b16d74845492c9317f1ed9790fed36250 100644
|
||||
index 085d9821933390c781fe11d441910501bf22d601..6c79434e2a5b59267c9801ac23c318c9c320f019 100644
|
||||
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
|
||||
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
|
||||
@@ -17,6 +17,7 @@ class NativeWidgetNSWindowBridge;
|
||||
@@ -95,10 +95,10 @@ index 94ac4fa8db42cb70fa5032a0c1eeb10ad94b8d9a..048cfe7b16d74845492c9317f1ed9790
|
||||
// The NSWindow used by BridgedNativeWidget. Provides hooks into AppKit that
|
||||
// can only be accomplished by overriding methods.
|
||||
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
index 517333705c9b68706159bff2396c3ba1f8dc1f55..f11ce18fb4d1567eb53e6b9272424eecb40e51e1 100644
|
||||
index 6e837d66209e6322324227bc596e40e56fccc495..3c3c5db57cfaba8e59867e74b81b4fad1cf7a8ed 100644
|
||||
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
@@ -113,7 +113,9 @@ void OrderChildWindow(NSWindow* child_window,
|
||||
@@ -101,7 +101,9 @@ void OrderChildWindow(NSWindow* child_window,
|
||||
} // namespace
|
||||
|
||||
@interface NSWindow (Private)
|
||||
@@ -108,7 +108,7 @@ index 517333705c9b68706159bff2396c3ba1f8dc1f55..f11ce18fb4d1567eb53e6b9272424eec
|
||||
- (BOOL)hasKeyAppearance;
|
||||
- (long long)_resizeDirectionForMouseLocation:(CGPoint)location;
|
||||
- (BOOL)_isConsideredOpenForPersistentState;
|
||||
@@ -161,6 +163,8 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event {
|
||||
@@ -139,6 +141,8 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event {
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -117,7 +117,7 @@ index 517333705c9b68706159bff2396c3ba1f8dc1f55..f11ce18fb4d1567eb53e6b9272424eec
|
||||
@implementation NativeWidgetMacNSWindowTitledFrame
|
||||
- (void)mouseDown:(NSEvent*)event {
|
||||
if (self.window.isMovable)
|
||||
@@ -187,6 +191,8 @@ - (BOOL)usesCustomDrawing {
|
||||
@@ -165,6 +169,8 @@ - (BOOL)usesCustomDrawing {
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -126,7 +126,7 @@ index 517333705c9b68706159bff2396c3ba1f8dc1f55..f11ce18fb4d1567eb53e6b9272424eec
|
||||
@implementation NativeWidgetMacNSWindow {
|
||||
@private
|
||||
CommandDispatcher* __strong _commandDispatcher;
|
||||
@@ -396,6 +402,8 @@ - (NSAccessibilityRole)accessibilityRole {
|
||||
@@ -353,6 +359,8 @@ - (NSAccessibilityRole)accessibilityRole {
|
||||
|
||||
// NSWindow overrides.
|
||||
|
||||
@@ -135,7 +135,7 @@ index 517333705c9b68706159bff2396c3ba1f8dc1f55..f11ce18fb4d1567eb53e6b9272424eec
|
||||
+ (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
|
||||
if (windowStyle & NSWindowStyleMaskTitled) {
|
||||
if (Class customFrame = [NativeWidgetMacNSWindowTitledFrame class])
|
||||
@@ -407,6 +415,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
|
||||
@@ -364,6 +372,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
|
||||
return [super frameViewClassForStyleMask:windowStyle];
|
||||
}
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ index 5a096477c123a782341115f964c4975301ccaf9a..ecfbb3b405425af346a6ba6788fc1d8f
|
||||
|
||||
} // namespace
|
||||
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
|
||||
index 5855f1272c65fb5b6c0e6a5493aded657d2eb2e9..4b175b312907cce2b6ef8b6eaf05f84c2aefba5b 100644
|
||||
index 7c702f140281faea37bf60094e7bb78104d9c1ad..07796eb52e7d64d6be9b5e32fba53bba21fef8d7 100644
|
||||
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
|
||||
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
|
||||
@@ -609,10 +609,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) {
|
||||
@@ -599,10 +599,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) {
|
||||
// this should be treated as an error and caught early.
|
||||
CHECK(bridged_view_);
|
||||
|
||||
@@ -238,7 +238,7 @@ index 835cce73b7ab8b38c37d3e2650e12303d9d918e3..4460a00497dfaee0ba90cd5d14888055
|
||||
+
|
||||
#endif // UI_BASE_COCOA_REMOTE_ACCESSIBILITY_API_H_
|
||||
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.h b/ui/views/cocoa/native_widget_mac_ns_window_host.h
|
||||
index 2daaf2b78b7a60d340c2ff1651f8b5450db4af0f..3080adea0402f9d57cbde5d4350605d463ee5c8e 100644
|
||||
index 6c2761b68d895fb1964e98865dd96cc37226fce0..563ee4da217ff4aabf2ef3452322da9e9ccefe4f 100644
|
||||
--- a/ui/views/cocoa/native_widget_mac_ns_window_host.h
|
||||
+++ b/ui/views/cocoa/native_widget_mac_ns_window_host.h
|
||||
@@ -30,7 +30,9 @@
|
||||
@@ -251,7 +251,7 @@ index 2daaf2b78b7a60d340c2ff1651f8b5450db4af0f..3080adea0402f9d57cbde5d4350605d4
|
||||
@class NSView;
|
||||
|
||||
namespace remote_cocoa {
|
||||
@@ -454,10 +456,12 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost
|
||||
@@ -456,10 +458,12 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost
|
||||
mojo::AssociatedRemote<remote_cocoa::mojom::NativeWidgetNSWindow>
|
||||
remote_ns_window_remote_;
|
||||
|
||||
@@ -265,10 +265,10 @@ index 2daaf2b78b7a60d340c2ff1651f8b5450db4af0f..3080adea0402f9d57cbde5d4350605d4
|
||||
// Used to force the NSApplication's focused accessibility element to be the
|
||||
// views::Views accessibility tree when the NSView for this is focused.
|
||||
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
|
||||
index 53bdb4d62294f128b8f4b7bdcddf1052e15f331f..d052f5bb619bf4092810c6d5dd489a38a60026b0 100644
|
||||
index 6e0f07f5e1107341d0232e4a76b7332620315c04..0135aada72ef0c3a85ab8d134462754eb31ad2df 100644
|
||||
--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm
|
||||
+++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
|
||||
@@ -339,7 +339,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
|
||||
@@ -352,7 +352,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
|
||||
NativeWidgetMacNSWindowHost::GetNativeViewAccessibleForNSView() const {
|
||||
if (in_process_ns_window_bridge_)
|
||||
return in_process_ns_window_bridge_->ns_view();
|
||||
@@ -280,7 +280,7 @@ index 53bdb4d62294f128b8f4b7bdcddf1052e15f331f..d052f5bb619bf4092810c6d5dd489a38
|
||||
}
|
||||
|
||||
gfx::NativeViewAccessible
|
||||
@@ -354,7 +358,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
|
||||
@@ -367,7 +371,11 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
|
||||
return [in_process_ns_window_bridge_->ns_view() window];
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ index 53bdb4d62294f128b8f4b7bdcddf1052e15f331f..d052f5bb619bf4092810c6d5dd489a38
|
||||
}
|
||||
|
||||
remote_cocoa::mojom::NativeWidgetNSWindow*
|
||||
@@ -1353,20 +1361,24 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
|
||||
@@ -1382,20 +1390,24 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
|
||||
void NativeWidgetMacNSWindowHost::SetRemoteAccessibilityTokens(
|
||||
const std::vector<uint8_t>& window_token,
|
||||
const std::vector<uint8_t>& view_token) {
|
||||
|
||||
@@ -61,10 +61,10 @@ index f1aadfefe2b35ff4f292a04d834679e1c3fe89e9..7cb39e4059719a4fc323586e83aba47d
|
||||
}
|
||||
|
||||
diff --git a/ui/color/win/native_color_mixers_win.cc b/ui/color/win/native_color_mixers_win.cc
|
||||
index d9df0c0be6abf72c4756fb8e0f1e4b8c308a09f3..07ad3bf7e422272f017695c8b0e0aebedb8d8330 100644
|
||||
index a0eb66d2a28b19ee92e2c3c23fd3eff5d64d502d..1d3b62d504884e036365256443c1a1b4f0c796ab 100644
|
||||
--- a/ui/color/win/native_color_mixers_win.cc
|
||||
+++ b/ui/color/win/native_color_mixers_win.cc
|
||||
@@ -202,6 +202,10 @@ void AddNativeUiColorMixer(ColorProvider* provider,
|
||||
@@ -205,6 +205,10 @@ void AddNativeUiColorMixer(ColorProvider* provider,
|
||||
SetAlpha(kColorNotificationInputForeground, gfx::kGoogleGreyAlpha700);
|
||||
mixer[kColorSliderTrack] = AlphaBlend(
|
||||
kColorNativeHighlight, kColorNativeWindow, gfx::kGoogleGreyAlpha400);
|
||||
@@ -75,7 +75,7 @@ index d9df0c0be6abf72c4756fb8e0f1e4b8c308a09f3..07ad3bf7e422272f017695c8b0e0aebe
|
||||
|
||||
// Window Background
|
||||
mixer[kColorBubbleFooterBackground] = {kColorNativeWindow};
|
||||
@@ -211,6 +215,7 @@ void AddNativeUiColorMixer(ColorProvider* provider,
|
||||
@@ -214,6 +218,7 @@ void AddNativeUiColorMixer(ColorProvider* provider,
|
||||
mixer[kColorFrameInactive] = {kColorNativeWindow};
|
||||
mixer[kColorPrimaryBackground] = {kColorNativeWindow};
|
||||
mixer[kColorTooltipBackground] = {kColorNativeWindow};
|
||||
@@ -83,7 +83,7 @@ index d9df0c0be6abf72c4756fb8e0f1e4b8c308a09f3..07ad3bf7e422272f017695c8b0e0aebe
|
||||
|
||||
// Window Text
|
||||
mixer[kColorAlertLowSeverity] = {kColorNativeWindowText};
|
||||
@@ -225,6 +230,7 @@ void AddNativeUiColorMixer(ColorProvider* provider,
|
||||
@@ -228,6 +233,7 @@ void AddNativeUiColorMixer(ColorProvider* provider,
|
||||
mixer[kColorTableGroupingIndicator] = {kColorNativeWindowText};
|
||||
mixer[kColorThrobber] = {kColorNativeWindowText};
|
||||
mixer[kColorTooltipForeground] = {kColorNativeWindowText};
|
||||
@@ -91,7 +91,7 @@ index d9df0c0be6abf72c4756fb8e0f1e4b8c308a09f3..07ad3bf7e422272f017695c8b0e0aebe
|
||||
|
||||
// Hyperlinks
|
||||
mixer[kColorForcedHotlight] = {kColorNativeHotlight};
|
||||
@@ -271,6 +277,7 @@ void AddNativeUiColorMixer(ColorProvider* provider,
|
||||
@@ -274,6 +280,7 @@ void AddNativeUiColorMixer(ColorProvider* provider,
|
||||
mixer[kColorTextfieldForeground] = {kColorNativeBtnText};
|
||||
mixer[kColorTextfieldForegroundPlaceholder] = {kColorNativeBtnText};
|
||||
mixer[kColorTextfieldForegroundDisabled] = {kColorNativeBtnText};
|
||||
|
||||
@@ -706,7 +706,7 @@ index 3f9a514fb41d72c5d06de6ac989f9d7c0513a4e7..0e7ada9df962808dad7caf074a08ebde
|
||||
// Tells the browser printing failed.
|
||||
PrintingFailed(int32 cookie, PrintFailureReason reason);
|
||||
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
|
||||
index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc86a1777cd 100644
|
||||
index 5a58210461222ed431624bf161b32770c5ae97e6..aec69c525cefd73e50f50883ac0dc8bee94e78c0 100644
|
||||
--- a/components/printing/renderer/print_render_frame_helper.cc
|
||||
+++ b/components/printing/renderer/print_render_frame_helper.cc
|
||||
@@ -45,6 +45,7 @@
|
||||
@@ -717,7 +717,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
#include "printing/units.h"
|
||||
#include "services/metrics/public/cpp/ukm_source_id.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
@@ -1322,14 +1323,14 @@ void PrintRenderFrameHelper::ScriptedPrint(bool user_initiated) {
|
||||
@@ -1237,14 +1238,14 @@ void PrintRenderFrameHelper::ScriptedPrint(bool user_initiated) {
|
||||
}
|
||||
|
||||
print_in_progress_ = true;
|
||||
@@ -734,7 +734,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
if (!weak_this) {
|
||||
return;
|
||||
}
|
||||
@@ -1360,7 +1361,7 @@ void PrintRenderFrameHelper::BindPrintRenderFrameReceiver(
|
||||
@@ -1275,7 +1276,7 @@ void PrintRenderFrameHelper::BindPrintRenderFrameReceiver(
|
||||
receivers_.Add(this, std::move(receiver));
|
||||
}
|
||||
|
||||
@@ -743,7 +743,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
ScopedIPC scoped_ipc(weak_ptr_factory_.GetWeakPtr());
|
||||
if (ipc_nesting_level_ > kAllowedIpcDepthForPrint)
|
||||
return;
|
||||
@@ -1375,7 +1376,7 @@ void PrintRenderFrameHelper::PrintRequestedPages() {
|
||||
@@ -1290,7 +1291,7 @@ void PrintRenderFrameHelper::PrintRequestedPages() {
|
||||
// plugin node and print that instead.
|
||||
auto plugin = delegate_->GetPdfElement(frame);
|
||||
|
||||
@@ -752,7 +752,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
|
||||
if (render_frame_gone_) {
|
||||
return;
|
||||
@@ -1462,7 +1463,8 @@ void PrintRenderFrameHelper::PrintForSystemDialog() {
|
||||
@@ -1377,7 +1378,8 @@ void PrintRenderFrameHelper::PrintForSystemDialog() {
|
||||
}
|
||||
|
||||
Print(frame, print_preview_context_.source_node(),
|
||||
@@ -762,7 +762,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
if (render_frame_gone_) {
|
||||
return;
|
||||
}
|
||||
@@ -1525,6 +1527,8 @@ void PrintRenderFrameHelper::PrintPreview(base::Value::Dict settings) {
|
||||
@@ -1440,6 +1442,8 @@ void PrintRenderFrameHelper::PrintPreview(base::Value::Dict settings) {
|
||||
if (ipc_nesting_level_ > kAllowedIpcDepthForPrint)
|
||||
return;
|
||||
|
||||
@@ -771,7 +771,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
print_preview_context_.OnPrintPreview();
|
||||
|
||||
#if BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
@@ -2158,7 +2162,8 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) {
|
||||
@@ -2065,7 +2069,8 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) {
|
||||
}
|
||||
|
||||
Print(duplicate_node.GetDocument().GetFrame(), duplicate_node,
|
||||
@@ -781,7 +781,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
// Check if `this` is still valid.
|
||||
if (!weak_this) {
|
||||
return;
|
||||
@@ -2174,7 +2179,9 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) {
|
||||
@@ -2081,7 +2086,9 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) {
|
||||
|
||||
void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame,
|
||||
const blink::WebNode& node,
|
||||
@@ -792,7 +792,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
// If still not finished with earlier print request simply ignore.
|
||||
if (prep_frame_view_)
|
||||
return;
|
||||
@@ -2182,7 +2189,7 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame,
|
||||
@@ -2089,7 +2096,7 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame,
|
||||
FrameReference frame_ref(frame);
|
||||
|
||||
uint32_t expected_page_count = 0;
|
||||
@@ -801,7 +801,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
DidFinishPrinting(PrintingResult::kFailPrintInit);
|
||||
return; // Failed to init print page settings.
|
||||
}
|
||||
@@ -2201,8 +2208,15 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame,
|
||||
@@ -2108,8 +2115,15 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame,
|
||||
print_pages_params_->params->print_scaling_option;
|
||||
|
||||
auto self = weak_ptr_factory_.GetWeakPtr();
|
||||
@@ -818,7 +818,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
// Check if `this` is still valid.
|
||||
if (!self)
|
||||
return;
|
||||
@@ -2446,35 +2460,47 @@ void PrintRenderFrameHelper::IPCProcessed() {
|
||||
@@ -2349,35 +2363,47 @@ void PrintRenderFrameHelper::IPCProcessed() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -876,7 +876,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2579,7 +2605,7 @@ mojom::PrintPagesParamsPtr PrintRenderFrameHelper::GetPrintSettingsFromUser(
|
||||
@@ -2482,7 +2508,7 @@ mojom::PrintPagesParamsPtr PrintRenderFrameHelper::GetPrintSettingsFromUser(
|
||||
std::move(params),
|
||||
base::BindOnce(
|
||||
[](base::OnceClosure quit_closure, mojom::PrintPagesParamsPtr* output,
|
||||
@@ -886,7 +886,7 @@ index 658a3c59993ac30feaea4d86148ed5adc9dcbdb1..afa788bbefc8f814af9b70ff5b5ebbc8
|
||||
std::move(quit_closure).Run();
|
||||
},
|
||||
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
|
||||
index 8d65b7b6440c8e653eb1b3f9c50b40944b7ae61b..eb6b4a42d507ff216fc07328c1907815a082ef19 100644
|
||||
index 5cbb2940f83af329ea38efca5bf3216056269654..8cf783d37589fdca88592eeb9b8cc91b6ae60203 100644
|
||||
--- a/components/printing/renderer/print_render_frame_helper.h
|
||||
+++ b/components/printing/renderer/print_render_frame_helper.h
|
||||
@@ -247,7 +247,7 @@ class PrintRenderFrameHelper
|
||||
|
||||
@@ -30,10 +30,10 @@ index fe010b1f001130fbdeaf4ef9ce7798e4baf958b5..28f1305f439be7f669e482ac0e4804c0
|
||||
// RenderWidgetHost on the primary main frame, and false otherwise.
|
||||
virtual bool IsWidgetForPrimaryMainFrame(RenderWidgetHostImpl*);
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
index 149ead7bbf52ad127747c5f80f0e35a13203502a..0f2a73f990fd41112d18ab6a9ed5bc43b90c235a 100644
|
||||
index 2d946e29c94a07a1aa4f4b6018ab1b564440f55d..b013e7eaa4a5db79d19378e437d8d3d18dd1b2aa 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
@@ -2121,6 +2121,9 @@ void RenderWidgetHostImpl::SetCursor(const ui::Cursor& cursor) {
|
||||
@@ -2127,6 +2127,9 @@ void RenderWidgetHostImpl::SetCursor(const ui::Cursor& cursor) {
|
||||
if (view_) {
|
||||
view_->UpdateCursor(cursor);
|
||||
}
|
||||
|
||||
2359
patches/chromium/revert_same_party_cookie_attribute_removal.patch
Normal file
2359
patches/chromium/revert_same_party_cookie_attribute_removal.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,10 +15,10 @@ Note that we also need to manually update embedder's
|
||||
`api::WebContents::IsFullscreenForTabOrPending` value.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index 586aa87098bd3db10440fe865a02c05e7b3be14f..cb372df065f83614ff6c2954035795e67eb69d0c 100644
|
||||
index ef79bf0e27bd0c5eb9fe5f0aef1cf6c6a2900e13..f121a6b0395d9ee1cf5d5c03ee1dc8968167428a 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -7417,6 +7417,17 @@ void RenderFrameHostImpl::EnterFullscreen(
|
||||
@@ -7435,6 +7435,17 @@ void RenderFrameHostImpl::EnterFullscreen(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
0
patches/libvpx/.patches
Normal file
0
patches/libvpx/.patches
Normal file
@@ -42,3 +42,10 @@ fix_isurl_implementation.patch
|
||||
ci_ensure_node_tests_set_electron_run_as_node.patch
|
||||
chore_update_fixtures_errors_force_colors_snapshot.patch
|
||||
fix_assert_module_in_the_renderer_process.patch
|
||||
tls_ensure_tls_sockets_are_closed_if_the_underlying_wrap_closes.patch
|
||||
test_deflake_test-tls-socket-close.patch
|
||||
net_fix_crash_due_to_simultaneous_close_shutdown_on_js_stream.patch
|
||||
net_use_asserts_in_js_socket_stream_to_catch_races_in_future.patch
|
||||
lib_fix_broadcastchannel_initialization_location.patch
|
||||
fix_handle_possible_disabled_sharedarraybuffer.patch
|
||||
win_process_avoid_assert_after_spawning_store_app_4152.patch
|
||||
|
||||
@@ -11,7 +11,7 @@ trying to see whether or not the lines are greyed out. One possibility
|
||||
would be to upstream a changed test that doesn't hardcode line numbers.
|
||||
|
||||
diff --git a/test/fixtures/errors/force_colors.snapshot b/test/fixtures/errors/force_colors.snapshot
|
||||
index 0334a0b4faa3633aa8617b9538873e7f3540513b..7f85ddc507c9c38ce85ed2a48f8152eef168717b 100644
|
||||
index 0334a0b4faa3633aa8617b9538873e7f3540513b..fa9989f55980aeddd3fa944318488c0289e3ab6e 100644
|
||||
--- a/test/fixtures/errors/force_colors.snapshot
|
||||
+++ b/test/fixtures/errors/force_colors.snapshot
|
||||
@@ -4,11 +4,12 @@ throw new Error('Should include grayed stack trace')
|
||||
@@ -27,7 +27,7 @@ index 0334a0b4faa3633aa8617b9538873e7f3540513b..7f85ddc507c9c38ce85ed2a48f8152ee
|
||||
+[90m at Object..js (node:internal*modules*cjs*loader:1326:10)[39m
|
||||
+[90m at Module.load (node:internal*modules*cjs*loader:1126:32)[39m
|
||||
+[90m at node:internal*modules*cjs*loader:967:12[39m
|
||||
+[90m at Function._load (node:electron*js2c*asar_bundle:757:32)[39m
|
||||
+[90m at Function._load (node:electron*js2c*asar_bundle:743:32)[39m
|
||||
+[90m at Function.executeUserEntryPoint [as runMain] (node:internal*modules*run_main:96:12)[39m
|
||||
[90m at node:internal*main*run_main_module:23:47[39m
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Mon, 2 Oct 2023 16:03:43 +0200
|
||||
Subject: fix: handle possible disabled SharedArrayBuffer
|
||||
|
||||
It's possible for SharedArrayBuffer to be disabled with the -no-harmony-sharedarraybuffer
|
||||
flag, and so we should guard uses with a check for potential undefined-ness.
|
||||
|
||||
This should be upstreamed to Node.js.
|
||||
|
||||
diff --git a/lib/internal/crypto/webidl.js b/lib/internal/crypto/webidl.js
|
||||
index 9f5340c223902c5ff61def05e8a4f470b4f328e8..d6dbfa482f9ebff3f99fb810e072cf9a03d1cd4d 100644
|
||||
--- a/lib/internal/crypto/webidl.js
|
||||
+++ b/lib/internal/crypto/webidl.js
|
||||
@@ -183,7 +183,10 @@ function isNonSharedArrayBuffer(V) {
|
||||
return ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V);
|
||||
}
|
||||
|
||||
+// SharedArrayBuffers can be disabled with --no-harmony-sharedarraybuffer.
|
||||
function isSharedArrayBuffer(V) {
|
||||
+ if (SharedArrayBuffer === undefined)
|
||||
+ return false;
|
||||
return ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V);
|
||||
}
|
||||
|
||||
diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js
|
||||
index be4d82086199855a10108528b3dacc663b839454..10c33bacc0529e12f52aaf1baf6d42489b2a75a7 100644
|
||||
--- a/lib/internal/main/worker_thread.js
|
||||
+++ b/lib/internal/main/worker_thread.js
|
||||
@@ -108,6 +108,7 @@ port.on('message', (message) => {
|
||||
|
||||
require('internal/worker').assignEnvironmentData(environmentData);
|
||||
|
||||
+ // SharedArrayBuffers can be disabled with --no-harmony-sharedarraybuffer.
|
||||
if (SharedArrayBuffer !== undefined) {
|
||||
// The counter is only passed to the workers created by the main thread,
|
||||
// not to workers created by other workers.
|
||||
@@ -0,0 +1,44 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Mon, 27 Feb 2023 12:56:15 +0100
|
||||
Subject: lib: fix BroadcastChannel initialization location
|
||||
|
||||
Refs https://github.com/nodejs/node/pull/40532.
|
||||
|
||||
Fixes a bug in the above, wherein BroadcastChannel should have been
|
||||
initialized in bootstrap/browser instead of bootstrap/node. That
|
||||
inadvertently made it such that there was incorrect handling of the
|
||||
DOM vs Node.js implementations of BroadcastChannel.
|
||||
|
||||
This will be upstreamed.
|
||||
|
||||
diff --git a/lib/internal/bootstrap/browser.js b/lib/internal/bootstrap/browser.js
|
||||
index 5be4dd6176482c724455cbbeeaa9680e849a091b..29ccee75d77da072735032f0a25363ac88a023ba 100644
|
||||
--- a/lib/internal/bootstrap/browser.js
|
||||
+++ b/lib/internal/bootstrap/browser.js
|
||||
@@ -12,6 +12,10 @@ const {
|
||||
} = require('internal/util');
|
||||
const config = internalBinding('config');
|
||||
|
||||
+// Non-standard extensions:
|
||||
+const { BroadcastChannel } = require('internal/worker/io');
|
||||
+exposeInterface(globalThis, 'BroadcastChannel', BroadcastChannel);
|
||||
+
|
||||
// https://console.spec.whatwg.org/#console-namespace
|
||||
exposeNamespace(globalThis, 'console',
|
||||
createGlobalConsole());
|
||||
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
|
||||
index 13ea68c96fd415f976aab0f291a1b7c688db1c58..0ca3de08fffb344c0330ce0f8d28b2d3d0b24350 100644
|
||||
--- a/lib/internal/bootstrap/node.js
|
||||
+++ b/lib/internal/bootstrap/node.js
|
||||
@@ -238,10 +238,6 @@ const {
|
||||
queueMicrotask,
|
||||
} = require('internal/process/task_queues');
|
||||
|
||||
-// Non-standard extensions:
|
||||
-const { BroadcastChannel } = require('internal/worker/io');
|
||||
-exposeInterface(globalThis, 'BroadcastChannel', BroadcastChannel);
|
||||
-
|
||||
defineOperation(globalThis, 'queueMicrotask', queueMicrotask);
|
||||
|
||||
const timers = require('timers');
|
||||
@@ -0,0 +1,171 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tim Perry <pimterry@gmail.com>
|
||||
Date: Thu, 24 Aug 2023 16:05:02 +0100
|
||||
Subject: net: fix crash due to simultaneous close/shutdown on JS Stream
|
||||
Sockets
|
||||
|
||||
A JS stream socket wraps a stream, exposing it as a socket for something
|
||||
on top which needs a socket specifically (e.g. an HTTP server).
|
||||
|
||||
If the internal stream is closed in the same tick as the layer on top
|
||||
attempts to close this stream, the race between doShutdown and doClose
|
||||
results in an uncatchable exception. A similar race can happen with
|
||||
doClose and doWrite.
|
||||
|
||||
It seems legitimate these can happen in parallel, so this resolves that
|
||||
by explicitly detecting and handling that situation: if a close is in
|
||||
progress, both doShutdown & doWrite allow doClose to run
|
||||
finishShutdown/Write for them, cancelling the operation, without trying
|
||||
to use this._handle (which will be null) in the meantime.
|
||||
|
||||
PR-URL: https://github.com/nodejs/node/pull/49400
|
||||
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
||||
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
|
||||
|
||||
diff --git a/lib/internal/js_stream_socket.js b/lib/internal/js_stream_socket.js
|
||||
index 8bc19296620b3fd0e5487165743f0f1bc2d342e7..68e1802a63b012b59418b79a0e68de5147543a23 100644
|
||||
--- a/lib/internal/js_stream_socket.js
|
||||
+++ b/lib/internal/js_stream_socket.js
|
||||
@@ -21,6 +21,7 @@ const { ERR_STREAM_WRAP } = require('internal/errors').codes;
|
||||
const kCurrentWriteRequest = Symbol('kCurrentWriteRequest');
|
||||
const kCurrentShutdownRequest = Symbol('kCurrentShutdownRequest');
|
||||
const kPendingShutdownRequest = Symbol('kPendingShutdownRequest');
|
||||
+const kPendingClose = Symbol('kPendingClose');
|
||||
|
||||
function isClosing() { return this[owner_symbol].isClosing(); }
|
||||
|
||||
@@ -94,6 +95,7 @@ class JSStreamSocket extends Socket {
|
||||
this[kCurrentWriteRequest] = null;
|
||||
this[kCurrentShutdownRequest] = null;
|
||||
this[kPendingShutdownRequest] = null;
|
||||
+ this[kPendingClose] = false;
|
||||
this.readable = stream.readable;
|
||||
this.writable = stream.writable;
|
||||
|
||||
@@ -135,10 +137,17 @@ class JSStreamSocket extends Socket {
|
||||
this[kPendingShutdownRequest] = req;
|
||||
return 0;
|
||||
}
|
||||
+
|
||||
assert(this[kCurrentWriteRequest] === null);
|
||||
assert(this[kCurrentShutdownRequest] === null);
|
||||
this[kCurrentShutdownRequest] = req;
|
||||
|
||||
+ if (this[kPendingClose]) {
|
||||
+ // If doClose is pending, the stream & this._handle are gone. We can't do
|
||||
+ // anything. doClose will call finishShutdown with ECANCELED for us shortly.
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
const handle = this._handle;
|
||||
|
||||
process.nextTick(() => {
|
||||
@@ -164,6 +173,13 @@ class JSStreamSocket extends Socket {
|
||||
assert(this[kCurrentWriteRequest] === null);
|
||||
assert(this[kCurrentShutdownRequest] === null);
|
||||
|
||||
+ if (this[kPendingClose]) {
|
||||
+ // If doClose is pending, the stream & this._handle are gone. We can't do
|
||||
+ // anything. doClose will call finishWrite with ECANCELED for us shortly.
|
||||
+ this[kCurrentWriteRequest] = req; // Store req, for doClose to cancel
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
const handle = this._handle;
|
||||
const self = this;
|
||||
|
||||
@@ -217,6 +233,8 @@ class JSStreamSocket extends Socket {
|
||||
}
|
||||
|
||||
doClose(cb) {
|
||||
+ this[kPendingClose] = true;
|
||||
+
|
||||
const handle = this._handle;
|
||||
|
||||
// When sockets of the "net" module destroyed, they will call
|
||||
@@ -234,6 +252,8 @@ class JSStreamSocket extends Socket {
|
||||
this.finishWrite(handle, uv.UV_ECANCELED);
|
||||
this.finishShutdown(handle, uv.UV_ECANCELED);
|
||||
|
||||
+ this[kPendingClose] = false;
|
||||
+
|
||||
cb();
|
||||
});
|
||||
}
|
||||
diff --git a/test/parallel/test-http2-client-connection-tunnelling.js b/test/parallel/test-http2-client-connection-tunnelling.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6e04121ca71ea81f49c7f50ec11d7fac735c80a9
|
||||
--- /dev/null
|
||||
+++ b/test/parallel/test-http2-client-connection-tunnelling.js
|
||||
@@ -0,0 +1,71 @@
|
||||
+'use strict';
|
||||
+
|
||||
+const common = require('../common');
|
||||
+const fixtures = require('../common/fixtures');
|
||||
+if (!common.hasCrypto)
|
||||
+ common.skip('missing crypto');
|
||||
+const assert = require('assert');
|
||||
+const net = require('net');
|
||||
+const tls = require('tls');
|
||||
+const h2 = require('http2');
|
||||
+
|
||||
+// This test sets up an H2 proxy server, and tunnels a request over one of its streams
|
||||
+// back to itself, via TLS, and then closes the TLS connection. On some Node versions
|
||||
+// (v18 & v20 up to 20.5.1) the resulting JS Stream Socket fails to shutdown correctly
|
||||
+// in this case, and crashes due to a null pointer in finishShutdown.
|
||||
+
|
||||
+const tlsOptions = {
|
||||
+ key: fixtures.readKey('agent1-key.pem'),
|
||||
+ cert: fixtures.readKey('agent1-cert.pem'),
|
||||
+ ALPNProtocols: ['h2']
|
||||
+};
|
||||
+
|
||||
+const netServer = net.createServer((socket) => {
|
||||
+ socket.allowHalfOpen = false;
|
||||
+ // ^ This allows us to trigger this reliably, but it's not strictly required
|
||||
+ // for the bug and crash to happen, skipping this just fails elsewhere later.
|
||||
+
|
||||
+ h2Server.emit('connection', socket);
|
||||
+});
|
||||
+
|
||||
+const h2Server = h2.createSecureServer(tlsOptions, (req, res) => {
|
||||
+ res.writeHead(200);
|
||||
+ res.end();
|
||||
+});
|
||||
+
|
||||
+h2Server.on('connect', (req, res) => {
|
||||
+ res.writeHead(200, {});
|
||||
+ netServer.emit('connection', res.stream);
|
||||
+});
|
||||
+
|
||||
+netServer.listen(0, common.mustCall(() => {
|
||||
+ const proxyClient = h2.connect(`https://localhost:${netServer.address().port}`, {
|
||||
+ rejectUnauthorized: false
|
||||
+ });
|
||||
+
|
||||
+ const proxyReq = proxyClient.request({
|
||||
+ ':method': 'CONNECT',
|
||||
+ ':authority': 'example.com:443'
|
||||
+ });
|
||||
+
|
||||
+ proxyReq.on('response', common.mustCall((response) => {
|
||||
+ assert.strictEqual(response[':status'], 200);
|
||||
+
|
||||
+ // Create a TLS socket within the tunnel, and start sending a request:
|
||||
+ const tlsSocket = tls.connect({
|
||||
+ socket: proxyReq,
|
||||
+ ALPNProtocols: ['h2'],
|
||||
+ rejectUnauthorized: false
|
||||
+ });
|
||||
+
|
||||
+ proxyReq.on('close', common.mustCall(() => {
|
||||
+ proxyClient.close();
|
||||
+ netServer.close();
|
||||
+ }));
|
||||
+
|
||||
+ // Forcibly kill the TLS socket
|
||||
+ tlsSocket.destroy();
|
||||
+
|
||||
+ // This results in an async error in affected Node versions, before the 'close' event
|
||||
+ }));
|
||||
+}));
|
||||
@@ -0,0 +1,30 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tim Perry <pimterry@gmail.com>
|
||||
Date: Fri, 25 Aug 2023 14:16:35 +0100
|
||||
Subject: net: use asserts in JS Socket Stream to catch races in future
|
||||
|
||||
PR-URL: https://github.com/nodejs/node/pull/49400
|
||||
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
||||
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
|
||||
|
||||
diff --git a/lib/internal/js_stream_socket.js b/lib/internal/js_stream_socket.js
|
||||
index 68e1802a63b012b59418b79a0e68de5147543a23..70d6d03069f3f1e85e66864c6c1e6de6084f5ea6 100644
|
||||
--- a/lib/internal/js_stream_socket.js
|
||||
+++ b/lib/internal/js_stream_socket.js
|
||||
@@ -149,6 +149,7 @@ class JSStreamSocket extends Socket {
|
||||
}
|
||||
|
||||
const handle = this._handle;
|
||||
+ assert(handle !== null);
|
||||
|
||||
process.nextTick(() => {
|
||||
// Ensure that write is dispatched asynchronously.
|
||||
@@ -181,6 +182,8 @@ class JSStreamSocket extends Socket {
|
||||
}
|
||||
|
||||
const handle = this._handle;
|
||||
+ assert(handle !== null);
|
||||
+
|
||||
const self = this;
|
||||
|
||||
let pending = bufs.length;
|
||||
28
patches/node/test_deflake_test-tls-socket-close.patch
Normal file
28
patches/node/test_deflake_test-tls-socket-close.patch
Normal file
@@ -0,0 +1,28 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Luigi Pinca <luigipinca@gmail.com>
|
||||
Date: Wed, 13 Sep 2023 08:04:39 +0200
|
||||
Subject: test: deflake test-tls-socket-close
|
||||
|
||||
Move the check for the destroyed state of the remote socket to the inner
|
||||
`setImmediate()`.
|
||||
|
||||
Refs: https://github.com/nodejs/node/pull/49327#issuecomment-1712525257
|
||||
PR-URL: https://github.com/nodejs/node/pull/49575
|
||||
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
|
||||
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
|
||||
|
||||
diff --git a/test/parallel/test-tls-socket-close.js b/test/parallel/test-tls-socket-close.js
|
||||
index 667b291309a4c5636a2c658fa8204b32c2e4df46..70af760d53bb4ddab62c99180d505e943ec269f6 100644
|
||||
--- a/test/parallel/test-tls-socket-close.js
|
||||
+++ b/test/parallel/test-tls-socket-close.js
|
||||
@@ -44,9 +44,9 @@ function connectClient(server) {
|
||||
|
||||
setImmediate(() => {
|
||||
assert.strictEqual(netSocket.destroyed, true);
|
||||
- assert.strictEqual(clientTlsSocket.destroyed, true);
|
||||
|
||||
setImmediate(() => {
|
||||
+ assert.strictEqual(clientTlsSocket.destroyed, true);
|
||||
assert.strictEqual(serverTlsSocket.destroyed, true);
|
||||
|
||||
tlsServer.close();
|
||||
@@ -7,10 +7,10 @@ Instead of disabling the tests, flag them as flaky so they still run
|
||||
but don't cause CI failures on flakes.
|
||||
|
||||
diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status
|
||||
index 913ae4b0b10a7d508d864539cf075fa9c2f9362c..985f1c0d0c0c6afc049bf1d89f91412ecf431215 100644
|
||||
index 913ae4b0b10a7d508d864539cf075fa9c2f9362c..0539a599e57d40c5da650dcf5ffe9a67763506e1 100644
|
||||
--- a/test/parallel/parallel.status
|
||||
+++ b/test/parallel/parallel.status
|
||||
@@ -5,6 +5,12 @@ prefix parallel
|
||||
@@ -5,6 +5,13 @@ prefix parallel
|
||||
# sample-test : PASS,FLAKY
|
||||
|
||||
[true] # This section applies to all platforms
|
||||
@@ -20,6 +20,7 @@ index 913ae4b0b10a7d508d864539cf075fa9c2f9362c..985f1c0d0c0c6afc049bf1d89f91412e
|
||||
+test-fetch: PASS, FLAKY
|
||||
+test-cluster-bind-privileged-port: PASS, FLAKY
|
||||
+test-cluster-shared-handle-bind-privileged-port: PASS, FLAKY
|
||||
+test-debugger-random-port-with-inspect-port: PASS, FLAKY
|
||||
|
||||
[$system==win32]
|
||||
# https://github.com/nodejs/node/issues/24497
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tim Perry <1526883+pimterry@users.noreply.github.com>
|
||||
Date: Fri, 1 Sep 2023 09:00:05 +0200
|
||||
Subject: tls: ensure TLS Sockets are closed if the underlying wrap closes
|
||||
|
||||
This fixes a potential segfault, among various other likely-related
|
||||
issues, which all occur because TLSSockets were not informed if their
|
||||
underlying stream was closed in many cases.
|
||||
|
||||
This also significantly modifies an existing TLS test. With this change
|
||||
in place, that test no longer works, as it tries to mess with internals
|
||||
to trigger a race, and those internals are now cleaned up earlier. This
|
||||
test has been simplified to a more general TLS shutdown test.
|
||||
|
||||
PR-URL: https://github.com/nodejs/node/pull/49327
|
||||
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
||||
Reviewed-By: Debadree Chatterjee <debadree333@gmail.com>
|
||||
|
||||
diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js
|
||||
index 1eff3b3fba8a05d5fade43c6cb00b6621daa8c3d..5bed23e1355e5e5a1965f15ca270fb372f751d66 100644
|
||||
--- a/lib/_tls_wrap.js
|
||||
+++ b/lib/_tls_wrap.js
|
||||
@@ -629,6 +629,9 @@ TLSSocket.prototype._wrapHandle = function(wrap, handle) {
|
||||
defineHandleReading(this, handle);
|
||||
|
||||
this.on('close', onSocketCloseDestroySSL);
|
||||
+ if (wrap) {
|
||||
+ wrap.on('close', () => this.destroy());
|
||||
+ }
|
||||
|
||||
return res;
|
||||
};
|
||||
diff --git a/test/parallel/test-http2-socket-close.js b/test/parallel/test-http2-socket-close.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..02db77bcf8480c79c77175ba802f9fe10ffc4efe
|
||||
--- /dev/null
|
||||
+++ b/test/parallel/test-http2-socket-close.js
|
||||
@@ -0,0 +1,67 @@
|
||||
+'use strict';
|
||||
+
|
||||
+const common = require('../common');
|
||||
+const fixtures = require('../common/fixtures');
|
||||
+if (!common.hasCrypto)
|
||||
+ common.skip('missing crypto');
|
||||
+const assert = require('assert');
|
||||
+const net = require('net');
|
||||
+const h2 = require('http2');
|
||||
+
|
||||
+const tlsOptions = {
|
||||
+ key: fixtures.readKey('agent1-key.pem'),
|
||||
+ cert: fixtures.readKey('agent1-cert.pem'),
|
||||
+ ALPNProtocols: ['h2']
|
||||
+};
|
||||
+
|
||||
+// Create a net server that upgrades sockets to HTTP/2 manually, handles the
|
||||
+// request, and then shuts down via a short socket timeout and a longer H2 session
|
||||
+// timeout. This is an unconventional way to shut down a session (the underlying
|
||||
+// socket closing first) but it should work - critically, it shouldn't segfault
|
||||
+// (as it did until Node v20.5.1).
|
||||
+
|
||||
+let serverRawSocket;
|
||||
+let serverH2Session;
|
||||
+
|
||||
+const netServer = net.createServer((socket) => {
|
||||
+ serverRawSocket = socket;
|
||||
+ h2Server.emit('connection', socket);
|
||||
+});
|
||||
+
|
||||
+const h2Server = h2.createSecureServer(tlsOptions, (req, res) => {
|
||||
+ res.writeHead(200);
|
||||
+ res.end();
|
||||
+});
|
||||
+
|
||||
+h2Server.on('session', (session) => {
|
||||
+ serverH2Session = session;
|
||||
+});
|
||||
+
|
||||
+netServer.listen(0, common.mustCall(() => {
|
||||
+ const proxyClient = h2.connect(`https://localhost:${netServer.address().port}`, {
|
||||
+ rejectUnauthorized: false
|
||||
+ });
|
||||
+
|
||||
+ proxyClient.on('close', common.mustCall(() => {
|
||||
+ netServer.close();
|
||||
+ }));
|
||||
+
|
||||
+ const req = proxyClient.request({
|
||||
+ ':method': 'GET',
|
||||
+ ':path': '/'
|
||||
+ });
|
||||
+
|
||||
+ req.on('response', common.mustCall((response) => {
|
||||
+ assert.strictEqual(response[':status'], 200);
|
||||
+
|
||||
+ // Asynchronously shut down the server's connections after the response,
|
||||
+ // but not in the order it typically expects:
|
||||
+ setTimeout(() => {
|
||||
+ serverRawSocket.destroy();
|
||||
+
|
||||
+ setTimeout(() => {
|
||||
+ serverH2Session.close();
|
||||
+ }, 10);
|
||||
+ }, 10);
|
||||
+ }));
|
||||
+}));
|
||||
diff --git a/test/parallel/test-tls-socket-close.js b/test/parallel/test-tls-socket-close.js
|
||||
index 87355cf8d7bd2d07bb0fab59491b68f3963f8809..667b291309a4c5636a2c658fa8204b32c2e4df46 100644
|
||||
--- a/test/parallel/test-tls-socket-close.js
|
||||
+++ b/test/parallel/test-tls-socket-close.js
|
||||
@@ -8,37 +8,18 @@ const tls = require('tls');
|
||||
const net = require('net');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
-// Regression test for https://github.com/nodejs/node/issues/8074
|
||||
-//
|
||||
-// This test has a dependency on the order in which the TCP connection is made,
|
||||
-// and TLS server handshake completes. It assumes those server side events occur
|
||||
-// before the client side write callback, which is not guaranteed by the TLS
|
||||
-// API. It usually passes with TLS1.3, but TLS1.3 didn't exist at the time the
|
||||
-// bug existed.
|
||||
-//
|
||||
-// Pin the test to TLS1.2, since the test shouldn't be changed in a way that
|
||||
-// doesn't trigger a segfault in Node.js 7.7.3:
|
||||
-// https://github.com/nodejs/node/issues/13184#issuecomment-303700377
|
||||
-tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
-
|
||||
const key = fixtures.readKey('agent2-key.pem');
|
||||
const cert = fixtures.readKey('agent2-cert.pem');
|
||||
|
||||
-let tlsSocket;
|
||||
-// tls server
|
||||
+let serverTlsSocket;
|
||||
const tlsServer = tls.createServer({ cert, key }, (socket) => {
|
||||
- tlsSocket = socket;
|
||||
- socket.on('error', common.mustCall((error) => {
|
||||
- assert.strictEqual(error.code, 'EINVAL');
|
||||
- tlsServer.close();
|
||||
- netServer.close();
|
||||
- }));
|
||||
+ serverTlsSocket = socket;
|
||||
});
|
||||
|
||||
+// A plain net server, that manually passes connections to the TLS
|
||||
+// server to be upgraded
|
||||
let netSocket;
|
||||
-// plain tcp server
|
||||
const netServer = net.createServer((socket) => {
|
||||
- // If client wants to use tls
|
||||
tlsServer.emit('connection', socket);
|
||||
|
||||
netSocket = socket;
|
||||
@@ -46,35 +27,32 @@ const netServer = net.createServer((socket) => {
|
||||
connectClient(netServer);
|
||||
}));
|
||||
|
||||
+// A client that connects, sends one message, and closes the raw connection:
|
||||
function connectClient(server) {
|
||||
- const tlsConnection = tls.connect({
|
||||
+ const clientTlsSocket = tls.connect({
|
||||
host: 'localhost',
|
||||
port: server.address().port,
|
||||
rejectUnauthorized: false
|
||||
});
|
||||
|
||||
- tlsConnection.write('foo', 'utf8', common.mustCall(() => {
|
||||
+ clientTlsSocket.write('foo', 'utf8', common.mustCall(() => {
|
||||
assert(netSocket);
|
||||
netSocket.setTimeout(common.platformTimeout(10), common.mustCall(() => {
|
||||
- assert(tlsSocket);
|
||||
- // This breaks if TLSSocket is already managing the socket:
|
||||
+ assert(serverTlsSocket);
|
||||
+
|
||||
netSocket.destroy();
|
||||
- const interval = setInterval(() => {
|
||||
- // Checking this way allows us to do the write at a time that causes a
|
||||
- // segmentation fault (not always, but often) in Node.js 7.7.3 and
|
||||
- // earlier. If we instead, for example, wait on the `close` event, then
|
||||
- // it will not segmentation fault, which is what this test is all about.
|
||||
- if (tlsSocket._handle._parent.bytesRead === 0) {
|
||||
- tlsSocket.write('bar');
|
||||
- clearInterval(interval);
|
||||
- }
|
||||
- }, 1);
|
||||
+
|
||||
+ setImmediate(() => {
|
||||
+ assert.strictEqual(netSocket.destroyed, true);
|
||||
+ assert.strictEqual(clientTlsSocket.destroyed, true);
|
||||
+
|
||||
+ setImmediate(() => {
|
||||
+ assert.strictEqual(serverTlsSocket.destroyed, true);
|
||||
+
|
||||
+ tlsServer.close();
|
||||
+ netServer.close();
|
||||
+ });
|
||||
+ });
|
||||
}));
|
||||
}));
|
||||
- tlsConnection.on('error', (e) => {
|
||||
- // Tolerate the occasional ECONNRESET.
|
||||
- // Ref: https://github.com/nodejs/node/issues/13184
|
||||
- if (e.code !== 'ECONNRESET')
|
||||
- throw e;
|
||||
- });
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jameson Nash <vtjnash@gmail.com>
|
||||
Date: Mon, 2 Oct 2023 15:15:18 +0200
|
||||
Subject: win,process: avoid assert after spawning Store app (#4152)
|
||||
|
||||
Make sure this handle is functional. The Windows kernel seems to have a
|
||||
bug that if the first use of AssignProcessToJobObject is for a Windows
|
||||
Store program, subsequent attempts to use the handle with fail with
|
||||
INVALID_PARAMETER (87). This is possilby because all uses of the handle
|
||||
must be for the same Terminal Services session. We can ensure it is
|
||||
tied to our current session now by adding ourself to it. We could
|
||||
remove ourself afterwards, but there doesn't seem to be a reason to.
|
||||
|
||||
Secondly, we start the process suspended so that we can make sure we
|
||||
added it to the job control object before it does anything itself (such
|
||||
as launch more jobs or exit).
|
||||
|
||||
Fixes: https://github.com/JuliaLang/julia/issues/51461
|
||||
|
||||
diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c
|
||||
index 24c633393fd15dcf87726b174d6b027a969e0f0d..4ad9fec900fa66b0e8c6894701e94f420de903a8 100644
|
||||
--- a/deps/uv/src/win/process.c
|
||||
+++ b/deps/uv/src/win/process.c
|
||||
@@ -102,6 +102,21 @@ static void uv__init_global_job_handle(void) {
|
||||
&info,
|
||||
sizeof info))
|
||||
uv_fatal_error(GetLastError(), "SetInformationJobObject");
|
||||
+
|
||||
+
|
||||
+ if (!AssignProcessToJobObject(uv_global_job_handle_, GetCurrentProcess())) {
|
||||
+ /* Make sure this handle is functional. The Windows kernel has a bug that
|
||||
+ * if the first use of AssignProcessToJobObject is for a Windows Store
|
||||
+ * program, subsequent attempts to use the handle with fail with
|
||||
+ * INVALID_PARAMETER (87). This is possibly because all uses of the handle
|
||||
+ * must be for the same Terminal Services session. We can ensure it is tied
|
||||
+ * to our current session now by adding ourself to it. We could remove
|
||||
+ * ourself afterwards, but there doesn't seem to be a reason to.
|
||||
+ */
|
||||
+ DWORD err = GetLastError();
|
||||
+ if (err != ERROR_ACCESS_DENIED)
|
||||
+ uv_fatal_error(err, "AssignProcessToJobObject");
|
||||
+ }
|
||||
}
|
||||
|
||||
|
||||
@@ -1098,6 +1113,7 @@ int uv_spawn(uv_loop_t* loop,
|
||||
* breakaway.
|
||||
*/
|
||||
process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
|
||||
+ process_flags |= CREATE_SUSPENDED;
|
||||
}
|
||||
|
||||
if (!CreateProcessW(application_path,
|
||||
@@ -1115,11 +1131,6 @@ int uv_spawn(uv_loop_t* loop,
|
||||
goto done;
|
||||
}
|
||||
|
||||
- /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
|
||||
-
|
||||
- process->process_handle = info.hProcess;
|
||||
- process->pid = info.dwProcessId;
|
||||
-
|
||||
/* If the process isn't spawned as detached, assign to the global job object
|
||||
* so windows will kill it when the parent process dies. */
|
||||
if (!(options->flags & UV_PROCESS_DETACHED)) {
|
||||
@@ -1142,6 +1153,19 @@ int uv_spawn(uv_loop_t* loop,
|
||||
}
|
||||
}
|
||||
|
||||
+ if (process_flags & CREATE_SUSPENDED) {
|
||||
+ if (ResumeThread(info.hThread) == ((DWORD)-1)) {
|
||||
+ err = GetLastError();
|
||||
+ TerminateProcess(info.hProcess, 1);
|
||||
+ goto done;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
|
||||
+
|
||||
+ process->process_handle = info.hProcess;
|
||||
+ process->pid = info.dwProcessId;
|
||||
+
|
||||
/* Set IPC pid to all IPC pipes. */
|
||||
for (i = 0; i < options->stdio_count; i++) {
|
||||
const uv_stdio_container_t* fdopt = &options->stdio[i];
|
||||
@@ -1,3 +1,4 @@
|
||||
fix_fallback_to_x11_capturer_on_wayland.patch
|
||||
fix_mark_pipewire_capturer_as_failed_after_session_is_closed.patch
|
||||
fix_check_pipewire_init_before_creating_generic_capturer.patch
|
||||
pipewire_capturer_make_restore_tokens_re-usable_more_than_one_time.patch
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jan Grulich <grulja@gmail.com>
|
||||
Date: Mon, 9 Oct 2023 18:54:31 +0200
|
||||
Subject: PipeWire capturer: make restore tokens re-usable more than one time
|
||||
|
||||
Do not automatically remove all tokens once we attempt to use them. This
|
||||
mitigates an issue with Google Meet where an additional instance of a
|
||||
DesktopCapturer is created and destroyed right away, taking away the
|
||||
token we would use otherwise. Also save the token under same SourceId
|
||||
once we get a new (but could be same) token from the restored session.
|
||||
|
||||
Bug: webrtc:15544
|
||||
Change-Id: I565b22f5bf6a4d8a3b7d6d757f9c1046c7a0557d
|
||||
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/322621
|
||||
Commit-Queue: Jan Grulich <grulja@gmail.com>
|
||||
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#40892}
|
||||
|
||||
diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
|
||||
index 5a67c18c1d1f62aa5e3162d9778ca665bac4a1bb..a5df76b0cdecd1e2e68f2f25c80c6b17de8bc808 100644
|
||||
--- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
|
||||
+++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
|
||||
@@ -82,8 +82,10 @@ void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result,
|
||||
<< static_cast<uint>(result);
|
||||
} else if (ScreenCastPortal* screencast_portal = GetScreenCastPortal()) {
|
||||
if (!screencast_portal->RestoreToken().empty()) {
|
||||
+ const SourceId token_id =
|
||||
+ selected_source_id_ ? selected_source_id_ : source_id_;
|
||||
RestoreTokenManager::GetInstance().AddToken(
|
||||
- source_id_, screencast_portal->RestoreToken());
|
||||
+ token_id, screencast_portal->RestoreToken());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +140,7 @@ void BaseCapturerPipeWire::Start(Callback* callback) {
|
||||
ScreenCastPortal::PersistMode::kTransient);
|
||||
if (selected_source_id_) {
|
||||
screencast_portal->SetRestoreToken(
|
||||
- RestoreTokenManager::GetInstance().TakeToken(selected_source_id_));
|
||||
+ RestoreTokenManager::GetInstance().GetToken(selected_source_id_));
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/modules/desktop_capture/linux/wayland/restore_token_manager.cc b/modules/desktop_capture/linux/wayland/restore_token_manager.cc
|
||||
index 5ca9b957a9e4f436bc09d4bc16019b169ae9ba9f..a17d9a49bb031efa340bfd61b4a6f8f5a86d09da 100644
|
||||
--- a/modules/desktop_capture/linux/wayland/restore_token_manager.cc
|
||||
+++ b/modules/desktop_capture/linux/wayland/restore_token_manager.cc
|
||||
@@ -23,10 +23,8 @@ void RestoreTokenManager::AddToken(DesktopCapturer::SourceId id,
|
||||
restore_tokens_.insert({id, token});
|
||||
}
|
||||
|
||||
-std::string RestoreTokenManager::TakeToken(DesktopCapturer::SourceId id) {
|
||||
- std::string token = restore_tokens_[id];
|
||||
- // Remove the token as it cannot be used anymore
|
||||
- restore_tokens_.erase(id);
|
||||
+std::string RestoreTokenManager::GetToken(DesktopCapturer::SourceId id) {
|
||||
+ const std::string token = restore_tokens_[id];
|
||||
return token;
|
||||
}
|
||||
|
||||
diff --git a/modules/desktop_capture/linux/wayland/restore_token_manager.h b/modules/desktop_capture/linux/wayland/restore_token_manager.h
|
||||
index 174bef121f74b7b2b529d681b86c4fb4218586ea..ad4f74790f2a5cfba304fc11d47c9924db9013d8 100644
|
||||
--- a/modules/desktop_capture/linux/wayland/restore_token_manager.h
|
||||
+++ b/modules/desktop_capture/linux/wayland/restore_token_manager.h
|
||||
@@ -27,7 +27,7 @@ class RestoreTokenManager {
|
||||
static RestoreTokenManager& GetInstance();
|
||||
|
||||
void AddToken(DesktopCapturer::SourceId id, const std::string& token);
|
||||
- std::string TakeToken(DesktopCapturer::SourceId id);
|
||||
+ std::string GetToken(DesktopCapturer::SourceId id);
|
||||
|
||||
// Returns a source ID which does not have any token associated with it yet.
|
||||
DesktopCapturer::SourceId GetUnusedId();
|
||||
34
script/get-git-version.py
Executable file
34
script/get-git-version.py
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
# Find the nearest tag to the current HEAD.
|
||||
# This is equivalent to our old logic of "use a value in package.json" for the
|
||||
# following reasons:
|
||||
#
|
||||
# 1. Whenever we updated the package.json we ALSO pushed a tag with the same
|
||||
# version.
|
||||
# 2. Whenever we _reverted_ a bump all we actually did was push a commit that
|
||||
# deleted the tag and changed the version number back.
|
||||
#
|
||||
# The only difference in the "git describe" technique is that technically a
|
||||
# commit can "change" its version number if a tag is created / removed
|
||||
# retroactively. i.e. the first time a commit is pushed it will be 1.2.3
|
||||
# and after the tag is made rebuilding the same commit will result in it being
|
||||
# 1.2.4.
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
['git', 'describe', '--tags', '--abbrev=0'],
|
||||
cwd=os.path.abspath(os.path.join(os.path.dirname(__file__), '..')),
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
version = output.strip().replace('v', '')
|
||||
print(version)
|
||||
except Exception:
|
||||
# When there is error we print a null version string instead of throwing an
|
||||
# exception, this is because for linux/bsd packages and some vendor builds
|
||||
# electron is built from a source code tarball and there is no git information
|
||||
# there.
|
||||
print('0.0.0-no-git-tag-found')
|
||||
@@ -1,22 +1,30 @@
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const { ELECTRON_DIR, getOutDir } = require('./utils');
|
||||
|
||||
// Print the value of electron_version set in gn config.
|
||||
module.exports.getElectronVersion = () => {
|
||||
// Find the nearest tag to the current HEAD
|
||||
// This is equivilant to our old logic of "use a value in package.json" for the following reasons
|
||||
//
|
||||
// 1. Whenever we updated the package.json we ALSO pushed a tag with the same version
|
||||
// 2. Whenever we _reverted_ a bump all we actually did was push a commit that deleted the tag and changed the version number back
|
||||
//
|
||||
// The only difference in the "git describe" technique is that technically a commit can "change" it's version
|
||||
// number if a tag is created / removed retroactively. i.e. the first time a commit is pushed it will be 1.2.3
|
||||
// and after the tag is made rebuilding the same commit will result in it being 1.2.4
|
||||
const output = spawnSync('git', ['describe', '--tags', '--abbrev=0'], {
|
||||
cwd: path.resolve(__dirname, '..', '..')
|
||||
});
|
||||
if (output.status !== 0) {
|
||||
console.error(output.stderr);
|
||||
throw new Error('Failed to get current electron version');
|
||||
// Read the override_electron_version from args.gn file.
|
||||
try {
|
||||
const outDir = path.resolve(ELECTRON_DIR, '..', 'out', getOutDir());
|
||||
const content = fs.readFileSync(path.join(outDir, 'args.gn'));
|
||||
const regex = /override_electron_version\s*=\s*["']([^"']+)["']/;
|
||||
const match = content.toString().match(regex);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
} catch (error) {
|
||||
// Error may happen when trying to get version before running gn, which is a
|
||||
// valid case and error will be ignored.
|
||||
}
|
||||
return output.stdout.toString().trim().replace(/^v/g, '');
|
||||
// Most win32 machines have python.exe but no python3.exe.
|
||||
const python = process.platform === 'win32' ? 'python.exe' : 'python3';
|
||||
// Get the version from git tag if it is not defined in gn args.
|
||||
const output = spawnSync(python, [path.join(ELECTRON_DIR, 'script', 'get-git-version.py')]);
|
||||
if (output.status !== 0) {
|
||||
throw new Error(`Failed to get git tag, script quit with ${output.status}: ${output.stdout}`);
|
||||
}
|
||||
return output.stdout.toString().trim();
|
||||
};
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
from lib.util import get_electron_version
|
||||
|
||||
print(get_electron_version())
|
||||
@@ -363,8 +363,10 @@ def upload_io_to_github(release, filename, filepath, version):
|
||||
(filename))
|
||||
script_path = os.path.join(
|
||||
ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-to-github.ts')
|
||||
execute([TS_NODE, script_path, filepath, filename, str(release['id']),
|
||||
version])
|
||||
upload_gh_output = execute([TS_NODE, script_path, filepath, filename,
|
||||
str(release['id']), version])
|
||||
if is_verbose_mode():
|
||||
print(upload_gh_output)
|
||||
|
||||
|
||||
def upload_sha256_checksum(version, file_path, key_prefix=None):
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "base/path_service.h"
|
||||
#include "base/system/sys_info.h"
|
||||
#include "base/values.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/icon_manager.h"
|
||||
#include "chrome/common/chrome_features.h"
|
||||
@@ -1478,23 +1479,8 @@ void App::SetUserAgentFallback(const std::string& user_agent) {
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
|
||||
bool App::IsRunningUnderARM64Translation() const {
|
||||
USHORT processMachine = 0;
|
||||
USHORT nativeMachine = 0;
|
||||
|
||||
auto IsWow64Process2 = reinterpret_cast<decltype(&::IsWow64Process2)>(
|
||||
GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process2"));
|
||||
|
||||
if (IsWow64Process2 == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return nativeMachine == IMAGE_FILE_MACHINE_ARM64;
|
||||
return base::win::OSInfo::IsRunningEmulatedOnArm64();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -953,6 +953,14 @@ void BaseWindow::AddTabbedWindow(NativeWindow* window,
|
||||
args->ThrowError("AddTabbedWindow cannot be called by a window on itself.");
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> BaseWindow::GetTabbingIdentifier() {
|
||||
auto tabbing_id = window_->GetTabbingIdentifier();
|
||||
if (!tabbing_id.has_value())
|
||||
return v8::Undefined(isolate());
|
||||
|
||||
return gin::ConvertToV8(isolate(), tabbing_id.value());
|
||||
}
|
||||
|
||||
void BaseWindow::SetAutoHideMenuBar(bool auto_hide) {
|
||||
window_->SetAutoHideMenuBar(auto_hide);
|
||||
}
|
||||
@@ -1305,6 +1313,7 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("moveTabToNewWindow", &BaseWindow::MoveTabToNewWindow)
|
||||
.SetMethod("toggleTabBar", &BaseWindow::ToggleTabBar)
|
||||
.SetMethod("addTabbedWindow", &BaseWindow::AddTabbedWindow)
|
||||
.SetProperty("tabbingIdentifier", &BaseWindow::GetTabbingIdentifier)
|
||||
.SetMethod("setWindowButtonVisibility",
|
||||
&BaseWindow::SetWindowButtonVisibility)
|
||||
.SetMethod("_getWindowButtonVisibility",
|
||||
|
||||
@@ -198,9 +198,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||
bool GetWindowButtonVisibility() const;
|
||||
void SetWindowButtonPosition(absl::optional<gfx::Point> position);
|
||||
absl::optional<gfx::Point> GetWindowButtonPosition() const;
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
bool IsHiddenInMissionControl();
|
||||
void SetHiddenInMissionControl(bool hidden);
|
||||
#endif
|
||||
@@ -215,6 +213,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||
void MoveTabToNewWindow();
|
||||
void ToggleTabBar();
|
||||
void AddTabbedWindow(NativeWindow* window, gin_helper::Arguments* args);
|
||||
v8::Local<v8::Value> GetTabbingIdentifier();
|
||||
void SetAutoHideMenuBar(bool auto_hide);
|
||||
bool IsMenuBarAutoHide();
|
||||
void SetMenuBarVisibility(bool visible);
|
||||
|
||||
@@ -216,7 +216,11 @@ void Notification::NotificationClosed() {
|
||||
|
||||
void Notification::Close() {
|
||||
if (notification_) {
|
||||
notification_->Dismiss();
|
||||
if (notification_->is_dismissed()) {
|
||||
notification_->Remove();
|
||||
} else {
|
||||
notification_->Dismiss();
|
||||
}
|
||||
notification_->set_delegate(nullptr);
|
||||
notification_.reset();
|
||||
}
|
||||
|
||||
@@ -90,16 +90,15 @@ gin::ObjectTemplateBuilder SystemPreferences::GetObjectTemplateBuilder(
|
||||
&SystemPreferences::IsSwipeTrackingFromScrollEventsEnabled)
|
||||
.SetMethod("getEffectiveAppearance",
|
||||
&SystemPreferences::GetEffectiveAppearance)
|
||||
.SetMethod("getAppLevelAppearance",
|
||||
&SystemPreferences::GetAppLevelAppearance)
|
||||
.SetMethod("setAppLevelAppearance",
|
||||
&SystemPreferences::SetAppLevelAppearance)
|
||||
.SetMethod("getSystemColor", &SystemPreferences::GetSystemColor)
|
||||
.SetMethod("canPromptTouchID", &SystemPreferences::CanPromptTouchID)
|
||||
.SetMethod("promptTouchID", &SystemPreferences::PromptTouchID)
|
||||
.SetMethod("isTrustedAccessibilityClient",
|
||||
&SystemPreferences::IsTrustedAccessibilityClient)
|
||||
.SetMethod("askForMediaAccess", &SystemPreferences::AskForMediaAccess)
|
||||
.SetProperty(
|
||||
"accessibilityDisplayShouldReduceTransparency",
|
||||
&SystemPreferences::AccessibilityDisplayShouldReduceTransparency)
|
||||
#endif
|
||||
.SetMethod("getAnimationSettings",
|
||||
&SystemPreferences::GetAnimationSettings);
|
||||
|
||||
@@ -96,6 +96,7 @@ class SystemPreferences
|
||||
gin::Arguments* args);
|
||||
void RemoveUserDefault(const std::string& name);
|
||||
bool IsSwipeTrackingFromScrollEventsEnabled();
|
||||
bool AccessibilityDisplayShouldReduceTransparency();
|
||||
|
||||
std::string GetSystemColor(gin_helper::ErrorThrower thrower,
|
||||
const std::string& color);
|
||||
@@ -112,8 +113,6 @@ class SystemPreferences
|
||||
// TODO(MarshallOfSound): Write tests for these methods once we
|
||||
// are running tests on a Mojave machine
|
||||
v8::Local<v8::Value> GetEffectiveAppearance(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> GetAppLevelAppearance(v8::Isolate* isolate);
|
||||
void SetAppLevelAppearance(gin::Arguments* args);
|
||||
#endif
|
||||
v8::Local<v8::Value> GetAnimationSettings(v8::Isolate* isolate);
|
||||
|
||||
|
||||
@@ -475,14 +475,7 @@ bool SystemPreferences::IsTrustedAccessibilityClient(bool prompt) {
|
||||
std::string SystemPreferences::GetColor(gin_helper::ErrorThrower thrower,
|
||||
const std::string& color) {
|
||||
NSColor* sysColor = nil;
|
||||
if (color == "alternate-selected-control-text") {
|
||||
sysColor = [NSColor alternateSelectedControlTextColor];
|
||||
EmitWarning(
|
||||
node::Environment::GetCurrent(thrower.isolate()),
|
||||
"'alternate-selected-control-text' is deprecated as an input to "
|
||||
"getColor. Use 'selected-content-background' instead.",
|
||||
"electron");
|
||||
} else if (color == "control-background") {
|
||||
if (color == "control-background") {
|
||||
sysColor = [NSColor controlBackgroundColor];
|
||||
} else if (color == "control") {
|
||||
sysColor = [NSColor controlColor];
|
||||
@@ -609,19 +602,9 @@ v8::Local<v8::Value> SystemPreferences::GetEffectiveAppearance(
|
||||
isolate, [NSApplication sharedApplication].effectiveAppearance);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> SystemPreferences::GetAppLevelAppearance(
|
||||
v8::Isolate* isolate) {
|
||||
return gin::ConvertToV8(isolate,
|
||||
[NSApplication sharedApplication].appearance);
|
||||
}
|
||||
|
||||
void SystemPreferences::SetAppLevelAppearance(gin::Arguments* args) {
|
||||
NSAppearance* appearance;
|
||||
if (args->GetNext(&appearance)) {
|
||||
[[NSApplication sharedApplication] setAppearance:appearance];
|
||||
} else {
|
||||
args->ThrowError();
|
||||
}
|
||||
bool SystemPreferences::AccessibilityDisplayShouldReduceTransparency() {
|
||||
return [[NSWorkspace sharedWorkspace]
|
||||
accessibilityDisplayShouldReduceTransparency];
|
||||
}
|
||||
|
||||
} // namespace electron::api
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "components/security_state/content/content_utils.h"
|
||||
#include "components/security_state/core/security_state.h"
|
||||
#include "content/browser/renderer_host/frame_tree_node.h" // nogncheck
|
||||
#include "content/browser/renderer_host/navigation_controller_impl.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_frame_host_manager.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_widget_host_impl.h" // nogncheck
|
||||
#include "content/browser/renderer_host/render_widget_host_view_base.h" // nogncheck
|
||||
@@ -468,9 +469,16 @@ base::IDMap<WebContents*>& GetAllWebContents() {
|
||||
void OnCapturePageDone(gin_helper::Promise<gfx::Image> promise,
|
||||
base::ScopedClosureRunner capture_handle,
|
||||
const SkBitmap& bitmap) {
|
||||
auto ui_task_runner = content::GetUIThreadTaskRunner({});
|
||||
if (!ui_task_runner->RunsTasksInCurrentSequence()) {
|
||||
ui_task_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(&OnCapturePageDone, std::move(promise),
|
||||
std::move(capture_handle), bitmap));
|
||||
return;
|
||||
}
|
||||
|
||||
// Hack to enable transparency in captured image
|
||||
promise.Resolve(gfx::Image::CreateFrom1xBitmap(bitmap));
|
||||
|
||||
capture_handle.RunAndReset();
|
||||
}
|
||||
|
||||
@@ -1324,12 +1332,6 @@ bool WebContents::HandleKeyboardEvent(
|
||||
bool WebContents::PlatformHandleKeyboardEvent(
|
||||
content::WebContents* source,
|
||||
const content::NativeWebKeyboardEvent& event) {
|
||||
// Escape exits tabbed fullscreen mode.
|
||||
if (event.windows_key_code == ui::VKEY_ESCAPE && is_html_fullscreen()) {
|
||||
ExitFullscreenModeForTab(source);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the webContents has preferences and to ignore shortcuts
|
||||
auto* web_preferences = WebContentsPreferences::From(source);
|
||||
if (web_preferences && web_preferences->ShouldIgnoreMenuShortcuts())
|
||||
@@ -1507,11 +1509,10 @@ void WebContents::FindReply(content::WebContents* web_contents,
|
||||
Emit("found-in-page", result.GetHandle());
|
||||
}
|
||||
|
||||
void WebContents::RequestExclusivePointerAccess(
|
||||
content::WebContents* web_contents,
|
||||
bool user_gesture,
|
||||
bool last_unlocked_by_target,
|
||||
bool allowed) {
|
||||
void WebContents::OnRequestToLockMouse(content::WebContents* web_contents,
|
||||
bool user_gesture,
|
||||
bool last_unlocked_by_target,
|
||||
bool allowed) {
|
||||
if (allowed) {
|
||||
exclusive_access_manager_.mouse_lock_controller()->RequestToLockMouse(
|
||||
web_contents, user_gesture, last_unlocked_by_target);
|
||||
@@ -1528,7 +1529,7 @@ void WebContents::RequestToLockMouse(content::WebContents* web_contents,
|
||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
||||
permission_helper->RequestPointerLockPermission(
|
||||
user_gesture, last_unlocked_by_target,
|
||||
base::BindOnce(&WebContents::RequestExclusivePointerAccess,
|
||||
base::BindOnce(&WebContents::OnRequestToLockMouse,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
@@ -1536,10 +1537,24 @@ void WebContents::LostMouseLock() {
|
||||
exclusive_access_manager_.mouse_lock_controller()->LostMouseLock();
|
||||
}
|
||||
|
||||
void WebContents::OnRequestKeyboardLock(content::WebContents* web_contents,
|
||||
bool esc_key_locked,
|
||||
bool allowed) {
|
||||
if (allowed) {
|
||||
exclusive_access_manager_.keyboard_lock_controller()->RequestKeyboardLock(
|
||||
web_contents, esc_key_locked);
|
||||
} else {
|
||||
web_contents->GotResponseToKeyboardLockRequest(false);
|
||||
}
|
||||
}
|
||||
|
||||
void WebContents::RequestKeyboardLock(content::WebContents* web_contents,
|
||||
bool esc_key_locked) {
|
||||
exclusive_access_manager_.keyboard_lock_controller()->RequestKeyboardLock(
|
||||
web_contents, esc_key_locked);
|
||||
auto* permission_helper =
|
||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
||||
permission_helper->RequestKeyboardLockPermission(
|
||||
esc_key_locked, base::BindOnce(&WebContents::OnRequestKeyboardLock,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
void WebContents::CancelKeyboardLockRequest(
|
||||
@@ -1726,13 +1741,6 @@ void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
|
||||
|
||||
void WebContents::PrimaryMainFrameRenderProcessGone(
|
||||
base::TerminationStatus status) {
|
||||
auto weak_this = GetWeakPtr();
|
||||
Emit("crashed", status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED);
|
||||
|
||||
// User might destroy WebContents in the crashed event.
|
||||
if (!weak_this || !web_contents())
|
||||
return;
|
||||
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
gin_helper::Dictionary details = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
@@ -2406,8 +2414,20 @@ void WebContents::LoadURL(const GURL& url,
|
||||
params.transition_type = ui::PageTransitionFromInt(
|
||||
ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
|
||||
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
|
||||
// Discard non-committed entries to ensure that we don't re-use a pending
|
||||
// entry
|
||||
|
||||
// It's not safe to start a new navigation or otherwise discard the current
|
||||
// one while the call that started it is still on the stack. See
|
||||
// http://crbug.com/347742.
|
||||
auto& ctrl_impl = static_cast<content::NavigationControllerImpl&>(
|
||||
web_contents()->GetController());
|
||||
if (ctrl_impl.in_navigate_to_pending_entry()) {
|
||||
Emit("did-fail-load", static_cast<int>(net::ERR_FAILED),
|
||||
net::ErrorToShortString(net::ERR_FAILED), url.possibly_invalid_spec(),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Discard non-committed entries to ensure we don't re-use a pending entry.
|
||||
web_contents()->GetController().DiscardNonCommittedEntries();
|
||||
web_contents()->GetController().LoadURLWithParams(params);
|
||||
|
||||
@@ -3427,22 +3447,16 @@ v8::Local<v8::Promise> WebContents::CapturePage(gin::Arguments* args) {
|
||||
}
|
||||
|
||||
auto* const view = web_contents()->GetRenderWidgetHostView();
|
||||
if (!view) {
|
||||
if (!view || view->GetViewBounds().size().IsEmpty()) {
|
||||
promise.Resolve(gfx::Image());
|
||||
return handle;
|
||||
}
|
||||
|
||||
#if !BUILDFLAG(IS_MAC)
|
||||
// If the view's renderer is suspended this may fail on Windows/Linux -
|
||||
// bail if so. See CopyFromSurface in
|
||||
// content/public/browser/render_widget_host_view.h.
|
||||
auto* rfh = web_contents()->GetPrimaryMainFrame();
|
||||
if (rfh &&
|
||||
rfh->GetVisibilityState() == blink::mojom::PageVisibilityState::kHidden) {
|
||||
promise.Resolve(gfx::Image());
|
||||
if (!view->IsSurfaceAvailableForCopy()) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Current display surface not available for capture");
|
||||
return handle;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_MAC)
|
||||
|
||||
auto capture_handle = web_contents()->IncrementCapturerCount(
|
||||
rect.size(), stay_hidden, stay_awake);
|
||||
|
||||
@@ -568,14 +568,17 @@ class WebContents : public ExclusiveAccessContext,
|
||||
const gfx::Rect& selection_rect,
|
||||
int active_match_ordinal,
|
||||
bool final_update) override;
|
||||
void RequestExclusivePointerAccess(content::WebContents* web_contents,
|
||||
bool user_gesture,
|
||||
bool last_unlocked_by_target,
|
||||
bool allowed);
|
||||
void OnRequestToLockMouse(content::WebContents* web_contents,
|
||||
bool user_gesture,
|
||||
bool last_unlocked_by_target,
|
||||
bool allowed);
|
||||
void RequestToLockMouse(content::WebContents* web_contents,
|
||||
bool user_gesture,
|
||||
bool last_unlocked_by_target) override;
|
||||
void LostMouseLock() override;
|
||||
void OnRequestKeyboardLock(content::WebContents* web_contents,
|
||||
bool esc_key_locked,
|
||||
bool allowed);
|
||||
void RequestKeyboardLock(content::WebContents* web_contents,
|
||||
bool esc_key_locked) override;
|
||||
void CancelKeyboardLockRequest(content::WebContents* web_contents) override;
|
||||
|
||||
@@ -42,12 +42,6 @@ bool WebContents::PlatformHandleKeyboardEvent(
|
||||
event.GetType() == content::NativeWebKeyboardEvent::Type::kChar)
|
||||
return false;
|
||||
|
||||
// Escape exits tabbed fullscreen mode.
|
||||
if (event.windows_key_code == ui::VKEY_ESCAPE && is_html_fullscreen()) {
|
||||
ExitFullscreenModeForTab(source);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the webContents has preferences and to ignore shortcuts
|
||||
auto* web_preferences = WebContentsPreferences::From(source);
|
||||
if (web_preferences && web_preferences->ShouldIgnoreMenuShortcuts())
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
#include "crypto/crypto_buildflags.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "electron/shell/common/api/api.mojom.h"
|
||||
#include "extensions/browser/api/messaging/messaging_api_message_filter.h"
|
||||
#include "extensions/browser/extension_navigation_ui_data.h"
|
||||
#include "mojo/public/cpp/bindings/binder_map.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "net/ssl/ssl_private_key.h"
|
||||
@@ -114,6 +114,7 @@
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "shell/common/platform_util.h"
|
||||
#include "shell/common/thread_restrictions.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
|
||||
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
|
||||
#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
|
||||
#include "third_party/blink/public/common/tokens/tokens.h"
|
||||
@@ -147,6 +148,7 @@
|
||||
#include "content/public/browser/child_process_security_policy.h"
|
||||
#include "content/public/browser/file_url_loader.h"
|
||||
#include "content/public/browser/web_ui_url_loader_factory.h"
|
||||
#include "extensions/browser/api/messaging/messaging_api_message_filter.h"
|
||||
#include "extensions/browser/api/mime_handler_private/mime_handler_private.h"
|
||||
#include "extensions/browser/api/web_request/web_request_api.h"
|
||||
#include "extensions/browser/browser_context_keyed_api_factory.h"
|
||||
@@ -154,7 +156,6 @@
|
||||
#include "extensions/browser/extension_host.h"
|
||||
#include "extensions/browser/extension_message_filter.h"
|
||||
#include "extensions/browser/extension_navigation_throttle.h"
|
||||
#include "extensions/browser/extension_navigation_ui_data.h"
|
||||
#include "extensions/browser/extension_prefs.h"
|
||||
#include "extensions/browser/extension_protocols.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
@@ -172,7 +173,6 @@
|
||||
#include "shell/browser/extensions/electron_extension_message_filter.h"
|
||||
#include "shell/browser/extensions/electron_extension_system.h"
|
||||
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
@@ -1220,12 +1220,12 @@ void ElectronBrowserClient::
|
||||
protocol_registry->RegisterURLLoaderFactories(factories,
|
||||
false /* allow_file_access */);
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
factories->emplace(
|
||||
extensions::kExtensionScheme,
|
||||
extensions::CreateExtensionServiceWorkerScriptURLLoaderFactory(
|
||||
browser_context));
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
}
|
||||
|
||||
bool ElectronBrowserClient::ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(
|
||||
@@ -1413,9 +1413,10 @@ void ElectronBrowserClient::OverrideURLLoaderFactoryParams(
|
||||
factory_params->disable_web_security = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
extensions::URLLoaderFactoryManager::OverrideURLLoaderFactoryParams(
|
||||
browser_context, origin, is_for_isolated_world, factory_params);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ElectronBrowserClient::
|
||||
@@ -1473,7 +1474,7 @@ void ElectronBrowserClient::
|
||||
},
|
||||
&render_frame_host));
|
||||
#endif
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
associated_registry.AddInterface<extensions::mojom::LocalFrameHost>(
|
||||
base::BindRepeating(
|
||||
[](content::RenderFrameHost* render_frame_host,
|
||||
|
||||
@@ -79,12 +79,12 @@
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
#include "base/environment.h"
|
||||
#include "chrome/browser/ui/views/dark_mode_manager_linux.h"
|
||||
#include "device/bluetooth/bluetooth_adapter_factory.h"
|
||||
#include "device/bluetooth/dbus/dbus_bluez_manager_wrapper_linux.h"
|
||||
#include "electron/electron_gtk_stubs.h"
|
||||
#include "ui/base/cursor/cursor_factory.h"
|
||||
#include "ui/base/ime/linux/linux_input_method_context_factory.h"
|
||||
#include "ui/gfx/color_utils.h"
|
||||
#include "ui/gtk/gtk_compat.h" // nogncheck
|
||||
#include "ui/gtk/gtk_util.h" // nogncheck
|
||||
#include "ui/linux/linux_ui.h"
|
||||
@@ -169,36 +169,8 @@ std::u16string MediaStringProvider(media::MessageId id) {
|
||||
}
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// GTK does not provide a way to check if current theme is dark, so we compare
|
||||
// the text and background luminosity to get a result.
|
||||
// This trick comes from FireFox.
|
||||
void UpdateDarkThemeSetting() {
|
||||
float bg = color_utils::GetRelativeLuminance(gtk::GetBgColor("GtkLabel"));
|
||||
float fg = color_utils::GetRelativeLuminance(gtk::GetFgColor("GtkLabel"));
|
||||
bool is_dark = fg > bg;
|
||||
// Pass it to NativeUi theme, which is used by the nativeTheme module and most
|
||||
// places in Electron.
|
||||
ui::NativeTheme::GetInstanceForNativeUi()->set_use_dark_colors(is_dark);
|
||||
// Pass it to Web Theme, to make "prefers-color-scheme" media query work.
|
||||
ui::NativeTheme::GetInstanceForWeb()->set_use_dark_colors(is_dark);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
class DarkThemeObserver : public ui::NativeThemeObserver {
|
||||
public:
|
||||
DarkThemeObserver() = default;
|
||||
|
||||
// ui::NativeThemeObserver:
|
||||
void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override {
|
||||
UpdateDarkThemeSetting();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// static
|
||||
ElectronBrowserMainParts* ElectronBrowserMainParts::self_ = nullptr;
|
||||
|
||||
@@ -432,17 +404,10 @@ void ElectronBrowserMainParts::ToolkitInitialized() {
|
||||
CHECK(electron::IsElectron_gdk_pixbufInitialized())
|
||||
<< "Failed to initialize libgdk_pixbuf-2.0.so.0";
|
||||
|
||||
// Chromium does not respect GTK dark theme setting, but they may change
|
||||
// in future and this code might be no longer needed. Check the Chromium
|
||||
// issue to keep updated:
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=998903
|
||||
UpdateDarkThemeSetting();
|
||||
// Update the native theme when GTK theme changes. The GetNativeTheme
|
||||
// here returns a NativeThemeGtk, which monitors GTK settings.
|
||||
dark_theme_observer_ = std::make_unique<DarkThemeObserver>();
|
||||
auto* linux_ui_theme = ui::LinuxUiTheme::GetForProfile(nullptr);
|
||||
CHECK(linux_ui_theme);
|
||||
linux_ui_theme->GetNativeTheme()->AddObserver(dark_theme_observer_.get());
|
||||
// source theme changes from system settings, including settings portal:
|
||||
// https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.Settings
|
||||
dark_mode_manager_ = std::make_unique<ui::DarkModeManagerLinux>();
|
||||
|
||||
ui::LinuxUi::SetInstance(linux_ui);
|
||||
|
||||
// Cursor theme changes are tracked by LinuxUI (via a CursorThemeManager
|
||||
|
||||
@@ -43,7 +43,8 @@ class Environment;
|
||||
|
||||
namespace ui {
|
||||
class LinuxUiGetter;
|
||||
}
|
||||
class DarkModeManagerLinux;
|
||||
} // namespace ui
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -65,10 +66,6 @@ class ViewsDelegate;
|
||||
class ViewsDelegateMac;
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
class DarkThemeObserver;
|
||||
#endif
|
||||
|
||||
class ElectronBrowserMainParts : public content::BrowserMainParts {
|
||||
public:
|
||||
ElectronBrowserMainParts();
|
||||
@@ -145,9 +142,7 @@ class ElectronBrowserMainParts : public content::BrowserMainParts {
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// Used to notify the native theme of changes to dark mode.
|
||||
std::unique_ptr<DarkThemeObserver> dark_theme_observer_;
|
||||
|
||||
std::unique_ptr<ui::DarkModeManagerLinux> dark_mode_manager_;
|
||||
std::unique_ptr<ui::LinuxUiGetter> linux_ui_getter_;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
#include "shell/browser/electron_navigation_throttle.h"
|
||||
|
||||
#include "content/browser/renderer_host/render_frame_host_impl.h" // nogncheck
|
||||
#include "content/public/browser/navigation_handle.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "ui/base/page_transition_types.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -37,11 +39,24 @@ ElectronNavigationThrottle::WillStartRequest() {
|
||||
return PROCEED;
|
||||
}
|
||||
|
||||
if (handle->IsRendererInitiated() &&
|
||||
bool is_renderer_initiated = handle->IsRendererInitiated();
|
||||
|
||||
// Chrome's internal pages always mark navigation as browser-initiated. Let's
|
||||
// ignore that.
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigator.cc;l=883-892;drc=0c8ffbe78dc0ef2047849e45cefb3f621043e956
|
||||
if (!is_renderer_initiated &&
|
||||
ui::PageTransitionIsWebTriggerable(handle->GetPageTransition())) {
|
||||
auto* render_frame_host = contents->GetPrimaryMainFrame();
|
||||
auto* rfh_impl =
|
||||
static_cast<content::RenderFrameHostImpl*>(render_frame_host);
|
||||
is_renderer_initiated = rfh_impl && rfh_impl->web_ui();
|
||||
}
|
||||
|
||||
if (is_renderer_initiated &&
|
||||
api_contents->EmitNavigationEvent("will-frame-navigate", handle)) {
|
||||
return CANCEL;
|
||||
}
|
||||
if (handle->IsRendererInitiated() && handle->IsInMainFrame() &&
|
||||
if (is_renderer_initiated && handle->IsInMainFrame() &&
|
||||
api_contents->EmitNavigationEvent("will-navigate", handle)) {
|
||||
return CANCEL;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ assert(enable_extensions,
|
||||
|
||||
function_registration("api_registration") {
|
||||
sources = [
|
||||
"//electron/shell/common/extensions/api/action.json",
|
||||
"//electron/shell/common/extensions/api/extension.json",
|
||||
"//electron/shell/common/extensions/api/resources_private.idl",
|
||||
"//electron/shell/common/extensions/api/scripting.idl",
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
// Copyright (c) 2023 Microsoft, GmbH
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/extensions/api/extension_action/extension_action_api.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "extensions/browser/event_router.h"
|
||||
#include "extensions/browser/extension_prefs.h"
|
||||
#include "extensions/browser/extension_util.h"
|
||||
#include "extensions/common/mojom/view_type.mojom.h"
|
||||
|
||||
using content::WebContents;
|
||||
|
||||
namespace extensions {
|
||||
|
||||
//
|
||||
// ExtensionActionAPI::Observer
|
||||
//
|
||||
|
||||
void ExtensionActionAPI::Observer::OnExtensionActionUpdated(
|
||||
ExtensionAction* extension_action,
|
||||
content::WebContents* web_contents,
|
||||
content::BrowserContext* browser_context) {}
|
||||
|
||||
void ExtensionActionAPI::Observer::OnExtensionActionAPIShuttingDown() {}
|
||||
|
||||
ExtensionActionAPI::Observer::~Observer() {}
|
||||
|
||||
//
|
||||
// ExtensionActionAPI
|
||||
//
|
||||
|
||||
static base::LazyInstance<BrowserContextKeyedAPIFactory<ExtensionActionAPI>>::
|
||||
DestructorAtExit g_extension_action_api_factory = LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
ExtensionActionAPI::ExtensionActionAPI(content::BrowserContext* context)
|
||||
: browser_context_(context), extension_prefs_(nullptr) {}
|
||||
|
||||
ExtensionActionAPI::~ExtensionActionAPI() {}
|
||||
|
||||
// static
|
||||
BrowserContextKeyedAPIFactory<ExtensionActionAPI>*
|
||||
ExtensionActionAPI::GetFactoryInstance() {
|
||||
return g_extension_action_api_factory.Pointer();
|
||||
}
|
||||
|
||||
// static
|
||||
ExtensionActionAPI* ExtensionActionAPI::Get(content::BrowserContext* context) {
|
||||
return BrowserContextKeyedAPIFactory<ExtensionActionAPI>::Get(context);
|
||||
}
|
||||
|
||||
ExtensionPrefs* ExtensionActionAPI::GetExtensionPrefs() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ExtensionActionAPI::Shutdown() {}
|
||||
|
||||
//
|
||||
// ExtensionActionFunction
|
||||
//
|
||||
|
||||
ExtensionActionFunction::ExtensionActionFunction() {}
|
||||
|
||||
ExtensionActionFunction::~ExtensionActionFunction() {}
|
||||
|
||||
ExtensionFunction::ResponseAction ExtensionActionFunction::Run() {
|
||||
return RunExtensionAction();
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionShowFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.show is not supported in Electron";
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionHideFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.hide is not supported in Electron";
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ActionIsEnabledFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.isEnabled is not supported in Electron";
|
||||
|
||||
return RespondNow(WithArguments(false));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionSetIconFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.setIcon is not supported in Electron";
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionSetTitleFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.setTitle is not supported in Electron";
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionSetPopupFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.setPopup is not supported in Electron";
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionSetBadgeTextFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.setBadgeText is not supported in Electron";
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionSetBadgeBackgroundColorFunction::RunExtensionAction() {
|
||||
LOG(INFO)
|
||||
<< "chrome.action.setBadgeBackgroundColor is not supported in Electron";
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ActionSetBadgeTextColorFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.setBadgeTextColor is not supported in Electron";
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionGetTitleFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.getTitle is not supported in Electron";
|
||||
|
||||
return RespondNow(WithArguments(""));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionGetPopupFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.getPopup is not supported in Electron";
|
||||
|
||||
return RespondNow(WithArguments(""));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.getBadgeText is not supported in Electron";
|
||||
|
||||
return RespondNow(WithArguments(""));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() {
|
||||
LOG(INFO)
|
||||
<< "chrome.action.getBadgeBackgroundColor is not supported in Electron";
|
||||
|
||||
base::Value::List list;
|
||||
return RespondNow(WithArguments(std::move(list)));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ActionGetBadgeTextColorFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.getBadgeTextColor is not supported in Electron";
|
||||
|
||||
base::Value::List list;
|
||||
return RespondNow(WithArguments(std::move(list)));
|
||||
}
|
||||
|
||||
ActionGetUserSettingsFunction::ActionGetUserSettingsFunction() = default;
|
||||
ActionGetUserSettingsFunction::~ActionGetUserSettingsFunction() = default;
|
||||
|
||||
ExtensionFunction::ResponseAction ActionGetUserSettingsFunction::Run() {
|
||||
LOG(INFO) << "chrome.action.getUserSettings is not supported in Electron";
|
||||
|
||||
base::Value::Dict ui_settings;
|
||||
return RespondNow(WithArguments(std::move(ui_settings)));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
ActionOpenPopupFunction::RunExtensionAction() {
|
||||
LOG(INFO) << "chrome.action.openPopup is not supported in Electron";
|
||||
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
@@ -0,0 +1,520 @@
|
||||
// Copyright (c) 2023 Microsoft, GmbH
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_
|
||||
#define SHELL_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "base/values.h"
|
||||
#include "extensions/browser/browser_context_keyed_api_factory.h"
|
||||
#include "extensions/browser/extension_action.h"
|
||||
#include "extensions/browser/extension_function.h"
|
||||
#include "extensions/browser/extension_host_registry.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
class WebContents;
|
||||
} // namespace content
|
||||
|
||||
namespace extensions {
|
||||
|
||||
class ExtensionHost;
|
||||
class ExtensionPrefs;
|
||||
|
||||
class ExtensionActionAPI : public BrowserContextKeyedAPI {
|
||||
public:
|
||||
class Observer {
|
||||
public:
|
||||
virtual void OnExtensionActionUpdated(
|
||||
ExtensionAction* extension_action,
|
||||
content::WebContents* web_contents,
|
||||
content::BrowserContext* browser_context);
|
||||
|
||||
virtual void OnExtensionActionAPIShuttingDown();
|
||||
|
||||
protected:
|
||||
virtual ~Observer();
|
||||
};
|
||||
|
||||
explicit ExtensionActionAPI(content::BrowserContext* context);
|
||||
|
||||
ExtensionActionAPI(const ExtensionActionAPI&) = delete;
|
||||
ExtensionActionAPI& operator=(const ExtensionActionAPI&) = delete;
|
||||
|
||||
~ExtensionActionAPI() override;
|
||||
|
||||
// Convenience method to get the instance for a profile.
|
||||
static ExtensionActionAPI* Get(content::BrowserContext* context);
|
||||
|
||||
static BrowserContextKeyedAPIFactory<ExtensionActionAPI>*
|
||||
GetFactoryInstance();
|
||||
|
||||
// Add or remove observers.
|
||||
void AddObserver(Observer* observer) {}
|
||||
void RemoveObserver(Observer* observer) {}
|
||||
|
||||
// Notifies that there has been a change in the given |extension_action|.
|
||||
void NotifyChange(ExtensionAction* extension_action,
|
||||
content::WebContents* web_contents,
|
||||
content::BrowserContext* browser_context) {}
|
||||
|
||||
// Dispatches the onClicked event for extension that owns the given action.
|
||||
void DispatchExtensionActionClicked(const ExtensionAction& extension_action,
|
||||
content::WebContents* web_contents,
|
||||
const Extension* extension) {}
|
||||
|
||||
// Clears the values for all ExtensionActions for the tab associated with the
|
||||
// given |web_contents| (and signals that page actions changed).
|
||||
void ClearAllValuesForTab(content::WebContents* web_contents) {}
|
||||
|
||||
private:
|
||||
friend class BrowserContextKeyedAPIFactory<ExtensionActionAPI>;
|
||||
|
||||
ExtensionPrefs* GetExtensionPrefs();
|
||||
|
||||
// BrowserContextKeyedAPI implementation.
|
||||
void Shutdown() override;
|
||||
static const char* service_name() { return "ExtensionActionAPI"; }
|
||||
static const bool kServiceRedirectedInIncognito = true;
|
||||
|
||||
raw_ptr<content::BrowserContext> browser_context_;
|
||||
|
||||
raw_ptr<ExtensionPrefs> extension_prefs_;
|
||||
};
|
||||
|
||||
// Implementation of the browserAction and pageAction APIs.
|
||||
class ExtensionActionFunction : public ExtensionFunction {
|
||||
protected:
|
||||
ExtensionActionFunction();
|
||||
~ExtensionActionFunction() override;
|
||||
ResponseAction Run() override;
|
||||
|
||||
virtual ResponseAction RunExtensionAction() = 0;
|
||||
};
|
||||
|
||||
//
|
||||
// Implementations of each extension action API.
|
||||
//
|
||||
// pageAction and browserAction bindings are created for these by extending them
|
||||
// then declaring an EXTENSION_FUNCTION_NAME.
|
||||
//
|
||||
|
||||
// show
|
||||
class ExtensionActionShowFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionShowFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// hide
|
||||
class ExtensionActionHideFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionHideFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// setIcon
|
||||
class ExtensionActionSetIconFunction : public ExtensionActionFunction {
|
||||
public:
|
||||
static void SetReportErrorForInvisibleIconForTesting(bool value);
|
||||
|
||||
protected:
|
||||
~ExtensionActionSetIconFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// setTitle
|
||||
class ExtensionActionSetTitleFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionSetTitleFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// setPopup
|
||||
class ExtensionActionSetPopupFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionSetPopupFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// setBadgeText
|
||||
class ExtensionActionSetBadgeTextFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionSetBadgeTextFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// setBadgeBackgroundColor
|
||||
class ExtensionActionSetBadgeBackgroundColorFunction
|
||||
: public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionSetBadgeBackgroundColorFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// getTitle
|
||||
class ExtensionActionGetTitleFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionGetTitleFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// getPopup
|
||||
class ExtensionActionGetPopupFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionGetPopupFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// openPopup
|
||||
class ExtensionActionOpenPopupFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionOpenPopupFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// getBadgeText
|
||||
class ExtensionActionGetBadgeTextFunction : public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionGetBadgeTextFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
// getBadgeBackgroundColor
|
||||
class ExtensionActionGetBadgeBackgroundColorFunction
|
||||
: public ExtensionActionFunction {
|
||||
protected:
|
||||
~ExtensionActionGetBadgeBackgroundColorFunction() override {}
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
//
|
||||
// action.* aliases for supported action APIs.
|
||||
//
|
||||
|
||||
class ActionSetIconFunction : public ExtensionActionSetIconFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.setIcon", ACTION_SETICON)
|
||||
|
||||
protected:
|
||||
~ActionSetIconFunction() override {}
|
||||
};
|
||||
|
||||
class ActionGetPopupFunction : public ExtensionActionGetPopupFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.getPopup", ACTION_GETPOPUP)
|
||||
|
||||
protected:
|
||||
~ActionGetPopupFunction() override {}
|
||||
};
|
||||
|
||||
class ActionSetPopupFunction : public ExtensionActionSetPopupFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.setPopup", ACTION_SETPOPUP)
|
||||
|
||||
protected:
|
||||
~ActionSetPopupFunction() override {}
|
||||
};
|
||||
|
||||
class ActionGetTitleFunction : public ExtensionActionGetTitleFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.getTitle", ACTION_GETTITLE)
|
||||
|
||||
protected:
|
||||
~ActionGetTitleFunction() override {}
|
||||
};
|
||||
|
||||
class ActionSetTitleFunction : public ExtensionActionSetTitleFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.setTitle", ACTION_SETTITLE)
|
||||
|
||||
protected:
|
||||
~ActionSetTitleFunction() override {}
|
||||
};
|
||||
|
||||
class ActionGetBadgeTextFunction : public ExtensionActionGetBadgeTextFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.getBadgeText", ACTION_GETBADGETEXT)
|
||||
|
||||
protected:
|
||||
~ActionGetBadgeTextFunction() override {}
|
||||
};
|
||||
|
||||
class ActionSetBadgeTextFunction : public ExtensionActionSetBadgeTextFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.setBadgeText", ACTION_SETBADGETEXT)
|
||||
|
||||
protected:
|
||||
~ActionSetBadgeTextFunction() override {}
|
||||
};
|
||||
|
||||
class ActionGetBadgeBackgroundColorFunction
|
||||
: public ExtensionActionGetBadgeBackgroundColorFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.getBadgeBackgroundColor",
|
||||
ACTION_GETBADGEBACKGROUNDCOLOR)
|
||||
|
||||
protected:
|
||||
~ActionGetBadgeBackgroundColorFunction() override {}
|
||||
};
|
||||
|
||||
class ActionSetBadgeBackgroundColorFunction
|
||||
: public ExtensionActionSetBadgeBackgroundColorFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.setBadgeBackgroundColor",
|
||||
ACTION_SETBADGEBACKGROUNDCOLOR)
|
||||
|
||||
protected:
|
||||
~ActionSetBadgeBackgroundColorFunction() override {}
|
||||
};
|
||||
|
||||
class ActionGetBadgeTextColorFunction : public ExtensionActionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.getBadgeTextColor",
|
||||
ACTION_GETBADGETEXTCOLOR)
|
||||
|
||||
protected:
|
||||
~ActionGetBadgeTextColorFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
class ActionSetBadgeTextColorFunction : public ExtensionActionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.setBadgeTextColor",
|
||||
ACTION_SETBADGETEXTCOLOR)
|
||||
|
||||
protected:
|
||||
~ActionSetBadgeTextColorFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
class ActionEnableFunction : public ExtensionActionShowFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.enable", ACTION_ENABLE)
|
||||
|
||||
protected:
|
||||
~ActionEnableFunction() override {}
|
||||
};
|
||||
|
||||
class ActionDisableFunction : public ExtensionActionHideFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.disable", ACTION_DISABLE)
|
||||
|
||||
protected:
|
||||
~ActionDisableFunction() override {}
|
||||
};
|
||||
|
||||
class ActionIsEnabledFunction : public ExtensionActionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.isEnabled", ACTION_ISENABLED)
|
||||
|
||||
protected:
|
||||
~ActionIsEnabledFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
class ActionGetUserSettingsFunction : public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.getUserSettings", ACTION_GETUSERSETTINGS)
|
||||
|
||||
ActionGetUserSettingsFunction();
|
||||
ActionGetUserSettingsFunction(const ActionGetUserSettingsFunction&) = delete;
|
||||
ActionGetUserSettingsFunction& operator=(
|
||||
const ActionGetUserSettingsFunction&) = delete;
|
||||
|
||||
ResponseAction Run() override;
|
||||
|
||||
protected:
|
||||
~ActionGetUserSettingsFunction() override;
|
||||
};
|
||||
|
||||
class ActionOpenPopupFunction : public ExtensionActionOpenPopupFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("action.openPopup", ACTION_OPENPOPUP)
|
||||
|
||||
protected:
|
||||
~ActionOpenPopupFunction() override = default;
|
||||
ResponseAction RunExtensionAction() override;
|
||||
};
|
||||
|
||||
//
|
||||
// browserAction.* aliases for supported browserAction APIs.
|
||||
//
|
||||
|
||||
class BrowserActionSetIconFunction : public ExtensionActionSetIconFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.setIcon", BROWSERACTION_SETICON)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetIconFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionSetTitleFunction : public ExtensionActionSetTitleFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.setTitle", BROWSERACTION_SETTITLE)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetTitleFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionSetPopupFunction : public ExtensionActionSetPopupFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.setPopup", BROWSERACTION_SETPOPUP)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetPopupFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionGetTitleFunction : public ExtensionActionGetTitleFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.getTitle", BROWSERACTION_GETTITLE)
|
||||
|
||||
protected:
|
||||
~BrowserActionGetTitleFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionGetPopupFunction : public ExtensionActionGetPopupFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.getPopup", BROWSERACTION_GETPOPUP)
|
||||
|
||||
protected:
|
||||
~BrowserActionGetPopupFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionSetBadgeTextFunction
|
||||
: public ExtensionActionSetBadgeTextFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.setBadgeText",
|
||||
BROWSERACTION_SETBADGETEXT)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetBadgeTextFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionSetBadgeBackgroundColorFunction
|
||||
: public ExtensionActionSetBadgeBackgroundColorFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.setBadgeBackgroundColor",
|
||||
BROWSERACTION_SETBADGEBACKGROUNDCOLOR)
|
||||
|
||||
protected:
|
||||
~BrowserActionSetBadgeBackgroundColorFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionGetBadgeTextFunction
|
||||
: public ExtensionActionGetBadgeTextFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.getBadgeText",
|
||||
BROWSERACTION_GETBADGETEXT)
|
||||
|
||||
protected:
|
||||
~BrowserActionGetBadgeTextFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionGetBadgeBackgroundColorFunction
|
||||
: public ExtensionActionGetBadgeBackgroundColorFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.getBadgeBackgroundColor",
|
||||
BROWSERACTION_GETBADGEBACKGROUNDCOLOR)
|
||||
|
||||
protected:
|
||||
~BrowserActionGetBadgeBackgroundColorFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionEnableFunction : public ExtensionActionShowFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.enable", BROWSERACTION_ENABLE)
|
||||
|
||||
protected:
|
||||
~BrowserActionEnableFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionDisableFunction : public ExtensionActionHideFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.disable", BROWSERACTION_DISABLE)
|
||||
|
||||
protected:
|
||||
~BrowserActionDisableFunction() override {}
|
||||
};
|
||||
|
||||
class BrowserActionOpenPopupFunction : public ExtensionActionOpenPopupFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("browserAction.openPopup",
|
||||
BROWSERACTION_OPEN_POPUP)
|
||||
|
||||
protected:
|
||||
~BrowserActionOpenPopupFunction() override {}
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
//
|
||||
// pageAction.* aliases for supported pageAction APIs.
|
||||
//
|
||||
|
||||
class PageActionShowFunction : public extensions::ExtensionActionShowFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.show", PAGEACTION_SHOW)
|
||||
|
||||
protected:
|
||||
~PageActionShowFunction() override {}
|
||||
};
|
||||
|
||||
class PageActionHideFunction : public extensions::ExtensionActionHideFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.hide", PAGEACTION_HIDE)
|
||||
|
||||
protected:
|
||||
~PageActionHideFunction() override {}
|
||||
};
|
||||
|
||||
class PageActionSetIconFunction
|
||||
: public extensions::ExtensionActionSetIconFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.setIcon", PAGEACTION_SETICON)
|
||||
|
||||
protected:
|
||||
~PageActionSetIconFunction() override {}
|
||||
};
|
||||
|
||||
class PageActionSetTitleFunction
|
||||
: public extensions::ExtensionActionSetTitleFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.setTitle", PAGEACTION_SETTITLE)
|
||||
|
||||
protected:
|
||||
~PageActionSetTitleFunction() override {}
|
||||
};
|
||||
|
||||
class PageActionSetPopupFunction
|
||||
: public extensions::ExtensionActionSetPopupFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.setPopup", PAGEACTION_SETPOPUP)
|
||||
|
||||
protected:
|
||||
~PageActionSetPopupFunction() override {}
|
||||
};
|
||||
|
||||
class PageActionGetTitleFunction
|
||||
: public extensions::ExtensionActionGetTitleFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.getTitle", PAGEACTION_GETTITLE)
|
||||
|
||||
protected:
|
||||
~PageActionGetTitleFunction() override {}
|
||||
};
|
||||
|
||||
class PageActionGetPopupFunction
|
||||
: public extensions::ExtensionActionGetPopupFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("pageAction.getPopup", PAGEACTION_GETPOPUP)
|
||||
|
||||
protected:
|
||||
~PageActionGetPopupFunction() override {}
|
||||
};
|
||||
|
||||
#endif // SHELL_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_ACTION_API_H_
|
||||
@@ -13,15 +13,15 @@
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "components/strings/grit/components_strings.h"
|
||||
#include "components/zoom/page_zoom_constants.h"
|
||||
#include "pdf/buildflags.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/webui/web_ui_util.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF)
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
#include "chrome/browser/pdf/pdf_extension_util.h"
|
||||
#include "pdf/pdf_features.h"
|
||||
#endif // BUILDFLAG(ENABLE_PDF)
|
||||
#endif // BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
|
||||
// To add a new component to this API, simply:
|
||||
// 1. Add your component to the Component enum in
|
||||
@@ -48,7 +48,7 @@ ExtensionFunction::ResponseAction ResourcesPrivateGetStringsFunction::Run() {
|
||||
|
||||
switch (component) {
|
||||
case api::resources_private::COMPONENT_PDF:
|
||||
#if BUILDFLAG(ENABLE_PDF)
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
pdf_extension_util::AddStrings(
|
||||
pdf_extension_util::PdfViewerContext::kPdfViewer, &dict);
|
||||
pdf_extension_util::AddAdditionalData(true, false, &dict);
|
||||
|
||||
@@ -7,20 +7,27 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/strings/pattern.h"
|
||||
#include "base/types/expected_macros.h"
|
||||
#include "chrome/common/url_constants.h"
|
||||
#include "components/url_formatter/url_fixer.h"
|
||||
#include "content/public/browser/navigation_entry.h"
|
||||
#include "extensions/browser/extension_api_frame_id_map.h"
|
||||
#include "extensions/browser/extension_prefs.h"
|
||||
#include "extensions/common/error_utils.h"
|
||||
#include "extensions/common/extension_features.h"
|
||||
#include "extensions/common/feature_switch.h"
|
||||
#include "extensions/common/manifest_constants.h"
|
||||
#include "extensions/common/mojom/host_id.mojom.h"
|
||||
#include "extensions/common/permissions/permissions_data.h"
|
||||
#include "extensions/common/switches.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "shell/browser/web_contents_zoom_controller.h"
|
||||
#include "shell/browser/window_list.h"
|
||||
#include "shell/common/extensions/api/tabs.h"
|
||||
#include "third_party/blink/public/common/chrome_debug_urls.h"
|
||||
#include "third_party/blink/public/common/page/page_zoom.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
@@ -468,17 +475,25 @@ bool IsKillURL(const GURL& url) {
|
||||
DCHECK(url.IsAboutBlank() || url.IsAboutSrcdoc());
|
||||
#endif
|
||||
|
||||
static const char* const kill_hosts[] = {
|
||||
chrome::kChromeUICrashHost, chrome::kChromeUIDelayedHangUIHost,
|
||||
chrome::kChromeUIHangUIHost, chrome::kChromeUIKillHost,
|
||||
// Disallow common renderer debug URLs.
|
||||
// Note: this would also disallow JavaScript URLs, but we already explicitly
|
||||
// check for those before calling into here from PrepareURLForNavigation.
|
||||
if (blink::IsRendererDebugURL(url)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!url.SchemeIs(content::kChromeUIScheme)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Also disallow a few more hosts which are not covered by the check above.
|
||||
static const char* const kKillHosts[] = {
|
||||
chrome::kChromeUIDelayedHangUIHost, chrome::kChromeUIHangUIHost,
|
||||
chrome::kChromeUIQuitHost, chrome::kChromeUIRestartHost,
|
||||
content::kChromeUIBrowserCrashHost, content::kChromeUIMemoryExhaustHost,
|
||||
};
|
||||
|
||||
if (!url.SchemeIs(content::kChromeUIScheme))
|
||||
return false;
|
||||
|
||||
return base::Contains(kill_hosts, url.host_piece());
|
||||
return base::Contains(kKillHosts, url.host_piece());
|
||||
}
|
||||
|
||||
GURL ResolvePossiblyRelativeURL(const std::string& url_string,
|
||||
@@ -489,10 +504,18 @@ GURL ResolvePossiblyRelativeURL(const std::string& url_string,
|
||||
|
||||
return url;
|
||||
}
|
||||
bool PrepareURLForNavigation(const std::string& url_string,
|
||||
const Extension* extension,
|
||||
GURL* return_url,
|
||||
std::string* error) {
|
||||
|
||||
bool AllowFileAccess(const ExtensionId& extension_id,
|
||||
content::BrowserContext* context) {
|
||||
return base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kDisableExtensionsFileAccessCheck) ||
|
||||
ExtensionPrefs::Get(context)->AllowFileAccess(extension_id);
|
||||
}
|
||||
|
||||
base::expected<GURL, std::string> PrepareURLForNavigation(
|
||||
const std::string& url_string,
|
||||
const Extension* extension,
|
||||
content::BrowserContext* browser_context) {
|
||||
GURL url = ResolvePossiblyRelativeURL(url_string, extension);
|
||||
|
||||
// Ideally, the URL would only be "fixed" for user input (e.g. for URLs
|
||||
@@ -504,34 +527,62 @@ bool PrepareURLForNavigation(const std::string& url_string,
|
||||
// Reject invalid URLs.
|
||||
if (!url.is_valid()) {
|
||||
const char kInvalidUrlError[] = "Invalid url: \"*\".";
|
||||
*error = ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string);
|
||||
return false;
|
||||
return base::unexpected(
|
||||
ErrorUtils::FormatErrorMessage(kInvalidUrlError, url_string));
|
||||
}
|
||||
|
||||
// Don't let the extension use JavaScript URLs in API triggered navigations.
|
||||
if (url.SchemeIs(url::kJavaScriptScheme)) {
|
||||
const char kJavaScriptUrlsNotAllowedInExtensionNavigations[] =
|
||||
"JavaScript URLs are not allowed in API based extension navigations. "
|
||||
"Use "
|
||||
"chrome.scripting.executeScript instead.";
|
||||
return base::unexpected(kJavaScriptUrlsNotAllowedInExtensionNavigations);
|
||||
}
|
||||
|
||||
// Don't let the extension crash the browser or renderers.
|
||||
if (IsKillURL(url)) {
|
||||
const char kNoCrashBrowserError[] =
|
||||
"I'm sorry. I'm afraid I can't do that.";
|
||||
*error = kNoCrashBrowserError;
|
||||
return false;
|
||||
return base::unexpected(kNoCrashBrowserError);
|
||||
}
|
||||
|
||||
// Don't let the extension navigate directly to devtools scheme pages, unless
|
||||
// they have applicable permissions.
|
||||
if (url.SchemeIs(content::kChromeDevToolsScheme) &&
|
||||
!(extension->permissions_data()->HasAPIPermission(
|
||||
extensions::mojom::APIPermissionID::kDevtools) ||
|
||||
extension->permissions_data()->HasAPIPermission(
|
||||
extensions::mojom::APIPermissionID::kDebugger))) {
|
||||
const char kCannotNavigateToDevtools[] =
|
||||
"Cannot navigate to a devtools:// page without either the devtools or "
|
||||
"debugger permission.";
|
||||
*error = kCannotNavigateToDevtools;
|
||||
return false;
|
||||
if (url.SchemeIs(content::kChromeDevToolsScheme)) {
|
||||
bool has_permission =
|
||||
extension && (extension->permissions_data()->HasAPIPermission(
|
||||
mojom::APIPermissionID::kDevtools) ||
|
||||
extension->permissions_data()->HasAPIPermission(
|
||||
mojom::APIPermissionID::kDebugger));
|
||||
if (!has_permission) {
|
||||
const char kCannotNavigateToDevtools[] =
|
||||
"Cannot navigate to a devtools:// page without either the devtools "
|
||||
"or "
|
||||
"debugger permission.";
|
||||
return base::unexpected(kCannotNavigateToDevtools);
|
||||
}
|
||||
}
|
||||
|
||||
return_url->Swap(&url);
|
||||
return true;
|
||||
// Don't let the extension navigate directly to chrome-untrusted scheme pages.
|
||||
if (url.SchemeIs(content::kChromeUIUntrustedScheme)) {
|
||||
const char kCannotNavigateToChromeUntrusted[] =
|
||||
"Cannot navigate to a chrome-untrusted:// page.";
|
||||
return base::unexpected(kCannotNavigateToChromeUntrusted);
|
||||
}
|
||||
|
||||
// Don't let the extension navigate directly to file scheme pages, unless
|
||||
// they have file access.
|
||||
if (url.SchemeIsFile() &&
|
||||
!AllowFileAccess(extension->id(), browser_context) &&
|
||||
base::FeatureList::IsEnabled(
|
||||
extensions_features::kRestrictFileURLNavigation)) {
|
||||
const char kFileUrlsNotAllowedInExtensionNavigations[] =
|
||||
"Cannot navigate to a file URL without local file access.";
|
||||
return base::unexpected(kFileUrlsNotAllowedInExtensionNavigations);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
TabsUpdateFunction::TabsUpdateFunction() : web_contents_(nullptr) {}
|
||||
@@ -566,22 +617,14 @@ ExtensionFunction::ResponseAction TabsUpdateFunction::Run() {
|
||||
bool TabsUpdateFunction::UpdateURL(const std::string& url_string,
|
||||
int tab_id,
|
||||
std::string* error) {
|
||||
GURL url;
|
||||
if (!PrepareURLForNavigation(url_string, extension(), &url, error)) {
|
||||
auto url =
|
||||
PrepareURLForNavigation(url_string, extension(), browser_context());
|
||||
if (!url.has_value()) {
|
||||
*error = std::move(url.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool is_javascript_scheme = url.SchemeIs(url::kJavaScriptScheme);
|
||||
// JavaScript URLs are forbidden in chrome.tabs.update().
|
||||
if (is_javascript_scheme) {
|
||||
const char kJavaScriptUrlsNotAllowedInTabsUpdate[] =
|
||||
"JavaScript URLs are not allowed in chrome.tabs.update. Use "
|
||||
"chrome.tabs.executeScript instead.";
|
||||
*error = kJavaScriptUrlsNotAllowedInTabsUpdate;
|
||||
return false;
|
||||
}
|
||||
|
||||
content::NavigationController::LoadURLParams load_params(url);
|
||||
content::NavigationController::LoadURLParams load_params(*url);
|
||||
|
||||
// Treat extension-initiated navigations as renderer-initiated so that the URL
|
||||
// does not show in the omnibox until it commits. This avoids URL spoofs
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "extensions/browser/api/i18n/i18n_api.h"
|
||||
#include "extensions/browser/extension_function_registry.h"
|
||||
#include "shell/browser/extensions/api/extension_action/extension_action_api.h"
|
||||
#include "shell/browser/extensions/api/generated_api_registration.h"
|
||||
#include "shell/browser/extensions/api/scripting/scripting_api.h"
|
||||
#include "shell/browser/extensions/api/tabs/tabs_api.h"
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "chrome/common/chrome_features.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "services/device/public/cpp/hid/hid_switches.h"
|
||||
#include "shell/browser/electron_permission_manager.h"
|
||||
#include "shell/browser/hid/hid_chooser_context.h"
|
||||
@@ -19,14 +21,16 @@
|
||||
#include "shell/browser/web_contents_permission_helper.h"
|
||||
#include "third_party/blink/public/common/permissions/permission_utils.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "extensions/common/constants.h"
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
||||
namespace {
|
||||
|
||||
electron::HidChooserContext* GetChooserContext(
|
||||
content::BrowserContext* browser_context) {
|
||||
if (!browser_context)
|
||||
return nullptr;
|
||||
return electron::HidChooserContextFactory::GetForBrowserContext(
|
||||
browser_context);
|
||||
}
|
||||
@@ -35,6 +39,70 @@ electron::HidChooserContext* GetChooserContext(
|
||||
|
||||
namespace electron {
|
||||
|
||||
// Manages the HidDelegate observers for a single browser context.
|
||||
class ElectronHidDelegate::ContextObservation
|
||||
: public HidChooserContext::DeviceObserver {
|
||||
public:
|
||||
ContextObservation(ElectronHidDelegate* parent,
|
||||
content::BrowserContext* browser_context)
|
||||
: parent_(parent), browser_context_(browser_context) {
|
||||
auto* chooser_context = GetChooserContext(browser_context_);
|
||||
device_observation_.Observe(chooser_context);
|
||||
}
|
||||
|
||||
ContextObservation(ContextObservation&) = delete;
|
||||
ContextObservation& operator=(ContextObservation&) = delete;
|
||||
~ContextObservation() override = default;
|
||||
|
||||
// HidChooserContext::DeviceObserver:
|
||||
void OnDeviceAdded(const device::mojom::HidDeviceInfo& device_info) override {
|
||||
for (auto& observer : observer_list_)
|
||||
observer.OnDeviceAdded(device_info);
|
||||
}
|
||||
|
||||
void OnDeviceRemoved(
|
||||
const device::mojom::HidDeviceInfo& device_info) override {
|
||||
for (auto& observer : observer_list_)
|
||||
observer.OnDeviceRemoved(device_info);
|
||||
}
|
||||
|
||||
void OnDeviceChanged(
|
||||
const device::mojom::HidDeviceInfo& device_info) override {
|
||||
for (auto& observer : observer_list_)
|
||||
observer.OnDeviceChanged(device_info);
|
||||
}
|
||||
|
||||
void OnHidManagerConnectionError() override {
|
||||
for (auto& observer : observer_list_)
|
||||
observer.OnHidManagerConnectionError();
|
||||
}
|
||||
|
||||
void OnHidChooserContextShutdown() override {
|
||||
parent_->observations_.erase(browser_context_);
|
||||
// Return since `this` is now deleted.
|
||||
}
|
||||
|
||||
void AddObserver(content::HidDelegate::Observer* observer) {
|
||||
observer_list_.AddObserver(observer);
|
||||
}
|
||||
|
||||
void RemoveObserver(content::HidDelegate::Observer* observer) {
|
||||
observer_list_.RemoveObserver(observer);
|
||||
}
|
||||
|
||||
private:
|
||||
// Safe because `parent_` owns `this`.
|
||||
const raw_ptr<ElectronHidDelegate> parent_;
|
||||
|
||||
// Safe because `this` is destroyed when the context is lost.
|
||||
const raw_ptr<content::BrowserContext> browser_context_;
|
||||
|
||||
base::ScopedObservation<HidChooserContext, HidChooserContext::DeviceObserver>
|
||||
device_observation_{this};
|
||||
|
||||
base::ObserverList<content::HidDelegate::Observer> observer_list_;
|
||||
};
|
||||
|
||||
ElectronHidDelegate::ElectronHidDelegate() = default;
|
||||
|
||||
ElectronHidDelegate::~ElectronHidDelegate() = default;
|
||||
@@ -45,10 +113,11 @@ std::unique_ptr<content::HidChooser> ElectronHidDelegate::RunChooser(
|
||||
std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
|
||||
content::HidChooser::Callback callback) {
|
||||
DCHECK(render_frame_host);
|
||||
auto* chooser_context =
|
||||
GetChooserContext(render_frame_host->GetBrowserContext());
|
||||
if (!device_observation_.IsObserving())
|
||||
device_observation_.Observe(chooser_context);
|
||||
auto* browser_context = render_frame_host->GetBrowserContext();
|
||||
|
||||
// Start observing HidChooserContext for permission and device events.
|
||||
GetContextObserver(browser_context);
|
||||
DCHECK(base::Contains(observations_, browser_context));
|
||||
|
||||
HidChooserController* controller = ControllerForFrame(render_frame_host);
|
||||
if (controller) {
|
||||
@@ -67,6 +136,9 @@ 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*>(
|
||||
@@ -81,91 +153,79 @@ bool ElectronHidDelegate::HasDevicePermission(
|
||||
content::BrowserContext* browser_context,
|
||||
const url::Origin& origin,
|
||||
const device::mojom::HidDeviceInfo& device) {
|
||||
return GetChooserContext(browser_context)
|
||||
->HasDevicePermission(origin, device);
|
||||
return browser_context && GetChooserContext(browser_context)
|
||||
->HasDevicePermission(origin, device);
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::RevokeDevicePermission(
|
||||
content::BrowserContext* browser_context,
|
||||
const url::Origin& origin,
|
||||
const device::mojom::HidDeviceInfo& device) {
|
||||
return GetChooserContext(browser_context)
|
||||
->RevokeDevicePermission(origin, device);
|
||||
if (browser_context) {
|
||||
GetChooserContext(browser_context)->RevokeDevicePermission(origin, device);
|
||||
}
|
||||
}
|
||||
|
||||
device::mojom::HidManager* ElectronHidDelegate::GetHidManager(
|
||||
content::BrowserContext* browser_context) {
|
||||
if (!browser_context)
|
||||
return nullptr;
|
||||
return GetChooserContext(browser_context)->GetHidManager();
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::AddObserver(content::BrowserContext* browser_context,
|
||||
Observer* observer) {
|
||||
observer_list_.AddObserver(observer);
|
||||
auto* chooser_context = GetChooserContext(browser_context);
|
||||
if (!device_observation_.IsObserving())
|
||||
device_observation_.Observe(chooser_context);
|
||||
if (!browser_context)
|
||||
return;
|
||||
GetContextObserver(browser_context)->AddObserver(observer);
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::RemoveObserver(
|
||||
content::BrowserContext* browser_context,
|
||||
content::HidDelegate::Observer* observer) {
|
||||
observer_list_.RemoveObserver(observer);
|
||||
if (!browser_context)
|
||||
return;
|
||||
DCHECK(base::Contains(observations_, browser_context));
|
||||
GetContextObserver(browser_context)->RemoveObserver(observer);
|
||||
}
|
||||
|
||||
const device::mojom::HidDeviceInfo* ElectronHidDelegate::GetDeviceInfo(
|
||||
content::BrowserContext* browser_context,
|
||||
const std::string& guid) {
|
||||
auto* chooser_context = GetChooserContext(browser_context);
|
||||
if (!chooser_context)
|
||||
return nullptr;
|
||||
return chooser_context->GetDeviceInfo(guid);
|
||||
}
|
||||
|
||||
bool ElectronHidDelegate::IsFidoAllowedForOrigin(
|
||||
content::BrowserContext* browser_context,
|
||||
const url::Origin& origin) {
|
||||
return base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kDisableHidBlocklist);
|
||||
auto* chooser_context = GetChooserContext(browser_context);
|
||||
return chooser_context && chooser_context->IsFidoAllowedForOrigin(origin);
|
||||
}
|
||||
|
||||
bool ElectronHidDelegate::IsServiceWorkerAllowedForOrigin(
|
||||
const url::Origin& origin) {
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
// WebHID is only available on extension service workers with feature flag
|
||||
// enabled for now.
|
||||
if (base::FeatureList::IsEnabled(
|
||||
features::kEnableWebHidOnExtensionServiceWorker) &&
|
||||
origin.scheme() == extensions::kExtensionScheme)
|
||||
return true;
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::OnDeviceAdded(
|
||||
const device::mojom::HidDeviceInfo& device_info) {
|
||||
for (auto& observer : observer_list_)
|
||||
observer.OnDeviceAdded(device_info);
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::OnDeviceRemoved(
|
||||
const device::mojom::HidDeviceInfo& device_info) {
|
||||
for (auto& observer : observer_list_)
|
||||
observer.OnDeviceRemoved(device_info);
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::OnDeviceChanged(
|
||||
const device::mojom::HidDeviceInfo& device_info) {
|
||||
for (auto& observer : observer_list_)
|
||||
observer.OnDeviceChanged(device_info);
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::OnHidManagerConnectionError() {
|
||||
device_observation_.Reset();
|
||||
|
||||
for (auto& observer : observer_list_)
|
||||
observer.OnHidManagerConnectionError();
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::OnHidChooserContextShutdown() {
|
||||
device_observation_.Reset();
|
||||
ElectronHidDelegate::ContextObservation*
|
||||
ElectronHidDelegate::GetContextObserver(
|
||||
content::BrowserContext* browser_context) {
|
||||
if (!base::Contains(observations_, browser_context)) {
|
||||
observations_.emplace(browser_context, std::make_unique<ContextObservation>(
|
||||
this, browser_context));
|
||||
}
|
||||
return observations_[browser_context].get();
|
||||
}
|
||||
|
||||
HidChooserController* ElectronHidDelegate::ControllerForFrame(
|
||||
|
||||
@@ -10,17 +10,24 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "base/observer_list.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "content/public/browser/hid_chooser.h"
|
||||
#include "content/public/browser/hid_delegate.h"
|
||||
#include "services/device/public/mojom/hid.mojom-forward.h"
|
||||
#include "shell/browser/hid/hid_chooser_context.h"
|
||||
#include "third_party/blink/public/mojom/hid/hid.mojom-forward.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
class RenderFrameHost;
|
||||
} // namespace content
|
||||
|
||||
namespace electron {
|
||||
|
||||
class HidChooserController;
|
||||
|
||||
class ElectronHidDelegate : public content::HidDelegate,
|
||||
public HidChooserContext::DeviceObserver {
|
||||
class ElectronHidDelegate : public content::HidDelegate {
|
||||
public:
|
||||
ElectronHidDelegate();
|
||||
ElectronHidDelegate(ElectronHidDelegate&) = delete;
|
||||
@@ -54,13 +61,6 @@ class ElectronHidDelegate : public content::HidDelegate,
|
||||
bool IsFidoAllowedForOrigin(content::BrowserContext* browser_context,
|
||||
const url::Origin& origin) override;
|
||||
bool IsServiceWorkerAllowedForOrigin(const url::Origin& origin) override;
|
||||
|
||||
// HidChooserContext::DeviceObserver:
|
||||
void OnDeviceAdded(const device::mojom::HidDeviceInfo&) override;
|
||||
void OnDeviceRemoved(const device::mojom::HidDeviceInfo&) override;
|
||||
void OnDeviceChanged(const device::mojom::HidDeviceInfo&) override;
|
||||
void OnHidManagerConnectionError() override;
|
||||
void OnHidChooserContextShutdown() override;
|
||||
void IncrementConnectionCount(content::BrowserContext* browser_context,
|
||||
const url::Origin& origin) override {}
|
||||
void DecrementConnectionCount(content::BrowserContext* browser_context,
|
||||
@@ -69,6 +69,14 @@ class ElectronHidDelegate : public content::HidDelegate,
|
||||
void DeleteControllerForFrame(content::RenderFrameHost* render_frame_host);
|
||||
|
||||
private:
|
||||
class ContextObservation;
|
||||
|
||||
ContextObservation* GetContextObserver(
|
||||
content::BrowserContext* browser_context);
|
||||
|
||||
base::flat_map<content::BrowserContext*, std::unique_ptr<ContextObservation>>
|
||||
observations_;
|
||||
|
||||
HidChooserController* ControllerForFrame(
|
||||
content::RenderFrameHost* render_frame_host);
|
||||
|
||||
@@ -78,10 +86,6 @@ class ElectronHidDelegate : public content::HidDelegate,
|
||||
std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters,
|
||||
content::HidChooser::Callback callback);
|
||||
|
||||
base::ScopedObservation<HidChooserContext, HidChooserContext::DeviceObserver>
|
||||
device_observation_{this};
|
||||
base::ObserverList<content::HidDelegate::Observer> observer_list_;
|
||||
|
||||
std::unordered_map<content::RenderFrameHost*,
|
||||
std::unique_ptr<HidChooserController>>
|
||||
controller_map_;
|
||||
@@ -91,23 +95,4 @@ class ElectronHidDelegate : public content::HidDelegate,
|
||||
|
||||
} // namespace electron
|
||||
|
||||
namespace base {
|
||||
|
||||
template <>
|
||||
struct ScopedObservationTraits<electron::HidChooserContext,
|
||||
electron::HidChooserContext::DeviceObserver> {
|
||||
static void AddObserver(
|
||||
electron::HidChooserContext* source,
|
||||
electron::HidChooserContext::DeviceObserver* observer) {
|
||||
source->AddDeviceObserver(observer);
|
||||
}
|
||||
static void RemoveObserver(
|
||||
electron::HidChooserContext* source,
|
||||
electron::HidChooserContext::DeviceObserver* observer) {
|
||||
source->RemoveDeviceObserver(observer);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_HID_ELECTRON_HID_DELEGATE_H_
|
||||
|
||||
@@ -29,9 +29,14 @@
|
||||
#include "shell/common/gin_converters/value_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "third_party/blink/public/common/permissions/permission_utils.h"
|
||||
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "base/containers/fixed_flat_set.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "extensions/common/constants.h"
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
||||
namespace electron {
|
||||
|
||||
const char kHidDeviceNameKey[] = "name";
|
||||
@@ -182,6 +187,26 @@ bool HidChooserContext::HasDevicePermission(
|
||||
origin, DeviceInfoToValue(device), browser_context_);
|
||||
}
|
||||
|
||||
bool HidChooserContext::IsFidoAllowedForOrigin(const url::Origin& origin) {
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
static constexpr auto kPrivilegedExtensionIds =
|
||||
base::MakeFixedFlatSet<base::StringPiece>({
|
||||
"ckcendljdlmgnhghiaomidhiiclmapok", // gnubbyd-v3 dev
|
||||
"lfboplenmmjcmpbkeemecobbadnmpfhi", // gnubbyd-v3 prod
|
||||
});
|
||||
|
||||
if (origin.scheme() == extensions::kExtensionScheme &&
|
||||
base::Contains(kPrivilegedExtensionIds, origin.host())) {
|
||||
return true;
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
||||
// This differs from upstream - we want to allow users greater
|
||||
// ability to communicate with FIDO devices in Electron.
|
||||
return base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kDisableHidBlocklist);
|
||||
}
|
||||
|
||||
void HidChooserContext::AddDeviceObserver(DeviceObserver* observer) {
|
||||
EnsureHidManagerConnection();
|
||||
device_observer_list_.AddObserver(observer);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "base/scoped_observation_traits.h"
|
||||
#include "base/unguessable_token.h"
|
||||
#include "components/keyed_service/core/keyed_service.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
@@ -77,6 +78,9 @@ class HidChooserContext : public KeyedService,
|
||||
bool HasDevicePermission(const url::Origin& origin,
|
||||
const device::mojom::HidDeviceInfo& device);
|
||||
|
||||
// Returns true if `origin` is allowed to access FIDO reports.
|
||||
bool IsFidoAllowedForOrigin(const url::Origin& origin);
|
||||
|
||||
// For ScopedObserver.
|
||||
void AddDeviceObserver(DeviceObserver* observer);
|
||||
void RemoveDeviceObserver(DeviceObserver* observer);
|
||||
@@ -138,4 +142,23 @@ class HidChooserContext : public KeyedService,
|
||||
|
||||
} // namespace electron
|
||||
|
||||
namespace base {
|
||||
|
||||
template <>
|
||||
struct ScopedObservationTraits<electron::HidChooserContext,
|
||||
electron::HidChooserContext::DeviceObserver> {
|
||||
static void AddObserver(
|
||||
electron::HidChooserContext* source,
|
||||
electron::HidChooserContext::DeviceObserver* observer) {
|
||||
source->AddDeviceObserver(observer);
|
||||
}
|
||||
static void RemoveObserver(
|
||||
electron::HidChooserContext* source,
|
||||
electron::HidChooserContext::DeviceObserver* observer) {
|
||||
source->RemoveDeviceObserver(observer);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_HID_HID_CHOOSER_CONTEXT_H_
|
||||
|
||||
@@ -85,12 +85,16 @@ HidChooserController::HidChooserController(
|
||||
filters_(std::move(filters)),
|
||||
exclusion_filters_(std::move(exclusion_filters)),
|
||||
callback_(std::move(callback)),
|
||||
initiator_document_(render_frame_host->GetWeakDocumentPtr()),
|
||||
origin_(content::WebContents::FromRenderFrameHost(render_frame_host)
|
||||
->GetPrimaryMainFrame()
|
||||
->GetLastCommittedOrigin()),
|
||||
frame_tree_node_id_(render_frame_host->GetFrameTreeNodeId()),
|
||||
hid_delegate_(hid_delegate),
|
||||
render_frame_host_id_(render_frame_host->GetGlobalId()) {
|
||||
// The use above of GetMainFrame is safe as content::HidService instances are
|
||||
// not created for fenced frames.
|
||||
DCHECK(!render_frame_host->IsNestedWithinFencedFrame());
|
||||
|
||||
chooser_context_ = HidChooserContextFactory::GetForBrowserContext(
|
||||
web_contents->GetBrowserContext())
|
||||
->AsWeakPtr();
|
||||
@@ -129,6 +133,7 @@ void HidChooserController::OnDeviceAdded(
|
||||
const device::mojom::HidDeviceInfo& device) {
|
||||
if (!DisplayDevice(device))
|
||||
return;
|
||||
|
||||
if (AddDeviceInfo(device)) {
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
@@ -142,8 +147,6 @@ void HidChooserController::OnDeviceAdded(
|
||||
session->Emit("hid-device-added", details);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void HidChooserController::OnDeviceRemoved(
|
||||
@@ -151,7 +154,8 @@ void HidChooserController::OnDeviceRemoved(
|
||||
if (!base::Contains(items_, PhysicalDeviceIdFromDeviceInfo(device)))
|
||||
return;
|
||||
|
||||
if (api::Session* session = GetSession(); session != nullptr) {
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_);
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
@@ -223,9 +227,8 @@ void HidChooserController::OnGotDevices(
|
||||
|
||||
for (auto& device : devices) {
|
||||
if (DisplayDevice(*device)) {
|
||||
if (AddDeviceInfo(*device)) {
|
||||
if (AddDeviceInfo(*device))
|
||||
devicesToDisplay.push_back(device->Clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +236,7 @@ void HidChooserController::OnGotDevices(
|
||||
// enumeration.
|
||||
if (chooser_context_)
|
||||
observation_.Observe(chooser_context_.get());
|
||||
|
||||
bool prevent_default = false;
|
||||
api::Session* session = GetSession();
|
||||
if (session) {
|
||||
@@ -255,26 +259,41 @@ void HidChooserController::OnGotDevices(
|
||||
|
||||
bool HidChooserController::DisplayDevice(
|
||||
const device::mojom::HidDeviceInfo& device) const {
|
||||
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kDisableHidBlocklist)) {
|
||||
// Do not pass the device to the chooser if it is excluded by the blocklist.
|
||||
if (device.is_excluded_by_blocklist)
|
||||
return false;
|
||||
// Check if `device` has a top-level collection with a FIDO usage. FIDO
|
||||
// devices may be displayed if the origin is privileged or the blocklist is
|
||||
// disabled.
|
||||
const bool has_fido_collection =
|
||||
base::Contains(device.collections, device::mojom::kPageFido,
|
||||
[](const auto& c) { return c->usage->usage_page; });
|
||||
|
||||
// Do not pass the device to the chooser if it has a top-level collection
|
||||
// with the FIDO usage page.
|
||||
//
|
||||
// Note: The HID blocklist also blocks top-level collections with the FIDO
|
||||
// usage page, but will not block the device if it has other (non-FIDO)
|
||||
// collections. The check below will exclude the device from the chooser
|
||||
// if it has any top-level FIDO collection.
|
||||
auto find_it =
|
||||
std::find_if(device.collections.begin(), device.collections.end(),
|
||||
[](const device::mojom::HidCollectionInfoPtr& c) {
|
||||
return c->usage->usage_page == device::mojom::kPageFido;
|
||||
});
|
||||
if (find_it != device.collections.end())
|
||||
return false;
|
||||
if (has_fido_collection) {
|
||||
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kDisableHidBlocklist) ||
|
||||
(chooser_context_ &&
|
||||
chooser_context_->IsFidoAllowedForOrigin(origin_))) {
|
||||
return FilterMatchesAny(device) && !IsExcluded(device);
|
||||
}
|
||||
|
||||
AddMessageToConsole(
|
||||
blink::mojom::ConsoleMessageLevel::kInfo,
|
||||
base::StringPrintf(
|
||||
"Chooser dialog is not displaying a FIDO HID device: vendorId=%d, "
|
||||
"productId=%d, name='%s', serial='%s'",
|
||||
device.vendor_id, device.product_id, device.product_name.c_str(),
|
||||
device.serial_number.c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (device.is_excluded_by_blocklist) {
|
||||
AddMessageToConsole(
|
||||
blink::mojom::ConsoleMessageLevel::kInfo,
|
||||
base::StringPrintf(
|
||||
"Chooser dialog is not displaying a device excluded by "
|
||||
"the HID blocklist: vendorId=%d, "
|
||||
"productId=%d, name='%s', serial='%s'",
|
||||
device.vendor_id, device.product_id, device.product_name.c_str(),
|
||||
device.serial_number.c_str()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return FilterMatchesAny(device) && !IsExcluded(device);
|
||||
@@ -303,6 +322,15 @@ bool HidChooserController::IsExcluded(
|
||||
return false;
|
||||
}
|
||||
|
||||
void HidChooserController::AddMessageToConsole(
|
||||
blink::mojom::ConsoleMessageLevel level,
|
||||
const std::string& message) const {
|
||||
if (content::RenderFrameHost* rfh =
|
||||
initiator_document_.AsRenderFrameHostIfValid()) {
|
||||
rfh->AddMessageToConsole(level, message);
|
||||
}
|
||||
}
|
||||
|
||||
bool HidChooserController::AddDeviceInfo(
|
||||
const device::mojom::HidDeviceInfo& device) {
|
||||
const auto& id = PhysicalDeviceIdFromDeviceInfo(device);
|
||||
@@ -340,10 +368,8 @@ void HidChooserController::UpdateDeviceInfo(
|
||||
auto physical_device_it = device_map_.find(id);
|
||||
DCHECK(physical_device_it != device_map_.end());
|
||||
auto& device_infos = physical_device_it->second;
|
||||
auto device_it = base::ranges::find_if(
|
||||
device_infos, [&device](const device::mojom::HidDeviceInfoPtr& d) {
|
||||
return d->guid == device.guid;
|
||||
});
|
||||
auto device_it = base::ranges::find(device_infos, device.guid,
|
||||
&device::mojom::HidDeviceInfo::guid);
|
||||
DCHECK(device_it != device_infos.end());
|
||||
*device_it = device.Clone();
|
||||
}
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "content/public/browser/global_routing_id.h"
|
||||
#include "content/public/browser/hid_chooser.h"
|
||||
#include "content/public/browser/weak_document_ptr.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "services/device/public/mojom/hid.mojom-forward.h"
|
||||
@@ -19,6 +21,7 @@
|
||||
#include "shell/browser/hid/electron_hid_delegate.h"
|
||||
#include "shell/browser/hid/hid_chooser_context.h"
|
||||
#include "shell/common/gin_converters/frame_converter.h"
|
||||
#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
|
||||
#include "third_party/blink/public/mojom/hid/hid.mojom.h"
|
||||
|
||||
namespace content {
|
||||
@@ -75,6 +78,8 @@ class HidChooserController
|
||||
bool DisplayDevice(const device::mojom::HidDeviceInfo& device) const;
|
||||
bool FilterMatchesAny(const device::mojom::HidDeviceInfo& device) const;
|
||||
bool IsExcluded(const device::mojom::HidDeviceInfo& device) const;
|
||||
void AddMessageToConsole(blink::mojom::ConsoleMessageLevel level,
|
||||
const std::string& message) const;
|
||||
|
||||
// Add |device_info| to |device_map_|. The device is added to the chooser item
|
||||
// representing the physical device. If the chooser item does not yet exist, a
|
||||
@@ -97,8 +102,8 @@ class HidChooserController
|
||||
std::vector<blink::mojom::HidDeviceFilterPtr> filters_;
|
||||
std::vector<blink::mojom::HidDeviceFilterPtr> exclusion_filters_;
|
||||
content::HidChooser::Callback callback_;
|
||||
content::WeakDocumentPtr initiator_document_;
|
||||
const url::Origin origin_;
|
||||
const int frame_tree_node_id_;
|
||||
|
||||
// The lifetime of the chooser context is tied to the browser context used to
|
||||
// create it, and may be destroyed while the chooser is still active.
|
||||
|
||||
@@ -99,6 +99,11 @@ NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
|
||||
options.Get(options::kTransparent, &transparent_);
|
||||
options.Get(options::kEnableLargerThanScreen, &enable_larger_than_screen_);
|
||||
options.Get(options::kTitleBarStyle, &title_bar_style_);
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
options.Get(options::kBackgroundMaterial, &background_material_);
|
||||
#elif BUILDFLAG(IS_MAC)
|
||||
options.Get(options::kVibrancyType, &vibrancy_);
|
||||
#endif
|
||||
|
||||
v8::Local<v8::Value> titlebar_overlay;
|
||||
if (options.Get(options::ktitleBarOverlay, &titlebar_overlay)) {
|
||||
@@ -352,10 +357,14 @@ void NativeWindow::SetContentSizeConstraints(
|
||||
size_constraints_.reset();
|
||||
}
|
||||
|
||||
// Windows/Linux:
|
||||
// The return value of GetContentSizeConstraints will be passed to Chromium
|
||||
// to set min/max sizes of window. Note that we are returning content size
|
||||
// instead of window size because that is what Chromium expects, see the
|
||||
// comment of |WidgetSizeIsClientSize| in Chromium's codebase to learn more.
|
||||
//
|
||||
// macOS:
|
||||
// The min/max sizes are set directly by calling NSWindow's methods.
|
||||
extensions::SizeConstraints NativeWindow::GetContentSizeConstraints() const {
|
||||
if (content_size_constraints_)
|
||||
return *content_size_constraints_;
|
||||
@@ -475,6 +484,10 @@ bool NativeWindow::AddTabbedWindow(NativeWindow* window) {
|
||||
return true; // for non-Mac platforms
|
||||
}
|
||||
|
||||
absl::optional<std::string> NativeWindow::GetTabbingIdentifier() const {
|
||||
return ""; // for non-Mac platforms
|
||||
}
|
||||
|
||||
void NativeWindow::SetVibrancy(const std::string& type) {
|
||||
vibrancy_ = type;
|
||||
}
|
||||
@@ -522,7 +535,7 @@ void NativeWindow::PreviewFile(const std::string& path,
|
||||
|
||||
void NativeWindow::CloseFilePreview() {}
|
||||
|
||||
gfx::Rect NativeWindow::GetWindowControlsOverlayRect() {
|
||||
absl::optional<gfx::Rect> NativeWindow::GetWindowControlsOverlayRect() {
|
||||
return overlay_rect_;
|
||||
}
|
||||
|
||||
@@ -650,6 +663,7 @@ void NativeWindow::NotifyWindowMoved() {
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowEnterFullScreen() {
|
||||
NotifyLayoutWindowControlsOverlay();
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.OnWindowEnterFullScreen();
|
||||
}
|
||||
@@ -675,6 +689,7 @@ void NativeWindow::NotifyWindowSheetEnd() {
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowLeaveFullScreen() {
|
||||
NotifyLayoutWindowControlsOverlay();
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.OnWindowLeaveFullScreen();
|
||||
}
|
||||
@@ -718,10 +733,10 @@ void NativeWindow::NotifyWindowSystemContextMenu(int x,
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyLayoutWindowControlsOverlay() {
|
||||
gfx::Rect bounding_rect = GetWindowControlsOverlayRect();
|
||||
if (!bounding_rect.IsEmpty()) {
|
||||
auto bounding_rect = GetWindowControlsOverlayRect();
|
||||
if (bounding_rect.has_value()) {
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.UpdateWindowControlsOverlay(bounding_rect);
|
||||
observer.UpdateWindowControlsOverlay(bounding_rect.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -255,6 +255,7 @@ class NativeWindow : public base::SupportsUserData,
|
||||
virtual void MoveTabToNewWindow();
|
||||
virtual void ToggleTabBar();
|
||||
virtual bool AddTabbedWindow(NativeWindow* window);
|
||||
virtual absl::optional<std::string> GetTabbingIdentifier() const;
|
||||
|
||||
// Toggle the menu bar.
|
||||
virtual void SetAutoHideMenuBar(bool auto_hide);
|
||||
@@ -284,7 +285,7 @@ class NativeWindow : public base::SupportsUserData,
|
||||
return weak_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
virtual gfx::Rect GetWindowControlsOverlayRect();
|
||||
virtual absl::optional<gfx::Rect> GetWindowControlsOverlayRect();
|
||||
virtual void SetWindowControlsOverlayRect(const gfx::Rect& overlay_rect);
|
||||
|
||||
// Methods called by the WebContents.
|
||||
|
||||
@@ -58,6 +58,8 @@ class NativeWindowMac : public NativeWindow,
|
||||
gfx::Rect GetBounds() override;
|
||||
bool IsNormal() override;
|
||||
gfx::Rect GetNormalBounds() override;
|
||||
void SetSizeConstraints(
|
||||
const extensions::SizeConstraints& window_constraints) override;
|
||||
void SetContentSizeConstraints(
|
||||
const extensions::SizeConstraints& size_constraints) override;
|
||||
void SetResizable(bool resizable) override;
|
||||
@@ -143,6 +145,7 @@ class NativeWindowMac : public NativeWindow,
|
||||
void MoveTabToNewWindow() override;
|
||||
void ToggleTabBar() override;
|
||||
bool AddTabbedWindow(NativeWindow* window) override;
|
||||
absl::optional<std::string> GetTabbingIdentifier() const override;
|
||||
void SetAspectRatio(double aspect_ratio,
|
||||
const gfx::Size& extra_size) override;
|
||||
void PreviewFile(const std::string& path,
|
||||
@@ -150,7 +153,7 @@ class NativeWindowMac : public NativeWindow,
|
||||
void CloseFilePreview() override;
|
||||
gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override;
|
||||
gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override;
|
||||
gfx::Rect GetWindowControlsOverlayRect() override;
|
||||
absl::optional<gfx::Rect> GetWindowControlsOverlayRect() override;
|
||||
void NotifyWindowEnterFullScreen() override;
|
||||
void NotifyWindowLeaveFullScreen() override;
|
||||
void SetActive(bool is_key) override;
|
||||
@@ -186,6 +189,9 @@ class NativeWindowMac : public NativeWindow,
|
||||
has_deferred_window_close_ = defer_close;
|
||||
}
|
||||
|
||||
void set_wants_to_be_visible(bool visible) { wants_to_be_visible_ = visible; }
|
||||
bool wants_to_be_visible() const { return wants_to_be_visible_; }
|
||||
|
||||
enum class VisualEffectState {
|
||||
kFollowWindow,
|
||||
kActive,
|
||||
@@ -252,6 +258,10 @@ class NativeWindowMac : public NativeWindow,
|
||||
// transition is complete.
|
||||
bool has_deferred_window_close_ = false;
|
||||
|
||||
// If true, the window is either visible, or wants to be visible but is
|
||||
// currently hidden due to having a hidden parent.
|
||||
bool wants_to_be_visible_ = false;
|
||||
|
||||
NSInteger attention_request_id_ = 0; // identifier from requestUserAttention
|
||||
|
||||
// The presentation options before entering kiosk mode.
|
||||
|
||||
@@ -496,6 +496,8 @@ void NativeWindowMac::Show() {
|
||||
return;
|
||||
}
|
||||
|
||||
set_wants_to_be_visible(true);
|
||||
|
||||
// Reattach the window to the parent to actually show it.
|
||||
if (parent())
|
||||
InternalSetParentWindow(parent(), true);
|
||||
@@ -528,6 +530,10 @@ void NativeWindowMac::Hide() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the window wants to be visible and has a parent, then the parent may
|
||||
// order it back in (in the period between orderOut: and close).
|
||||
set_wants_to_be_visible(false);
|
||||
|
||||
DetachChildren();
|
||||
|
||||
// Detach the window from the parent before.
|
||||
@@ -663,6 +669,9 @@ void NativeWindowMac::RemoveChildFromParentWindow() {
|
||||
|
||||
void NativeWindowMac::AttachChildren() {
|
||||
for (auto* child : child_windows_) {
|
||||
if (!static_cast<NativeWindowMac*>(child)->wants_to_be_visible())
|
||||
continue;
|
||||
|
||||
auto* child_nswindow = child->GetNativeWindow().GetNativeNSWindow();
|
||||
if ([child_nswindow parentWindow] == window_)
|
||||
continue;
|
||||
@@ -676,8 +685,6 @@ void NativeWindowMac::AttachChildren() {
|
||||
}
|
||||
|
||||
void NativeWindowMac::DetachChildren() {
|
||||
DCHECK(child_windows_.size() == [[window_ childWindows] count]);
|
||||
|
||||
// Hide all children before hiding/minimizing the window.
|
||||
// NativeWidgetNSWindowBridge::NotifyVisibilityChangeDown()
|
||||
// will DCHECK otherwise.
|
||||
@@ -774,6 +781,16 @@ gfx::Rect NativeWindowMac::GetNormalBounds() {
|
||||
// return widget()->GetRestoredBounds();
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetSizeConstraints(
|
||||
const extensions::SizeConstraints& window_constraints) {
|
||||
// Apply the size constraints to NSWindow.
|
||||
if (window_constraints.HasMinimumSize())
|
||||
[window_ setMinSize:window_constraints.GetMinimumSize().ToCGSize()];
|
||||
if (window_constraints.HasMaximumSize())
|
||||
[window_ setMaxSize:window_constraints.GetMaximumSize().ToCGSize()];
|
||||
NativeWindow::SetSizeConstraints(window_constraints);
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetContentSizeConstraints(
|
||||
const extensions::SizeConstraints& size_constraints) {
|
||||
auto convertSize = [this](const gfx::Size& size) {
|
||||
@@ -788,6 +805,7 @@ void NativeWindowMac::SetContentSizeConstraints(
|
||||
}
|
||||
};
|
||||
|
||||
// Apply the size constraints to NSWindow.
|
||||
NSView* content = [window_ contentView];
|
||||
if (size_constraints.HasMinimumSize()) {
|
||||
NSSize min_size = convertSize(size_constraints.GetMinimumSize());
|
||||
@@ -1422,11 +1440,21 @@ void NativeWindowMac::UpdateVibrancyRadii(bool fullscreen) {
|
||||
|
||||
if (vibrantView != nil && !vibrancy_type_.empty()) {
|
||||
const bool no_rounded_corner = !HasStyleMask(NSWindowStyleMaskTitled);
|
||||
if (!has_frame() && !is_modal() && !no_rounded_corner) {
|
||||
const int macos_version = base::mac::MacOSMajorVersion();
|
||||
|
||||
// Modal window corners are rounded on macOS >= 11 or higher if the user
|
||||
// hasn't passed noRoundedCorners.
|
||||
bool should_round_modal =
|
||||
!no_rounded_corner && (macos_version >= 11 ? true : !is_modal());
|
||||
// Nonmodal window corners are rounded if they're frameless and the user
|
||||
// hasn't passed noRoundedCorners.
|
||||
bool should_round_nonmodal = !no_rounded_corner && !has_frame();
|
||||
|
||||
if (should_round_nonmodal || should_round_modal) {
|
||||
CGFloat radius;
|
||||
if (fullscreen) {
|
||||
radius = 0.0f;
|
||||
} else if (@available(macOS 11.0, *)) {
|
||||
} else if (macos_version >= 11) {
|
||||
radius = 9.0f;
|
||||
} else {
|
||||
// Smaller corner radius on versions prior to Big Sur.
|
||||
@@ -1635,6 +1663,13 @@ bool NativeWindowMac::AddTabbedWindow(NativeWindow* window) {
|
||||
return true;
|
||||
}
|
||||
|
||||
absl::optional<std::string> NativeWindowMac::GetTabbingIdentifier() const {
|
||||
if ([window_ tabbingMode] == NSWindowTabbingModeDisallowed)
|
||||
return absl::nullopt;
|
||||
|
||||
return base::SysNSStringToUTF8([window_ tabbingIdentifier]);
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetAspectRatio(double aspect_ratio,
|
||||
const gfx::Size& extra_size) {
|
||||
NativeWindow::SetAspectRatio(aspect_ratio, extra_size);
|
||||
@@ -1875,23 +1910,33 @@ void NativeWindowMac::SetForwardMouseMessages(bool forward) {
|
||||
[window_ setAcceptsMouseMovedEvents:forward];
|
||||
}
|
||||
|
||||
gfx::Rect NativeWindowMac::GetWindowControlsOverlayRect() {
|
||||
if (titlebar_overlay_ && buttons_proxy_ &&
|
||||
window_button_visibility_.value_or(true)) {
|
||||
absl::optional<gfx::Rect> NativeWindowMac::GetWindowControlsOverlayRect() {
|
||||
if (!titlebar_overlay_)
|
||||
return absl::nullopt;
|
||||
|
||||
// On macOS, when in fullscreen mode, window controls (the menu bar, title
|
||||
// bar, and toolbar) are attached to a separate NSView that slides down from
|
||||
// the top of the screen, independent of, and overlapping the WebContents.
|
||||
// Disable WCO when in fullscreen, because this space is inaccessible to
|
||||
// WebContents. https://crbug.com/915110.
|
||||
if (IsFullscreen())
|
||||
return gfx::Rect();
|
||||
|
||||
if (buttons_proxy_ && window_button_visibility_.value_or(true)) {
|
||||
NSRect buttons = [buttons_proxy_ getButtonsContainerBounds];
|
||||
gfx::Rect overlay;
|
||||
overlay.set_width(GetContentSize().width() - NSWidth(buttons));
|
||||
if ([buttons_proxy_ useCustomHeight]) {
|
||||
overlay.set_height(titlebar_overlay_height());
|
||||
} else {
|
||||
overlay.set_height(NSHeight(buttons));
|
||||
}
|
||||
overlay.set_height([buttons_proxy_ useCustomHeight]
|
||||
? titlebar_overlay_height()
|
||||
: NSHeight(buttons));
|
||||
|
||||
if (!base::i18n::IsRTL())
|
||||
overlay.set_x(NSMaxX(buttons));
|
||||
|
||||
return overlay;
|
||||
}
|
||||
return gfx::Rect();
|
||||
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "base/win/atl.h" // Must be before UIAutomationCore.h
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "content/public/browser/browser_accessibility_state.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
@@ -165,10 +166,41 @@ gfx::ResizeEdge GetWindowResizeEdge(WPARAM param) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsMutexPresent(const wchar_t* name) {
|
||||
base::win::ScopedHandle mutex_holder(::CreateMutex(nullptr, false, name));
|
||||
return ::GetLastError() == ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
bool IsLibraryLoaded(const wchar_t* name) {
|
||||
HMODULE hmodule = nullptr;
|
||||
::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, name,
|
||||
&hmodule);
|
||||
return hmodule != nullptr;
|
||||
}
|
||||
|
||||
// The official way to get screen reader status is to call:
|
||||
// SystemParametersInfo(SPI_GETSCREENREADER) && UiaClientsAreListening()
|
||||
// However it has false positives (for example when user is using touch screens)
|
||||
// and will cause performance issues in some apps.
|
||||
bool IsScreenReaderActive() {
|
||||
UINT screenReader = 0;
|
||||
SystemParametersInfo(SPI_GETSCREENREADER, 0, &screenReader, 0);
|
||||
return screenReader && UiaClientsAreListening();
|
||||
if (IsMutexPresent(L"NarratorRunning"))
|
||||
return true;
|
||||
|
||||
static const wchar_t* names[] = {// NVDA
|
||||
L"nvdaHelperRemote.dll",
|
||||
// JAWS
|
||||
L"jhook.dll",
|
||||
// Window-Eyes
|
||||
L"gwhk64.dll", L"gwmhook.dll",
|
||||
// ZoomText
|
||||
L"AiSquared.Infuser.HookLib.dll"};
|
||||
|
||||
for (auto* name : names) {
|
||||
if (IsLibraryLoaded(name))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -27,10 +27,14 @@ void Notification::NotificationClicked() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Notification::NotificationDismissed() {
|
||||
void Notification::NotificationDismissed(bool should_destroy) {
|
||||
if (delegate())
|
||||
delegate()->NotificationClosed();
|
||||
Destroy();
|
||||
|
||||
set_is_dismissed(true);
|
||||
|
||||
if (should_destroy)
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Notification::NotificationFailed(const std::string& error) {
|
||||
@@ -39,6 +43,8 @@ void Notification::NotificationFailed(const std::string& error) {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Notification::Remove() {}
|
||||
|
||||
void Notification::Destroy() {
|
||||
presenter()->RemoveNotification(this);
|
||||
}
|
||||
|
||||
@@ -50,13 +50,19 @@ class Notification {
|
||||
|
||||
// Shows the notification.
|
||||
virtual void Show(const NotificationOptions& options) = 0;
|
||||
// Closes the notification, this instance will be destroyed after the
|
||||
// notification gets closed.
|
||||
|
||||
// Dismisses the notification. On some platforms this will result in full
|
||||
// removal and destruction of the notification, but if the initial dismissal
|
||||
// does not fully get rid of the notification it will be destroyed in Remove.
|
||||
virtual void Dismiss() = 0;
|
||||
|
||||
// Removes the notification if it was not fully removed during dismissal,
|
||||
// as can happen on some platforms including Windows.
|
||||
virtual void Remove();
|
||||
|
||||
// Should be called by derived classes.
|
||||
void NotificationClicked();
|
||||
void NotificationDismissed();
|
||||
void NotificationDismissed(bool should_destroy = true);
|
||||
void NotificationFailed(const std::string& error = "");
|
||||
|
||||
// delete this.
|
||||
@@ -68,10 +74,12 @@ class Notification {
|
||||
|
||||
void set_delegate(NotificationDelegate* delegate) { delegate_ = delegate; }
|
||||
void set_notification_id(const std::string& id) { notification_id_ = id; }
|
||||
void set_is_dismissed(bool dismissed) { is_dismissed_ = dismissed; }
|
||||
|
||||
NotificationDelegate* delegate() const { return delegate_; }
|
||||
NotificationPresenter* presenter() const { return presenter_; }
|
||||
const std::string& notification_id() const { return notification_id_; }
|
||||
bool is_dismissed() const { return is_dismissed_; }
|
||||
|
||||
// disable copy
|
||||
Notification(const Notification&) = delete;
|
||||
@@ -85,6 +93,7 @@ class Notification {
|
||||
raw_ptr<NotificationDelegate> delegate_;
|
||||
raw_ptr<NotificationPresenter> presenter_;
|
||||
std::string notification_id_;
|
||||
bool is_dismissed_ = false;
|
||||
|
||||
base::WeakPtrFactory<Notification> weak_factory_{this};
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "shell/browser/notifications/win/windows_toast_notification.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <shlobj.h>
|
||||
#include <wrl\wrappers\corewrappers.h>
|
||||
|
||||
@@ -34,6 +36,8 @@ using ABI::Windows::Data::Xml::Dom::IXmlNodeList;
|
||||
using ABI::Windows::Data::Xml::Dom::IXmlText;
|
||||
using Microsoft::WRL::Wrappers::HStringReference;
|
||||
|
||||
namespace winui = ABI::Windows::UI;
|
||||
|
||||
#define RETURN_IF_FAILED(hr) \
|
||||
do { \
|
||||
HRESULT _hrTemp = hr; \
|
||||
@@ -47,8 +51,7 @@ using Microsoft::WRL::Wrappers::HStringReference;
|
||||
std::string _msgTemp = msg; \
|
||||
if (FAILED(_hrTemp)) { \
|
||||
std::string _err = _msgTemp + ",ERROR " + std::to_string(_hrTemp); \
|
||||
if (IsDebuggingNotifications()) \
|
||||
LOG(INFO) << _err; \
|
||||
DebugLog(_err); \
|
||||
Notification::NotificationFailed(_err); \
|
||||
return _hrTemp; \
|
||||
} \
|
||||
@@ -58,17 +61,23 @@ namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsDebuggingNotifications() {
|
||||
return base::Environment::Create()->HasVar("ELECTRON_DEBUG_NOTIFICATIONS");
|
||||
// This string needs to be max 16 characters to work on Windows 10 prior to
|
||||
// applying Creators Update (build 15063).
|
||||
constexpr wchar_t kGroup[] = L"Notifications";
|
||||
|
||||
void DebugLog(std::string_view log_msg) {
|
||||
if (base::Environment::Create()->HasVar("ELECTRON_DEBUG_NOTIFICATIONS"))
|
||||
LOG(INFO) << log_msg;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
ComPtr<ABI::Windows::UI::Notifications::IToastNotificationManagerStatics>
|
||||
ComPtr<winui::Notifications::IToastNotificationManagerStatics>
|
||||
WindowsToastNotification::toast_manager_;
|
||||
|
||||
// static
|
||||
ComPtr<ABI::Windows::UI::Notifications::IToastNotifier>
|
||||
ComPtr<winui::Notifications::IToastNotifier>
|
||||
WindowsToastNotification::toast_notifier_;
|
||||
|
||||
// static
|
||||
@@ -112,17 +121,37 @@ WindowsToastNotification::~WindowsToastNotification() {
|
||||
|
||||
void WindowsToastNotification::Show(const NotificationOptions& options) {
|
||||
if (SUCCEEDED(ShowInternal(options))) {
|
||||
if (IsDebuggingNotifications())
|
||||
LOG(INFO) << "Notification created";
|
||||
DebugLog("Notification created");
|
||||
|
||||
if (delegate())
|
||||
delegate()->NotificationDisplayed();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsToastNotification::Remove() {
|
||||
DebugLog("Removing notification from action center");
|
||||
|
||||
ComPtr<winui::Notifications::IToastNotificationManagerStatics2>
|
||||
toast_manager2;
|
||||
if (FAILED(toast_manager_.As(&toast_manager2)))
|
||||
return;
|
||||
|
||||
ComPtr<winui::Notifications::IToastNotificationHistory> notification_history;
|
||||
if (FAILED(toast_manager2->get_History(¬ification_history)))
|
||||
return;
|
||||
|
||||
ScopedHString app_id;
|
||||
if (!GetAppUserModelID(&app_id))
|
||||
return;
|
||||
|
||||
ScopedHString group(kGroup);
|
||||
ScopedHString tag(base::as_wcstr(base::UTF8ToUTF16(notification_id())));
|
||||
notification_history->RemoveGroupedTagWithId(tag, group, app_id);
|
||||
}
|
||||
|
||||
void WindowsToastNotification::Dismiss() {
|
||||
if (IsDebuggingNotifications())
|
||||
LOG(INFO) << "Hiding notification";
|
||||
DebugLog("Hiding notification");
|
||||
|
||||
toast_notifier_->Hide(toast_notification_.Get());
|
||||
}
|
||||
|
||||
@@ -151,8 +180,7 @@ HRESULT WindowsToastNotification::ShowInternal(
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
ComPtr<ABI::Windows::UI::Notifications::IToastNotificationFactory>
|
||||
toast_factory;
|
||||
ComPtr<winui::Notifications::IToastNotificationFactory> toast_factory;
|
||||
REPORT_AND_RETURN_IF_FAILED(
|
||||
Windows::Foundation::GetActivationFactory(toast_str, &toast_factory),
|
||||
"WinAPI: GetActivationFactory failed");
|
||||
@@ -161,6 +189,19 @@ HRESULT WindowsToastNotification::ShowInternal(
|
||||
toast_xml.Get(), &toast_notification_),
|
||||
"WinAPI: CreateToastNotification failed");
|
||||
|
||||
ComPtr<winui::Notifications::IToastNotification2> toast2;
|
||||
REPORT_AND_RETURN_IF_FAILED(
|
||||
toast_notification_->QueryInterface(IID_PPV_ARGS(&toast2)),
|
||||
"WinAPI: Getting Notification interface failed");
|
||||
|
||||
ScopedHString group(kGroup);
|
||||
REPORT_AND_RETURN_IF_FAILED(toast2->put_Group(group),
|
||||
"WinAPI: Setting group failed");
|
||||
|
||||
ScopedHString tag(base::as_wcstr(base::UTF8ToUTF16(notification_id())));
|
||||
REPORT_AND_RETURN_IF_FAILED(toast2->put_Tag(tag),
|
||||
"WinAPI: Setting tag failed");
|
||||
|
||||
REPORT_AND_RETURN_IF_FAILED(SetupCallbacks(toast_notification_.Get()),
|
||||
"WinAPI: SetupCallbacks failed");
|
||||
|
||||
@@ -170,22 +211,20 @@ HRESULT WindowsToastNotification::ShowInternal(
|
||||
}
|
||||
|
||||
HRESULT WindowsToastNotification::GetToastXml(
|
||||
ABI::Windows::UI::Notifications::IToastNotificationManagerStatics*
|
||||
toastManager,
|
||||
winui::Notifications::IToastNotificationManagerStatics* toastManager,
|
||||
const std::u16string& title,
|
||||
const std::u16string& msg,
|
||||
const std::wstring& icon_path,
|
||||
const std::u16string& timeout_type,
|
||||
bool silent,
|
||||
IXmlDocument** toast_xml) {
|
||||
ABI::Windows::UI::Notifications::ToastTemplateType template_type;
|
||||
winui::Notifications::ToastTemplateType template_type;
|
||||
if (title.empty() || msg.empty()) {
|
||||
// Single line toast.
|
||||
template_type =
|
||||
icon_path.empty()
|
||||
? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01
|
||||
: ABI::Windows::UI::Notifications::
|
||||
ToastTemplateType_ToastImageAndText01;
|
||||
? winui::Notifications::ToastTemplateType_ToastText01
|
||||
: winui::Notifications::ToastTemplateType_ToastImageAndText01;
|
||||
REPORT_AND_RETURN_IF_FAILED(
|
||||
toast_manager_->GetTemplateContent(template_type, toast_xml),
|
||||
"XML: Fetching XML ToastImageAndText01 template failed");
|
||||
@@ -199,9 +238,8 @@ HRESULT WindowsToastNotification::GetToastXml(
|
||||
// Title and body toast.
|
||||
template_type =
|
||||
icon_path.empty()
|
||||
? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText02
|
||||
: ABI::Windows::UI::Notifications::
|
||||
ToastTemplateType_ToastImageAndText02;
|
||||
? winui::Notifications::ToastTemplateType_ToastText02
|
||||
: winui::Notifications::ToastTemplateType_ToastImageAndText02;
|
||||
REPORT_AND_RETURN_IF_FAILED(
|
||||
toastManager->GetTemplateContent(template_type, toast_xml),
|
||||
"XML: Fetching XML ToastImageAndText02 template failed");
|
||||
@@ -567,7 +605,7 @@ HRESULT WindowsToastNotification::XmlDocumentFromString(
|
||||
}
|
||||
|
||||
HRESULT WindowsToastNotification::SetupCallbacks(
|
||||
ABI::Windows::UI::Notifications::IToastNotification* toast) {
|
||||
winui::Notifications::IToastNotification* toast) {
|
||||
event_handler_ = Make<ToastEventHandler>(this);
|
||||
RETURN_IF_FAILED(
|
||||
toast->add_Activated(event_handler_.Get(), &activated_token_));
|
||||
@@ -578,7 +616,7 @@ HRESULT WindowsToastNotification::SetupCallbacks(
|
||||
}
|
||||
|
||||
bool WindowsToastNotification::RemoveCallbacks(
|
||||
ABI::Windows::UI::Notifications::IToastNotification* toast) {
|
||||
winui::Notifications::IToastNotification* toast) {
|
||||
if (FAILED(toast->remove_Activated(activated_token_)))
|
||||
return false;
|
||||
|
||||
@@ -597,32 +635,29 @@ ToastEventHandler::ToastEventHandler(Notification* notification)
|
||||
ToastEventHandler::~ToastEventHandler() = default;
|
||||
|
||||
IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||
ABI::Windows::UI::Notifications::IToastNotification* sender,
|
||||
winui::Notifications::IToastNotification* sender,
|
||||
IInspectable* args) {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&Notification::NotificationClicked, notification_));
|
||||
if (IsDebuggingNotifications())
|
||||
LOG(INFO) << "Notification clicked";
|
||||
DebugLog("Notification clicked");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||
ABI::Windows::UI::Notifications::IToastNotification* sender,
|
||||
ABI::Windows::UI::Notifications::IToastDismissedEventArgs* e) {
|
||||
winui::Notifications::IToastNotification* sender,
|
||||
winui::Notifications::IToastDismissedEventArgs* e) {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&Notification::NotificationDismissed, notification_));
|
||||
if (IsDebuggingNotifications())
|
||||
LOG(INFO) << "Notification dismissed";
|
||||
|
||||
FROM_HERE, base::BindOnce(&Notification::NotificationDismissed,
|
||||
notification_, false));
|
||||
DebugLog("Notification dismissed");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||
ABI::Windows::UI::Notifications::IToastNotification* sender,
|
||||
ABI::Windows::UI::Notifications::IToastFailedEventArgs* e) {
|
||||
winui::Notifications::IToastNotification* sender,
|
||||
winui::Notifications::IToastFailedEventArgs* e) {
|
||||
HRESULT error;
|
||||
e->get_ErrorCode(&error);
|
||||
std::string errorMessage =
|
||||
@@ -630,8 +665,7 @@ IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE, base::BindOnce(&Notification::NotificationFailed,
|
||||
notification_, errorMessage));
|
||||
if (IsDebuggingNotifications())
|
||||
LOG(INFO) << errorMessage;
|
||||
DebugLog(errorMessage);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ class WindowsToastNotification : public Notification {
|
||||
// Notification:
|
||||
void Show(const NotificationOptions& options) override;
|
||||
void Dismiss() override;
|
||||
void Remove() override;
|
||||
|
||||
private:
|
||||
friend class ToastEventHandler;
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/common/webplugininfo.h"
|
||||
#include "extensions/buildflags/buildflags.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/origin.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/extension_util.h"
|
||||
#include "extensions/common/constants.h"
|
||||
@@ -36,7 +36,7 @@ base::flat_map<std::string, std::string>
|
||||
PluginUtils::GetMimeTypeToExtensionIdMap(
|
||||
content::BrowserContext* browser_context) {
|
||||
base::flat_map<std::string, std::string> mime_type_to_extension_id_map;
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
std::vector<std::string> allowed_extension_ids =
|
||||
MimeTypesHandler::GetMIMETypeAllowlist();
|
||||
// Go through the white-listed extensions and try to use them to intercept
|
||||
|
||||
@@ -262,6 +262,7 @@ using FullScreenTransitionState =
|
||||
[super windowDidMiniaturize:notification];
|
||||
is_minimized_ = true;
|
||||
|
||||
shell_->set_wants_to_be_visible(false);
|
||||
shell_->NotifyWindowMinimize();
|
||||
}
|
||||
|
||||
@@ -269,6 +270,7 @@ using FullScreenTransitionState =
|
||||
[super windowDidDeminiaturize:notification];
|
||||
is_minimized_ = false;
|
||||
|
||||
shell_->set_wants_to_be_visible(true);
|
||||
shell_->AttachChildren();
|
||||
shell_->SetWindowLevel(level_);
|
||||
shell_->NotifyWindowRestore();
|
||||
|
||||
@@ -66,14 +66,18 @@
|
||||
|
||||
// Manages the PopUpButtonHandler.
|
||||
@interface ElectronAccessoryView : NSView
|
||||
@property(nonatomic, strong) PopUpButtonHandler* popUpButtonHandler;
|
||||
@end
|
||||
|
||||
@implementation ElectronAccessoryView
|
||||
|
||||
@synthesize popUpButtonHandler;
|
||||
|
||||
- (void)dealloc {
|
||||
auto* popupButton =
|
||||
static_cast<NSPopUpButton*>([[self subviews] objectAtIndex:1]);
|
||||
popupButton.target = nil;
|
||||
popUpButtonHandler = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -149,6 +153,7 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
||||
|
||||
[accessoryView addSubview:label];
|
||||
[accessoryView addSubview:popupButton];
|
||||
[accessoryView setPopUpButtonHandler:popUpButtonHandler];
|
||||
|
||||
[dialog setAccessoryView:accessoryView];
|
||||
}
|
||||
|
||||
@@ -142,8 +142,11 @@ void InspectableWebContentsViewViews::CloseDevTools() {
|
||||
|
||||
devtools_visible_ = false;
|
||||
if (devtools_window_) {
|
||||
inspectable_web_contents()->SaveDevToolsBounds(
|
||||
devtools_window_->GetWindowBoundsInScreen());
|
||||
auto save_bounds = devtools_window_->IsMinimized()
|
||||
? devtools_window_->GetRestoredBounds()
|
||||
: devtools_window_->GetWindowBoundsInScreen();
|
||||
inspectable_web_contents()->SaveDevToolsBounds(save_bounds);
|
||||
|
||||
devtools_window_.reset();
|
||||
devtools_window_web_view_ = nullptr;
|
||||
devtools_window_delegate_ = nullptr;
|
||||
@@ -216,6 +219,8 @@ const std::u16string InspectableWebContentsViewViews::GetTitle() {
|
||||
void InspectableWebContentsViewViews::Layout() {
|
||||
if (!devtools_web_view_->GetVisible()) {
|
||||
contents_web_view_->SetBoundsRect(GetContentsBounds());
|
||||
// Propagate layout call to all children, for example browser views.
|
||||
View::Layout();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -233,6 +238,9 @@ void InspectableWebContentsViewViews::Layout() {
|
||||
devtools_web_view_->SetBoundsRect(new_devtools_bounds);
|
||||
contents_web_view_->SetBoundsRect(new_contents_bounds);
|
||||
|
||||
// Propagate layout call to all children, for example browser views.
|
||||
View::Layout();
|
||||
|
||||
if (GetDelegate())
|
||||
GetDelegate()->DevToolsResized();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user