mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
21 Commits
v28.0.0-be
...
v29.0.0-ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29270f3df5 | ||
|
|
657e88b173 | ||
|
|
09bab60a9e | ||
|
|
666907d50d | ||
|
|
73a42d0b7b | ||
|
|
f65d1f3d55 | ||
|
|
ba4d6d08a7 | ||
|
|
b6ec19a582 | ||
|
|
f7b1c75c72 | ||
|
|
6d0d350e13 | ||
|
|
f362e089b1 | ||
|
|
c9f6f15df6 | ||
|
|
bbd2236bdd | ||
|
|
5d6023ae0d | ||
|
|
dc4476d480 | ||
|
|
ce4ae584e3 | ||
|
|
344f8fd384 | ||
|
|
592a30aa0b | ||
|
|
3c31246343 | ||
|
|
7ab2a82166 | ||
|
|
5c821d3379 |
@@ -1210,7 +1210,7 @@ commands:
|
||||
build-type: << parameters.build-type >>
|
||||
- *step-maybe-electron-dist-strip
|
||||
- step-electron-dist-build:
|
||||
additional-targets: shell_browser_ui_unittests electron:node_headers third_party/electron_node:overlapped-checker electron:hunspell_dictionaries_zip
|
||||
additional-targets: electron:node_headers third_party/electron_node:overlapped-checker electron:hunspell_dictionaries_zip
|
||||
|
||||
- *step-show-goma-stats
|
||||
|
||||
|
||||
19
BUILD.gn
19
BUILD.gn
@@ -1328,25 +1328,6 @@ if (is_mac) {
|
||||
}
|
||||
}
|
||||
|
||||
test("shell_browser_ui_unittests") {
|
||||
sources = [
|
||||
"//electron/shell/browser/ui/accelerator_util_unittests.cc",
|
||||
"//electron/shell/browser/ui/run_all_unittests.cc",
|
||||
]
|
||||
|
||||
configs += [ ":electron_lib_config" ]
|
||||
|
||||
deps = [
|
||||
":electron_lib",
|
||||
"//base",
|
||||
"//base/test:test_support",
|
||||
"//testing/gmock",
|
||||
"//testing/gtest",
|
||||
"//ui/base",
|
||||
"//ui/strings",
|
||||
]
|
||||
}
|
||||
|
||||
template("dist_zip") {
|
||||
_runtime_deps_target = "${target_name}__deps"
|
||||
_runtime_deps_file =
|
||||
|
||||
2
DEPS
2
DEPS
@@ -4,7 +4,7 @@ vars = {
|
||||
'chromium_version':
|
||||
'119.0.6045.0',
|
||||
'node_version':
|
||||
'v18.18.0',
|
||||
'v18.18.2',
|
||||
'nan_version':
|
||||
'e14bdcd1f72d62bca1d541b66da43130384ec213',
|
||||
'squirrel.mac_version':
|
||||
|
||||
@@ -41,9 +41,9 @@ Each Electron release provides binaries for macOS, Windows, and Linux.
|
||||
* macOS (Catalina and up): Electron provides 64-bit Intel and ARM binaries for macOS. Apple Silicon support was added in Electron 11.
|
||||
* Windows (Windows 10 and up): Electron provides `ia32` (`x86`), `x64` (`amd64`), and `arm64` binaries for Windows. Windows on ARM support was added in Electron 5.0.8. Support for Windows 7, 8 and 8.1 was [removed in Electron 23, in line with Chromium's Windows deprecation policy](https://www.electronjs.org/blog/windows-7-to-8-1-deprecation-notice).
|
||||
* Linux: The prebuilt binaries of Electron are built on Ubuntu 20.04. They have also been verified to work on:
|
||||
* Ubuntu 14.04 and newer
|
||||
* Fedora 24 and newer
|
||||
* Debian 8 and newer
|
||||
* Ubuntu 18.04 and newer
|
||||
* Fedora 32 and newer
|
||||
* Debian 10 and newer
|
||||
|
||||
## Quick start & Electron Fiddle
|
||||
|
||||
|
||||
@@ -156,7 +156,6 @@ for:
|
||||
- gn gen out/ffmpeg "--args=import(\"//electron/build/args/ffmpeg.gn\") %GN_EXTRA_ARGS%"
|
||||
- ninja -C out/ffmpeg electron:electron_ffmpeg_zip
|
||||
- ninja -C out/Default electron:electron_dist_zip
|
||||
- ninja -C out/Default shell_browser_ui_unittests
|
||||
- gn desc out/Default v8:run_mksnapshot_default args > out/Default/default_mksnapshot_args
|
||||
# Remove unused args from mksnapshot_args
|
||||
- ps: >-
|
||||
@@ -195,7 +194,7 @@ for:
|
||||
if ($env:SHOULD_SKIP_ARTIFACT_VALIDATION -eq 'true') {
|
||||
Write-warning "Skipping artifact validation for doc-only $env:APPVEYOR_PROJECT_NAME"
|
||||
} else {
|
||||
$artifacts_to_validate = 'dist.zip','windows_toolchain_profile.json','shell_browser_ui_unittests.exe','chromedriver.zip','ffmpeg.zip','node_headers.zip','mksnapshot.zip','electron.lib','hunspell_dictionaries.zip'
|
||||
$artifacts_to_validate = 'dist.zip','windows_toolchain_profile.json','chromedriver.zip','ffmpeg.zip','node_headers.zip','mksnapshot.zip','electron.lib','hunspell_dictionaries.zip'
|
||||
foreach($artifact_name in $artifacts_to_validate) {
|
||||
if ($artifact_name -eq 'ffmpeg.zip') {
|
||||
$artifact_file = "out\ffmpeg\ffmpeg.zip"
|
||||
@@ -233,7 +232,6 @@ for:
|
||||
- cd C:\projects\src
|
||||
- if exist out\Default\windows_toolchain_profile.json ( appveyor-retry appveyor PushArtifact out\Default\windows_toolchain_profile.json )
|
||||
- if exist out\Default\dist.zip (appveyor-retry appveyor PushArtifact out\Default\dist.zip)
|
||||
- if exist out\Default\shell_browser_ui_unittests.exe (appveyor-retry appveyor PushArtifact out\Default\shell_browser_ui_unittests.exe)
|
||||
- if exist out\Default\chromedriver.zip (appveyor-retry appveyor PushArtifact out\Default\chromedriver.zip)
|
||||
- if exist out\ffmpeg\ffmpeg.zip (appveyor-retry appveyor PushArtifact out\ffmpeg\ffmpeg.zip)
|
||||
- if exist node_headers.zip (appveyor-retry appveyor PushArtifact node_headers.zip)
|
||||
@@ -277,7 +275,7 @@ for:
|
||||
if ($job.name -eq "Build Arm on X64 Windows") {
|
||||
$jobId = $job.jobId
|
||||
foreach($artifact_name in $artifacts_to_download) {
|
||||
if ($artifact_name -eq 'shell_browser_ui_unittests.exe' -Or $artifact_name -eq 'electron.lib') {
|
||||
if ($artifact_name -eq 'electron.lib') {
|
||||
$outfile = "src\out\Default\$artifact_name"
|
||||
} else {
|
||||
$outfile = $artifact_name
|
||||
|
||||
@@ -154,7 +154,6 @@ for:
|
||||
- gn gen out/ffmpeg "--args=import(\"//electron/build/args/ffmpeg.gn\") %GN_EXTRA_ARGS%"
|
||||
- ninja -C out/ffmpeg electron:electron_ffmpeg_zip
|
||||
- ninja -C out/Default electron:electron_dist_zip
|
||||
- ninja -C out/Default shell_browser_ui_unittests
|
||||
- gn desc out/Default v8:run_mksnapshot_default args > out/Default/default_mksnapshot_args
|
||||
# Remove unused args from mksnapshot_args
|
||||
- ps: >-
|
||||
@@ -193,7 +192,7 @@ for:
|
||||
if ($env:SHOULD_SKIP_ARTIFACT_VALIDATION -eq 'true') {
|
||||
Write-warning "Skipping artifact validation for doc-only $env:APPVEYOR_PROJECT_NAME"
|
||||
} else {
|
||||
$artifacts_to_validate = 'dist.zip','windows_toolchain_profile.json','shell_browser_ui_unittests.exe','chromedriver.zip','ffmpeg.zip','node_headers.zip','mksnapshot.zip','electron.lib','hunspell_dictionaries.zip'
|
||||
$artifacts_to_validate = 'dist.zip','windows_toolchain_profile.json','chromedriver.zip','ffmpeg.zip','node_headers.zip','mksnapshot.zip','electron.lib','hunspell_dictionaries.zip'
|
||||
foreach($artifact_name in $artifacts_to_validate) {
|
||||
if ($artifact_name -eq 'ffmpeg.zip') {
|
||||
$artifact_file = "out\ffmpeg\ffmpeg.zip"
|
||||
@@ -231,7 +230,6 @@ for:
|
||||
- cd C:\projects\src
|
||||
- if exist out\Default\windows_toolchain_profile.json ( appveyor-retry appveyor PushArtifact out\Default\windows_toolchain_profile.json )
|
||||
- if exist out\Default\dist.zip (appveyor-retry appveyor PushArtifact out\Default\dist.zip)
|
||||
- if exist out\Default\shell_browser_ui_unittests.exe (appveyor-retry appveyor PushArtifact out\Default\shell_browser_ui_unittests.exe)
|
||||
- if exist out\Default\chromedriver.zip (appveyor-retry appveyor PushArtifact out\Default\chromedriver.zip)
|
||||
- if exist out\ffmpeg\ffmpeg.zip (appveyor-retry appveyor PushArtifact out\ffmpeg\ffmpeg.zip)
|
||||
- if exist node_headers.zip (appveyor-retry appveyor PushArtifact node_headers.zip)
|
||||
@@ -268,12 +266,12 @@ for:
|
||||
# Download build artifacts
|
||||
$apiUrl = 'https://ci.appveyor.com/api'
|
||||
$build_info = Invoke-RestMethod -Method Get -Uri "$apiUrl/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/builds/$env:APPVEYOR_BUILD_ID"
|
||||
$artifacts_to_download = @('dist.zip','shell_browser_ui_unittests.exe','chromedriver.zip','ffmpeg.zip','node_headers.zip','mksnapshot.zip','electron.lib')
|
||||
$artifacts_to_download = @('dist.zip','chromedriver.zip','ffmpeg.zip','node_headers.zip','mksnapshot.zip','electron.lib')
|
||||
foreach ($job in $build_info.build.jobs) {
|
||||
if ($job.name -eq "Build") {
|
||||
$jobId = $job.jobId
|
||||
foreach($artifact_name in $artifacts_to_download) {
|
||||
if ($artifact_name -eq 'shell_browser_ui_unittests.exe' -Or $artifact_name -eq 'electron.lib') {
|
||||
if ($artifact_name -eq 'electron.lib') {
|
||||
$outfile = "src\out\Default\$artifact_name"
|
||||
} else {
|
||||
$outfile = $artifact_name
|
||||
@@ -307,7 +305,6 @@ for:
|
||||
$env:npm_config_arch = "ia32"
|
||||
}
|
||||
- echo Running main test suite & node script/yarn test -- --trace-uncaught --runners=main --enable-logging=file --log-file=%cd%\electron.log
|
||||
- echo Running native test suite & node script/yarn test -- --trace-uncaught --runners=native --enable-logging=file --log-file=%cd%\electron.log
|
||||
- cd ..
|
||||
- echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
|
||||
- echo "About to verify mksnapshot"
|
||||
|
||||
@@ -391,21 +391,6 @@ which contains more information about why the child process disappeared. It
|
||||
isn't always because it crashed. The `killed` boolean can be replaced by
|
||||
checking `reason === 'killed'` when you switch to that event.
|
||||
|
||||
### Event: 'renderer-process-crashed' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
* `killed` boolean
|
||||
|
||||
Emitted when the renderer process of `webContents` crashes or is killed.
|
||||
|
||||
**Deprecated:** This event is superceded by the `render-process-gone` event
|
||||
which contains more information about why the render process disappeared. It
|
||||
isn't always because it crashed. The `killed` boolean can be replaced by
|
||||
checking `reason === 'killed'` when you switch to that event.
|
||||
|
||||
### Event: 'render-process-gone'
|
||||
|
||||
Returns:
|
||||
@@ -1134,11 +1119,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
|
||||
@@ -1278,10 +1263,10 @@ Returns `boolean` - Whether the current desktop environment is Unity launcher.
|
||||
### `app.getLoginItemSettings([options])` _macOS_ _Windows_
|
||||
|
||||
* `options` Object (optional)
|
||||
* `path` string (optional) _Windows_ - The executable path to compare against.
|
||||
Defaults to `process.execPath`.
|
||||
* `args` string[] (optional) _Windows_ - The command-line arguments to compare
|
||||
against. Defaults to an empty array.
|
||||
* `type` string (optional) _macOS_ - Can be one of `mainAppService`, `agentService`, `daemonService`, or `loginItemService`. Defaults to `mainAppService`. Only available on macOS 13 and up. See [app.setLoginItemSettings](app.md#appsetloginitemsettingssettings-macos-windows) for more information about each type.
|
||||
* `serviceName` string (optional) _macOS_ - The name of the service. Required if `type` is non-default. Only available on macOS 13 and up.
|
||||
* `path` string (optional) _Windows_ - The executable path to compare against. Defaults to `process.execPath`.
|
||||
* `args` string[] (optional) _Windows_ - The command-line arguments to compare against. Defaults to an empty array.
|
||||
|
||||
If you provided `path` and `args` options to `app.setLoginItemSettings`, then you
|
||||
need to pass the same arguments here for `openAtLogin` to be set correctly.
|
||||
@@ -1289,17 +1274,11 @@ need to pass the same arguments here for `openAtLogin` to be set correctly.
|
||||
Returns `Object`:
|
||||
|
||||
* `openAtLogin` boolean - `true` if the app is set to open at login.
|
||||
* `openAsHidden` boolean _macOS_ - `true` if the app is set to open as hidden at login.
|
||||
This setting is not available on [MAS builds][mas-builds].
|
||||
* `wasOpenedAtLogin` boolean _macOS_ - `true` if the app was opened at login
|
||||
automatically. This setting is not available on [MAS builds][mas-builds].
|
||||
* `wasOpenedAsHidden` boolean _macOS_ - `true` if the app was opened as a hidden login
|
||||
item. This indicates that the app should not open any windows at startup.
|
||||
This setting is not available on [MAS builds][mas-builds].
|
||||
* `restoreState` boolean _macOS_ - `true` if the app was opened as a login item that
|
||||
should restore the state from the previous session. This indicates that the
|
||||
app should restore the windows that were open the last time the app was
|
||||
closed. This setting is not available on [MAS builds][mas-builds].
|
||||
* `openAsHidden` boolean _macOS_ _Deprecated_ - `true` if the app is set to open as hidden at login. This does not work on macOS 13 and up.
|
||||
* `wasOpenedAtLogin` boolean _macOS_ _Deprecated_ - `true` if the app was opened at login automatically. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
|
||||
* `wasOpenedAsHidden` boolean _macOS_ _Deprecated_ - `true` if the app was opened as a hidden login item. This indicates that the app should not open any windows at startup. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
|
||||
* `restoreState` boolean _macOS_ _Deprecated_ - `true` if the app was opened as a login item that should restore the state from the previous session. This indicates that the app should restore the windows that were open the last time the app was closed. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
|
||||
* `status` string _macOS_ - can be one of `not-registered`, `enabled`, `requires-approval`, or `not-found`.
|
||||
* `executableWillLaunchAtLogin` boolean _Windows_ - `true` if app is set to open at login and its run key is not deactivated. This differs from `openAtLogin` as it ignores the `args` option, this property will be true if the given executable would be launched at login with **any** arguments.
|
||||
* `launchItems` Object[] _Windows_
|
||||
* `name` string _Windows_ - name value of a registry entry.
|
||||
@@ -1313,10 +1292,14 @@ Returns `Object`:
|
||||
* `settings` Object
|
||||
* `openAtLogin` boolean (optional) - `true` to open the app at login, `false` to remove
|
||||
the app as a login item. Defaults to `false`.
|
||||
* `openAsHidden` boolean (optional) _macOS_ - `true` to open the app as hidden. Defaults to
|
||||
`false`. The user can edit this setting from the System Preferences so
|
||||
`app.getLoginItemSettings().wasOpenedAsHidden` should be checked when the app
|
||||
is opened to know the current value. This setting is not available on [MAS builds][mas-builds].
|
||||
* `openAsHidden` boolean (optional) _macOS_ _Deprecated_ - `true` to open the app as hidden. Defaults to `false`. The user can edit this setting from the System Preferences so `app.getLoginItemSettings().wasOpenedAsHidden` should be checked when the app is opened to know the current value. This setting is not available on [MAS build
|
||||
s][mas-builds] or on macOS 13 and up.
|
||||
* `type` string (optional) _macOS_ - The type of service to add as a login item. Defaults to `mainAppService`. Only available on macOS 13 and up.
|
||||
* `mainAppService` - The primary application.
|
||||
* `agentService` - The property list name for a launch agent. The property list name must correspond to a property list in the app’s `Contents/Library/LaunchAgents` directory.
|
||||
* `daemonService` string (optional) _macOS_ - The property list name for a launch agent. The property list name must correspond to a property list in the app’s `Contents/Library/LaunchDaemons` directory.
|
||||
* `loginItemService` string (optional) _macOS_ - The property list name for a login item service. The property list name must correspond to a property list in the app’s `Contents/Library/LoginItems` directory.
|
||||
* `serviceName` string (optional) _macOS_ - The name of the service. Required if `type` is non-default. Only available on macOS 13 and up.
|
||||
* `path` string (optional) _Windows_ - The executable to launch at login.
|
||||
Defaults to `process.execPath`.
|
||||
* `args` string[] (optional) _Windows_ - The command-line arguments to pass to
|
||||
@@ -1325,6 +1308,7 @@ Returns `Object`:
|
||||
* `enabled` boolean (optional) _Windows_ - `true` will change the startup approved registry key and `enable / disable` the App in Task Manager and Windows Settings.
|
||||
Defaults to `true`.
|
||||
* `name` string (optional) _Windows_ - value name to write into registry. Defaults to the app's AppUserModelId().
|
||||
|
||||
Set the app's login item settings.
|
||||
|
||||
To work with Electron's `autoUpdater` on Windows, which uses [Squirrel][Squirrel-Windows],
|
||||
@@ -1349,6 +1333,8 @@ app.setLoginItemSettings({
|
||||
})
|
||||
```
|
||||
|
||||
For more information about setting different services as login items on macOS 13 and up, see [`SMAppService`](https://developer.apple.com/documentation/servicemanagement/smappservice?language=objc).
|
||||
|
||||
### `app.isAccessibilitySupportEnabled()` _macOS_ _Windows_
|
||||
|
||||
Returns `boolean` - `true` if Chrome's accessibility support is enabled,
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -460,20 +460,6 @@ win.webContents.on('will-prevent-unload', (event) => {
|
||||
|
||||
**Note:** This will be emitted for `BrowserViews` but will _not_ be respected - this is because we have chosen not to tie the `BrowserView` lifecycle to its owning BrowserWindow should one exist per the [specification](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event).
|
||||
|
||||
#### Event: 'crashed' _Deprecated_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `killed` boolean
|
||||
|
||||
Emitted when the renderer process crashes or is killed.
|
||||
|
||||
**Deprecated:** This event is superceded by the `render-process-gone` event
|
||||
which contains more information about why the render process disappeared. It
|
||||
isn't always because it crashed. The `killed` boolean can be replaced by
|
||||
checking `reason === 'killed'` when you switch to that event.
|
||||
|
||||
#### Event: 'render-process-gone'
|
||||
|
||||
Returns:
|
||||
@@ -1211,7 +1197,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)`.
|
||||
|
||||
@@ -986,14 +986,6 @@ ipcRenderer.on('ping', () => {
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'crashed' _Deprecated_
|
||||
|
||||
Fired when the renderer process crashes or is killed.
|
||||
|
||||
**Deprecated:** This event is superceded by the `render-process-gone` event
|
||||
which contains more information about why the render process disappeared. It
|
||||
isn't always because it crashed.
|
||||
|
||||
### Event: 'render-process-gone'
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -12,6 +12,36 @@ This document uses the following convention to categorize breaking changes:
|
||||
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
|
||||
* **Removed:** An API or feature was removed, and is no longer supported by Electron.
|
||||
|
||||
## Planned Breaking API Changes (29.0)
|
||||
|
||||
### Removed: `renderer-process-crashed` event on `app`
|
||||
|
||||
The `renderer-process-crashed` event on `app` has been removed.
|
||||
Use the new `render-process-gone` event instead.
|
||||
|
||||
```js
|
||||
// Removed
|
||||
app.on('renderer-process-crashed', (event, webContents, killed) => { /* ... */ })
|
||||
|
||||
// Replace with
|
||||
app.on('render-process-gone', (event, webContents, details) => { /* ... */ })
|
||||
```
|
||||
|
||||
### Removed: `crashed` event on `WebContents` and `<webview>`
|
||||
|
||||
The `crashed` events on `WebContents` and `<webview>` have been removed.
|
||||
Use the new `render-process-gone` event instead.
|
||||
|
||||
```js
|
||||
// Removed
|
||||
win.webContents.on('crashed', (event, killed) => { /* ... */ })
|
||||
webview.addEventListener('crashed', (event) => { /* ... */ })
|
||||
|
||||
// Replace with
|
||||
win.webContents.on('render-process-gone', (event, details) => { /* ... */ })
|
||||
webview.addEventListener('render-process-gone', (event) => { /* ... */ })
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (28.0)
|
||||
|
||||
### Behavior Changed: `WebContents.backgroundThrottling` set to false affects all `WebContents` in the host `BrowserWindow`
|
||||
@@ -103,6 +133,19 @@ win.webContents.on('render-process-gone', (event, details) => { /* ... */ })
|
||||
webview.addEventListener('render-process-gone', (event) => { /* ... */ })
|
||||
```
|
||||
|
||||
### Deprecated: `gpu-process-crashed` event on `app`
|
||||
|
||||
The `gpu-process-crashed` event on `app` has been deprecated.
|
||||
Use the new `child-process-gone` event instead.
|
||||
|
||||
```js
|
||||
// Deprecated
|
||||
app.on('gpu-process-crashed', (event, killed) => { /* ... */ })
|
||||
|
||||
// Replace with
|
||||
app.on('child-process-gone', (event, details) => { /* ... */ })
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (27.0)
|
||||
|
||||
### Removed: macOS 10.13 / 10.14 support
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -82,9 +82,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
})
|
||||
```
|
||||
|
||||
You can create a `renderer.d.ts` declaration file and globally augment the `Window` interface:
|
||||
You can create a `interface.d.ts` declaration file and globally augment the `Window` interface:
|
||||
|
||||
```typescript title='renderer.d.ts' @ts-noisolate
|
||||
```typescript title='interface.d.ts' @ts-noisolate
|
||||
export interface IElectronAPI {
|
||||
loadPreferences: () => Promise<void>,
|
||||
}
|
||||
|
||||
@@ -261,6 +261,14 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
fs.writeSync(logFDs.get(asarPath), `${offset}: ${filePath}\n`);
|
||||
};
|
||||
|
||||
const shouldThrowStatError = (options: any) => {
|
||||
if (options && typeof options === 'object' && options.throwIfNoEntry === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const { lstatSync } = fs;
|
||||
fs.lstatSync = (pathArgument: string, options: any) => {
|
||||
const pathInfo = splitPath(pathArgument);
|
||||
@@ -268,10 +276,16 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
const { asarPath, filePath } = pathInfo;
|
||||
|
||||
const archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
if (!archive) {
|
||||
if (shouldThrowStatError(options)) throw createError(AsarError.INVALID_ARCHIVE, { asarPath });
|
||||
return null;
|
||||
}
|
||||
|
||||
const stats = archive.stat(filePath);
|
||||
if (!stats) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
if (!stats) {
|
||||
if (shouldThrowStatError(options)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath });
|
||||
return null;
|
||||
}
|
||||
|
||||
return asarStatsToFsStats(stats);
|
||||
};
|
||||
|
||||
@@ -118,7 +118,3 @@ 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'];
|
||||
});
|
||||
|
||||
@@ -665,10 +665,6 @@ WebContents.prototype._init = function () {
|
||||
ipcMain.emit(channel, event, message);
|
||||
});
|
||||
|
||||
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) => {
|
||||
app.emit('render-process-gone', event, this, details);
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ export const webViewEvents: Record<string, readonly string[]> = {
|
||||
'did-navigate-in-page': ['url', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
|
||||
'-focus-change': ['focus'],
|
||||
close: [],
|
||||
crashed: [],
|
||||
'render-process-gone': ['details'],
|
||||
'plugin-crashed': ['name', 'version'],
|
||||
destroyed: [],
|
||||
|
||||
@@ -90,7 +90,6 @@ fix_adapt_exclusive_access_for_electron_needs.patch
|
||||
fix_aspect_ratio_with_max_size.patch
|
||||
fix_crash_when_saving_edited_pdf_files.patch
|
||||
port_autofill_colors_to_the_color_pipeline.patch
|
||||
build_disable_partition_alloc_on_mac.patch
|
||||
fix_non-client_mouse_tracking_and_message_bubbling_on_windows.patch
|
||||
build_make_libcxx_abi_unstable_false_for_electron.patch
|
||||
introduce_ozoneplatform_electron_can_call_x11_property.patch
|
||||
@@ -135,3 +134,4 @@ fix_activate_background_material_on_windows.patch
|
||||
fix_move_autopipsettingshelper_behind_branding_buildflag.patch
|
||||
revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch
|
||||
fix_handle_no_top_level_aura_window_in_webcontentsimpl.patch
|
||||
ensure_messageports_get_gced_when_not_referenced.patch
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: VerteDinde <vertedinde@electronjs.org>
|
||||
Date: Tue, 1 Mar 2022 12:07:25 -0800
|
||||
Subject: build: disable partition alloc on mac
|
||||
|
||||
Enabling partition alloc caused a crash when spawning
|
||||
a child process. This patch disables partition alloc for mac,
|
||||
and can be removed when the crash in fork is resolved.
|
||||
Related issue: https://github.com/electron/electron/issues/32718
|
||||
|
||||
diff --git a/build_overrides/partition_alloc.gni b/build_overrides/partition_alloc.gni
|
||||
index a6306a0e042d9fa58568d690fdc76f973782299f..72e47348b644d49e1f63b0693fad01df3bb24f01 100644
|
||||
--- a/build_overrides/partition_alloc.gni
|
||||
+++ b/build_overrides/partition_alloc.gni
|
||||
@@ -46,7 +46,7 @@ _disable_partition_alloc_everywhere =
|
||||
if (is_ios) {
|
||||
_is_partition_alloc_everywhere_platform = ios_partition_alloc_enabled
|
||||
} else {
|
||||
- _is_partition_alloc_everywhere_platform = !is_nacl
|
||||
+ _is_partition_alloc_everywhere_platform = !is_nacl && !is_mac
|
||||
}
|
||||
|
||||
# Under Windows debug build, the allocator shim is not compatible with CRT.
|
||||
@@ -0,0 +1,73 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yoav Weiss <yoavweiss@chromium.org>
|
||||
Date: Mon, 9 Oct 2023 14:21:44 +0000
|
||||
Subject: Ensure MessagePorts get GCed when not referenced
|
||||
|
||||
[1] regressed MessagePort memory and caused them to no longer be
|
||||
collected after not being referenced.
|
||||
This CL fixes that by turning the mutual pointers between attached
|
||||
MessagePorts to be WeakMembers.
|
||||
|
||||
[1] https://chromium-review.googlesource.com/4919476
|
||||
|
||||
Bug: 1487835
|
||||
Change-Id: Icd7eba09a217ad5d588958504d5c4794d7d8a295
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4919476
|
||||
Commit-Queue: Yoav Weiss <yoavweiss@chromium.org>
|
||||
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1207067}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/messaging/message_port.cc b/third_party/blink/renderer/core/messaging/message_port.cc
|
||||
index 0fd5bbf170efa02d3e710de3cb6733158faec858..15f5f0f6aa05d3b17adae87286c92df9cc26a712 100644
|
||||
--- a/third_party/blink/renderer/core/messaging/message_port.cc
|
||||
+++ b/third_party/blink/renderer/core/messaging/message_port.cc
|
||||
@@ -162,8 +162,10 @@ MessagePortChannel MessagePort::Disentangle() {
|
||||
DCHECK(!IsNeutered());
|
||||
port_descriptor_.GiveDisentangledHandle(connector_->PassMessagePipe());
|
||||
connector_ = nullptr;
|
||||
- if (initially_entangled_port_) {
|
||||
- initially_entangled_port_->OnEntangledPortDisconnected();
|
||||
+ // Using a variable here places the WeakMember pointer on the stack, ensuring
|
||||
+ // it doesn't get GCed while it's being used.
|
||||
+ if (auto* entangled_port = initially_entangled_port_.Get()) {
|
||||
+ entangled_port->OnEntangledPortDisconnected();
|
||||
}
|
||||
OnEntangledPortDisconnected();
|
||||
return MessagePortChannel(std::move(port_descriptor_));
|
||||
@@ -340,7 +342,10 @@ bool MessagePort::Accept(mojo::Message* mojo_message) {
|
||||
// lifetime of this function.
|
||||
std::unique_ptr<scheduler::TaskAttributionTracker::TaskScope>
|
||||
task_attribution_scope;
|
||||
- if (initially_entangled_port_ && message.sender_origin &&
|
||||
+ // Using a variable here places the WeakMember pointer on the stack, ensuring
|
||||
+ // it doesn't get GCed while it's being used.
|
||||
+ auto* entangled_port = initially_entangled_port_.Get();
|
||||
+ if (entangled_port && message.sender_origin &&
|
||||
message.sender_origin->IsSameOriginWith(context->GetSecurityOrigin()) &&
|
||||
context->IsSameAgentCluster(message.sender_agent_cluster_id) &&
|
||||
context->IsWindow()) {
|
||||
@@ -364,9 +369,9 @@ bool MessagePort::Accept(mojo::Message* mojo_message) {
|
||||
ThreadScheduler::Current()->GetTaskAttributionTracker()) {
|
||||
// Since `initially_entangled_port_` is not nullptr, neither should be
|
||||
// its `post_message_task_container_`.
|
||||
- CHECK(initially_entangled_port_->post_message_task_container_);
|
||||
+ CHECK(entangled_port->post_message_task_container_);
|
||||
scheduler::TaskAttributionInfo* parent_task =
|
||||
- initially_entangled_port_->post_message_task_container_
|
||||
+ entangled_port->post_message_task_container_
|
||||
->GetAndDecrementPostMessageTask(message.parent_task_id);
|
||||
task_attribution_scope = tracker->CreateTaskScope(
|
||||
script_state, parent_task,
|
||||
diff --git a/third_party/blink/renderer/core/messaging/message_port.h b/third_party/blink/renderer/core/messaging/message_port.h
|
||||
index 98ed58a9a765f5101d9b421507bf25db4359d7e5..a178d15c11b1e5fb1ff74d182021fe39e9d9b107 100644
|
||||
--- a/third_party/blink/renderer/core/messaging/message_port.h
|
||||
+++ b/third_party/blink/renderer/core/messaging/message_port.h
|
||||
@@ -195,7 +195,7 @@ class CORE_EXPORT MessagePort : public EventTarget,
|
||||
|
||||
// The entangled port. Only set on initial entanglement, and gets unset as
|
||||
// soon as the ports are disentangled.
|
||||
- Member<MessagePort> initially_entangled_port_;
|
||||
+ WeakMember<MessagePort> initially_entangled_port_;
|
||||
|
||||
Member<PostMessageTaskContainer> post_message_task_container_;
|
||||
};
|
||||
@@ -849,10 +849,10 @@ index 0000000000000000000000000000000000000000..fb000f8ee7647c375bc190d1729d67bb
|
||||
+}
|
||||
diff --git a/deps/nghttp2/BUILD.gn b/deps/nghttp2/BUILD.gn
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..23eed033d31eced6a85c794eee550655af7a9a1d
|
||||
index 0000000000000000000000000000000000000000..7d2ca477db2415f43ababa270d8aefa3124b2765
|
||||
--- /dev/null
|
||||
+++ b/deps/nghttp2/BUILD.gn
|
||||
@@ -0,0 +1,48 @@
|
||||
@@ -0,0 +1,51 @@
|
||||
+config("nghttp2_config") {
|
||||
+ defines = [ "NGHTTP2_STATICLIB" ]
|
||||
+ include_dirs = [ "lib/includes" ]
|
||||
@@ -894,11 +894,14 @@ index 0000000000000000000000000000000000000000..23eed033d31eced6a85c794eee550655
|
||||
+ "lib/nghttp2_pq.c",
|
||||
+ "lib/nghttp2_priority_spec.c",
|
||||
+ "lib/nghttp2_queue.c",
|
||||
+ "lib/nghttp2_ratelim.c",
|
||||
+ "lib/nghttp2_rcbuf.c",
|
||||
+ "lib/nghttp2_session.c",
|
||||
+ "lib/nghttp2_stream.c",
|
||||
+ "lib/nghttp2_submit.c",
|
||||
+ "lib/nghttp2_time.c",
|
||||
+ "lib/nghttp2_version.c",
|
||||
+ "lib/sfparse.c"
|
||||
+ ]
|
||||
+}
|
||||
diff --git a/deps/simdutf/BUILD.gn b/deps/simdutf/BUILD.gn
|
||||
@@ -929,10 +932,10 @@ index 0000000000000000000000000000000000000000..bfbd4e656db1a6c73048443f96f1d576
|
||||
+}
|
||||
diff --git a/deps/uv/BUILD.gn b/deps/uv/BUILD.gn
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..ae4d3bf68882f1aa6d7440448050fbdd9a17dca7
|
||||
index 0000000000000000000000000000000000000000..29e66144dbb67704bad0d78c4b3f5713537c7987
|
||||
--- /dev/null
|
||||
+++ b/deps/uv/BUILD.gn
|
||||
@@ -0,0 +1,194 @@
|
||||
@@ -0,0 +1,197 @@
|
||||
+config("libuv_config") {
|
||||
+ include_dirs = [ "include" ]
|
||||
+
|
||||
@@ -1005,7 +1008,6 @@ index 0000000000000000000000000000000000000000..ae4d3bf68882f1aa6d7440448050fbdd
|
||||
+ "src/strscpy.h",
|
||||
+ "src/strtok.c",
|
||||
+ "src/strtok.h",
|
||||
+ "src/thread-common.c",
|
||||
+ "src/threadpool.c",
|
||||
+ "src/timer.c",
|
||||
+ "src/uv-common.c",
|
||||
@@ -1110,7 +1112,11 @@ index 0000000000000000000000000000000000000000..ae4d3bf68882f1aa6d7440448050fbdd
|
||||
+ if (is_linux) {
|
||||
+ defines += [ "_GNU_SOURCE" ]
|
||||
+ sources += [
|
||||
+ "src/unix/linux.c",
|
||||
+ "src/unix/epoll.c",
|
||||
+ "src/unix/linux-core.c",
|
||||
+ "src/unix/linux-inotify.c",
|
||||
+ "src/unix/linux-syscalls.c",
|
||||
+ "src/unix/linux-syscalls.h",
|
||||
+ "src/unix/procfs-exepath.c",
|
||||
+ "src/unix/random-getrandom.c",
|
||||
+ "src/unix/random-sysctl-linux.c",
|
||||
@@ -1173,10 +1179,10 @@ index 0000000000000000000000000000000000000000..2c9d2826c85bdd033f1df1d6188df636
|
||||
+}
|
||||
diff --git a/filenames.json b/filenames.json
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..12d4706fae0d8253db8de1fc2c4e31a6380cc122
|
||||
index 0000000000000000000000000000000000000000..7d86765bf229275ba55d37a452ef24832a7afadb
|
||||
--- /dev/null
|
||||
+++ b/filenames.json
|
||||
@@ -0,0 +1,663 @@
|
||||
@@ -0,0 +1,664 @@
|
||||
+// This file is automatically generated by generate_gn_filenames_json.py
|
||||
+// DO NOT EDIT
|
||||
+{
|
||||
@@ -1290,6 +1296,7 @@ index 0000000000000000000000000000000000000000..12d4706fae0d8253db8de1fc2c4e31a6
|
||||
+ "deps/uv/include/uv/linux.h",
|
||||
+ "deps/uv/include/uv/os390.h",
|
||||
+ "deps/uv/include/uv/posix.h",
|
||||
+ "deps/uv/include/uv/stdint-msvc2008.h",
|
||||
+ "deps/uv/include/uv/sunos.h",
|
||||
+ "deps/uv/include/uv/threadpool.h",
|
||||
+ "deps/uv/include/uv/tree.h",
|
||||
@@ -2100,7 +2107,7 @@ index 0000000000000000000000000000000000000000..4ab828dcbf322a9e28674e48c4a6868b
|
||||
+ args = rebase_path(inputs + outputs, root_build_dir)
|
||||
+}
|
||||
diff --git a/src/node_version.h b/src/node_version.h
|
||||
index 7e90c796f3bd076048a2114a1ba31db7ee876440..60a17104e0b1b19038efc1a1468ee92642d3ffd6 100644
|
||||
index 88cc915177bcf8b86eb8f9f16b215cb6d06b3aa7..8df52c22dd76a850d421075effc44fbfb32307db 100644
|
||||
--- a/src/node_version.h
|
||||
+++ b/src/node_version.h
|
||||
@@ -89,7 +89,10 @@
|
||||
|
||||
@@ -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 4c33acbc2d5c12ac8750b72e0796284176af3da2..56fae731aeec1f3a2870fba56eb0fb24e5d4b87f 100644
|
||||
index 4c33acbc2d5c12ac8750b72e0796284176af3da2..035f00b0a4a76a606ba707617b56c9beb074052e 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 4c33acbc2d5c12ac8750b72e0796284176af3da2..56fae731aeec1f3a2870fba56eb0fb24
|
||||
+[90m at Module._extensions..js (node:internal*modules*cjs*loader:1326:10)[39m
|
||||
+[90m at Module.load (node:internal*modules*cjs*loader:1126:32)[39m
|
||||
+[90m at Module._load (node:internal*modules*cjs*loader:967:12)[39m
|
||||
+[90m at Module._load (node:electron*js2c*asar_bundle:763:32)[39m
|
||||
+[90m at Module._load (node:electron*js2c*asar_bundle:775:32)[39m
|
||||
+[90m at Function.executeUserEntryPoint [as runMain] (node:internal*modules*run_main:101:12)[39m
|
||||
[90m at node:internal*main*run_main_module:23:47[39m
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ index 0f5ddfb3ca21b7e5b38d0a4ce4b9e77387597199..ba815202fb157aa82859ec0518523cf6
|
||||
.. c:function:: int uv_loop_close(uv_loop_t* loop)
|
||||
|
||||
diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h
|
||||
index 02397dd0fdd43d51f86c0dde9a62046702f12bdb..3375600023e39ddacf62cc17deb4f206db942084 100644
|
||||
index ee1c94ccd389915ea7572cce044256a7788025ad..d31abf714d5d1433ec8473ccb1aae3b6615c477a 100644
|
||||
--- a/deps/uv/include/uv.h
|
||||
+++ b/deps/uv/include/uv.h
|
||||
@@ -260,7 +260,8 @@ typedef struct uv_metrics_s uv_metrics_t;
|
||||
@@ -252,7 +252,8 @@ typedef struct uv_statfs_s uv_statfs_t;
|
||||
|
||||
typedef enum {
|
||||
UV_LOOP_BLOCK_SIGNAL = 0,
|
||||
@@ -40,7 +40,7 @@ index 02397dd0fdd43d51f86c0dde9a62046702f12bdb..3375600023e39ddacf62cc17deb4f206
|
||||
|
||||
typedef enum {
|
||||
diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c
|
||||
index 0ff2669e30a628dbb2df9e28ba14b38cf14114e5..679b17bda476d2a9c072ce8261234f837b56422f 100644
|
||||
index e1805c323795e5b0c465d80100eebeb7bf838caa..dd4358c0cdaa97ba8fadf4d9755993803beddd18 100644
|
||||
--- a/deps/uv/src/unix/async.c
|
||||
+++ b/deps/uv/src/unix/async.c
|
||||
@@ -38,7 +38,6 @@
|
||||
@@ -49,18 +49,18 @@ index 0ff2669e30a628dbb2df9e28ba14b38cf14114e5..679b17bda476d2a9c072ce8261234f83
|
||||
|
||||
-static void uv__async_send(uv_loop_t* loop);
|
||||
static int uv__async_start(uv_loop_t* loop);
|
||||
static void uv__cpu_relax(void);
|
||||
|
||||
@@ -78,7 +77,7 @@ int uv_async_send(uv_async_t* handle) {
|
||||
|
||||
@@ -70,7 +69,7 @@ int uv_async_send(uv_async_t* handle) {
|
||||
return 0;
|
||||
|
||||
/* Wake up the other thread's event loop. */
|
||||
if (atomic_exchange(pending, 1) == 0)
|
||||
- uv__async_send(handle->loop);
|
||||
+ uv__loop_interrupt(handle->loop);
|
||||
- uv__async_send(handle->loop);
|
||||
+ uv__loop_interrupt(handle->loop);
|
||||
|
||||
/* Set the loop to not-busy. */
|
||||
atomic_fetch_add(busy, -1);
|
||||
@@ -178,40 +177,6 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
/* Tell the other thread we're done. */
|
||||
if (cmpxchgi(&handle->pending, 1, 2) != 1)
|
||||
@@ -165,40 +164,6 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
}
|
||||
|
||||
|
||||
@@ -102,10 +102,10 @@ index 0ff2669e30a628dbb2df9e28ba14b38cf14114e5..679b17bda476d2a9c072ce8261234f83
|
||||
int pipefd[2];
|
||||
int err;
|
||||
diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c
|
||||
index 25c5181f370e94983e8a5f797f02f7a8dc207e00..f4d9059796d2c65339a5d48ecb273b09d9364d21 100644
|
||||
index 54c769f37f2331136c87a37c13fb4e3f9a8f22f9..ac52ab79a5fc3050effd2b1f2f605cee9b1ab336 100644
|
||||
--- a/deps/uv/src/unix/core.c
|
||||
+++ b/deps/uv/src/unix/core.c
|
||||
@@ -926,6 +926,9 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
@@ -900,6 +900,9 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
loop->watchers[w->fd] = w;
|
||||
loop->nfds++;
|
||||
}
|
||||
@@ -115,20 +115,20 @@ index 25c5181f370e94983e8a5f797f02f7a8dc207e00..f4d9059796d2c65339a5d48ecb273b09
|
||||
}
|
||||
|
||||
|
||||
@@ -957,6 +960,9 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
@@ -931,6 +934,9 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
}
|
||||
else if (uv__queue_empty(&w->watcher_queue))
|
||||
uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue);
|
||||
else if (QUEUE_EMPTY(&w->watcher_queue))
|
||||
QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
|
||||
+
|
||||
+ if (uv__get_internal_fields(loop)->flags & UV_LOOP_INTERRUPT_ON_IO_CHANGE)
|
||||
+ uv__loop_interrupt(loop);
|
||||
}
|
||||
|
||||
|
||||
@@ -973,6 +979,9 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) {
|
||||
@@ -947,6 +953,9 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) {
|
||||
void uv__io_feed(uv_loop_t* loop, uv__io_t* w) {
|
||||
if (uv__queue_empty(&w->pending_queue))
|
||||
uv__queue_insert_tail(&loop->pending_queue, &w->pending_queue);
|
||||
if (QUEUE_EMPTY(&w->pending_queue))
|
||||
QUEUE_INSERT_TAIL(&loop->pending_queue, &w->pending_queue);
|
||||
+
|
||||
+ if (uv__get_internal_fields(loop)->flags & UV_LOOP_INTERRUPT_ON_IO_CHANGE)
|
||||
+ uv__loop_interrupt(loop);
|
||||
@@ -136,7 +136,7 @@ index 25c5181f370e94983e8a5f797f02f7a8dc207e00..f4d9059796d2c65339a5d48ecb273b09
|
||||
|
||||
|
||||
diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c
|
||||
index a9468e8e19cbede795032980c47eb83aee1e0c68..2d28cf48efc3718de19b901b7e08b8a857d20740 100644
|
||||
index a88e71c339351f2ebcdd6c3f933fc3b1122910ed..46fc03264b6cc1a3a4d8faf5ec5a754fc07c9b6d 100644
|
||||
--- a/deps/uv/src/unix/loop.c
|
||||
+++ b/deps/uv/src/unix/loop.c
|
||||
@@ -217,6 +217,11 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
|
||||
@@ -193,10 +193,10 @@ index a9468e8e19cbede795032980c47eb83aee1e0c68..2d28cf48efc3718de19b901b7e08b8a8
|
||||
+ abort();
|
||||
+}
|
||||
diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h
|
||||
index cd57e5a35153d0557351b60cce0c5be7a4468b60..660caef30b1637b8009de5e55ee34f48d17e4dd0 100644
|
||||
index 6001b0cf68d0b0268b578218b664a737f43c9521..5d2212571f4bcb648ab332f0c5650d0fdb37c03a 100644
|
||||
--- a/deps/uv/src/uv-common.h
|
||||
+++ b/deps/uv/src/uv-common.h
|
||||
@@ -144,6 +144,8 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);
|
||||
@@ -140,6 +140,8 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);
|
||||
|
||||
void uv__loop_close(uv_loop_t* loop);
|
||||
|
||||
@@ -205,7 +205,7 @@ index cd57e5a35153d0557351b60cce0c5be7a4468b60..660caef30b1637b8009de5e55ee34f48
|
||||
int uv__read_start(uv_stream_t* stream,
|
||||
uv_alloc_cb alloc_cb,
|
||||
uv_read_cb read_cb);
|
||||
@@ -280,6 +282,10 @@ void uv__threadpool_cleanup(void);
|
||||
@@ -268,6 +270,10 @@ void uv__threadpool_cleanup(void);
|
||||
if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break; \
|
||||
(h)->flags |= UV_HANDLE_ACTIVE; \
|
||||
if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_add(h); \
|
||||
@@ -217,7 +217,7 @@ index cd57e5a35153d0557351b60cce0c5be7a4468b60..660caef30b1637b8009de5e55ee34f48
|
||||
while (0)
|
||||
|
||||
diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c
|
||||
index e9885a0f1ff3890a8d957c8793e22b01cedc0e97..ae3d09878253fe7169ad7b74b3faea0223f89de5 100644
|
||||
index 67af93e6571ed4324d80b6dfb2ff93db7b9cd9b1..e88008b6a288d1508c5c117d814000d63cab81c3 100644
|
||||
--- a/deps/uv/src/win/core.c
|
||||
+++ b/deps/uv/src/win/core.c
|
||||
@@ -384,10 +384,20 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
|
||||
@@ -242,7 +242,7 @@ index e9885a0f1ff3890a8d957c8793e22b01cedc0e97..ae3d09878253fe7169ad7b74b3faea02
|
||||
return -1;
|
||||
}
|
||||
diff --git a/deps/uv/test/test-embed.c b/deps/uv/test/test-embed.c
|
||||
index bbe56e176db17a502d7f3864ba529212f553590a..b0da9d1cddc69428e9fb3379d1338cf893ab93d2 100644
|
||||
index 1d3355fdc67310feb63738c9e30724f0e77f7895..77a63dbb4d188b2ad571c814dbc6cbc6fe5fa205 100644
|
||||
--- a/deps/uv/test/test-embed.c
|
||||
+++ b/deps/uv/test/test-embed.c
|
||||
@@ -25,54 +25,184 @@
|
||||
@@ -463,13 +463,13 @@ index bbe56e176db17a502d7f3864ba529212f553590a..b0da9d1cddc69428e9fb3379d1338cf8
|
||||
+ run_loop();
|
||||
+ ASSERT_EQ(main_timer_called, 1);
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h
|
||||
index 78ff9c2d1621676feab5d357609970cdf1ba5864..204160f324ad1a80c9b042e62c4bedcb745666ba 100644
|
||||
index b19c10c7e40c77061337416fd623c53d0863d276..1f54ac21c36902bccb35c64cefa8bb14dd675bb0 100644
|
||||
--- a/deps/uv/test/test-list.h
|
||||
+++ b/deps/uv/test/test-list.h
|
||||
@@ -273,6 +273,7 @@ TEST_DECLARE (process_priority)
|
||||
@@ -265,6 +265,7 @@ TEST_DECLARE (process_priority)
|
||||
TEST_DECLARE (has_ref)
|
||||
TEST_DECLARE (active)
|
||||
TEST_DECLARE (embed)
|
||||
@@ -477,7 +477,7 @@ index 78ff9c2d1621676feab5d357609970cdf1ba5864..204160f324ad1a80c9b042e62c4bedcb
|
||||
TEST_DECLARE (async)
|
||||
TEST_DECLARE (async_null_cb)
|
||||
TEST_DECLARE (eintr_handling)
|
||||
@@ -894,6 +895,7 @@ TASK_LIST_START
|
||||
@@ -867,6 +868,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (active)
|
||||
|
||||
TEST_ENTRY (embed)
|
||||
|
||||
@@ -6,10 +6,10 @@ Subject: fix: suppress clang -Wdeprecated-declarations in libuv
|
||||
Should be upstreamed.
|
||||
|
||||
diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c
|
||||
index f6ec79cd57b5010ed5fd6829d952bcdacc8b7671..5cda078a55f7825d135a107fa98e1aa3527dd147 100644
|
||||
index 99432053cc3b242e514268b7aba2e2d83a9e64f2..750a5424953aad104ba1e865fefd55d316485917 100644
|
||||
--- a/deps/uv/src/win/util.c
|
||||
+++ b/deps/uv/src/win/util.c
|
||||
@@ -1685,10 +1685,17 @@ int uv_os_uname(uv_utsname_t* buffer) {
|
||||
@@ -1743,10 +1743,17 @@ int uv_os_uname(uv_utsname_t* buffer) {
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(suppress : 4996)
|
||||
#endif
|
||||
|
||||
@@ -18,10 +18,10 @@ 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 3e451e2291d6ed200ec258e874becbbea22bbc27..a71a08bdd60166ef1d4ef490ff3e083b44188852 100644
|
||||
index 24c633393fd15dcf87726b174d6b027a969e0f0d..4ad9fec900fa66b0e8c6894701e94f420de903a8 100644
|
||||
--- a/deps/uv/src/win/process.c
|
||||
+++ b/deps/uv/src/win/process.c
|
||||
@@ -105,6 +105,21 @@ static void uv__init_global_job_handle(void) {
|
||||
@@ -102,6 +102,21 @@ static void uv__init_global_job_handle(void) {
|
||||
&info,
|
||||
sizeof info))
|
||||
uv_fatal_error(GetLastError(), "SetInformationJobObject");
|
||||
@@ -43,7 +43,7 @@ index 3e451e2291d6ed200ec258e874becbbea22bbc27..a71a08bdd60166ef1d4ef490ff3e083b
|
||||
}
|
||||
|
||||
|
||||
@@ -1102,6 +1117,7 @@ int uv_spawn(uv_loop_t* loop,
|
||||
@@ -1098,6 +1113,7 @@ int uv_spawn(uv_loop_t* loop,
|
||||
* breakaway.
|
||||
*/
|
||||
process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
|
||||
@@ -51,7 +51,7 @@ index 3e451e2291d6ed200ec258e874becbbea22bbc27..a71a08bdd60166ef1d4ef490ff3e083b
|
||||
}
|
||||
|
||||
if (!CreateProcessW(application_path,
|
||||
@@ -1119,11 +1135,6 @@ int uv_spawn(uv_loop_t* loop,
|
||||
@@ -1115,11 +1131,6 @@ int uv_spawn(uv_loop_t* loop,
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ index 3e451e2291d6ed200ec258e874becbbea22bbc27..a71a08bdd60166ef1d4ef490ff3e083b
|
||||
/* 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)) {
|
||||
@@ -1146,6 +1157,19 @@ int uv_spawn(uv_loop_t* loop,
|
||||
@@ -1142,6 +1153,19 @@ int uv_spawn(uv_loop_t* loop,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -1,3 +0,0 @@
|
||||
[
|
||||
"shell_browser_ui_unittests"
|
||||
]
|
||||
@@ -15,7 +15,6 @@ const fail = '✗'.red;
|
||||
|
||||
const args = require('minimist')(process.argv, {
|
||||
string: ['runners', 'target', 'electronVersion'],
|
||||
boolean: ['buildNativeTests'],
|
||||
unknown: arg => unknownFlags.push(arg)
|
||||
});
|
||||
|
||||
@@ -35,8 +34,7 @@ const BASE = path.resolve(__dirname, '../..');
|
||||
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
||||
|
||||
const runners = new Map([
|
||||
['main', { description: 'Main process specs', run: runMainProcessElectronTests }],
|
||||
['native', { description: 'Native specs', run: runNativeElectronTests }]
|
||||
['main', { description: 'Main process specs', run: runMainProcessElectronTests }]
|
||||
]);
|
||||
|
||||
const specHashPath = path.resolve(__dirname, '../spec/.hash');
|
||||
@@ -183,57 +181,6 @@ async function runTestUsingElectron (specDir, testName) {
|
||||
console.log(`${pass} Electron ${testName} process tests passed.`);
|
||||
}
|
||||
|
||||
async function runNativeElectronTests () {
|
||||
let testTargets = require('./native-test-targets.json');
|
||||
const outDir = `out/${utils.getOutDir()}`;
|
||||
|
||||
// If native tests are being run, only one arg would be relevant
|
||||
if (args.target && !testTargets.includes(args.target)) {
|
||||
console.log(`${fail} ${args.target} must be a subset of [${[testTargets].join(', ')}]`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Optionally build all native test targets
|
||||
if (args.buildNativeTests) {
|
||||
for (const target of testTargets) {
|
||||
const build = childProcess.spawnSync('ninja', ['-C', outDir, target], {
|
||||
cwd: path.resolve(__dirname, '../..'),
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
// Exit if test target failed to build
|
||||
if (build.status !== 0) {
|
||||
console.log(`${fail} ${target} failed to build.`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a specific target was passed, only build and run that target
|
||||
if (args.target) testTargets = [args.target];
|
||||
|
||||
// Run test targets
|
||||
const failures = [];
|
||||
for (const target of testTargets) {
|
||||
console.info('\nRunning native test for target:', target);
|
||||
const testRun = childProcess.spawnSync(`./${outDir}/${target}`, {
|
||||
cwd: path.resolve(__dirname, '../..'),
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
// Collect failures and log at end
|
||||
if (testRun.status !== 0) failures.push({ target });
|
||||
}
|
||||
|
||||
// Exit if any failures
|
||||
if (failures.length > 0) {
|
||||
console.log(`${fail} Electron native tests failed for the following targets: `, failures);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`${pass} Electron native tests passed.`);
|
||||
}
|
||||
|
||||
async function runMainProcessElectronTests () {
|
||||
await runTestUsingElectron('spec', 'main');
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "shell/browser/ui/cocoa/electron_bundle_mover.h"
|
||||
#endif
|
||||
|
||||
@@ -364,8 +365,11 @@ struct Converter<Browser::LoginItemSettings> {
|
||||
dict.Get("path", &(out->path));
|
||||
dict.Get("args", &(out->args));
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
dict.Get("enabled", &(out->enabled));
|
||||
dict.Get("name", &(out->name));
|
||||
dict.Get("enabled", &(out->enabled));
|
||||
#elif BUILDFLAG(IS_MAC)
|
||||
dict.Get("serviceName", &(out->service_name));
|
||||
dict.Get("type", &(out->type));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -373,16 +377,19 @@ struct Converter<Browser::LoginItemSettings> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
Browser::LoginItemSettings val) {
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
dict.Set("launchItems", val.launch_items);
|
||||
dict.Set("executableWillLaunchAtLogin",
|
||||
val.executable_will_launch_at_login);
|
||||
#elif BUILDFLAG(IS_MAC)
|
||||
if (base::mac::MacOSMajorVersion() >= 13)
|
||||
dict.Set("status", val.status);
|
||||
#endif
|
||||
dict.Set("openAtLogin", val.open_at_login);
|
||||
dict.Set("openAsHidden", val.open_as_hidden);
|
||||
dict.Set("restoreState", val.restore_state);
|
||||
dict.Set("wasOpenedAtLogin", val.opened_at_login);
|
||||
dict.Set("wasOpenedAsHidden", val.opened_as_hidden);
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
dict.Set("launchItems", val.launch_items);
|
||||
dict.Set("executableWillLaunchAtLogin",
|
||||
val.executable_will_launch_at_login);
|
||||
#endif
|
||||
return dict.GetHandle();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -469,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();
|
||||
}
|
||||
|
||||
@@ -1870,7 +1877,7 @@ bool WebContents::EmitNavigationEvent(
|
||||
dict.Set("url", url);
|
||||
dict.Set("isSameDocument", is_same_document);
|
||||
dict.Set("isMainFrame", is_main_frame);
|
||||
dict.Set("frame", frame_host);
|
||||
dict.SetGetter("frame", frame_host);
|
||||
dict.SetGetter("initiator", initiator_frame_host);
|
||||
|
||||
EmitWithoutEvent(event_name, event, url, is_same_document, is_main_frame,
|
||||
@@ -3462,22 +3469,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);
|
||||
|
||||
@@ -135,7 +135,11 @@ class Browser : public WindowListObserver {
|
||||
std::u16string path;
|
||||
std::vector<std::u16string> args;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
std::string type = "mainAppService";
|
||||
std::string service_name;
|
||||
std::string status;
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
// used in browser::setLoginItemSettings
|
||||
bool enabled = true;
|
||||
std::wstring name;
|
||||
@@ -205,9 +209,9 @@ class Browser : public WindowListObserver {
|
||||
void ApplyForcedRTL();
|
||||
|
||||
// Bounce the dock icon.
|
||||
enum class BounceType {
|
||||
kCritical = 0, // NSCriticalRequest
|
||||
kInformational = 10, // NSInformationalRequest
|
||||
enum class BounceType{
|
||||
kCritical = 0, // NSCriticalRequest
|
||||
kInformational = 10, // NSInformationalRequest
|
||||
};
|
||||
int DockBounce(BounceType type);
|
||||
void DockCancelBounce(int request_id);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#import <ServiceManagement/ServiceManagement.h>
|
||||
|
||||
#include "base/apple/bridging.h"
|
||||
#include "base/apple/bundle_locations.h"
|
||||
#include "base/apple/scoped_cftyperef.h"
|
||||
@@ -19,6 +21,7 @@
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "net/base/mac/url_conversions.h"
|
||||
#include "shell/browser/badging/badge_manager.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/mac/dict_util.h"
|
||||
#include "shell/browser/mac/electron_application.h"
|
||||
#include "shell/browser/mac/electron_application_delegate.h"
|
||||
@@ -85,6 +88,15 @@ bool CheckLoginItemStatus(bool* is_hidden) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Browser::LoginItemSettings GetLoginItemSettingsDeprecated() {
|
||||
Browser::LoginItemSettings settings;
|
||||
settings.open_at_login = CheckLoginItemStatus(&settings.open_as_hidden);
|
||||
settings.restore_state = base::mac::WasLaunchedAsLoginItemRestoreState();
|
||||
settings.opened_at_login = base::mac::WasLaunchedAsLoginOrResumeItem();
|
||||
settings.opened_as_hidden = base::mac::WasLaunchedAsHiddenLoginItem();
|
||||
return settings;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
@@ -367,28 +379,71 @@ void Browser::ApplyForcedRTL() {
|
||||
Browser::LoginItemSettings Browser::GetLoginItemSettings(
|
||||
const LoginItemSettings& options) {
|
||||
LoginItemSettings settings;
|
||||
if (options.type != "mainAppService" && options.service_name.empty()) {
|
||||
gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate())
|
||||
.ThrowTypeError("'name' is required when type is not mainAppService");
|
||||
return settings;
|
||||
}
|
||||
|
||||
#if IS_MAS_BUILD()
|
||||
settings.open_at_login = platform_util::GetLoginItemEnabled();
|
||||
const std::string status =
|
||||
platform_util::GetLoginItemEnabled(options.type, options.service_name);
|
||||
settings.open_at_login =
|
||||
status == "enabled" || status == "enabled-deprecated";
|
||||
if (@available(macOS 13, *))
|
||||
settings.status = status;
|
||||
#else
|
||||
settings.open_at_login = CheckLoginItemStatus(&settings.open_as_hidden);
|
||||
settings.restore_state = base::mac::WasLaunchedAsLoginItemRestoreState();
|
||||
settings.opened_at_login = base::mac::WasLaunchedAsLoginOrResumeItem();
|
||||
settings.opened_as_hidden = base::mac::WasLaunchedAsHiddenLoginItem();
|
||||
// If the app was previously set as a LoginItem with the deprecated API,
|
||||
// we should report its LoginItemSettings via the old API.
|
||||
LoginItemSettings settings_deprecated = GetLoginItemSettingsDeprecated();
|
||||
if (@available(macOS 13, *)) {
|
||||
const std::string status =
|
||||
platform_util::GetLoginItemEnabled(options.type, options.service_name);
|
||||
if (status == "enabled-deprecated") {
|
||||
settings = settings_deprecated;
|
||||
} else {
|
||||
settings.open_at_login = status == "enabled";
|
||||
settings.status = status;
|
||||
}
|
||||
} else {
|
||||
settings = settings_deprecated;
|
||||
}
|
||||
#endif
|
||||
return settings;
|
||||
}
|
||||
|
||||
void Browser::SetLoginItemSettings(LoginItemSettings settings) {
|
||||
#if IS_MAS_BUILD()
|
||||
if (!platform_util::SetLoginItemEnabled(settings.open_at_login)) {
|
||||
LOG(ERROR) << "Unable to set login item enabled on sandboxed app.";
|
||||
if (settings.type != "mainAppService" && settings.service_name.empty()) {
|
||||
gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate())
|
||||
.ThrowTypeError("'name' is required when type is not mainAppService");
|
||||
return;
|
||||
}
|
||||
#if IS_MAS_BUILD()
|
||||
platform_util::SetLoginItemEnabled(settings.type, settings.service_name,
|
||||
settings.open_at_login);
|
||||
#else
|
||||
if (settings.open_at_login) {
|
||||
base::mac::AddToLoginItems(base::apple::MainBundlePath(),
|
||||
settings.open_as_hidden);
|
||||
const base::FilePath bundle_path = base::apple::MainBundlePath();
|
||||
if (@available(macOS 13, *)) {
|
||||
// If the app was previously set as a LoginItem with the old API, remove it
|
||||
// as a LoginItem via the old API before re-enabling with the new API.
|
||||
const std::string status =
|
||||
platform_util::GetLoginItemEnabled("mainAppService", "");
|
||||
if (status == "enabled-deprecated") {
|
||||
base::mac::RemoveFromLoginItems(bundle_path);
|
||||
if (settings.open_at_login) {
|
||||
platform_util::SetLoginItemEnabled(settings.type, settings.service_name,
|
||||
settings.open_at_login);
|
||||
}
|
||||
} else {
|
||||
platform_util::SetLoginItemEnabled(settings.type, settings.service_name,
|
||||
settings.open_at_login);
|
||||
}
|
||||
} else {
|
||||
base::mac::RemoveFromLoginItems(base::apple::MainBundlePath());
|
||||
if (settings.open_at_login) {
|
||||
base::mac::AddToLoginItems(bundle_path, settings.open_as_hidden);
|
||||
} else {
|
||||
base::mac::RemoveFromLoginItems(bundle_path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#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"
|
||||
@@ -36,6 +37,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,11 +110,11 @@ std::unique_ptr<content::HidChooser> ElectronHidDelegate::RunChooser(
|
||||
std::vector<blink::mojom::HidDeviceFilterPtr> filters,
|
||||
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) {
|
||||
@@ -101,16 +166,14 @@ device::mojom::HidManager* ElectronHidDelegate::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);
|
||||
GetContextObserver(browser_context)->AddObserver(observer);
|
||||
}
|
||||
|
||||
void ElectronHidDelegate::RemoveObserver(
|
||||
content::BrowserContext* browser_context,
|
||||
content::HidDelegate::Observer* observer) {
|
||||
observer_list_.RemoveObserver(observer);
|
||||
DCHECK(base::Contains(observations_, browser_context));
|
||||
GetContextObserver(browser_context)->RemoveObserver(observer);
|
||||
}
|
||||
|
||||
const device::mojom::HidDeviceInfo* ElectronHidDelegate::GetDeviceInfo(
|
||||
@@ -140,33 +203,14 @@ bool ElectronHidDelegate::IsServiceWorkerAllowedForOrigin(
|
||||
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_
|
||||
|
||||
@@ -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"
|
||||
@@ -138,4 +139,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_
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#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/web_contents.h"
|
||||
|
||||
@@ -537,7 +537,7 @@ void NativeWindow::PreviewFile(const std::string& path,
|
||||
|
||||
void NativeWindow::CloseFilePreview() {}
|
||||
|
||||
gfx::Rect NativeWindow::GetWindowControlsOverlayRect() {
|
||||
absl::optional<gfx::Rect> NativeWindow::GetWindowControlsOverlayRect() {
|
||||
return overlay_rect_;
|
||||
}
|
||||
|
||||
@@ -665,6 +665,7 @@ void NativeWindow::NotifyWindowMoved() {
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowEnterFullScreen() {
|
||||
NotifyLayoutWindowControlsOverlay();
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.OnWindowEnterFullScreen();
|
||||
}
|
||||
@@ -690,6 +691,7 @@ void NativeWindow::NotifyWindowSheetEnd() {
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowLeaveFullScreen() {
|
||||
NotifyLayoutWindowControlsOverlay();
|
||||
for (NativeWindowObserver& observer : observers_)
|
||||
observer.OnWindowLeaveFullScreen();
|
||||
}
|
||||
@@ -733,10 +735,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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -286,7 +286,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.
|
||||
|
||||
@@ -153,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;
|
||||
|
||||
@@ -1899,23 +1899,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -49,8 +49,11 @@ bool GetFolderPath(int key, base::FilePath* result);
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
bool GetLoginItemEnabled();
|
||||
bool SetLoginItemEnabled(bool enabled);
|
||||
std::string GetLoginItemEnabled(const std::string& type,
|
||||
const std::string& service_name);
|
||||
bool SetLoginItemEnabled(const std::string& type,
|
||||
const std::string& service_name,
|
||||
bool enabled);
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
|
||||
@@ -77,6 +77,81 @@ std::string OpenPathOnThread(const base::FilePath& full_path) {
|
||||
return success ? "" : "Failed to open path";
|
||||
}
|
||||
|
||||
// https://developer.apple.com/documentation/servicemanagement/1561515-service_management_errors?language=objc
|
||||
std::string GetLaunchStringForError(NSError* error) {
|
||||
if (@available(macOS 13, *)) {
|
||||
switch ([error code]) {
|
||||
case kSMErrorAlreadyRegistered:
|
||||
return "The application is already registered";
|
||||
case kSMErrorAuthorizationFailure:
|
||||
return "The authorization requested failed";
|
||||
case kSMErrorLaunchDeniedByUser:
|
||||
return "The user denied the app's launch request";
|
||||
case kSMErrorInternalFailure:
|
||||
return "An internal failure has occurred";
|
||||
case kSMErrorInvalidPlist:
|
||||
return "The app's property list is invalid";
|
||||
case kSMErrorInvalidSignature:
|
||||
return "The app's code signature doesn't meet the requirements to "
|
||||
"perform the operation";
|
||||
case kSMErrorJobMustBeEnabled:
|
||||
return "The specified job is not enabled";
|
||||
case kSMErrorJobNotFound:
|
||||
return "The system can't find the specified job";
|
||||
case kSMErrorJobPlistNotFound:
|
||||
return "The app's property list cannot be found";
|
||||
case kSMErrorServiceUnavailable:
|
||||
return "The service necessary to perform this operation is unavailable "
|
||||
"or is no longer accepting requests";
|
||||
case kSMErrorToolNotValid:
|
||||
return "The specified path doesn't exist or the helper tool at the "
|
||||
"specified path isn't valid";
|
||||
default:
|
||||
return "Failed to register the login item";
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
SMAppService* GetServiceForType(const std::string& type,
|
||||
const std::string& name)
|
||||
API_AVAILABLE(macosx(13.0)) {
|
||||
NSString* service_name = [NSString stringWithUTF8String:name.c_str()];
|
||||
if (type == "mainAppService") {
|
||||
return [SMAppService mainAppService];
|
||||
} else if (type == "agentService") {
|
||||
return [SMAppService agentServiceWithPlistName:service_name];
|
||||
} else if (type == "daemonService") {
|
||||
return [SMAppService daemonServiceWithPlistName:service_name];
|
||||
} else if (type == "loginService") {
|
||||
return [SMAppService loginItemServiceWithIdentifier:service_name];
|
||||
} else {
|
||||
LOG(ERROR) << "Unrecognized login item type";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetLoginItemEnabledDeprecated() {
|
||||
BOOL enabled = NO;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
// SMJobCopyDictionary does not work in sandbox (see rdar://13626319)
|
||||
CFArrayRef jobs = SMCopyAllJobDictionaries(kSMDomainUserLaunchd);
|
||||
#pragma clang diagnostic pop
|
||||
NSArray* jobs_ = CFBridgingRelease(jobs);
|
||||
NSString* identifier = GetLoginHelperBundleIdentifier();
|
||||
if (jobs_ && [jobs_ count] > 0) {
|
||||
for (NSDictionary* job in jobs_) {
|
||||
if ([identifier isEqualToString:[job objectForKey:@"Label"]]) {
|
||||
enabled = [[job objectForKey:@"OnDemand"] boolValue];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace platform_util {
|
||||
@@ -167,29 +242,50 @@ void Beep() {
|
||||
NSBeep();
|
||||
}
|
||||
|
||||
bool GetLoginItemEnabled() {
|
||||
BOOL enabled = NO;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
// SMJobCopyDictionary does not work in sandbox (see rdar://13626319)
|
||||
CFArrayRef jobs = SMCopyAllJobDictionaries(kSMDomainUserLaunchd);
|
||||
#pragma clang diagnostic pop
|
||||
NSArray* jobs_ = CFBridgingRelease(jobs);
|
||||
NSString* identifier = GetLoginHelperBundleIdentifier();
|
||||
if (jobs_ && [jobs_ count] > 0) {
|
||||
for (NSDictionary* job in jobs_) {
|
||||
if ([identifier isEqualToString:[job objectForKey:@"Label"]]) {
|
||||
enabled = [[job objectForKey:@"OnDemand"] boolValue];
|
||||
break;
|
||||
}
|
||||
std::string GetLoginItemEnabled(const std::string& type,
|
||||
const std::string& service_name) {
|
||||
bool enabled = GetLoginItemEnabledDeprecated();
|
||||
if (@available(macOS 13, *)) {
|
||||
SMAppService* service = GetServiceForType(type, service_name);
|
||||
SMAppServiceStatus status = [service status];
|
||||
if (status == SMAppServiceStatusNotRegistered)
|
||||
return "not-registered";
|
||||
else if (status == SMAppServiceStatusEnabled)
|
||||
return "enabled";
|
||||
else if (status == SMAppServiceStatusRequiresApproval)
|
||||
return "requires-approval";
|
||||
else if (status == SMAppServiceStatusNotFound) {
|
||||
// If the login item was enabled with the old API, return that.
|
||||
return enabled ? "enabled-deprecated" : "not-found";
|
||||
}
|
||||
}
|
||||
return enabled;
|
||||
return enabled ? "enabled" : "not-registered";
|
||||
}
|
||||
|
||||
bool SetLoginItemEnabled(bool enabled) {
|
||||
NSString* identifier = GetLoginHelperBundleIdentifier();
|
||||
return SMLoginItemSetEnabled((__bridge CFStringRef)identifier, enabled);
|
||||
bool SetLoginItemEnabled(const std::string& type,
|
||||
const std::string& service_name,
|
||||
bool enabled) {
|
||||
if (@available(macOS 13, *)) {
|
||||
#if IS_MAS_BUILD()
|
||||
// If the app was previously set as a LoginItem with the old API, remove it
|
||||
// as a LoginItem via the old API before re-enabling with the new API.
|
||||
if (GetLoginItemEnabledDeprecated() && enabled) {
|
||||
NSString* identifier = GetLoginHelperBundleIdentifier();
|
||||
SMLoginItemSetEnabled((__bridge CFStringRef)identifier, false);
|
||||
}
|
||||
#endif
|
||||
SMAppService* service = GetServiceForType(type, service_name);
|
||||
NSError* error = nil;
|
||||
bool result = enabled ? [service registerAndReturnError:&error]
|
||||
: [service unregisterAndReturnError:&error];
|
||||
if (error != nil)
|
||||
LOG(ERROR) << "Unable to set login item: "
|
||||
<< GetLaunchStringForError(error);
|
||||
return result;
|
||||
} else {
|
||||
NSString* identifier = GetLoginHelperBundleIdentifier();
|
||||
return SMLoginItemSetEnabled((__bridge CFStringRef)identifier, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace platform_util
|
||||
|
||||
@@ -9,9 +9,9 @@ import { promisify } from 'node:util';
|
||||
import { app, BrowserWindow, Menu, session, net as electronNet, WebContents } from 'electron/main';
|
||||
import { closeWindow, closeAllWindows } from './lib/window-helpers';
|
||||
import { ifdescribe, ifit, listen, waitUntil } from './lib/spec-helpers';
|
||||
import { expectDeprecationMessages } from './lib/deprecate-helpers';
|
||||
import { once } from 'node:events';
|
||||
import split = require('split')
|
||||
import * as semver from 'semver';
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||
|
||||
@@ -527,27 +527,6 @@ describe('app module', () => {
|
||||
expect(webContents.id).to.equal(w.webContents.id);
|
||||
});
|
||||
|
||||
// FIXME: re-enable this test on win32.
|
||||
ifit(process.platform !== 'win32')('should emit renderer-process-crashed event when renderer crashes', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
expectDeprecationMessages(async () => {
|
||||
const emitted = once(app, 'renderer-process-crashed') as Promise<[any, WebContents, boolean]>;
|
||||
w.webContents.executeJavaScript('process.crash()');
|
||||
|
||||
const [, webContents, killed] = await emitted;
|
||||
expect(webContents).to.equal(w.webContents);
|
||||
expect(killed).to.be.false();
|
||||
}, '\'renderer-process-crashed event\' is deprecated and will be removed. Please use \'render-process-gone event\' instead.');
|
||||
});
|
||||
|
||||
// FIXME: re-enable this test on win32.
|
||||
ifit(process.platform !== 'win32')('should emit render-process-gone event when renderer crashes', async () => {
|
||||
w = new BrowserWindow({
|
||||
@@ -616,6 +595,9 @@ describe('app module', () => {
|
||||
});
|
||||
|
||||
ifdescribe(process.platform !== 'linux' && !process.mas)('app.get/setLoginItemSettings API', function () {
|
||||
const isMac = process.platform === 'darwin';
|
||||
const isWin = process.platform === 'win32';
|
||||
|
||||
const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
|
||||
const processStartArgs = [
|
||||
'--processStart', `"${path.basename(process.execPath)}"`,
|
||||
@@ -631,6 +613,8 @@ describe('app module', () => {
|
||||
'/f',
|
||||
'/d'
|
||||
];
|
||||
const productVersion = isMac ? cp.execSync('sw_vers -productVersion').toString().trim() : '';
|
||||
const isVenturaOrHigher = semver.gt(semver.coerce(productVersion) || '0.0.0', '13.0.0');
|
||||
|
||||
beforeEach(() => {
|
||||
app.setLoginItemSettings({ openAtLogin: false });
|
||||
@@ -644,18 +628,19 @@ describe('app module', () => {
|
||||
app.setLoginItemSettings({ name: 'additionalEntry', openAtLogin: false });
|
||||
});
|
||||
|
||||
ifit(process.platform !== 'win32')('sets and returns the app as a login item', function () {
|
||||
ifit(!isWin)('sets and returns the app as a login item', () => {
|
||||
app.setLoginItemSettings({ openAtLogin: true });
|
||||
expect(app.getLoginItemSettings()).to.deep.equal({
|
||||
openAtLogin: true,
|
||||
openAsHidden: false,
|
||||
wasOpenedAtLogin: false,
|
||||
wasOpenedAsHidden: false,
|
||||
restoreState: false
|
||||
});
|
||||
|
||||
const settings = app.getLoginItemSettings();
|
||||
expect(settings.openAtLogin).to.equal(true);
|
||||
expect(settings.openAsHidden).to.equal(false);
|
||||
expect(settings.wasOpenedAtLogin).to.equal(false);
|
||||
expect(settings.wasOpenedAsHidden).to.equal(false);
|
||||
expect(settings.restoreState).to.equal(false);
|
||||
if (isVenturaOrHigher) expect(settings.status).to.equal('enabled');
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('sets and returns the app as a login item (windows)', function () {
|
||||
ifit(isWin)('sets and returns the app as a login item (windows)', () => {
|
||||
app.setLoginItemSettings({ openAtLogin: true, enabled: true });
|
||||
expect(app.getLoginItemSettings()).to.deep.equal({
|
||||
openAtLogin: true,
|
||||
@@ -692,18 +677,21 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifit(process.platform !== 'win32')('adds a login item that loads in hidden mode', function () {
|
||||
ifit(!isWin)('adds a login item that loads in hidden mode', () => {
|
||||
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true });
|
||||
expect(app.getLoginItemSettings()).to.deep.equal({
|
||||
openAtLogin: true,
|
||||
openAsHidden: process.platform === 'darwin' && !process.mas, // Only available on macOS
|
||||
wasOpenedAtLogin: false,
|
||||
wasOpenedAsHidden: false,
|
||||
restoreState: false
|
||||
});
|
||||
|
||||
const settings = app.getLoginItemSettings();
|
||||
expect(settings.openAtLogin).to.equal(true);
|
||||
|
||||
const hasOpenAsHidden = process.platform === 'darwin' && !isVenturaOrHigher;
|
||||
expect(settings.openAsHidden).to.equal(hasOpenAsHidden);
|
||||
expect(settings.wasOpenedAtLogin).to.equal(false);
|
||||
expect(settings.wasOpenedAsHidden).to.equal(false);
|
||||
expect(settings.restoreState).to.equal(false);
|
||||
if (isVenturaOrHigher) expect(settings.status).to.equal('enabled');
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('adds a login item that loads in hidden mode (windows)', function () {
|
||||
ifit(isWin)('adds a login item that loads in hidden mode (windows)', () => {
|
||||
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true });
|
||||
expect(app.getLoginItemSettings()).to.deep.equal({
|
||||
openAtLogin: true,
|
||||
@@ -722,7 +710,7 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('correctly sets and unsets the LoginItem', function () {
|
||||
it('correctly sets and unsets the LoginItem', () => {
|
||||
expect(app.getLoginItemSettings().openAtLogin).to.equal(false);
|
||||
|
||||
app.setLoginItemSettings({ openAtLogin: true });
|
||||
@@ -732,20 +720,76 @@ describe('app module', () => {
|
||||
expect(app.getLoginItemSettings().openAtLogin).to.equal(false);
|
||||
});
|
||||
|
||||
ifit(process.platform === 'darwin')('correctly sets and unsets the LoginItem as hidden', function () {
|
||||
ifit(isMac)('correctly sets and unsets the LoginItem as hidden', () => {
|
||||
expect(app.getLoginItemSettings().openAtLogin).to.equal(false);
|
||||
expect(app.getLoginItemSettings().openAsHidden).to.equal(false);
|
||||
|
||||
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true });
|
||||
expect(app.getLoginItemSettings().openAtLogin).to.equal(true);
|
||||
expect(app.getLoginItemSettings().openAsHidden).to.equal(true);
|
||||
expect(app.getLoginItemSettings().openAsHidden).to.equal(!isVenturaOrHigher);
|
||||
|
||||
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: false });
|
||||
expect(app.getLoginItemSettings().openAtLogin).to.equal(true);
|
||||
expect(app.getLoginItemSettings().openAsHidden).to.equal(false);
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('allows you to pass a custom executable and arguments', function () {
|
||||
ifdescribe(isMac)('using SMAppService', () => {
|
||||
ifit(isVenturaOrHigher)('can set a login item', () => {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: true,
|
||||
type: 'mainAppService'
|
||||
});
|
||||
|
||||
expect(app.getLoginItemSettings()).to.deep.equal({
|
||||
status: 'enabled',
|
||||
openAtLogin: true,
|
||||
openAsHidden: false,
|
||||
restoreState: false,
|
||||
wasOpenedAtLogin: false,
|
||||
wasOpenedAsHidden: false
|
||||
});
|
||||
});
|
||||
|
||||
ifit(isVenturaOrHigher)('throws when setting non-default type with no name', () => {
|
||||
expect(() => {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: true,
|
||||
type: 'daemonService'
|
||||
});
|
||||
}).to.throw(/'name' is required when type is not mainAppService/);
|
||||
});
|
||||
|
||||
ifit(isVenturaOrHigher)('throws when getting non-default type with no name', () => {
|
||||
expect(() => {
|
||||
app.getLoginItemSettings({
|
||||
type: 'daemonService'
|
||||
});
|
||||
}).to.throw(/'name' is required when type is not mainAppService/);
|
||||
});
|
||||
|
||||
ifit(isVenturaOrHigher)('can unset a login item', () => {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: true,
|
||||
type: 'mainAppService'
|
||||
});
|
||||
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: false,
|
||||
type: 'mainAppService'
|
||||
});
|
||||
|
||||
expect(app.getLoginItemSettings()).to.deep.equal({
|
||||
status: 'not-registered',
|
||||
openAtLogin: false,
|
||||
openAsHidden: false,
|
||||
restoreState: false,
|
||||
wasOpenedAtLogin: false,
|
||||
wasOpenedAsHidden: false
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ifit(isWin)('allows you to pass a custom executable and arguments', () => {
|
||||
app.setLoginItemSettings({ openAtLogin: true, path: updateExe, args: processStartArgs, enabled: true });
|
||||
expect(app.getLoginItemSettings().openAtLogin).to.equal(false);
|
||||
const openAtLoginTrueEnabledTrue = app.getLoginItemSettings({
|
||||
@@ -775,7 +819,7 @@ describe('app module', () => {
|
||||
expect(openAtLoginFalseEnabledFalse.executableWillLaunchAtLogin).to.equal(false);
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('allows you to pass a custom name', function () {
|
||||
ifit(isWin)('allows you to pass a custom name', () => {
|
||||
app.setLoginItemSettings({ openAtLogin: true });
|
||||
app.setLoginItemSettings({ openAtLogin: true, name: 'additionalEntry', enabled: false });
|
||||
expect(app.getLoginItemSettings()).to.deep.equal({
|
||||
@@ -818,7 +862,7 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('finds launch items independent of args', function () {
|
||||
ifit(isWin)('finds launch items independent of args', () => {
|
||||
app.setLoginItemSettings({ openAtLogin: true, args: ['arg1'] });
|
||||
app.setLoginItemSettings({ openAtLogin: true, name: 'additionalEntry', enabled: false, args: ['arg2'] });
|
||||
expect(app.getLoginItemSettings()).to.deep.equal({
|
||||
@@ -844,7 +888,7 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('finds launch items independent of path quotation or casing', function () {
|
||||
ifit(isWin)('finds launch items independent of path quotation or casing', () => {
|
||||
const expectation = {
|
||||
openAtLogin: false,
|
||||
openAsHidden: false,
|
||||
@@ -880,7 +924,7 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('detects disabled by TaskManager', async function () {
|
||||
ifit(isWin)('detects disabled by TaskManager', async () => {
|
||||
app.setLoginItemSettings({ openAtLogin: true, name: 'additionalEntry', enabled: true, args: ['arg1'] });
|
||||
const appProcess = cp.spawn('reg', [...regAddArgs, '030000000000000000000000']);
|
||||
await once(appProcess, 'exit');
|
||||
@@ -901,7 +945,7 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('detects enabled by TaskManager', async function () {
|
||||
ifit(isWin)('detects enabled by TaskManager', async () => {
|
||||
const expectation = {
|
||||
openAtLogin: false,
|
||||
openAsHidden: false,
|
||||
|
||||
@@ -2224,7 +2224,22 @@ describe('BrowserWindow module', () => {
|
||||
expect(visible).to.equal('hidden');
|
||||
});
|
||||
|
||||
it('resolves after the window is hidden', async () => {
|
||||
it('resolves when the window is occluded', async () => {
|
||||
const w1 = new BrowserWindow({ show: false });
|
||||
w1.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
await once(w1, 'ready-to-show');
|
||||
w1.show();
|
||||
|
||||
const w2 = new BrowserWindow({ show: false });
|
||||
w2.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
await once(w2, 'ready-to-show');
|
||||
w2.show();
|
||||
|
||||
const visibleImage = await w1.capturePage();
|
||||
expect(visibleImage.isEmpty()).to.equal(false);
|
||||
});
|
||||
|
||||
it('resolves when the window is not visible', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
await once(w, 'ready-to-show');
|
||||
@@ -2233,21 +2248,10 @@ describe('BrowserWindow module', () => {
|
||||
const visibleImage = await w.capturePage();
|
||||
expect(visibleImage.isEmpty()).to.equal(false);
|
||||
|
||||
w.hide();
|
||||
w.minimize();
|
||||
|
||||
const hiddenImage = await w.capturePage();
|
||||
const isEmpty = process.platform !== 'darwin';
|
||||
expect(hiddenImage.isEmpty()).to.equal(isEmpty);
|
||||
});
|
||||
|
||||
it('resolves after the window is hidden and capturer count is non-zero', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.webContents.setBackgroundThrottling(false);
|
||||
w.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
await once(w, 'ready-to-show');
|
||||
|
||||
const image = await w.capturePage();
|
||||
expect(image.isEmpty()).to.equal(false);
|
||||
expect(hiddenImage.isEmpty()).to.equal(false);
|
||||
});
|
||||
|
||||
it('preserves transparency', async () => {
|
||||
|
||||
@@ -317,11 +317,7 @@ describe('ipc module', () => {
|
||||
await once(ipcMain, 'closed');
|
||||
});
|
||||
|
||||
// TODO(@vertedinde): This broke upstream in CL https://chromium-review.googlesource.com/c/chromium/src/+/4831380
|
||||
// The behavior seems to be an intentional change, we need to either A) implement the task_container_ model in
|
||||
// our renderer message ports or B) patch how we handle renderer message ports being garbage collected
|
||||
// crbug: https://bugs.chromium.org/p/chromium/issues/detail?id=1487835
|
||||
it.skip('is emitted when the other end of a port is garbage-collected', async () => {
|
||||
it('is emitted when the other end of a port is garbage-collected', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
w.loadURL('about:blank');
|
||||
await w.webContents.executeJavaScript(`(${async function () {
|
||||
|
||||
@@ -6,7 +6,6 @@ import * as http from 'node:http';
|
||||
import { BrowserWindow, ipcMain, webContents, session, app, BrowserView, WebContents } from 'electron/main';
|
||||
import { closeAllWindows } from './lib/window-helpers';
|
||||
import { ifdescribe, defer, waitUntil, listen, ifit } from './lib/spec-helpers';
|
||||
import { expectDeprecationMessages } from './lib/deprecate-helpers';
|
||||
import { once } from 'node:events';
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
|
||||
@@ -2344,30 +2343,6 @@ describe('webContents module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('crashed event', () => {
|
||||
it('does not crash main process when destroying WebContents in it', (done) => {
|
||||
const contents = (webContents as typeof ElectronInternal.WebContents).create({ nodeIntegration: true });
|
||||
contents.once('crashed', () => {
|
||||
contents.destroy();
|
||||
done();
|
||||
});
|
||||
contents.loadURL('about:blank').then(() => contents.forcefullyCrashRenderer());
|
||||
});
|
||||
|
||||
it('logs a warning', async () => {
|
||||
const contents = (webContents as typeof ElectronInternal.WebContents).create({ nodeIntegration: true });
|
||||
await contents.loadURL('about:blank');
|
||||
|
||||
expectDeprecationMessages(async () => {
|
||||
const crashEvent = once(contents, 'crashed');
|
||||
contents.forcefullyCrashRenderer();
|
||||
const [, killed] = await crashEvent;
|
||||
|
||||
expect(killed).to.be.a('boolean');
|
||||
}, '\'crashed event\' is deprecated and will be removed. Please use \'render-process-gone event\' instead.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('context-menu event', () => {
|
||||
afterEach(closeAllWindows);
|
||||
it('emits when right-clicked in page', async () => {
|
||||
|
||||
@@ -490,6 +490,15 @@ describe('asar package', function () {
|
||||
}).to.throw(/ENOENT/);
|
||||
}
|
||||
});
|
||||
|
||||
itremote('returns null when can not find file with throwIfNoEntry === false', function () {
|
||||
const ref2 = ['file4', 'file5', path.join('dir1', 'file4')];
|
||||
for (let j = 0, len = ref2.length; j < len; j++) {
|
||||
const file = ref2[j];
|
||||
const p = path.join(asarDir, 'a.asar', file);
|
||||
expect(fs.lstatSync(p, { throwIfNoEntry: false })).to.equal(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('fs.lstat', function () {
|
||||
|
||||
@@ -429,9 +429,17 @@ win2.once('ready-to-show', () => {
|
||||
app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) });
|
||||
app.exit(0);
|
||||
|
||||
app.configureHostResolver({ secureDnsMode: 'off' });
|
||||
|
||||
// @ts-expect-error Invalid type value
|
||||
app.configureHostResolver({ secureDnsMode: 'foo' });
|
||||
|
||||
// @ts-expect-error Removed API
|
||||
console.log(app.runningUnderRosettaTranslation);
|
||||
|
||||
// @ts-expect-error Removed API
|
||||
app.on('renderer-process-crashed', () => {});
|
||||
|
||||
// auto-updater
|
||||
// https://github.com/electron/electron/blob/main/docs/api/auto-updater.md
|
||||
|
||||
@@ -1284,6 +1292,11 @@ win4.webContents.on('devtools-open-url', (event, url) => {
|
||||
console.log(url);
|
||||
});
|
||||
|
||||
win4.webContents.insertCSS('body {}', { cssOrigin: 'user' });
|
||||
|
||||
// @ts-expect-error Invalid type value
|
||||
win4.webContents.insertCSS('body {}', { cssOrigin: 'foo' });
|
||||
|
||||
win4.loadURL('http://github.com');
|
||||
|
||||
// @ts-expect-error Removed API
|
||||
@@ -1296,6 +1309,9 @@ win4.webContents.on('scroll-touch-edge', () => {});
|
||||
// @ts-expect-error Removed API
|
||||
win4.webContents.on('scroll-touch-end', () => {});
|
||||
|
||||
// @ts-expect-error Removed API
|
||||
win4.webContents.on('crashed', () => {});
|
||||
|
||||
// TouchBar
|
||||
// https://github.com/electron/electron/blob/main/docs/api/touch-bar.md
|
||||
|
||||
|
||||
Reference in New Issue
Block a user