mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
21 Commits
v40.5.0
...
roller/nod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
893faf4f04 | ||
|
|
2d12f059b4 | ||
|
|
f715930d49 | ||
|
|
477fe8566e | ||
|
|
a9837ed476 | ||
|
|
1f7269f482 | ||
|
|
2813b89824 | ||
|
|
a4560db9f0 | ||
|
|
adfc062313 | ||
|
|
8dc34b4b25 | ||
|
|
6cc5ad763d | ||
|
|
4c5637c687 | ||
|
|
0d71ed0f29 | ||
|
|
fc63000ee7 | ||
|
|
8d41dbe65f | ||
|
|
f3b90cc91c | ||
|
|
a78a8cd30c | ||
|
|
2511f78120 | ||
|
|
f711af1080 | ||
|
|
616026ce1c | ||
|
|
ada2c4e072 |
2
.github/actions/build-electron/action.yml
vendored
2
.github/actions/build-electron/action.yml
vendored
@@ -95,7 +95,7 @@ runs:
|
||||
# Upload build stats to Datadog
|
||||
if ($env:DD_API_KEY) {
|
||||
try {
|
||||
npx node electron\script\build-stats.mjs out\Default\siso.exe.INFO --upload-stats
|
||||
npx node electron\script\build-stats.mjs out\Default\siso.exe.INFO --upload-stats ; $LASTEXITCODE = 0
|
||||
} catch {
|
||||
Write-Host "Build stats upload failed, continuing..."
|
||||
}
|
||||
|
||||
2
.github/workflows/update-website-docs.yml
vendored
2
.github/workflows/update-website-docs.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
echo "isLatestRelease=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Trigger website docs update
|
||||
if: ${{ steps.check-if-latest-release.outputs.isLatestRelease }}
|
||||
if: ${{ steps.check-if-latest-release.outputs.isLatestRelease == 'true' }}
|
||||
env:
|
||||
GH_REPO: electron/website
|
||||
GH_TOKEN: ${{ fromJSON(steps.secret-service.outputs.secrets).WEBSITE_DOCS_UPDATER_APP_TOKEN }}
|
||||
|
||||
4
DEPS
4
DEPS
@@ -2,9 +2,9 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'144.0.7559.177',
|
||||
'144.0.7559.225',
|
||||
'node_version':
|
||||
'v24.13.1',
|
||||
'v24.14.0',
|
||||
'nan_version':
|
||||
'675cefebca42410733da8a454c8d9391fcebfbc2',
|
||||
'squirrel.mac_version':
|
||||
|
||||
@@ -250,7 +250,9 @@ Returns:
|
||||
|
||||
Emitted when the user clicks the native macOS new tab button. The new
|
||||
tab button is only visible if the current `BrowserWindow` has a
|
||||
`tabbingIdentifier`
|
||||
`tabbingIdentifier`.
|
||||
|
||||
You must create a window in this handler in order for macOS tabbing to work as expected.
|
||||
|
||||
### Event: 'browser-window-blur'
|
||||
|
||||
@@ -1329,7 +1331,7 @@ Returns `boolean` - Whether the current desktop environment is Unity launcher.
|
||||
### `app.getLoginItemSettings([options])` _macOS_ _Windows_
|
||||
|
||||
* `options` Object (optional)
|
||||
* `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.
|
||||
* `type` string (optional) _macOS_ - Can be `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.
|
||||
@@ -1344,13 +1346,13 @@ Returns `Object`:
|
||||
* `wasOpenedAtLogin` boolean _macOS_ - `true` if the app was opened at login automatically.
|
||||
* `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`.
|
||||
* `status` string _macOS_ - can be `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.
|
||||
* `path` string _Windows_ - The executable to an app that corresponds to a registry entry.
|
||||
* `args` string[] _Windows_ - the command-line arguments to pass to the executable.
|
||||
* `scope` string _Windows_ - one of `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
|
||||
* `scope` string _Windows_ - can be `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
|
||||
* `enabled` boolean _Windows_ - `true` if the app registry key is startup approved and therefore shows as `enabled` in Task Manager and Windows settings.
|
||||
|
||||
### `app.setLoginItemSettings(settings)` _macOS_ _Windows_
|
||||
|
||||
@@ -351,7 +351,11 @@ Emitted when the window has closed a sheet.
|
||||
|
||||
#### Event: 'new-window-for-tab' _macOS_
|
||||
|
||||
Emitted when the native new tab button is clicked.
|
||||
Emitted when the user clicks the native macOS new tab button. The new
|
||||
tab button is only visible if the current `BrowserWindow` has a
|
||||
`tabbingIdentifier`.
|
||||
|
||||
You must create a window in this handler in order for macOS tabbing to work as expected.
|
||||
|
||||
#### Event: 'system-context-menu' _Windows_ _Linux_
|
||||
|
||||
|
||||
@@ -431,7 +431,11 @@ Emitted when the window has closed a sheet.
|
||||
|
||||
#### Event: 'new-window-for-tab' _macOS_
|
||||
|
||||
Emitted when the native new tab button is clicked.
|
||||
Emitted when the user clicks the native macOS new tab button. The new
|
||||
tab button is only visible if the current `BrowserWindow` has a
|
||||
`tabbingIdentifier`.
|
||||
|
||||
You must create a window in this handler in order for macOS tabbing to work as expected.
|
||||
|
||||
#### Event: 'system-context-menu' _Windows_ _Linux_
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# sharedTexture
|
||||
|
||||
> Import shared textures into Electron and converts platform specific handles into [`VideoFrame`](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame). Supports all Web rendering systems, and can be transferred across Electron processes. Read [here](https://github.com/electron/electron/blob/main/shell/common/api/shared_texture/README.md) for more information.
|
||||
> Import shared textures into Electron and converts platform specific handles into [`VideoFrame`](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame). Supports all Web rendering systems, and can be transferred across Electron processes. Read [here](../../shell/common/api/shared_texture/README.md) for more information.
|
||||
|
||||
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
|
||||
|
||||
|
||||
@@ -156,6 +156,8 @@
|
||||
`WebContents` when the preferred size changes. Default is `false`.
|
||||
* `transparent` boolean (optional) - Whether to enable background transparency for the guest page. Default is `true`. **Note:** The guest page's text and background colors are derived from the [color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme) of its root element. When transparency is enabled, the text color will still change accordingly but the background will remain transparent.
|
||||
* `enableDeprecatedPaste` boolean (optional) _Deprecated_ - Whether to enable the `paste` [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand). Default is `false`.
|
||||
* `focusOnNavigation` boolean (optional) - Whether to focus the WebContents
|
||||
when navigating. Default is `true`.
|
||||
|
||||
[chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment
|
||||
[runtime-enabled-features]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/runtime_enabled_features.json5
|
||||
|
||||
@@ -933,7 +933,7 @@ copying data between CPU and GPU memory, with Chromium's hardware acceleration s
|
||||
Only a limited number of textures can exist at the same time, so it's important that you call `texture.release()` as soon as you're done with the texture.
|
||||
By managing the texture lifecycle by yourself, you can safely pass the `texture.textureInfo` to other processes through IPC.
|
||||
|
||||
More details can be found in the [offscreen rendering tutorial](../tutorial/offscreen-rendering.md). To learn about how to handle the texture in native code, refer to [offscreen rendering's code documentation.](https://github.com/electron/electron/blob/main/shell/browser/osr/README.md).
|
||||
More details can be found in the [offscreen rendering tutorial](../tutorial/offscreen-rendering.md). To learn about how to handle the texture in native code, refer to [offscreen rendering's code documentation.](../../shell/browser/osr/README.md).
|
||||
|
||||
```js
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
@@ -41,7 +41,7 @@ e init --root=~/electron --bootstrap testing
|
||||
```
|
||||
|
||||
The `--bootstrap` flag also runs `e sync` (synchronizes source code branches from
|
||||
[`DEPS`](https://github.com/electron/electron/blob/main/DEPS) using
|
||||
[`DEPS`](../../DEPS) using
|
||||
[`gclient`](https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/HEAD/README.gclient.md))
|
||||
and `e build` (compiles the Electron binary into the `${root}/src/out` folder).
|
||||
|
||||
@@ -63,7 +63,7 @@ Some quick tips on building once your checkout is set up:
|
||||
* **Updating your checkout:** Run git commands such as `git checkout <branch>` and `git pull` from `${root}/src/electron`.
|
||||
Whenever you update your commit `HEAD`, make sure to `e sync` before `e build` to sync dependencies
|
||||
such as Chromium and Node.js. This is especially relevant because the Chromium version in
|
||||
[`DEPS`](https://github.com/electron/electron/blob/main/DEPS) changes frequently.
|
||||
[`DEPS`](../../DEPS) changes frequently.
|
||||
* **Rebuilding:** When making changes to code in `${root}/src/electron/` in a local branch, you only need to re-run `e build`.
|
||||
* **Adding patches:** When contributing changes in `${root}/src/` outside of `${root}/src/electron/`, you need to do so
|
||||
via Electron's [patch system](./patches.md). The `e patches` command can export all relevant patches to
|
||||
@@ -98,7 +98,7 @@ Project configurations can be found in the `.gn` and `.gni` files in the `electr
|
||||
|
||||
The following `gn` files contain the main rules for building Electron:
|
||||
|
||||
* [`BUILD.gn`](https://github.com/electron/electron/blob/main/BUILD.gn) defines how Electron itself
|
||||
* [`BUILD.gn`](../../BUILD.gn) defines how Electron itself
|
||||
is built and includes the default configurations for linking with Chromium.
|
||||
* [`build/args/{testing,release,all}.gn`](https://github.com/electron/electron/tree/main/build/args)
|
||||
contain the default build arguments for building Electron.
|
||||
|
||||
@@ -6,7 +6,7 @@ This is not a comprehensive end-all guide to creating an Electron Browser API, r
|
||||
|
||||
## Add your files to Electron's project configuration
|
||||
|
||||
Electron uses [GN](https://gn.googlesource.com/gn) as a meta build system to generate files for its compiler, [Ninja](https://ninja-build.org/). This means that in order to tell Electron to compile your code, we have to add your API's code and header file names into [`filenames.gni`](https://github.com/electron/electron/blob/main/filenames.gni).
|
||||
Electron uses [GN](https://gn.googlesource.com/gn) as a meta build system to generate files for its compiler, [Ninja](https://ninja-build.org/). This means that in order to tell Electron to compile your code, we have to add your API's code and header file names into [`filenames.gni`](../../filenames.gni).
|
||||
|
||||
You will need to append your API file names alphabetically into the appropriate files like so:
|
||||
|
||||
@@ -127,7 +127,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
|
||||
## Link your Electron API with Node
|
||||
|
||||
In the [`typings/internal-ambient.d.ts`](https://github.com/electron/electron/blob/main/typings/internal-ambient.d.ts) file, we need to append a new property onto the `Process` interface like so:
|
||||
In the [`typings/internal-ambient.d.ts`](../../typings/internal-ambient.d.ts) file, we need to append a new property onto the `Process` interface like so:
|
||||
|
||||
```ts title='typings/internal-ambient.d.ts' @ts-nocheck
|
||||
interface Process {
|
||||
@@ -141,7 +141,7 @@ At the very bottom of your `api_name.cc` file:
|
||||
NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_{api_name},Initialize)
|
||||
```
|
||||
|
||||
In your [`shell/common/node_bindings.cc`](https://github.com/electron/electron/blob/main/shell/common/node_bindings.cc) file, add your node binding name to Electron's built-in modules.
|
||||
In your [`shell/common/node_bindings.cc`](../../shell/common/node_bindings.cc) file, add your node binding name to Electron's built-in modules.
|
||||
|
||||
```cpp title='shell/common/node_bindings.cc'
|
||||
#define ELECTRON_BROWSER_MODULES(V) \
|
||||
@@ -159,7 +159,7 @@ We will need to create a new TypeScript file in the path that follows:
|
||||
|
||||
`"lib/browser/api/{electron_browser_{api_name}}.ts"`
|
||||
|
||||
An example of the contents of this file can be found [here](https://github.com/electron/electron/blob/main/lib/browser/api/native-theme.ts).
|
||||
An example of the contents of this file can be found [here](../../lib/browser/api/native-theme.ts).
|
||||
|
||||
### Expose your module to TypeScript
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ $ git push origin my-branch
|
||||
### Step 9: Opening the Pull Request
|
||||
|
||||
From within GitHub, opening a new pull request will present you with a template
|
||||
that should be filled out. It can be found [here](https://github.com/electron/electron/blob/main/.github/PULL_REQUEST_TEMPLATE.md).
|
||||
that should be filled out. It can be found [here](../../.github/PULL_REQUEST_TEMPLATE.md).
|
||||
|
||||
If you do not adequately complete this template, your PR may be delayed in being merged as maintainers
|
||||
seek more information or clarify ambiguities.
|
||||
@@ -218,8 +218,7 @@ seem unfamiliar, refer to this
|
||||
|
||||
#### Approval and Request Changes Workflow
|
||||
|
||||
All pull requests require approval from a
|
||||
[Code Owner](https://github.com/electron/electron/blob/main/.github/CODEOWNERS)
|
||||
All pull requests require approval from a [Code Owner](../../.github/CODEOWNERS)
|
||||
of the area you modified in order to land. Whenever a maintainer reviews a pull
|
||||
request they may request changes. These may be small, such as fixing a typo, or
|
||||
may involve substantive changes. Such requests are intended to be helpful, but
|
||||
|
||||
@@ -10,7 +10,7 @@ to understand the source code better.
|
||||
## Project structure
|
||||
|
||||
Electron is a complex project containing multiple upstream dependencies, which are tracked in source
|
||||
control via the [`DEPS`](https://github.com/electron/electron/blob/main/DEPS) file. When
|
||||
control via the [`DEPS`](../../DEPS) file. When
|
||||
[initializing a local Electron checkout](./build-instructions-gn.md), Electron's source code is just one
|
||||
of many nested folders within the project root.
|
||||
|
||||
|
||||
@@ -15,6 +15,14 @@ Currently, ASAR integrity checking is supported on:
|
||||
* macOS as of `electron>=16.0.0`
|
||||
* Windows as of `electron>=30.0.0`
|
||||
|
||||
> [!NOTE]
|
||||
> ASAR integrity is fully supported in Mac App Store (MAS) builds and is recommended
|
||||
> as a best practice. While MAS-installed applications have their `Resources/` folder
|
||||
> protected by the system (owned by root), ASAR integrity still provides an additional
|
||||
> layer of security. It is especially important if you use Electron's MAS build but
|
||||
> distribute your app through channels other than the Mac App Store (such as direct
|
||||
> download), since those installations won't have the system-level read-only protections.
|
||||
|
||||
In order to enable ASAR integrity checking, you also need to ensure that your `app.asar` file
|
||||
was generated by a version of the `@electron/asar` npm package that supports ASAR integrity.
|
||||
|
||||
|
||||
@@ -197,4 +197,4 @@ Somewhere in the Electron binary, there will be a sequence of bytes that look li
|
||||
|
||||
To flip a fuse, you find its position in the fuse wire and change it to "0" or "1" depending on the state you'd like.
|
||||
|
||||
You can view the current schema [here](https://github.com/electron/electron/blob/main/build/fuses/fuses.json5).
|
||||
You can view the current schema [here](../../build/fuses/fuses.json5).
|
||||
|
||||
@@ -37,7 +37,7 @@ setting.
|
||||
The frames are directly copied in GPU textures, thus this mode is very fast because
|
||||
there's no CPU-GPU memory copies overhead, and you can directly import the shared
|
||||
texture to your own rendering program. You can read more details
|
||||
[here](https://github.com/electron/electron/blob/main/shell/browser/osr/README.md).
|
||||
[here](../../shell/common/api/shared_texture/README.md).
|
||||
|
||||
2. Use CPU shared memory bitmap
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ toc_max_heading_level: 3
|
||||
|
||||
:::info Reporting security issues
|
||||
For information on how to properly disclose an Electron vulnerability,
|
||||
see [SECURITY.md](https://github.com/electron/electron/blob/main/SECURITY.md).
|
||||
see [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
For upstream Chromium vulnerabilities: Electron keeps up to date with alternating
|
||||
Chromium releases. For more information, see the
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
* For information on supported releases, see the [Electron Releases](./electron-timelines.md) doc.
|
||||
* For community support on Electron, see the [Community page](https://www.electronjs.org/community).
|
||||
* For platform support info, see the [README](https://github.com/electron/electron/blob/main/README.md).
|
||||
* For platform support info, see the [README](../../README.md).
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"lint:gn": "node ./script/lint.js --gn",
|
||||
"lint:docs": "remark docs -qf && npm run lint:js-in-markdown && npm run create-typescript-definitions && npm run lint:ts-check-js-in-markdown && npm run lint:docs-fiddles && npm run lint:docs-relative-links && npm run lint:markdown && npm run lint:api-history",
|
||||
"lint:docs-fiddles": "standard \"docs/fiddles/**/*.js\"",
|
||||
"lint:docs-relative-links": "lint-roller-markdown-links --root docs \"**/*.md\"",
|
||||
"lint:docs-relative-links": "lint-roller-markdown-links --resource-root . --root docs \"**/*.md\"",
|
||||
"lint:markdown": "node ./script/lint.js --md",
|
||||
"lint:ts-check-js-in-markdown": "lint-roller-markdown-ts-check --root docs \"**/*.md\" --ignore \"breaking-changes.md\"",
|
||||
"lint:js-in-markdown": "lint-roller-markdown-standard --root docs \"**/*.md\"",
|
||||
|
||||
@@ -147,3 +147,4 @@ fix_os_crypt_async_cookie_encryption.patch
|
||||
graphite_handle_out_of_order_recording_errors.patch
|
||||
move_wayland_pointer_lock_overrides_to_common_code.patch
|
||||
loaf_add_feature_to_enable_sourceurl_for_all_protocols.patch
|
||||
fix_update_dbus_signal_signature_for_xdg_globalshortcuts_portal.patch
|
||||
|
||||
@@ -4,7 +4,7 @@ Date: Wed, 28 Jun 2023 21:11:40 +0900
|
||||
Subject: fix: harden blink::ScriptState::MaybeFrom
|
||||
|
||||
NOTE: since https://chromium-review.googlesource.com/c/chromium/src/+/6973697
|
||||
the patch is only needed for 32-bit builds.
|
||||
the patch is only needed for 32-bit builds or builds where the V8 sandbox is disabled.
|
||||
|
||||
This is needed as side effect of https://chromium-review.googlesource.com/c/chromium/src/+/4609446
|
||||
which now gets blink::ExecutionContext from blink::ScriptState
|
||||
@@ -56,18 +56,18 @@ index cecf528475cb832ed1876381878eade582bc83d6..71308b2d963c2d083328aad6be356dc5
|
||||
|
||||
enum EmbedderDataTag : uint16_t {
|
||||
diff --git a/third_party/blink/renderer/platform/bindings/script_state.cc b/third_party/blink/renderer/platform/bindings/script_state.cc
|
||||
index 8b6522c9299bef5ab766795b64a1ba30bc382a12..a714aeb8a62886bedb3820b33b49b1ebdb9c7cc0 100644
|
||||
index 8b6522c9299bef5ab766795b64a1ba30bc382a12..4615dc04a3814a096898a36c7bbeb30f960a8b4d 100644
|
||||
--- a/third_party/blink/renderer/platform/bindings/script_state.cc
|
||||
+++ b/third_party/blink/renderer/platform/bindings/script_state.cc
|
||||
@@ -14,6 +14,12 @@ namespace blink {
|
||||
|
||||
ScriptState::CreateCallback ScriptState::s_create_callback_ = nullptr;
|
||||
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+int const ScriptState::kScriptStateTag = 0x6e6f64;
|
||||
+void* const ScriptState::kScriptStateTagPtr = const_cast<void*>(
|
||||
+ static_cast<const void*>(&ScriptState::kScriptStateTag));
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
+
|
||||
// static
|
||||
void ScriptState::SetCreateCallback(CreateCallback create_callback) {
|
||||
@@ -76,10 +76,10 @@ index 8b6522c9299bef5ab766795b64a1ba30bc382a12..a714aeb8a62886bedb3820b33b49b1eb
|
||||
context_.SetWeak(this, &OnV8ContextCollectedCallback);
|
||||
context->SetAlignedPointerInEmbedderData(kV8ContextPerContextDataIndex, this,
|
||||
gin::kBlinkScriptState);
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+ context->SetAlignedPointerInEmbedderData(
|
||||
+ kV8ContextPerContextDataTagIndex, ScriptState::kScriptStateTagPtr, v8::kEmbedderDataTypeTagDefault);
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
RendererResourceCoordinator::Get()->OnScriptStateCreated(this,
|
||||
execution_context);
|
||||
}
|
||||
@@ -87,15 +87,15 @@ index 8b6522c9299bef5ab766795b64a1ba30bc382a12..a714aeb8a62886bedb3820b33b49b1eb
|
||||
// Cut the reference from V8 context to ScriptState.
|
||||
GetContext()->SetAlignedPointerInEmbedderData(
|
||||
kV8ContextPerContextDataIndex, nullptr, gin::kBlinkScriptState);
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+ GetContext()->SetAlignedPointerInEmbedderData(
|
||||
+ kV8ContextPerContextDataTagIndex, nullptr, v8::kEmbedderDataTypeTagDefault);
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
reference_from_v8_context_.Clear();
|
||||
|
||||
// Cut the reference from ScriptState to V8 context.
|
||||
diff --git a/third_party/blink/renderer/platform/bindings/script_state.h b/third_party/blink/renderer/platform/bindings/script_state.h
|
||||
index 5ccdf26cead17031d510589b74288cbe79692779..bf3023d5305c05c5d92953b5bf5f655f964e5c28 100644
|
||||
index 5ccdf26cead17031d510589b74288cbe79692779..54ede003ebe0a46e624c9d67f7272b8898bbc83e 100644
|
||||
--- a/third_party/blink/renderer/platform/bindings/script_state.h
|
||||
+++ b/third_party/blink/renderer/platform/bindings/script_state.h
|
||||
@@ -6,6 +6,7 @@
|
||||
@@ -110,7 +110,7 @@ index 5ccdf26cead17031d510589b74288cbe79692779..bf3023d5305c05c5d92953b5bf5f655f
|
||||
kV8ContextPerContextDataIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+ if (context->GetNumberOfEmbedderDataFields() <=
|
||||
+ kV8ContextPerContextDataTagIndex ||
|
||||
+ context->GetAlignedPointerFromEmbedderData(
|
||||
@@ -119,7 +119,7 @@ index 5ccdf26cead17031d510589b74288cbe79692779..bf3023d5305c05c5d92953b5bf5f655f
|
||||
+ ScriptState::kScriptStateTagPtr) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
ScriptState* script_state =
|
||||
static_cast<ScriptState*>(context->GetAlignedPointerFromEmbedderData(
|
||||
isolate, kV8ContextPerContextDataIndex, gin::kBlinkScriptState));
|
||||
@@ -127,13 +127,13 @@ index 5ccdf26cead17031d510589b74288cbe79692779..bf3023d5305c05c5d92953b5bf5f655f
|
||||
static_cast<int>(gin::kPerContextDataStartIndex) +
|
||||
static_cast<int>(gin::kEmbedderBlink);
|
||||
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+ static void* const kScriptStateTagPtr;
|
||||
+ static int const kScriptStateTag;
|
||||
+ static constexpr int kV8ContextPerContextDataTagIndex =
|
||||
+ static_cast<int>(gin::kPerContextDataStartIndex) +
|
||||
+ static_cast<int>(gin::kEmbedderBlinkTag);
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
+
|
||||
// For accessing information about the last script compilation via
|
||||
// internals.idl.
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Mon, 16 Feb 2026 22:33:57 +0100
|
||||
Subject: fix: update DBus signal signature for XDG GlobalShortcuts portal
|
||||
|
||||
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7143562
|
||||
|
||||
The Activated signal from the XDG GlobalShortcuts portal has signature "osta{sv}",
|
||||
but ConnectToSignal was declared with "ost". The strict ReadMessage parser
|
||||
fails on the extra trailing options vardict. Fix by updating the signature
|
||||
to match the spec.
|
||||
|
||||
This should be upstreamed.
|
||||
|
||||
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc
|
||||
index 505e59edad7acb41f272f1e323cfd90744e4701b..ead80cfac902a7630cf502b03cb7d2ee048944ac 100644
|
||||
--- a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc
|
||||
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc
|
||||
@@ -78,7 +78,7 @@ void GlobalAcceleratorListenerLinux::OnServiceStarted(bool service_started) {
|
||||
global_shortcuts_proxy_ = bus_->GetObjectProxy(
|
||||
kPortalServiceName, dbus::ObjectPath(kPortalObjectPath));
|
||||
|
||||
- dbus_utils::ConnectToSignal<"ost">(
|
||||
+ dbus_utils::ConnectToSignal<"osta{sv}">(
|
||||
global_shortcuts_proxy_, kGlobalShortcutsInterface, kSignalActivated,
|
||||
base::BindRepeating(&GlobalAcceleratorListenerLinux::OnActivatedSignal,
|
||||
weak_ptr_factory_.GetWeakPtr()),
|
||||
@@ -299,13 +299,14 @@ void GlobalAcceleratorListenerLinux::OnBindShortcuts(
|
||||
}
|
||||
|
||||
void GlobalAcceleratorListenerLinux::OnActivatedSignal(
|
||||
- dbus_utils::ConnectToSignalResultSig<"ost"> result) {
|
||||
+ dbus_utils::ConnectToSignalResultSig<"osta{sv}"> result) {
|
||||
if (!result.has_value()) {
|
||||
LOG(ERROR) << "Failed to parse Activated signal.";
|
||||
return;
|
||||
}
|
||||
|
||||
- auto [session_handle, shortcut_id, timestamp] = std::move(result.value());
|
||||
+ auto [session_handle, shortcut_id, timestamp, options] =
|
||||
+ std::move(result.value());
|
||||
|
||||
// Only process the signal if it comes from our current session.
|
||||
if (!session_proxy_ || session_proxy_->object_path() != session_handle) {
|
||||
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h
|
||||
index 3838de75a0a3791f774e2ddedec0f6c7f2f30157..91a205e6c526aa02b46b1590fa427e8875a67280 100644
|
||||
--- a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h
|
||||
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h
|
||||
@@ -97,7 +97,8 @@ class GlobalAcceleratorListenerLinux : public GlobalAcceleratorListener {
|
||||
base::expected<dbus_xdg::Dictionary, dbus_xdg::ResponseError> results);
|
||||
|
||||
// Callbacks for DBus signals.
|
||||
- void OnActivatedSignal(dbus_utils::ConnectToSignalResultSig<"ost"> result);
|
||||
+ void OnActivatedSignal(
|
||||
+ dbus_utils::ConnectToSignalResultSig<"osta{sv}"> result);
|
||||
|
||||
void OnSignalConnected(const std::string& interface_name,
|
||||
const std::string& signal_name,
|
||||
@@ -1,13 +1,13 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Thu, 7 Apr 2022 20:30:16 +0900
|
||||
Subject: Make gtk::GetLibGdkPixbuf public
|
||||
Subject: Make gtk::GetLibGdkPixbuf and gtk::GetLibGdk public
|
||||
|
||||
Allows embedders to get a handle to the gdk_pixbuf
|
||||
library already loaded in the process.
|
||||
Allows embedders to get handles to the gdk_pixbuf
|
||||
and gdk libraries already loaded in the process.
|
||||
|
||||
diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
|
||||
index e05b4f2eb1b22d5a647cb020bae4e4052a2e735c..c06af1c03487fafe76fde3bfa157a7d265e2f3a0 100644
|
||||
index e05b4f2eb1b22d5a647cb020bae4e4052a2e735c..86524a419606bea3e7d090415fda8f2d8ce24df2 100644
|
||||
--- a/ui/gtk/gtk_compat.cc
|
||||
+++ b/ui/gtk/gtk_compat.cc
|
||||
@@ -78,11 +78,6 @@ void* GetLibGio() {
|
||||
@@ -22,7 +22,7 @@ index e05b4f2eb1b22d5a647cb020bae4e4052a2e735c..c06af1c03487fafe76fde3bfa157a7d2
|
||||
void* GetLibGdk3() {
|
||||
static void* libgdk3 = DlOpen("libgdk-3.so.0");
|
||||
return libgdk3;
|
||||
@@ -175,6 +170,11 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
@@ -175,6 +170,15 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -30,20 +30,27 @@ index e05b4f2eb1b22d5a647cb020bae4e4052a2e735c..c06af1c03487fafe76fde3bfa157a7d2
|
||||
+ static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
|
||||
+ return libgdk_pixbuf;
|
||||
+}
|
||||
+
|
||||
+void* GetLibGdk() {
|
||||
+ return GtkCheckVersion(4) ? GetLibGtk4() : GetLibGdk3();
|
||||
+}
|
||||
+
|
||||
bool LoadGtk(ui::LinuxUiBackend backend) {
|
||||
static bool loaded = LoadGtkImpl(backend);
|
||||
return loaded;
|
||||
diff --git a/ui/gtk/gtk_compat.h b/ui/gtk/gtk_compat.h
|
||||
index 841e2e8fcdbe2da4aac487badd4d352476e461a2..e458df649546fa3bee10e24f0edac147186cc152 100644
|
||||
index 841e2e8fcdbe2da4aac487badd4d352476e461a2..043c3ab4dde02ca71798034e8cb2b3f2d2677af7 100644
|
||||
--- a/ui/gtk/gtk_compat.h
|
||||
+++ b/ui/gtk/gtk_compat.h
|
||||
@@ -42,6 +42,9 @@ using SkColor = uint32_t;
|
||||
@@ -42,6 +42,12 @@ using SkColor = uint32_t;
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+// Get handle to the currently loaded gdk_pixbuf library in the process.
|
||||
+void* GetLibGdkPixbuf();
|
||||
+
|
||||
+// Get handle to the currently loaded gdk library in the process.
|
||||
+void* GetLibGdk();
|
||||
+
|
||||
// Loads libgtk and related libraries and returns true on success.
|
||||
bool LoadGtk(ui::LinuxUiBackend backend);
|
||||
|
||||
@@ -516,7 +516,7 @@ index 020050de162705651b4eb8378880cd4eb017d46c..2d3554861a570271d6f9b9a2c8b1de53
|
||||
// The NSWindow used by BridgedNativeWidget. Provides hooks into AppKit that
|
||||
// can only be accomplished by overriding methods.
|
||||
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6b192912c 100644
|
||||
index a474e70cf3c10405b6f94f129f5a7312bb81fd73..adaff0f3d676eeaef93ac9a4a3b55fc0e572d33b 100644
|
||||
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
@@ -21,6 +21,7 @@
|
||||
@@ -553,7 +553,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
@end
|
||||
|
||||
struct NSEdgeAndCornerThicknesses {
|
||||
@@ -158,13 +163,17 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event;
|
||||
@@ -158,13 +163,30 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event;
|
||||
@implementation NSView (CRFrameViewAdditions)
|
||||
// If a mouseDown: falls through to the frame view, turn it into a window drag.
|
||||
- (void)cr_mouseDownOnFrameView:(NSEvent*)event {
|
||||
@@ -561,6 +561,19 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
if ([self.window _resizeDirectionForMouseLocation:event.locationInWindow] !=
|
||||
-1)
|
||||
return;
|
||||
+#else
|
||||
+ // For MAS builds, approximate the resize direction check.
|
||||
+ if (self.window.styleMask & NSWindowStyleMaskResizable) {
|
||||
+ constexpr CGFloat kResizeThreshold = 5.0;
|
||||
+ NSPoint location = event.locationInWindow;
|
||||
+ NSRect frame = self.window.frame;
|
||||
+ CGFloat width = NSWidth(frame);
|
||||
+ CGFloat height = NSHeight(frame);
|
||||
+ if (location.x < kResizeThreshold || location.x > width - kResizeThreshold ||
|
||||
+ location.y < kResizeThreshold || location.y > height - kResizeThreshold) {
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+#endif
|
||||
[self.window performWindowDragWithEvent:event];
|
||||
}
|
||||
@@ -571,7 +584,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
@implementation NativeWidgetMacNSWindowTitledFrame
|
||||
- (void)mouseDown:(NSEvent*)event {
|
||||
if (self.window.isMovable)
|
||||
@@ -192,6 +201,8 @@ - (BOOL)usesCustomDrawing {
|
||||
@@ -192,6 +214,8 @@ - (BOOL)usesCustomDrawing {
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -580,7 +593,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
@implementation NativeWidgetMacNSWindow {
|
||||
@private
|
||||
CommandDispatcher* __strong _commandDispatcher;
|
||||
@@ -241,6 +252,7 @@ - (instancetype)initWithContentRect:(NSRect)contentRect
|
||||
@@ -241,6 +265,7 @@ - (instancetype)initWithContentRect:(NSRect)contentRect
|
||||
// bubbles and the find bar, but these should not be movable.
|
||||
// Instead, let's push this up to the parent window which should be
|
||||
// the browser.
|
||||
@@ -588,7 +601,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
- (void)_zoomToScreenEdge:(NSUInteger)edge {
|
||||
if (self.parentWindow) {
|
||||
[self.parentWindow _zoomToScreenEdge:edge];
|
||||
@@ -248,6 +260,7 @@ - (void)_zoomToScreenEdge:(NSUInteger)edge {
|
||||
@@ -248,6 +273,7 @@ - (void)_zoomToScreenEdge:(NSUInteger)edge {
|
||||
[super _zoomToScreenEdge:edge];
|
||||
}
|
||||
}
|
||||
@@ -596,7 +609,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
|
||||
// This override helps diagnose lifetime issues in crash stacktraces by
|
||||
// inserting a symbol on NativeWidgetMacNSWindow and should be kept even if it
|
||||
@@ -393,6 +406,8 @@ - (NSAccessibilityRole)accessibilityRole {
|
||||
@@ -393,6 +419,8 @@ - (NSAccessibilityRole)accessibilityRole {
|
||||
|
||||
// NSWindow overrides.
|
||||
|
||||
@@ -605,7 +618,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
+ (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
|
||||
if (windowStyle & NSWindowStyleMaskTitled) {
|
||||
if (Class customFrame = [NativeWidgetMacNSWindowTitledFrame class])
|
||||
@@ -404,6 +419,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
|
||||
@@ -404,6 +432,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
|
||||
return [super frameViewClassForStyleMask:windowStyle];
|
||||
}
|
||||
|
||||
@@ -614,7 +627,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
- (BOOL)_isTitleHidden {
|
||||
bool shouldShowWindowTitle = YES;
|
||||
if (_bridge)
|
||||
@@ -428,12 +445,14 @@ - (BOOL)_usesCustomDrawing {
|
||||
@@ -428,12 +458,14 @@ - (BOOL)_usesCustomDrawing {
|
||||
// if it were valid to set that style for windows, setting the window style
|
||||
// recalculates and re-caches a bunch of stuff, so a surgical override is the
|
||||
// cleanest approach.
|
||||
@@ -629,7 +642,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
|
||||
+ (void)_getExteriorResizeEdgeThicknesses:
|
||||
(NSEdgeAndCornerThicknesses*)outThicknesses
|
||||
@@ -687,9 +706,11 @@ - (id)archiver:(NSKeyedArchiver*)archiver willEncodeObject:(id)object {
|
||||
@@ -687,9 +719,11 @@ - (id)archiver:(NSKeyedArchiver*)archiver willEncodeObject:(id)object {
|
||||
}
|
||||
|
||||
- (void)saveRestorableState {
|
||||
@@ -641,7 +654,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
|
||||
// Certain conditions, such as in the Speedometer 3 benchmark, can trigger a
|
||||
// rapid succession of calls to saveRestorableState. If there's no pending
|
||||
@@ -756,6 +777,7 @@ - (void)reallySaveRestorableState {
|
||||
@@ -756,6 +790,7 @@ - (void)reallySaveRestorableState {
|
||||
// affects its restorable state changes.
|
||||
- (void)invalidateRestorableState {
|
||||
[super invalidateRestorableState];
|
||||
@@ -649,7 +662,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
if ([self _isConsideredOpenForPersistentState]) {
|
||||
if (_willUpdateRestorableState)
|
||||
return;
|
||||
@@ -768,6 +790,7 @@ - (void)invalidateRestorableState {
|
||||
@@ -768,6 +803,7 @@ - (void)invalidateRestorableState {
|
||||
_willUpdateRestorableState = NO;
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
}
|
||||
@@ -657,7 +670,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
}
|
||||
|
||||
// On newer SDKs, _canMiniaturize respects NSWindowStyleMaskMiniaturizable in
|
||||
@@ -932,6 +955,7 @@ - (void)maybeRemoveTreeFromOrderingGroups {
|
||||
@@ -932,6 +968,7 @@ - (void)maybeRemoveTreeFromOrderingGroups {
|
||||
// Since _removeFromGroups: is not documented it could go away in newer
|
||||
// versions of macOS. If the selector does not exist, DumpWithoutCrashing() so
|
||||
// we hear about the change.
|
||||
@@ -665,7 +678,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
if (![NSWindow instancesRespondToSelector:@selector(_removeFromGroups:)]) {
|
||||
base::debug::DumpWithoutCrashing();
|
||||
return;
|
||||
@@ -949,6 +973,7 @@ - (void)maybeRemoveTreeFromOrderingGroups {
|
||||
@@ -949,6 +986,7 @@ - (void)maybeRemoveTreeFromOrderingGroups {
|
||||
[currentWindow _removeFromGroups:child];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2065,6 +2065,10 @@ void WebContents::ReadyToCommitNavigation(
|
||||
// Only focus for top-level contents.
|
||||
if (type_ != Type::kBrowserWindow)
|
||||
return;
|
||||
// Don't focus if focusOnNavigation is disabled.
|
||||
auto* prefs = WebContentsPreferences::From(web_contents());
|
||||
if (prefs && !prefs->ShouldFocusOnNavigation())
|
||||
return;
|
||||
web_contents()->SetInitialFocus();
|
||||
}
|
||||
|
||||
@@ -4656,6 +4660,19 @@ gin_helper::Handle<WebContents> WebContents::CreateFromWebPreferences(
|
||||
existing_preferences->SetFromDictionary(web_preferences_dict);
|
||||
web_contents->SetBackgroundColor(
|
||||
existing_preferences->GetBackgroundColor());
|
||||
|
||||
double zoom_factor;
|
||||
if (web_preferences.Get(options::kZoomFactor, &zoom_factor)) {
|
||||
auto* zoom_controller = WebContentsZoomController::FromWebContents(
|
||||
web_contents->web_contents());
|
||||
if (zoom_controller) {
|
||||
zoom_controller->SetDefaultZoomFactor(zoom_factor);
|
||||
// Also set the current zoom level immediately, since the page
|
||||
// has already navigated by the time we wrap the webContents.
|
||||
zoom_controller->SetZoomLevel(
|
||||
blink::ZoomFactorToZoomLevel(zoom_factor));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Create one if not.
|
||||
|
||||
@@ -83,8 +83,16 @@ void WebContentsView::ApplyBorderRadius() {
|
||||
|
||||
int WebContentsView::NonClientHitTest(const gfx::Point& point) {
|
||||
if (api_web_contents_) {
|
||||
// Convert the point to the contents view's coordinate space rather than
|
||||
// the InspectableWebContentsView's coordinate space, because the draggable
|
||||
// region is relative to the web content area. When DevTools is docked
|
||||
// (e.g. to the left), the contents view is offset within the parent,
|
||||
// so we need to account for that offset.
|
||||
auto* inspectable_view =
|
||||
api_web_contents_->inspectable_web_contents()->GetView();
|
||||
auto* contents_view = inspectable_view->GetContentsView();
|
||||
gfx::Point local_point(point);
|
||||
views::View::ConvertPointFromWidget(view(), &local_point);
|
||||
views::View::ConvertPointFromWidget(contents_view, &local_point);
|
||||
SkRegion* region = api_web_contents_->draggable_region();
|
||||
if (region && region->contains(local_point.x(), local_point.y()))
|
||||
return HTCAPTION;
|
||||
|
||||
@@ -258,7 +258,7 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
|
||||
// Empties the source menu items to the destination.
|
||||
- (void)moveMenuItems:(NSMenu*)source to:(NSMenu*)destination {
|
||||
const NSInteger count = [source numberOfItems];
|
||||
const NSInteger count = source.numberOfItems;
|
||||
for (NSInteger index = 0; index < count; index++) {
|
||||
NSMenuItem* removedItem = [source itemAtIndex:0];
|
||||
[source removeItemAtIndex:0];
|
||||
@@ -269,25 +269,25 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
// Replaces the item's submenu instance with the singleton recent documents
|
||||
// menu. Previously replaced menu items will be recovered.
|
||||
- (void)replaceSubmenuShowingRecentDocuments:(NSMenuItem*)item {
|
||||
NSMenu* recentDocumentsMenu = [recentDocumentsMenuItem_ submenu];
|
||||
NSMenu* recentDocumentsMenu = recentDocumentsMenuItem_.submenu;
|
||||
|
||||
// Remove menu items in recent documents back to swap menu
|
||||
[self moveMenuItems:recentDocumentsMenu to:recentDocumentsMenuSwap_];
|
||||
// Swap back the submenu
|
||||
[recentDocumentsMenuItem_ setSubmenu:recentDocumentsMenuSwap_];
|
||||
recentDocumentsMenuItem_.submenu = recentDocumentsMenuSwap_;
|
||||
|
||||
// Retain the item's submenu for a future recovery
|
||||
recentDocumentsMenuSwap_ = [item submenu];
|
||||
recentDocumentsMenuSwap_ = item.submenu;
|
||||
|
||||
// Repopulate with items from the submenu to be replaced
|
||||
[self moveMenuItems:recentDocumentsMenuSwap_ to:recentDocumentsMenu];
|
||||
// Update the submenu's title
|
||||
[recentDocumentsMenu setTitle:[recentDocumentsMenuSwap_ title]];
|
||||
recentDocumentsMenu.title = recentDocumentsMenuSwap_.title;
|
||||
// Replace submenu
|
||||
[item setSubmenu:recentDocumentsMenu];
|
||||
item.submenu = recentDocumentsMenu;
|
||||
|
||||
DCHECK_EQ([item action], @selector(submenuAction:));
|
||||
DCHECK_EQ([item target], recentDocumentsMenu);
|
||||
DCHECK_EQ(item.action, @selector(submenuAction:));
|
||||
DCHECK_EQ(item.target, recentDocumentsMenu);
|
||||
|
||||
// Remember the new menu item that carries the recent documents menu
|
||||
recentDocumentsMenuItem_ = item;
|
||||
@@ -303,7 +303,7 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
NSArray* services = [NSSharingService sharingServicesForItems:items];
|
||||
for (NSSharingService* service in services)
|
||||
[menu addItem:[self menuItemForService:service withItems:items]];
|
||||
[menu setDelegate:self];
|
||||
menu.delegate = self;
|
||||
return menu;
|
||||
}
|
||||
|
||||
@@ -313,9 +313,9 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:service.menuItemTitle
|
||||
action:@selector(performShare:)
|
||||
keyEquivalent:@""];
|
||||
[item setTarget:self];
|
||||
[item setImage:service.image];
|
||||
[item setRepresentedObject:@{@"service" : service, @"items" : items}];
|
||||
item.target = self;
|
||||
item.image = service.image;
|
||||
item.representedObject = @{@"service" : service, @"items" : items};
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -398,7 +398,7 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
submenu.delegate = self;
|
||||
|
||||
// Set submenu's role.
|
||||
if ((role == u"window" || role == u"windowmenu") && [submenu numberOfItems])
|
||||
if ((role == u"window" || role == u"windowmenu") && submenu.numberOfItems)
|
||||
[NSApp setWindowsMenu:submenu];
|
||||
else if (role == u"help")
|
||||
[NSApp setHelpMenu:submenu];
|
||||
@@ -463,6 +463,14 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
// Called by AppKit before displaying a menu and when a key equivalent is
|
||||
// pressed. This ensures menu item states (enabled, checked, hidden) are
|
||||
// refreshed from the model even when the menu is closed, which is necessary
|
||||
// since we set autoenablesItems = NO.
|
||||
- (void)menuNeedsUpdate:(NSMenu*)menu {
|
||||
[self refreshMenuTree:menu];
|
||||
}
|
||||
|
||||
- (void)applyStateToMenuItem:(NSMenuItem*)item {
|
||||
id represented = item.representedObject;
|
||||
if (!represented)
|
||||
@@ -487,7 +495,7 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
// When the menu is closed, we need to allow shortcuts to be triggered even
|
||||
// if the menu item is disabled. So we only disable the menu item when the
|
||||
// menu is open. This matches behavior of |validateUserInterfaceItem|.
|
||||
item.enabled = model->IsEnabledAt(index) || !isMenuOpen_;
|
||||
item.enabled = model->IsEnabledAt(index);
|
||||
item.hidden = !model->IsVisibleAt(index);
|
||||
item.state = model->IsItemCheckedAt(index) ? NSControlStateValueOn
|
||||
: NSControlStateValueOff;
|
||||
@@ -566,7 +574,6 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
[menu removeItem:item];
|
||||
}
|
||||
|
||||
[self refreshMenuTree:menu];
|
||||
if (model_)
|
||||
model_->MenuWillShow();
|
||||
}
|
||||
@@ -577,7 +584,6 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
return;
|
||||
|
||||
isMenuOpen_ = NO;
|
||||
[self refreshMenuTree:menu];
|
||||
|
||||
// There are two scenarios where we should emit menu-did-close:
|
||||
// 1. It's a popup and the top level menu is closed.
|
||||
|
||||
@@ -82,11 +82,13 @@ GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) {
|
||||
constexpr GdkColorspace kColorspace = GDK_COLORSPACE_RGB;
|
||||
constexpr gboolean kHasAlpha = true;
|
||||
constexpr int kBitsPerSample = 8;
|
||||
return gdk_pixbuf_new_from_bytes(
|
||||
g_bytes_new(std::data(bytes), std::size(bytes)), kColorspace, kHasAlpha,
|
||||
kBitsPerSample, width, height,
|
||||
GBytes* gbytes = g_bytes_new(std::data(bytes), std::size(bytes));
|
||||
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_bytes(
|
||||
gbytes, kColorspace, kHasAlpha, kBitsPerSample, width, height,
|
||||
gdk_pixbuf_calculate_rowstride(kColorspace, kHasAlpha, kBitsPerSample,
|
||||
width, height));
|
||||
g_bytes_unref(gbytes);
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
} // namespace gtk_util
|
||||
|
||||
@@ -62,9 +62,9 @@ class InspectableWebContentsView : public views::View {
|
||||
// views::View:
|
||||
void Layout(PassKey) override;
|
||||
|
||||
private:
|
||||
views::View* GetContentsView() const;
|
||||
|
||||
private:
|
||||
// Owns us.
|
||||
raw_ptr<InspectableWebContents> inspectable_web_contents_;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "components/input/native_web_keyboard_event.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "shell/browser/ui/views/menu_bar.h"
|
||||
#include "ui/events/keycodes/dom/keycode_converter.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
|
||||
namespace electron {
|
||||
@@ -21,9 +22,21 @@ bool IsAltKey(const input::NativeWebKeyboardEvent& event) {
|
||||
|
||||
bool IsAltModifier(const input::NativeWebKeyboardEvent& event) {
|
||||
using Mods = input::NativeWebKeyboardEvent::Modifiers;
|
||||
|
||||
// AltGraph (AltGr) should not be treated as a single Alt keypress for
|
||||
// menu-bar toggling.
|
||||
if (event.windows_key_code == ui::VKEY_ALTGR ||
|
||||
ui::KeycodeConverter::DomKeyToKeyString(event.dom_key) == "AltGraph") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (event.GetModifiers() & Mods::kKeyModifiers) == Mods::kAltKey;
|
||||
}
|
||||
|
||||
bool IsSingleAltKey(const input::NativeWebKeyboardEvent& event) {
|
||||
return IsAltKey(event) && IsAltModifier(event);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RootView::RootView(NativeWindow* window)
|
||||
@@ -98,7 +111,7 @@ void RootView::HandleKeyEvent(const input::NativeWebKeyboardEvent& event) {
|
||||
return;
|
||||
|
||||
// Show accelerator when "Alt" is pressed.
|
||||
if (menu_bar_visible_ && IsAltKey(event))
|
||||
if (menu_bar_visible_ && IsSingleAltKey(event))
|
||||
menu_bar_->SetAcceleratorVisibility(
|
||||
event.GetType() == blink::WebInputEvent::Type::kRawKeyDown);
|
||||
|
||||
@@ -121,11 +134,11 @@ void RootView::HandleKeyEvent(const input::NativeWebKeyboardEvent& event) {
|
||||
|
||||
// Toggle the menu bar only when a single Alt is released.
|
||||
if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown &&
|
||||
IsAltKey(event)) {
|
||||
IsSingleAltKey(event)) {
|
||||
// When a single Alt is pressed:
|
||||
menu_bar_alt_pressed_ = true;
|
||||
} else if (event.GetType() == blink::WebInputEvent::Type::kKeyUp &&
|
||||
IsAltKey(event) && menu_bar_alt_pressed_) {
|
||||
IsSingleAltKey(event) && menu_bar_alt_pressed_) {
|
||||
// When a single Alt is released right after a Alt is pressed:
|
||||
menu_bar_alt_pressed_ = false;
|
||||
if (menu_bar_autohide_)
|
||||
|
||||
@@ -197,6 +197,14 @@ void ElectronDesktopWindowTreeHostWin::SetAllowScreenshots(bool allow) {
|
||||
UpdateAllowScreenshots();
|
||||
}
|
||||
|
||||
// Refs https://chromium-review.googlesource.com/c/chromium/src/+/7095963
|
||||
// Chromium's fullscreen handler conflicts with ours and results in incorrect
|
||||
// restoration.
|
||||
void ElectronDesktopWindowTreeHostWin::Restore() {
|
||||
::SendMessage(GetAcceleratedWidget(), WM_SYSCOMMAND,
|
||||
static_cast<WPARAM>(SC_RESTORE), 0);
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostWin::UpdateAllowScreenshots() {
|
||||
bool allowed = views::DesktopWindowTreeHostWin::AreScreenshotsAllowed();
|
||||
if (allowed == allow_screenshots_)
|
||||
|
||||
@@ -50,6 +50,7 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin,
|
||||
LRESULT* result) override;
|
||||
void HandleVisibilityChanged(bool visible) override;
|
||||
void SetAllowScreenshots(bool allow) override;
|
||||
void Restore() override;
|
||||
|
||||
// ui::NativeThemeObserver:
|
||||
void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
|
||||
|
||||
@@ -149,6 +149,7 @@ void WebContentsPreferences::Clear() {
|
||||
preload_path_ = std::nullopt;
|
||||
v8_cache_options_ = blink::mojom::V8CacheOptions::kDefault;
|
||||
deprecated_paste_enabled_ = false;
|
||||
focus_on_navigation_ = true;
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
scroll_bounce_ = false;
|
||||
@@ -249,6 +250,8 @@ void WebContentsPreferences::SetFromDictionary(
|
||||
web_preferences.Get(options::kEnableDeprecatedPaste,
|
||||
&deprecated_paste_enabled_);
|
||||
|
||||
web_preferences.Get(options::kFocusOnNavigation, &focus_on_navigation_);
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
web_preferences.Get(options::kScrollBounce, &scroll_bounce_);
|
||||
#endif
|
||||
|
||||
@@ -78,6 +78,7 @@ class WebContentsPreferences
|
||||
bool ShouldDisablePopups() const { return disable_popups_; }
|
||||
bool IsWebSecurityEnabled() const { return web_security_; }
|
||||
std::optional<base::FilePath> GetPreloadPath() const { return preload_path_; }
|
||||
bool ShouldFocusOnNavigation() const { return focus_on_navigation_; }
|
||||
bool IsSandboxed() const;
|
||||
|
||||
private:
|
||||
@@ -134,6 +135,7 @@ class WebContentsPreferences
|
||||
std::optional<base::FilePath> preload_path_;
|
||||
blink::mojom::V8CacheOptions v8_cache_options_;
|
||||
bool deprecated_paste_enabled_ = false;
|
||||
bool focus_on_navigation_;
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
bool scroll_bounce_;
|
||||
|
||||
@@ -222,6 +222,9 @@ inline constexpr std::string_view kSpellcheck = "spellcheck";
|
||||
inline constexpr std::string_view kEnableDeprecatedPaste =
|
||||
"enableDeprecatedPaste";
|
||||
|
||||
// Whether to focus the webContents on navigation.
|
||||
inline constexpr std::string_view kFocusOnNavigation = "focusOnNavigation";
|
||||
|
||||
inline constexpr std::string_view kModal = "modal";
|
||||
|
||||
} // namespace options
|
||||
|
||||
@@ -34,7 +34,9 @@
|
||||
#include "dbus/bus.h"
|
||||
#include "dbus/message.h"
|
||||
#include "dbus/object_proxy.h"
|
||||
#include "ui/gtk/gtk_compat.h" // nogncheck
|
||||
|
||||
#include "electron/electron_gtk_stubs.h"
|
||||
#include "shell/common/platform_util_internal.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
@@ -405,6 +407,16 @@ bool PlatformTrashItem(const base::FilePath& full_path, std::string* error) {
|
||||
} // namespace internal
|
||||
|
||||
void Beep() {
|
||||
// `gdk_display_beep` is actually stubbed out, and the function pointer the
|
||||
// stub uses may be nullptr. We need to initialize the stub here to ensure
|
||||
// that is not the case so that we can avoid a crash.
|
||||
// TODO: move this elsewhere if / when we start using stubs for more
|
||||
// GDK functions than just `gdk_display_beep`.
|
||||
if (!electron::IsElectron_gdkInitialized()) {
|
||||
electron::InitializeElectron_gdk(gtk::GetLibGdk());
|
||||
CHECK(electron::IsElectron_gdkInitialized())
|
||||
<< "Failed to initialize libgdk";
|
||||
}
|
||||
auto* display = gdk_display_get_default();
|
||||
if (!display)
|
||||
return;
|
||||
|
||||
@@ -3983,6 +3983,28 @@ describe('BrowserWindow module', () => {
|
||||
expect(webPreferences!.contextIsolation).to.equal(false);
|
||||
});
|
||||
|
||||
it('should apply zoomFactor from setWindowOpenHandler overrideBrowserWindowOptions', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
sandbox: true
|
||||
}
|
||||
});
|
||||
|
||||
w.webContents.setWindowOpenHandler(() => ({
|
||||
action: 'allow',
|
||||
overrideBrowserWindowOptions: {
|
||||
webPreferences: {
|
||||
zoomFactor: 2.0
|
||||
}
|
||||
}
|
||||
}));
|
||||
w.loadFile(path.join(fixtures, 'api', 'new-window.html'));
|
||||
const [childWindow] = await once(w.webContents, 'did-create-window') as [BrowserWindow, any];
|
||||
await once(childWindow.webContents, 'did-finish-load');
|
||||
expect(childWindow.webContents.getZoomFactor()).to.be.closeTo(2.0, 0.1);
|
||||
});
|
||||
|
||||
it('should set ipc event sender correctly', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
@@ -4888,6 +4910,27 @@ describe('BrowserWindow module', () => {
|
||||
await restore;
|
||||
expect(w.isMaximized()).to.equal(true);
|
||||
});
|
||||
|
||||
ifit(process.platform !== 'linux')('should not break fullscreen state', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.show();
|
||||
|
||||
const enterFS = once(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
await enterFS;
|
||||
expect(w.isFullScreen()).to.be.true('not fullscreen');
|
||||
|
||||
w.restore();
|
||||
await setTimeout(1000);
|
||||
|
||||
expect(w.isFullScreen()).to.be.true('not fullscreen after restore');
|
||||
expect(w.isMinimized()).to.be.false('should not be minimized');
|
||||
|
||||
// Clean up fullscreen state.
|
||||
const leaveFS = once(w, 'leave-full-screen');
|
||||
w.setFullScreen(false);
|
||||
await leaveFS;
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(dsanders11): Enable once maximize event works on Linux again on CI
|
||||
@@ -5888,6 +5931,23 @@ describe('BrowserWindow module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'linux')('menu bar AltGr behavior', () => {
|
||||
it('does not toggle auto-hide menu bar visibility', async () => {
|
||||
const w = new BrowserWindow({ show: false, autoHideMenuBar: true });
|
||||
w.setMenuBarVisibility(false);
|
||||
expect(w.isMenuBarVisible()).to.be.false('isMenuBarVisible');
|
||||
|
||||
w.show();
|
||||
await once(w, 'show');
|
||||
w.webContents.focus();
|
||||
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'AltGr' });
|
||||
w.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'AltGr' });
|
||||
await setTimeout();
|
||||
|
||||
expect(w.isMenuBarVisible()).to.be.false('isMenuBarVisible');
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform !== 'darwin')('when fullscreen state is changed', () => {
|
||||
it('correctly remembers state prior to fullscreen change', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
|
||||
@@ -1610,6 +1610,35 @@ describe('webContents module', () => {
|
||||
await expect(blurPromise).to.eventually.be.fulfilled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('focusOnNavigation webPreference', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
it('focuses the webContents on navigation by default', async () => {
|
||||
const w = new BrowserWindow({ show: true });
|
||||
await once(w, 'focus');
|
||||
await w.loadURL('about:blank');
|
||||
await moveFocusToDevTools(w);
|
||||
expect(w.webContents.isFocused()).to.be.false();
|
||||
await w.loadURL('data:text/html,<body>test</body>');
|
||||
expect(w.webContents.isFocused()).to.be.true();
|
||||
});
|
||||
|
||||
it('does not focus the webContents on navigation when focusOnNavigation is false', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: true,
|
||||
webPreferences: {
|
||||
focusOnNavigation: false
|
||||
}
|
||||
});
|
||||
await once(w, 'focus');
|
||||
await w.loadURL('about:blank');
|
||||
await moveFocusToDevTools(w);
|
||||
expect(w.webContents.isFocused()).to.be.false();
|
||||
await w.loadURL('data:text/html,<body>test</body>');
|
||||
expect(w.webContents.isFocused()).to.be.false();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOSProcessId()', () => {
|
||||
|
||||
@@ -398,4 +398,38 @@ describe('WebContentsView', () => {
|
||||
v.setBorderRadius(100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('focusOnNavigation webPreference', () => {
|
||||
it('focuses the webContents on navigation by default', async () => {
|
||||
const w = new BrowserWindow();
|
||||
await once(w, 'focus');
|
||||
const v = new WebContentsView();
|
||||
w.setContentView(v);
|
||||
await v.webContents.loadURL('about:blank');
|
||||
const devToolsFocused = once(v.webContents, 'devtools-focused');
|
||||
v.webContents.openDevTools({ mode: 'right' });
|
||||
await devToolsFocused;
|
||||
expect(v.webContents.isFocused()).to.be.false();
|
||||
await v.webContents.loadURL('data:text/html,<body>test</body>');
|
||||
expect(v.webContents.isFocused()).to.be.true();
|
||||
});
|
||||
|
||||
it('does not focus the webContents on navigation when focusOnNavigation is false', async () => {
|
||||
const w = new BrowserWindow();
|
||||
await once(w, 'focus');
|
||||
const v = new WebContentsView({
|
||||
webPreferences: {
|
||||
focusOnNavigation: false
|
||||
}
|
||||
});
|
||||
w.setContentView(v);
|
||||
await v.webContents.loadURL('about:blank');
|
||||
const devToolsFocused = once(v.webContents, 'devtools-focused');
|
||||
v.webContents.openDevTools({ mode: 'right' });
|
||||
await devToolsFocused;
|
||||
expect(v.webContents.isFocused()).to.be.false();
|
||||
await v.webContents.loadURL('data:text/html,<body>test</body>');
|
||||
expect(v.webContents.isFocused()).to.be.false();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -189,6 +189,14 @@ app.whenReady().then(async () => {
|
||||
chai.config.truncateThreshold = 0;
|
||||
|
||||
const runner = mocha.run(cb);
|
||||
|
||||
const RETRY_EVENT = Mocha?.Runner?.constants?.EVENT_TEST_RETRY || 'retry';
|
||||
|
||||
runner.on(RETRY_EVENT, (test, err) => {
|
||||
console.log(`Failure in test: "${test.fullTitle()}"`);
|
||||
if (err?.stack) console.log(err.stack.split('\n').slice(0, 3).join('\n'));
|
||||
console.log(`Retrying test (${test.currentRetry() + 1}/${test.retries()})...`);
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.error('An error occurred while running the spec runner');
|
||||
console.error(err);
|
||||
|
||||
Reference in New Issue
Block a user