mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
26 Commits
v35.1.5
...
cherry-pic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d608364b81 | ||
|
|
5622e6252a | ||
|
|
ee54727582 | ||
|
|
f6f9e23fe2 | ||
|
|
6e56fed2cb | ||
|
|
3437927a78 | ||
|
|
1543089e2f | ||
|
|
dc9f6ecd54 | ||
|
|
6d07f541b1 | ||
|
|
0675686451 | ||
|
|
ee04cb9ebe | ||
|
|
592d0155f1 | ||
|
|
1d2107ebff | ||
|
|
3d535afc28 | ||
|
|
88a1448b31 | ||
|
|
b2e695c2e2 | ||
|
|
c438ed4790 | ||
|
|
b1fdf2a8c7 | ||
|
|
48a7d5d45b | ||
|
|
f2a27511b1 | ||
|
|
23035a587e | ||
|
|
f66c385080 | ||
|
|
323aca8822 | ||
|
|
abef5d6eed | ||
|
|
c6c4ace8ef | ||
|
|
f26645e9ab |
@@ -2,7 +2,7 @@ version: '3'
|
||||
|
||||
services:
|
||||
buildtools:
|
||||
image: ghcr.io/electron/devcontainer:9f11982e806f439d0a0a8ebbbf566cd5e0d9e952
|
||||
image: ghcr.io/electron/devcontainer:424eedbf277ad9749ffa9219068aa72ed4a5e373
|
||||
|
||||
volumes:
|
||||
- ..:/workspaces/gclient/src/electron:cached
|
||||
|
||||
8
.github/actions/checkout/action.yml
vendored
8
.github/actions/checkout/action.yml
vendored
@@ -179,3 +179,11 @@ runs:
|
||||
else
|
||||
echo "Cache key persisted in $final_cache_path"
|
||||
fi
|
||||
- name: Wait for active SSH sessions
|
||||
shell: bash
|
||||
if: always() && !cancelled()
|
||||
run: |
|
||||
while [ -f /var/.ssh-lock ]
|
||||
do
|
||||
sleep 60
|
||||
done
|
||||
|
||||
@@ -10,8 +10,10 @@ runs:
|
||||
git config --global core.filemode false
|
||||
git config --global core.autocrlf false
|
||||
git config --global branch.autosetuprebase always
|
||||
git config --global core.fscache true
|
||||
git config --global core.preloadindex true
|
||||
fi
|
||||
export BUILD_TOOLS_SHA=8246e57791b0af4ae5975eb96f09855f9269b1cd
|
||||
export BUILD_TOOLS_SHA=6e8526315ea3b4828882497e532b8340e64e053c
|
||||
npm i -g @electron/build-tools
|
||||
e auto-update disable
|
||||
e d auto-update disable
|
||||
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952'
|
||||
default: '424eedbf277ad9749ffa9219068aa72ed4a5e373'
|
||||
required: true
|
||||
skip-macos:
|
||||
type: boolean
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
id: set-output
|
||||
run: |
|
||||
if [ -z "${{ inputs.build-image-sha }}" ]; then
|
||||
echo "build-image-sha=9f11982e806f439d0a0a8ebbbf566cd5e0d9e952" >> "$GITHUB_OUTPUT"
|
||||
echo "build-image-sha=424eedbf277ad9749ffa9219068aa72ed4a5e373" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
2
.github/workflows/linux-publish.yml
vendored
2
.github/workflows/linux-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952'
|
||||
default: '424eedbf277ad9749ffa9219068aa72ed4a5e373'
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
required: false
|
||||
|
||||
2
.github/workflows/macos-publish.yml
vendored
2
.github/workflows/macos-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952'
|
||||
default: '424eedbf277ad9749ffa9219068aa72ed4a5e373'
|
||||
required: true
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
|
||||
@@ -134,6 +134,8 @@ jobs:
|
||||
git config --global core.filemode false
|
||||
git config --global core.autocrlf false
|
||||
git config --global branch.autosetuprebase always
|
||||
git config --global core.fscache true
|
||||
git config --global core.preloadindex true
|
||||
git clone --filter=tree:0 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
# Ensure depot_tools does not update.
|
||||
test -d depot_tools && cd depot_tools
|
||||
|
||||
2
.github/workflows/windows-publish.yml
vendored
2
.github/workflows/windows-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '9f11982e806f439d0a0a8ebbbf566cd5e0d9e952'
|
||||
default: '424eedbf277ad9749ffa9219068aa72ed4a5e373'
|
||||
required: true
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
|
||||
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'134.0.6998.179',
|
||||
'134.0.6998.205',
|
||||
'node_version':
|
||||
'v22.14.0',
|
||||
'nan_version':
|
||||
|
||||
@@ -20,45 +20,87 @@ document.
|
||||
|
||||
#### `commandLine.appendSwitch(switch[, value])`
|
||||
|
||||
* `switch` string - A command-line switch, without the leading `--`
|
||||
* `value` string (optional) - A value for the given switch
|
||||
* `switch` string - A command-line switch, without the leading `--`.
|
||||
* `value` string (optional) - A value for the given switch.
|
||||
|
||||
Append a switch (with optional `value`) to Chromium's command line.
|
||||
|
||||
**Note:** This will not affect `process.argv`. The intended usage of this function is to
|
||||
control Chromium's behavior.
|
||||
|
||||
```js
|
||||
const { app } = require('electron')
|
||||
|
||||
app.commandLine.appendSwitch('remote-debugging-port', '8315')
|
||||
```
|
||||
|
||||
#### `commandLine.appendArgument(value)`
|
||||
|
||||
* `value` string - The argument to append to the command line
|
||||
* `value` string - The argument to append to the command line.
|
||||
|
||||
Append an argument to Chromium's command line. The argument will be quoted
|
||||
correctly. Switches will precede arguments regardless of appending order.
|
||||
|
||||
If you're appending an argument like `--switch=value`, consider using `appendSwitch('switch', 'value')` instead.
|
||||
|
||||
```js
|
||||
const { app } = require('electron')
|
||||
|
||||
app.commandLine.appendArgument('--enable-experimental-web-platform-features')
|
||||
```
|
||||
|
||||
**Note:** This will not affect `process.argv`. The intended usage of this function is to
|
||||
control Chromium's behavior.
|
||||
|
||||
#### `commandLine.hasSwitch(switch)`
|
||||
|
||||
* `switch` string - A command-line switch
|
||||
* `switch` string - A command-line switch.
|
||||
|
||||
Returns `boolean` - Whether the command-line switch is present.
|
||||
|
||||
```js
|
||||
const { app } = require('electron')
|
||||
|
||||
app.commandLine.appendSwitch('remote-debugging-port', '8315')
|
||||
const hasPort = app.commandLine.hasSwitch('remote-debugging-port')
|
||||
console.log(hasPort) // true
|
||||
```
|
||||
|
||||
#### `commandLine.getSwitchValue(switch)`
|
||||
|
||||
* `switch` string - A command-line switch
|
||||
* `switch` string - A command-line switch.
|
||||
|
||||
Returns `string` - The command-line switch value.
|
||||
|
||||
This function is meant to obtain Chromium command line switches. It is not
|
||||
meant to be used for application-specific command line arguments. For the
|
||||
latter, please use `process.argv`.
|
||||
|
||||
```js
|
||||
const { app } = require('electron')
|
||||
|
||||
app.commandLine.appendSwitch('remote-debugging-port', '8315')
|
||||
const portValue = app.commandLine.getSwitchValue('remote-debugging-port')
|
||||
console.log(portValue) // '8315'
|
||||
```
|
||||
|
||||
**Note:** When the switch is not present or has no value, it returns empty string.
|
||||
|
||||
#### `commandLine.removeSwitch(switch)`
|
||||
|
||||
* `switch` string - A command-line switch
|
||||
* `switch` string - A command-line switch.
|
||||
|
||||
Removes the specified switch from Chromium's command line.
|
||||
|
||||
```js
|
||||
const { app } = require('electron')
|
||||
|
||||
app.commandLine.appendSwitch('remote-debugging-port', '8315')
|
||||
console.log(app.commandLine.hasSwitch('remote-debugging-port')) // true
|
||||
|
||||
app.commandLine.removeSwitch('remote-debugging-port')
|
||||
console.log(app.commandLine.hasSwitch('remote-debugging-port')) // false
|
||||
```
|
||||
|
||||
**Note:** This will not affect `process.argv`. The intended usage of this function is to
|
||||
control Chromium's behavior.
|
||||
|
||||
@@ -63,6 +63,14 @@ Your application should then always use `shouldUseDarkColors` to determine what
|
||||
A `boolean` for if the OS / Chromium currently has high-contrast mode enabled
|
||||
or is being instructed to show a high-contrast UI.
|
||||
|
||||
### `nativeTheme.shouldUseDarkColorsForSystemIntegratedUI` _macOS_ _Windows_ _Readonly_
|
||||
|
||||
A `boolean` property indicating whether or not the system theme has been set to dark or light.
|
||||
|
||||
On Windows this property distinguishes between system and app light/dark theme, returning
|
||||
`true` if the system theme is set to dark theme and `false` otherwise. On macOS the return
|
||||
value will be the same as `nativeTheme.shouldUseDarkColors`.
|
||||
|
||||
### `nativeTheme.shouldUseInvertedColorScheme` _macOS_ _Windows_ _Readonly_
|
||||
|
||||
A `boolean` for if the OS / Chromium currently has an inverted color scheme
|
||||
|
||||
@@ -2389,7 +2389,7 @@ A [`WebFrameMain`](web-frame-main.md) property that represents the top frame of
|
||||
|
||||
#### `contents.opener` _Readonly_
|
||||
|
||||
A [`WebFrameMain`](web-frame-main.md) property that represents the frame that opened this WebContents, either
|
||||
A [`WebFrameMain | null`](web-frame-main.md) property that represents the frame that opened this WebContents, either
|
||||
with open(), or by navigating a link with a target attribute.
|
||||
|
||||
[keyboardevent]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
|
||||
|
||||
@@ -14,6 +14,14 @@ This document uses the following convention to categorize breaking changes:
|
||||
|
||||
## Planned Breaking API Changes (35.0)
|
||||
|
||||
### Behavior Changes: `app.commandLine`
|
||||
|
||||
`app.commandLine` will convert upper-cases switches and arguments to lowercase.
|
||||
|
||||
`app.commandLine` was only meant to handle chromium switches (which aren't case-sensitive) and switches passed via `app.commandLine` will not be passed down to any of the child processes.
|
||||
|
||||
If you were using `app.commandLine` to control the behavior of the main process, you should do this via `process.argv`.
|
||||
|
||||
### Behavior Changed: Dialog API's `defaultPath` option on Linux
|
||||
|
||||
On Linux, the required portal version for file dialogs has been reverted
|
||||
|
||||
@@ -37,7 +37,6 @@ the illusion of a circular window.
|
||||
open on the user's system).
|
||||
* The window will not be transparent when DevTools is opened.
|
||||
* On _Windows_:
|
||||
* Transparent windows will not work when DWM is disabled.
|
||||
* Transparent windows can not be maximized using the Windows system menu or by double
|
||||
clicking the title bar. The reasoning behind this can be seen on
|
||||
PR [#28207](https://github.com/electron/electron/pull/28207).
|
||||
|
||||
@@ -54,7 +54,7 @@ BrowserWindow.prototype._init = function (this: BWT) {
|
||||
});
|
||||
this.on('close', (event) => {
|
||||
queueMicrotask(() => {
|
||||
if (!unresponsiveEvent && !event.defaultPrevented) {
|
||||
if (!unresponsiveEvent && !event?.defaultPrevented) {
|
||||
unresponsiveEvent = setTimeout(emitUnresponsiveEvent, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -78,7 +78,9 @@ export const roleList: Record<RoleId, Role> = {
|
||||
minimize: {
|
||||
label: 'Minimize',
|
||||
accelerator: 'CommandOrControl+M',
|
||||
windowMethod: w => w.minimize()
|
||||
windowMethod: w => {
|
||||
if (w.minimizable) w.minimize();
|
||||
}
|
||||
},
|
||||
paste: {
|
||||
label: 'Paste',
|
||||
|
||||
@@ -267,8 +267,17 @@ WebContents.prototype.print = function (options: ElectronInternal.WebContentsPri
|
||||
throw new TypeError('webContents.print(): Invalid print settings specified.');
|
||||
}
|
||||
|
||||
const pageSize = options.pageSize ?? 'A4';
|
||||
if (typeof pageSize === 'object') {
|
||||
const { pageSize } = options;
|
||||
if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) {
|
||||
const mediaSize = PDFPageSizes[pageSize];
|
||||
options.mediaSize = {
|
||||
...mediaSize,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: mediaSize.width_microns,
|
||||
imageable_area_top_microns: mediaSize.height_microns
|
||||
};
|
||||
} else if (typeof pageSize === 'object') {
|
||||
if (!pageSize.height || !pageSize.width) {
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
@@ -290,16 +299,7 @@ WebContents.prototype.print = function (options: ElectronInternal.WebContentsPri
|
||||
imageable_area_right_microns: width,
|
||||
imageable_area_top_microns: height
|
||||
};
|
||||
} else if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) {
|
||||
const mediaSize = PDFPageSizes[pageSize];
|
||||
options.mediaSize = {
|
||||
...mediaSize,
|
||||
imageable_area_left_microns: 0,
|
||||
imageable_area_bottom_microns: 0,
|
||||
imageable_area_right_microns: mediaSize.width_microns,
|
||||
imageable_area_top_microns: mediaSize.height_microns
|
||||
};
|
||||
} else {
|
||||
} else if (pageSize !== undefined) {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -218,7 +218,6 @@ if (packagePath) {
|
||||
} else {
|
||||
// Call appCodeLoaded before just for safety, it doesn't matter here as _load is synchronous
|
||||
appCodeLoaded!();
|
||||
process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false);
|
||||
Module._load(path.join(packagePath, mainStartupScript), Module, true);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -131,7 +131,7 @@ osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch
|
||||
feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch
|
||||
chore_partial_revert_of.patch
|
||||
fix_software_compositing_infinite_loop.patch
|
||||
fix_add_method_which_disables_headless_mode_on_native_widget.patch
|
||||
fix_adjust_headless_mode_handling_in_native_widget.patch
|
||||
refactor_unfilter_unresponsive_events.patch
|
||||
build_disable_thin_lto_mac.patch
|
||||
build_add_public_config_simdutf_config.patch
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Cezary Kulakowski <cezary@openfin.co>
|
||||
Date: Mon, 22 Jul 2024 16:23:13 +0200
|
||||
Subject: fix: add method which disables headless mode on native widget
|
||||
Subject: fix: adjust headless mode handling in native widget
|
||||
|
||||
We need this method as we create window in headless mode and we
|
||||
switch it back to normal mode only after inital paint is done in
|
||||
@@ -9,8 +9,27 @@ order to get some events like WebContents.beginFrameSubscription.
|
||||
If we don't set `is_headless_` to false then some child windows
|
||||
e.g. autofill popups will be created in headless mode leading to
|
||||
ui problems (like dissapearing popup during typing in html's
|
||||
input list.
|
||||
input list).
|
||||
|
||||
We also need to ensure that an initial paint is scheduled when
|
||||
the compositor is unsuspended in headles mode.
|
||||
|
||||
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
|
||||
index fed3d6a70139443d76ce6181df69bb490c46a081..06783f617ec63702a95d460fc6f14dd94f95917e 100644
|
||||
--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm
|
||||
+++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
|
||||
@@ -654,9 +654,10 @@ void HandleAccelerator(const ui::Accelerator& accelerator,
|
||||
// case it will never become visible but we want its compositor to produce
|
||||
// frames for screenshooting and screencasting.
|
||||
UpdateCompositorProperties();
|
||||
- layer()->SetVisible(is_visible_);
|
||||
+ layer()->SetVisible(is_visible_ || is_headless_mode_window_);
|
||||
if (is_visible_ || is_headless_mode_window_) {
|
||||
compositor_->Unsuspend();
|
||||
+ layer()->SchedulePaint(layer()->bounds());
|
||||
}
|
||||
|
||||
// Register the CGWindowID (used to identify this window for video capture)
|
||||
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
|
||||
index 6dae25a61bfaf26c59f044628629771c36be73f3..2e76f18cf48303462c7489a01d002a3531695b83 100644
|
||||
--- a/ui/views/widget/widget.h
|
||||
@@ -15,10 +15,10 @@ capturer was window or screen-specific, as the IDs remain valid for
|
||||
generic capturer as well.
|
||||
|
||||
diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc
|
||||
index 56e7c4058bbdd4d13c5ac90eb41972ad63da8abf..f5d78bb1c70f656c91596fd389aa0b70533b6148 100644
|
||||
index 60eabf1a69089049d4cddb81f87efca4f096a3b6..6618477648b7148ba66f5bb695be8eb6da045849 100644
|
||||
--- a/content/browser/media/capture/desktop_capture_device.cc
|
||||
+++ b/content/browser/media/capture/desktop_capture_device.cc
|
||||
@@ -899,8 +899,14 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
|
||||
@@ -811,8 +811,14 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
|
||||
|
||||
switch (source.type) {
|
||||
case DesktopMediaID::TYPE_SCREEN: {
|
||||
@@ -35,7 +35,7 @@ index 56e7c4058bbdd4d13c5ac90eb41972ad63da8abf..f5d78bb1c70f656c91596fd389aa0b70
|
||||
if (screen_capturer && screen_capturer->SelectSource(source.id)) {
|
||||
capturer = std::make_unique<webrtc::DesktopAndCursorComposer>(
|
||||
std::move(screen_capturer), options);
|
||||
@@ -913,8 +919,14 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
|
||||
@@ -825,8 +831,14 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
|
||||
}
|
||||
|
||||
case DesktopMediaID::TYPE_WINDOW: {
|
||||
|
||||
@@ -7,24 +7,6 @@ This allows embedders to tell Node.js what the first "real" file is when
|
||||
they use themselves as the entry point. We should try to upstream some form
|
||||
of this.
|
||||
|
||||
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
|
||||
index 9b5772fe9b8babbb892c7a5ec79258472da55a76..3568fd6ea0816f62d97d46f5d497bb1b23bf4e25 100644
|
||||
--- a/lib/internal/modules/cjs/loader.js
|
||||
+++ b/lib/internal/modules/cjs/loader.js
|
||||
@@ -1555,6 +1555,13 @@ Module.prototype._compile = function(content, filename, format) {
|
||||
this[kIsExecuting] = true;
|
||||
if (this[kIsMainSymbol] && getOptionValue('--inspect-brk')) {
|
||||
const { callAndPauseOnStart } = internalBinding('inspector');
|
||||
+ // process._firstFileName is used by Embedders to tell node what
|
||||
+ // the first "real" file is when they use themselves as the entry
|
||||
+ // point
|
||||
+ if (process._firstFileName) {
|
||||
+ resolvedArgv = process._firstFileName;
|
||||
+ delete process._firstFileName;
|
||||
+ }
|
||||
result = callAndPauseOnStart(compiledWrapper, thisValue, exports,
|
||||
require, module, filename, dirname,
|
||||
process, localGlobal, localBuffer);
|
||||
diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
|
||||
index d1c05d1717cdc825c4e48885c963c9ed65bcf51c..278665921c5160ff10b3178db27d4df319fab6b3 100644
|
||||
--- a/lib/internal/process/pre_execution.js
|
||||
|
||||
@@ -13,7 +13,7 @@ To fix this issue, provide the interface oom_error_callback to enable a
|
||||
custom oom error callback set from Electron.
|
||||
|
||||
diff --git a/src/api/environment.cc b/src/api/environment.cc
|
||||
index 32fc075e97eebca6c47e796ac5308915746ffa2a..e72bee385865c7d34e9eea6b90c6d911d592f8af 100644
|
||||
index fc9b056d2f7e25109100fbde5f3ab0aebc8c619a..9b155213ce301df7e396a4a113992499fc7e9910 100644
|
||||
--- a/src/api/environment.cc
|
||||
+++ b/src/api/environment.cc
|
||||
@@ -241,7 +241,10 @@ void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
|
||||
|
||||
@@ -43,20 +43,6 @@ index 59b5a16f1309a5e4055bccfdb7a529045ad30402..bfdaf6211466a01b64b7942f7b16c480
|
||||
|
||||
let filename = call.getFileName();
|
||||
const line = call.getLineNumber() - 1;
|
||||
diff --git a/src/api/environment.cc b/src/api/environment.cc
|
||||
index fc9b056d2f7e25109100fbde5f3ab0aebc8c619a..32fc075e97eebca6c47e796ac5308915746ffa2a 100644
|
||||
--- a/src/api/environment.cc
|
||||
+++ b/src/api/environment.cc
|
||||
@@ -247,6 +247,9 @@ void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
|
||||
auto* prepare_stack_trace_cb = s.prepare_stack_trace_callback ?
|
||||
s.prepare_stack_trace_callback : PrepareStackTraceCallback;
|
||||
isolate->SetPrepareStackTraceCallback(prepare_stack_trace_cb);
|
||||
+ } else {
|
||||
+ auto env = Environment::GetCurrent(isolate);
|
||||
+ env->set_prepare_stack_trace_callback(Local<Function>());
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/node_options.cc b/src/node_options.cc
|
||||
index 3608ab2b4aeb09e985ca98e23f2dff23567ade71..620776c06d835eb1bfeed060751c570e8d435b29 100644
|
||||
--- a/src/node_options.cc
|
||||
|
||||
@@ -278,7 +278,7 @@ const LINTERS = [{
|
||||
}, {
|
||||
key: 'md',
|
||||
roots: ['.'],
|
||||
ignoreRoots: ['node_modules', 'spec/node_modules'],
|
||||
ignoreRoots: ['.git', 'node_modules', 'spec/node_modules'],
|
||||
test: filename => filename.endsWith('.md'),
|
||||
run: async (opts, filenames) => {
|
||||
let errors = false;
|
||||
|
||||
@@ -47,7 +47,7 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||
// Copy the show setting to webContents, but only if we don't want to paint
|
||||
// when initially hidden
|
||||
bool paint_when_initially_hidden = true;
|
||||
options.Get("paintWhenInitiallyHidden", &paint_when_initially_hidden);
|
||||
options.Get(options::kPaintWhenInitiallyHidden, &paint_when_initially_hidden);
|
||||
if (!paint_when_initially_hidden) {
|
||||
bool show = true;
|
||||
options.Get(options::kShow, &show);
|
||||
@@ -219,14 +219,14 @@ void BrowserWindow::CloseImmediately() {
|
||||
}
|
||||
|
||||
void BrowserWindow::Focus() {
|
||||
if (api_web_contents_->IsOffScreen())
|
||||
if (api_web_contents_ && api_web_contents_->IsOffScreen())
|
||||
FocusOnWebView();
|
||||
else
|
||||
BaseWindow::Focus();
|
||||
}
|
||||
|
||||
void BrowserWindow::Blur() {
|
||||
if (api_web_contents_->IsOffScreen())
|
||||
if (api_web_contents_ && api_web_contents_->IsOffScreen())
|
||||
BlurWebView();
|
||||
else
|
||||
BaseWindow::Blur();
|
||||
|
||||
@@ -63,6 +63,10 @@ bool NativeTheme::ShouldUseHighContrastColors() {
|
||||
return ui_theme_->UserHasContrastPreference();
|
||||
}
|
||||
|
||||
bool NativeTheme::ShouldUseDarkColorsForSystemIntegratedUI() {
|
||||
return ui_theme_->ShouldUseDarkColorsForSystemIntegratedUI();
|
||||
}
|
||||
|
||||
bool NativeTheme::InForcedColorsMode() {
|
||||
return ui_theme_->InForcedColorsMode();
|
||||
}
|
||||
@@ -109,6 +113,8 @@ gin::ObjectTemplateBuilder NativeTheme::GetObjectTemplateBuilder(
|
||||
&NativeTheme::SetThemeSource)
|
||||
.SetProperty("shouldUseHighContrastColors",
|
||||
&NativeTheme::ShouldUseHighContrastColors)
|
||||
.SetProperty("shouldUseDarkColorsForSystemIntegratedUI",
|
||||
&NativeTheme::ShouldUseDarkColorsForSystemIntegratedUI)
|
||||
.SetProperty("shouldUseInvertedColorScheme",
|
||||
&NativeTheme::ShouldUseInvertedColorScheme)
|
||||
.SetProperty("inForcedColorsMode", &NativeTheme::InForcedColorsMode)
|
||||
|
||||
@@ -48,6 +48,7 @@ class NativeTheme final : public gin::Wrappable<NativeTheme>,
|
||||
ui::NativeTheme::ThemeSource GetThemeSource() const;
|
||||
bool ShouldUseDarkColors();
|
||||
bool ShouldUseHighContrastColors();
|
||||
bool ShouldUseDarkColorsForSystemIntegratedUI();
|
||||
bool ShouldUseInvertedColorScheme();
|
||||
bool InForcedColorsMode();
|
||||
bool GetPrefersReducedTransparency();
|
||||
|
||||
@@ -330,6 +330,9 @@ void UtilityProcessWrapper::PostMessage(gin::Arguments* args) {
|
||||
return;
|
||||
|
||||
blink::TransferableMessage transferable_message;
|
||||
gin_helper::ErrorThrower thrower(args->isolate());
|
||||
|
||||
// |message| is any value that can be serialized to StructuredClone.
|
||||
v8::Local<v8::Value> message_value;
|
||||
if (args->GetNext(&message_value)) {
|
||||
if (!electron::SerializeV8Value(args->isolate(), message_value,
|
||||
@@ -342,9 +345,25 @@ void UtilityProcessWrapper::PostMessage(gin::Arguments* args) {
|
||||
v8::Local<v8::Value> transferables;
|
||||
std::vector<gin::Handle<MessagePort>> wrapped_ports;
|
||||
if (args->GetNext(&transferables)) {
|
||||
std::vector<v8::Local<v8::Value>> wrapped_port_values;
|
||||
if (!gin::ConvertFromV8(args->isolate(), transferables,
|
||||
&wrapped_port_values)) {
|
||||
thrower.ThrowTypeError("transferables must be an array of MessagePorts");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < wrapped_port_values.size(); ++i) {
|
||||
if (!gin_helper::IsValidWrappable(wrapped_port_values[i],
|
||||
&MessagePort::kWrapperInfo)) {
|
||||
thrower.ThrowTypeError(
|
||||
base::StrCat({"Port at index ", base::NumberToString(i),
|
||||
" is not a valid port"}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gin::ConvertFromV8(args->isolate(), transferables, &wrapped_ports)) {
|
||||
gin_helper::ErrorThrower(args->isolate())
|
||||
.ThrowTypeError("Invalid value for transfer");
|
||||
thrower.ThrowTypeError("Passed an invalid MessagePort");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3035,9 +3035,8 @@ void OnGetDeviceNameToUse(base::WeakPtr<content::WebContents> web_contents,
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user has passed a deviceName use it, otherwise use default printer.
|
||||
// Use user-passed deviceName, otherwise default printer.
|
||||
print_settings.Set(printing::kSettingDeviceName, info.second);
|
||||
|
||||
if (!print_settings.FindInt(printing::kSettingDpiHorizontal)) {
|
||||
gfx::Size dpi = GetDefaultPrinterDPI(info.second);
|
||||
print_settings.Set(printing::kSettingDpiHorizontal, dpi.width());
|
||||
@@ -3096,6 +3095,17 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.IsEmptyObject()) {
|
||||
auto* print_view_manager =
|
||||
PrintViewManagerElectron::FromWebContents(web_contents());
|
||||
if (!print_view_manager)
|
||||
return;
|
||||
|
||||
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents());
|
||||
print_view_manager->PrintNow(rfh, std::move(settings), std::move(callback));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set optional silent printing.
|
||||
bool silent = false;
|
||||
options.Get("silent", &silent);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
#include "shell/common/gin_helper/event_emitter_caller.h"
|
||||
#include "shell/common/gin_helper/wrappable.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/v8_util.h"
|
||||
#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
|
||||
@@ -26,25 +27,6 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsValidWrappable(const v8::Local<v8::Value>& val) {
|
||||
if (!val->IsObject())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::Object> port = val.As<v8::Object>();
|
||||
|
||||
if (port->InternalFieldCount() != gin::kNumberOfInternalFields)
|
||||
return false;
|
||||
|
||||
const auto* info = static_cast<gin::WrapperInfo*>(
|
||||
port->GetAlignedPointerFromInternalField(gin::kWrapperInfoIndex));
|
||||
|
||||
return info && info->embedder == gin::kEmbedderNativeGin;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
gin::WrapperInfo MessagePort::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
|
||||
MessagePort::MessagePort() = default;
|
||||
@@ -77,16 +59,14 @@ void MessagePort::PostMessage(gin::Arguments* args) {
|
||||
blink::TransferableMessage transferable_message;
|
||||
gin_helper::ErrorThrower thrower(args->isolate());
|
||||
|
||||
// |message| is any value that can be serialized to StructuredClone.
|
||||
v8::Local<v8::Value> message_value;
|
||||
if (!args->GetNext(&message_value)) {
|
||||
thrower.ThrowTypeError("Expected at least one argument to postMessage");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!electron::SerializeV8Value(args->isolate(), message_value,
|
||||
&transferable_message)) {
|
||||
// SerializeV8Value sets an exception.
|
||||
return;
|
||||
if (args->GetNext(&message_value)) {
|
||||
if (!electron::SerializeV8Value(args->isolate(), message_value,
|
||||
&transferable_message)) {
|
||||
// SerializeV8Value sets an exception.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> transferables;
|
||||
@@ -100,7 +80,8 @@ void MessagePort::PostMessage(gin::Arguments* args) {
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < wrapped_port_values.size(); ++i) {
|
||||
if (!IsValidWrappable(wrapped_port_values[i])) {
|
||||
if (!gin_helper::IsValidWrappable(wrapped_port_values[i],
|
||||
&MessagePort::kWrapperInfo)) {
|
||||
thrower.ThrowTypeError("Port at index " + base::NumberToString(i) +
|
||||
" is not a valid port");
|
||||
return;
|
||||
|
||||
@@ -221,11 +221,10 @@ bool ElectronHidDelegate::IsServiceWorkerAllowedForOrigin(
|
||||
ElectronHidDelegate::ContextObservation*
|
||||
ElectronHidDelegate::GetContextObserver(
|
||||
content::BrowserContext* browser_context) {
|
||||
if (!observations_.contains(browser_context)) {
|
||||
observations_.emplace(browser_context, std::make_unique<ContextObservation>(
|
||||
this, browser_context));
|
||||
}
|
||||
return observations_[browser_context].get();
|
||||
auto& observation = observations_[browser_context];
|
||||
if (!observation)
|
||||
observation = std::make_unique<ContextObservation>(this, browser_context);
|
||||
return observation.get();
|
||||
}
|
||||
|
||||
HidChooserController* ElectronHidDelegate::ControllerForFrame(
|
||||
|
||||
@@ -94,8 +94,6 @@ gfx::Size GetExpandedWindowSize(const NativeWindow* window, gfx::Size size) {
|
||||
NativeWindow::NativeWindow(const gin_helper::Dictionary& options,
|
||||
NativeWindow* parent)
|
||||
: widget_(std::make_unique<views::Widget>()), parent_(parent) {
|
||||
++next_id_;
|
||||
|
||||
options.Get(options::kFrame, &has_frame_);
|
||||
options.Get(options::kTransparent, &transparent_);
|
||||
options.Get(options::kEnableLargerThanScreen, &enable_larger_than_screen_);
|
||||
@@ -816,9 +814,6 @@ void NativeWindow::HandlePendingFullscreenTransitions() {
|
||||
SetFullScreen(next_transition);
|
||||
}
|
||||
|
||||
// static
|
||||
int32_t NativeWindow::next_id_ = 0;
|
||||
|
||||
bool NativeWindow::IsTranslucent() const {
|
||||
// Transparent windows are translucent
|
||||
if (transparent()) {
|
||||
|
||||
@@ -407,7 +407,7 @@ class NativeWindow : public base::SupportsUserData,
|
||||
NativeWindow* parent() const { return parent_; }
|
||||
bool is_modal() const { return is_modal_; }
|
||||
|
||||
int32_t window_id() const { return next_id_; }
|
||||
int32_t window_id() const { return window_id_; }
|
||||
|
||||
void add_child_window(NativeWindow* child) {
|
||||
child_windows_.push_back(child);
|
||||
@@ -470,7 +470,8 @@ class NativeWindow : public base::SupportsUserData,
|
||||
private:
|
||||
std::unique_ptr<views::Widget> widget_;
|
||||
|
||||
static int32_t next_id_;
|
||||
static inline int32_t next_id_ = 0;
|
||||
const int32_t window_id_ = ++next_id_;
|
||||
|
||||
// The content view, weak ref.
|
||||
raw_ptr<views::View> content_view_ = nullptr;
|
||||
|
||||
@@ -154,6 +154,9 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
bool hiddenInMissionControl = false;
|
||||
options.Get(options::kHiddenInMissionControl, &hiddenInMissionControl);
|
||||
|
||||
bool paint_when_initially_hidden = true;
|
||||
options.Get(options::kPaintWhenInitiallyHidden, &paint_when_initially_hidden);
|
||||
|
||||
// The window without titlebar is treated the same with frameless window.
|
||||
if (title_bar_style_ != TitleBarStyle::kNormal)
|
||||
set_has_frame(false);
|
||||
@@ -194,8 +197,8 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
params.bounds = bounds;
|
||||
params.delegate = this;
|
||||
params.type = views::Widget::InitParams::TYPE_WINDOW;
|
||||
// Allow painting before shown, to be later disabled in ElectronNSWindow.
|
||||
params.headless_mode = true;
|
||||
// Possibly allow painting before shown - later disabled in ElectronNSWindow.
|
||||
params.headless_mode = paint_when_initially_hidden;
|
||||
if (IsTranslucent()) {
|
||||
params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
|
||||
if (ui::OzonePlatform::GetInstance()->IsWindowCompositingSupported()) {
|
||||
// Set the opaque region.
|
||||
std::vector<gfx::Rect> opaque_region;
|
||||
if (!IsShowingFrame()) {
|
||||
if (IsShowingFrame()) {
|
||||
// The opaque region is a list of rectangles that contain only fully
|
||||
// opaque pixels of the window. We need to convert the clipping
|
||||
// rounded-rect into this format.
|
||||
|
||||
@@ -44,14 +44,14 @@ ui::SelectFileDialog::FileTypeInfo GetFilterInfo(const Filters& filters) {
|
||||
ui::SelectFileDialog::FileTypeInfo file_type_info;
|
||||
|
||||
for (const auto& [name, extension_group] : filters) {
|
||||
file_type_info.extension_description_overrides.push_back(
|
||||
base::UTF8ToUTF16(name));
|
||||
|
||||
const bool has_all_files_wildcard = std::ranges::any_of(
|
||||
extension_group, [](const auto& ext) { return ext == "*"; });
|
||||
|
||||
if (has_all_files_wildcard) {
|
||||
file_type_info.include_all_files = true;
|
||||
} else {
|
||||
file_type_info.extension_description_overrides.push_back(
|
||||
base::UTF8ToUTF16(name));
|
||||
file_type_info.extensions.emplace_back(extension_group);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ class WebContentsPermissionHelper
|
||||
SERIAL,
|
||||
HID,
|
||||
USB,
|
||||
KEYBOARD_LOCK,
|
||||
FILE_SYSTEM,
|
||||
};
|
||||
|
||||
|
||||
@@ -4,50 +4,59 @@
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "services/network/public/cpp/network_switches.h"
|
||||
#include "shell/common/gin_converters/base_converter.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "third_party/abseil-cpp/absl/strings/ascii.h"
|
||||
|
||||
namespace {
|
||||
bool HasSwitch(const std::string& switch_string) {
|
||||
auto switch_str = base::ToLowerASCII(switch_string);
|
||||
|
||||
bool HasSwitch(const std::string& name) {
|
||||
return base::CommandLine::ForCurrentProcess()->HasSwitch(name);
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
return command_line->HasSwitch(switch_str);
|
||||
}
|
||||
|
||||
base::CommandLine::StringType GetSwitchValue(const std::string& name) {
|
||||
return base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(name);
|
||||
base::CommandLine::StringType GetSwitchValue(gin_helper::ErrorThrower thrower,
|
||||
const std::string& switch_string) {
|
||||
auto switch_str = base::ToLowerASCII(switch_string);
|
||||
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
return command_line->GetSwitchValueNative(switch_str);
|
||||
}
|
||||
|
||||
void AppendSwitch(const std::string& switch_string,
|
||||
gin_helper::Arguments* args) {
|
||||
auto switch_str = base::ToLowerASCII(switch_string);
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
|
||||
if (base::EndsWith(switch_string, "-path",
|
||||
base::CompareCase::INSENSITIVE_ASCII) ||
|
||||
switch_string == network::switches::kLogNetLog) {
|
||||
base::FilePath path;
|
||||
args->GetNext(&path);
|
||||
command_line->AppendSwitchPath(switch_string, path);
|
||||
command_line->AppendSwitchPath(switch_str, path);
|
||||
return;
|
||||
}
|
||||
|
||||
base::CommandLine::StringType value;
|
||||
if (args->GetNext(&value))
|
||||
command_line->AppendSwitchNative(switch_string, value);
|
||||
command_line->AppendSwitchNative(switch_str, value);
|
||||
else
|
||||
command_line->AppendSwitch(switch_string);
|
||||
command_line->AppendSwitch(switch_str);
|
||||
}
|
||||
|
||||
void RemoveSwitch(const std::string& switch_string) {
|
||||
auto switch_str = base::ToLowerASCII(switch_string);
|
||||
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
command_line->RemoveSwitch(switch_string);
|
||||
command_line->RemoveSwitch(switch_str);
|
||||
}
|
||||
|
||||
void AppendArg(const std::string& arg) {
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
|
||||
command_line->AppendArg(arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -183,6 +183,16 @@ class Dictionary : public gin::Dictionary {
|
||||
|
||||
bool IsEmpty() const { return isolate() == nullptr || GetHandle().IsEmpty(); }
|
||||
|
||||
bool IsEmptyObject() const {
|
||||
if (IsEmpty())
|
||||
return true;
|
||||
|
||||
v8::Local<v8::Context> context = isolate()->GetCurrentContext();
|
||||
v8::Local<v8::Array> props =
|
||||
GetHandle()->GetOwnPropertyNames(context).ToLocalChecked();
|
||||
return props->Length() == 0;
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> GetHandle() const {
|
||||
return gin::ConvertToV8(isolate(),
|
||||
*static_cast<const gin::Dictionary*>(this))
|
||||
|
||||
@@ -10,6 +10,23 @@
|
||||
|
||||
namespace gin_helper {
|
||||
|
||||
bool IsValidWrappable(const v8::Local<v8::Value>& val,
|
||||
const gin::WrapperInfo* wrapper_info) {
|
||||
if (!val->IsObject())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::Object> port = val.As<v8::Object>();
|
||||
if (port->InternalFieldCount() != gin::kNumberOfInternalFields)
|
||||
return false;
|
||||
|
||||
const gin::WrapperInfo* info = static_cast<gin::WrapperInfo*>(
|
||||
port->GetAlignedPointerFromInternalField(gin::kWrapperInfoIndex));
|
||||
if (info != wrapper_info)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WrappableBase::WrappableBase() = default;
|
||||
|
||||
WrappableBase::~WrappableBase() {
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
namespace gin_helper {
|
||||
|
||||
bool IsValidWrappable(const v8::Local<v8::Value>& obj,
|
||||
const gin::WrapperInfo* wrapper_info);
|
||||
|
||||
namespace internal {
|
||||
|
||||
void* FromV8Impl(v8::Isolate* isolate, v8::Local<v8::Value> val);
|
||||
|
||||
@@ -33,6 +33,10 @@ inline constexpr std::string_view kMaximizable = "maximizable";
|
||||
inline constexpr std::string_view kFullScreenable = "fullscreenable";
|
||||
inline constexpr std::string_view kClosable = "closable";
|
||||
|
||||
// Whether to paint when the window is initially hidden.
|
||||
inline constexpr std::string_view kPaintWhenInitiallyHidden =
|
||||
"paintWhenInitiallyHidden";
|
||||
|
||||
// whether to keep the window out of mission control
|
||||
inline constexpr std::string_view kHiddenInMissionControl =
|
||||
"hiddenInMissionControl";
|
||||
|
||||
@@ -36,7 +36,7 @@ class V8Serializer : public v8::ValueSerializer::Delegate {
|
||||
|
||||
bool Serialize(v8::Local<v8::Value> value, blink::CloneableMessage* out) {
|
||||
gin_helper::MicrotasksScope microtasks_scope{
|
||||
isolate_->GetCurrentContext(), false,
|
||||
isolate_->GetCurrentContext(), true,
|
||||
v8::MicrotasksScope::kDoNotRunMicrotasks};
|
||||
WriteBlinkEnvelope(19);
|
||||
|
||||
|
||||
@@ -4929,6 +4929,18 @@ describe('BrowserWindow module', () => {
|
||||
expect(w.getChildWindows().length).to.equal(0);
|
||||
});
|
||||
|
||||
it('can handle parent window close with focus or blur events', (done) => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
const c = new BrowserWindow({ show: false, parent: w });
|
||||
|
||||
c.on('closed', () => {
|
||||
w.focus();
|
||||
done();
|
||||
});
|
||||
|
||||
w.close();
|
||||
});
|
||||
|
||||
ifit(process.platform === 'darwin')('only shows the intended window when a child with siblings is shown', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
const childOne = new BrowserWindow({ show: false, parent: w });
|
||||
@@ -6462,6 +6474,31 @@ describe('BrowserWindow module', () => {
|
||||
w.loadFile(path.join(fixtures, 'pages', 'send-after-node.html'));
|
||||
});
|
||||
|
||||
// TODO(codebytere): fix on Windows and Linux too
|
||||
ifdescribe(process.platform === 'darwin')('window.webContents initial paint', () => {
|
||||
afterEach(closeAllWindows);
|
||||
it('paints when a window is initially hidden', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
|
||||
const entries = await w.webContents.executeJavaScript(`
|
||||
new Promise((resolve) => {
|
||||
const observer = new PerformanceObserver((performance) => {
|
||||
observer.disconnect();
|
||||
resolve(performance.getEntries());
|
||||
});
|
||||
observer.observe({ entryTypes: ['paint'] });
|
||||
});
|
||||
|
||||
const header = document.createElement('h1');
|
||||
header.innerText = 'Paint me!!';
|
||||
document.getElementById('div').appendChild(header);
|
||||
`);
|
||||
|
||||
expect(JSON.stringify(entries)).to.eq('{}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('window.webContents.focus()', () => {
|
||||
afterEach(closeAllWindows);
|
||||
it('focuses window', async () => {
|
||||
|
||||
@@ -236,6 +236,23 @@ describe('ipc module', () => {
|
||||
expect(ev.senderFrame.routingId).to.equal(w.webContents.mainFrame.routingId);
|
||||
});
|
||||
|
||||
it('throws when the transferable is invalid', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
w.loadURL('about:blank');
|
||||
const p = once(ipcMain, 'port');
|
||||
await w.webContents.executeJavaScript(`(${function () {
|
||||
try {
|
||||
const buffer = new ArrayBuffer(10);
|
||||
// @ts-expect-error
|
||||
require('electron').ipcRenderer.postMessage('port', '', [buffer]);
|
||||
} catch (e) {
|
||||
require('electron').ipcRenderer.postMessage('port', { error: (e as Error).message });
|
||||
}
|
||||
}})()`);
|
||||
const [, msg] = await p;
|
||||
expect(msg.error).to.eql('Invalid value for transfer');
|
||||
});
|
||||
|
||||
it('can communicate between main and renderer', async () => {
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
w.loadURL('about:blank');
|
||||
@@ -411,6 +428,20 @@ describe('ipc module', () => {
|
||||
expect(port2).not.to.be.null();
|
||||
});
|
||||
|
||||
it('should not throw when supported values are passed as message', () => {
|
||||
const { port1 } = new MessageChannelMain();
|
||||
|
||||
// @ts-expect-error - this shouldn't crash.
|
||||
expect(() => { port1.postMessage(); }).to.not.throw();
|
||||
|
||||
expect(() => { port1.postMessage(undefined); }).to.not.throw();
|
||||
expect(() => { port1.postMessage(42); }).to.not.throw();
|
||||
expect(() => { port1.postMessage(false); }).to.not.throw();
|
||||
expect(() => { port1.postMessage([]); }).to.not.throw();
|
||||
expect(() => { port1.postMessage('hello'); }).to.not.throw();
|
||||
expect(() => { port1.postMessage({ hello: 'goodbye' }); }).to.not.throw();
|
||||
});
|
||||
|
||||
it('throws an error when an invalid parameter is sent to postMessage', () => {
|
||||
const { port1 } = new MessageChannelMain();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { BrowserWindow, app, Menu, MenuItem, MenuItemConstructorOptions } from '
|
||||
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { ifdescribe } from './lib/spec-helpers';
|
||||
import { ifit, ifdescribe } from './lib/spec-helpers';
|
||||
import { closeAllWindows } from './lib/window-helpers';
|
||||
import { roleList, execute } from '../lib/browser/api/menu-item-roles';
|
||||
|
||||
@@ -205,6 +205,22 @@ describe('MenuItems', () => {
|
||||
const canExecute = execute(item.role as any, win, win.webContents);
|
||||
expect(canExecute).to.be.true('can execute');
|
||||
});
|
||||
|
||||
ifit(process.platform === 'win32')('does not execute minimize role when minimizable false', () => {
|
||||
const win = new BrowserWindow({ minimizable: false });
|
||||
const menu = Menu.buildFromTemplate([{
|
||||
label: 'text',
|
||||
role: 'minimize'
|
||||
}]);
|
||||
|
||||
Menu.setApplicationMenu(menu);
|
||||
menu._executeCommand({}, menu.items[0].commandId);
|
||||
expect(win.isMinimized()).to.equal(false);
|
||||
|
||||
win.setMinimizable(true);
|
||||
menu._executeCommand({}, menu.items[0].commandId);
|
||||
expect(win.isMinimized()).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('MenuItem command id', () => {
|
||||
|
||||
@@ -102,6 +102,12 @@ describe('nativeTheme module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('nativeTheme.shouldUseDarkColorsForSystemIntegratedUI', () => {
|
||||
it('returns a boolean', () => {
|
||||
expect(nativeTheme.shouldUseDarkColorsForSystemIntegratedUI).to.be.a('boolean');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nativeTheme.inForcedColorsMode', () => {
|
||||
it('returns a boolean', () => {
|
||||
expect(nativeTheme.inForcedColorsMode).to.be.a('boolean');
|
||||
|
||||
@@ -983,6 +983,17 @@ describe('node feature', () => {
|
||||
});
|
||||
});
|
||||
|
||||
itremote('handles assert module assertions as expected', () => {
|
||||
const assert = require('node:assert');
|
||||
try {
|
||||
assert.ok(false);
|
||||
expect.fail('assert.ok(false) should throw');
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
expect(err).to.be.instanceOf(assert.AssertionError);
|
||||
}
|
||||
});
|
||||
|
||||
it('Can find a module using a package.json main field', () => {
|
||||
const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')], { stdio: 'inherit' });
|
||||
expect(result.status).to.equal(0);
|
||||
|
||||
1
typings/internal-ambient.d.ts
vendored
1
typings/internal-ambient.d.ts
vendored
@@ -255,7 +255,6 @@ declare namespace NodeJS {
|
||||
once(event: 'document-end', listener: () => any): this;
|
||||
|
||||
// Additional properties
|
||||
_firstFileName?: string;
|
||||
_serviceStartupScript: string;
|
||||
_getOrCreateArchive?: (path: string) => NodeJS.AsarArchive | null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user