Compare commits

..

1 Commits

Author SHA1 Message Date
Shelley Vohr
d0b644e80f fix: use uppercase trace event phases for perfetto compatibility
Node.js's trace_events_async_hooks.js uses lowercase phase characters
('b'/'e' for NESTABLE_ASYNC_BEGIN/END) when calling V8's trace builtin.
In perfetto mode, V8's builtins-trace.cc only handles uppercase phases
('B'/'E'/'I'), causing a "Trace event phase must be a number" TypeError
that breaks utility process startup when contentTracing is active.

This was already fixed for lib/internal/http.js in the existing
build_enable_perfetto patch but trace_events_async_hooks.js was missed.

Also fixes the --trace-startup spec by removing embedded quotes from
the glob pattern argument that triggered a PERFETTO_CHECK assertion in
perfetto's NameMatchesPattern.
2026-03-12 15:46:48 +01:00
15 changed files with 36 additions and 338 deletions

View File

@@ -192,7 +192,6 @@ gh label list --repo electron/electron --search target/ --json name,color --jq '
```bash
npm run lint # Run all linters
npm run lint:clang-format # C++ formatting
npm run lint:api-history # Validate API history YAML blocks in docs
```
## Key Files

View File

@@ -1,58 +0,0 @@
# Electron Documentation Guide
## API History Migration
Guide: `docs/development/api-history-migration-guide.md`
Style rules: `docs/development/style-guide.md` (see "API History" section)
Schema: `docs/api-history.schema.json`
Lint: `npm run lint:api-history`
### Format
Place YAML history block directly after the Markdown header, before parameters:
````md
### `module.method(args)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/XXXXX
```
-->
* `arg` type - Description.
````
### Finding when an API was added
- `git log --all --reverse --oneline -S "methodName" -- docs/api/file.md` — find first commit adding a method name
- `git log --reverse -L :FunctionName:path/to/source.cc` — trace C++ implementation history
- `git log --grep="keyword" --oneline` — find merge commits referencing PRs
- `gh pr view <number> --repo electron/electron --json baseRefName` — verify PR targets main (not a backport)
- Always use the main-branch PR URL in history blocks, not backport PRs
### Cross-referencing breaking changes
- Search `docs/breaking-changes.md` for the API name to find deprecations/removals
- Use `git blame` on the breaking-changes entry to find the associated PR
- Add `breaking-changes-header` field using the heading ID from breaking-changes.md
### Placement rules
- Only add blocks to actual API entries (methods, events, properties with backtick signatures)
- Do NOT add blocks to section headers like `## Methods`, `### Instance Methods`, `## Events`
- Module-level blocks go after the `# moduleName` heading, before the module description quote
- For changes affecting multiple APIs, add a block under each affected top-level heading (see style guide "Change affecting multiple APIs")
### Key details
- `added` and `deprecated` arrays have `maxItems: 1`; `changes` can have multiple items
- `changes` entries require a `description` field; `added`/`deprecated` do not
- Wrap descriptions in double quotes to avoid YAML parsing issues with special characters
- Early Electron APIs (pre-2015) use merge-commit PRs (e.g., `Merge pull request #534`)
- Very early APIs (2013-2014, e.g., `ipcMain.on`, `ipcRenderer.send`) predate GitHub PRs — skip history blocks for these
- When multiple APIs were added in the same PR, they all reference the same PR URL
- Promisification PRs (e.g., #17355) count as `changes` entries with a description
- These PRs are breaking changes and should have their notes as "This method now returns a Promise instead of using a callback function."
- APIs that were deprecated and then removed from docs don't need history blocks (the removal is in `breaking-changes.md`)

View File

@@ -55,13 +55,6 @@ The `globalShortcut` module has the following methods:
### `globalShortcut.register(accelerator, callback)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/534
```
-->
* `accelerator` string - An [accelerator](../tutorial/keyboard-shortcuts.md#accelerators) shortcut.
* `callback` Function
@@ -84,13 +77,6 @@ the app has been authorized as a [trusted accessibility client](https://develope
### `globalShortcut.registerAll(accelerators, callback)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/15542
```
-->
* `accelerators` string[] - An array of [accelerator](../tutorial/keyboard-shortcuts.md#accelerators) shortcuts.
* `callback` Function
@@ -110,13 +96,6 @@ the app has been authorized as a [trusted accessibility client](https://develope
### `globalShortcut.isRegistered(accelerator)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/534
```
-->
* `accelerator` string - An [accelerator](../tutorial/keyboard-shortcuts.md#accelerators) shortcut.
Returns `boolean` - Whether this application has registered `accelerator`.
@@ -127,24 +106,10 @@ don't want applications to fight for global shortcuts.
### `globalShortcut.unregister(accelerator)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/534
```
-->
* `accelerator` string - An [accelerator](../tutorial/keyboard-shortcuts.md#accelerators) shortcut.
Unregisters the global shortcut of `accelerator`.
### `globalShortcut.unregisterAll()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/534
```
-->
Unregisters all of the global shortcuts.

View File

@@ -35,13 +35,6 @@ webContentsView.webContents.loadURL('https://electronjs.org')
## Class: ImageView extends `View`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/46760
```
-->
> A View that displays an image.
Process: [Main](../glossary.md#main-process)
@@ -56,13 +49,6 @@ Process: [Main](../glossary.md#main-process)
### `new ImageView()` _Experimental_
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/46760
```
-->
Creates an ImageView.
### Instance Methods
@@ -72,13 +58,6 @@ addition to those inherited from [View](view.md):
#### `image.setImage(image)` _Experimental_
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/46760
```
-->
* `image` NativeImage
Sets the image for this `ImageView`. Note that only image formats supported by

View File

@@ -1,12 +1,5 @@
# inAppPurchase
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
```
-->
> In-app purchases on Mac App Store.
Process: [Main](../glossary.md#main-process)
@@ -17,13 +10,6 @@ The `inAppPurchase` module emits the following events:
### Event: 'transactions-updated'
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
```
-->
Returns:
* `event` Event
@@ -37,19 +23,6 @@ The `inAppPurchase` module has the following methods:
### `inAppPurchase.purchaseProduct(productID[, opts])`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
changes:
- pr-url: https://github.com/electron/electron/pull/17355
description: "This method now returns a Promise instead of using a callback function."
breaking-changes-header: api-changed-callback-based-versions-of-promisified-apis
- pr-url: https://github.com/electron/electron/pull/35902
description: "Added `username` option to `opts` parameter."
```
-->
* `productID` string
* `opts` Integer | Object (optional) - If specified as an integer, defines the quantity.
* `quantity` Integer (optional) - The number of items the user wants to purchase.
@@ -61,17 +34,6 @@ You should listen for the `transactions-updated` event as soon as possible and c
### `inAppPurchase.getProducts(productIDs)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/12464
changes:
- pr-url: https://github.com/electron/electron/pull/17355
description: "This method now returns a Promise instead of using a callback function."
breaking-changes-header: api-changed-callback-based-versions-of-promisified-apis
```
-->
* `productIDs` string[] - The identifiers of the products to get.
Returns `Promise<Product[]>` - Resolves with an array of [`Product`](structures/product.md) objects.
@@ -80,59 +42,24 @@ Retrieves the product descriptions.
### `inAppPurchase.canMakePayments()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
```
-->
Returns `boolean` - whether a user can make a payment.
### `inAppPurchase.restoreCompletedTransactions()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/21461
```
-->
Restores finished transactions. This method can be called either to install purchases on additional devices, or to restore purchases for an application that the user deleted and reinstalled.
[The payment queue](https://developer.apple.com/documentation/storekit/skpaymentqueue?language=objc) delivers a new transaction for each previously completed transaction that can be restored. Each transaction includes a copy of the original transaction.
### `inAppPurchase.getReceiptURL()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/11292
```
-->
Returns `string` - the path to the receipt.
### `inAppPurchase.finishAllTransactions()`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/12464
```
-->
Completes all pending transactions.
### `inAppPurchase.finishTransactionByDate(date)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/12464
```
-->
* `date` string - The ISO formatted date of the transaction to finish.
Completes the pending transactions corresponding to the date.

View File

@@ -46,13 +46,6 @@ Listens to `channel`, when a new message arrives `listener` would be called with
### `ipcMain.off(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/44651
```
-->
* `channel` string
* `listener` Function
* `event` [IpcMainEvent][ipc-main-event]
@@ -96,13 +89,6 @@ Removes all listeners from the specified `channel`. Removes all listeners from a
### `ipcMain.handle(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/18449
```
-->
* `channel` string
* `listener` Function\<Promise\<any\> | any\>
* `event` [IpcMainInvokeEvent][ipc-main-invoke-event]
@@ -140,13 +126,6 @@ provided to the renderer process. Please refer to
### `ipcMain.handleOnce(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/18449
```
-->
* `channel` string
* `listener` Function\<Promise\<any\> | any\>
* `event` [IpcMainInvokeEvent][ipc-main-invoke-event]
@@ -157,13 +136,6 @@ Handles a single `invoke`able IPC message, then removes the listener. See
### `ipcMain.removeHandler(channel)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/18449
```
-->
* `channel` string
Removes any handler for `channel`, if present.

View File

@@ -59,13 +59,6 @@ for more info.
### `ipcRenderer.off(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/39816
```
-->
* `channel` string
* `listener` Function
* `event` [IpcRendererEvent][ipc-renderer-event]
@@ -86,13 +79,6 @@ only the next time a message is sent to `channel`, after which it is removed.
### `ipcRenderer.addListener(channel, listener)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/39816
```
-->
* `channel` string
* `listener` Function
* `event` [IpcRendererEvent][ipc-renderer-event]
@@ -143,13 +129,6 @@ If you want to receive a single response from the main process, like the result
### `ipcRenderer.invoke(channel, ...args)`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/18449
```
-->
* `channel` string
* `...args` any[]
@@ -230,13 +209,6 @@ and replies by setting `event.returnValue`.
### `ipcRenderer.postMessage(channel, message, [transfer])`
<!--
```YAML history
added:
- pr-url: https://github.com/electron/electron/pull/22404
```
-->
* `channel` string
* `message` any
* `transfer` MessagePort[] (optional)

View File

@@ -59,17 +59,6 @@ npm install electron --save-dev --ignore-scripts
npx install-electron --no
```
If you need to test changes across platforms or architectures, you should now use the
`ELECTRON_INSTALL_ARCH` and `ELECTRON_INSTALL_PLATFORM` environment variables.
```sh
# before: pass npm config flag on install command
npm install --platform=mas electron --save-dev
# after: add env var when you first run the Electron command
npm install electron --save-dev
ELECTRON_INSTALL_PLATFORM=mas npx electron . --no
```
### Removed: `quotas` object from `Session.clearStorageData(options)`
When calling `Session.clearStorageData(options)`, the `options.quotas` object is no longer supported because it has been

View File

@@ -51,40 +51,20 @@ any dependencies in your app will not be installed.
## Customization
If you want to change the architecture that is downloaded (e.g., `x64` on an
`arm64` machine), you can set the `ELECTRON_INSTALL_ARCH` environment variable:
`arm64` machine), you can use the `--arch` flag with npm install or set the
`npm_config_arch` environment variable:
```sh
# Inside an npm script or with npx
ELECTRON_INSTALL_ARCH=x64 electron .
```shell
npm install --arch=x64 electron
```
Supported architectures are a subset of Node.js [`process.arch`](https://nodejs.org/api/process.html#processarch)
values, and include:
* `x64` (Intel Mac and 64-bit Windows)
* `ia32` (32-bit Windows)
* `arm64` (Apple silicon, Windows on ARM, ARM64 Linux)
* `arm` (32-bit ARM)
In addition to changing the architecture, you can also specify the platform
(e.g., `win32`, `linux`, etc.) using the `--platform` flag:
```sh
# Inside an npm script or with npx
ELECTRON_INSTALL_PLATFORM=mas electron .
```shell
npm install --platform=win32 electron
```
Supported platforms are Node-like [platform strings](https://nodejs.org/api/process.html#processplatform):
* `darwin`
* `mas` ([Mac App Store](./mac-app-store-submission-guide.md))
* `win32`
* `linux`
> [!TIP]
> To see all available platform/architecture combinations for a particular release, see the artifacts
> on [Electron's GitHub Releases](https://github.com/electron/electron/releases).
## Proxies
If you need to use an HTTP proxy, you need to set the `ELECTRON_GET_USE_PROXY` variable to any

View File

@@ -78,7 +78,6 @@ auto_filenames = {
"docs/api/web-utils.md",
"docs/api/webview-tag.md",
"docs/api/window-open.md",
"docs/api/structures/activation-arguments.md",
"docs/api/structures/base-window-options.md",
"docs/api/structures/bluetooth-device.md",
"docs/api/structures/browser-window-options.md",

View File

@@ -21,11 +21,8 @@ if (isInstalled()) {
process.exit(0);
}
const platform = process.env.ELECTRON_INSTALL_PLATFORM || process.env.npm_config_platform || process.platform;
let arch =
process.env.ELECTRON_INSTALL_ARCH ||
process.env.npm_config_arch ||
process.arch;
const platform = process.env.npm_config_platform || process.platform;
let arch = process.env.npm_config_arch || process.arch;
if (platform === 'darwin' && process.platform === 'darwin' && arch === 'x64' &&
process.env.npm_config_arch === undefined) {

View File

@@ -63,6 +63,31 @@ index f8b4fd7c4ca5a0907806c7e804de8c951675a36a..209e3bcf8be5a23ac528dcd673bed82c
}
function ipToInt(ip) {
diff --git a/lib/internal/trace_events_async_hooks.js b/lib/internal/trace_events_async_hooks.js
index a9f517ffc9e4eea5bc68997ffadc85d43dde2a52..d3db6bf119a6bb9cea1d069957f89cc7f99512b7 100644
--- a/lib/internal/trace_events_async_hooks.js
+++ b/lib/internal/trace_events_async_hooks.js
@@ -11,15 +11,13 @@ const { trace } = internalBinding('trace_events');
const async_wrap = internalBinding('async_wrap');
const async_hooks = require('async_hooks');
const {
- CHAR_LOWERCASE_B,
- CHAR_LOWERCASE_E,
+ CHAR_UPPERCASE_B,
+ CHAR_UPPERCASE_E,
} = require('internal/constants');
-// Use small letters such that chrome://tracing groups by the name.
-// The behavior is not only useful but the same as the events emitted using
-// the specific C++ macros.
-const kBeforeEvent = CHAR_LOWERCASE_B;
-const kEndEvent = CHAR_LOWERCASE_E;
+// See v8/src/builtins/builtins-trace.cc - must be uppercase for perfetto
+const kBeforeEvent = CHAR_UPPERCASE_B;
+const kEndEvent = CHAR_UPPERCASE_E;
const kTraceEventCategory = 'node,node.async_hooks';
const kEnabled = Symbol('enabled');
diff --git a/node.gyp b/node.gyp
index f5cd416b5fe7a51084bc4af9a4427a8e62599fd8..5eb70ce3820f2b82121bc102c5182ab768cbef36 100644
--- a/node.gyp

View File

@@ -4,8 +4,6 @@
#include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h"
#include <memory>
#include "base/win/windows_version.h"
#include "electron/buildflags/buildflags.h"
#include "shell/browser/api/electron_api_web_contents.h"
@@ -13,7 +11,6 @@
#include "shell/browser/ui/views/win_frame_view.h"
#include "shell/browser/win/dark_mode.h"
#include "ui/base/win/hwnd_metrics.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace electron {
@@ -153,25 +150,6 @@ bool ElectronDesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) {
return views::DesktopWindowTreeHostWin::HandleMouseEvent(event);
}
void ElectronDesktopWindowTreeHostWin::HandleKeyEvent(ui::KeyEvent* event) {
// Chromium's base implementation intentionally drops ALT+SPACE keydown
// events so that the subsequent WM_SYSCHAR can open the system menu.
// We defer the event instead, so that if the system-context-menu event
// is prevented by the user, we can replay it to the renderer.
if ((event->type() == ui::EventType::kKeyPressed) &&
(event->key_code() == ui::VKEY_SPACE) &&
(event->flags() & ui::EF_ALT_DOWN) &&
!(event->flags() & ui::EF_CONTROL_DOWN)) {
if (views::Widget* widget = GetWidget();
widget && widget->non_client_view()) {
deferred_alt_space_event_ = std::make_unique<ui::KeyEvent>(*event);
return;
}
}
views::DesktopWindowTreeHostWin::HandleKeyEvent(event);
}
bool ElectronDesktopWindowTreeHostWin::HandleIMEMessage(UINT message,
WPARAM w_param,
LPARAM l_param,
@@ -187,20 +165,8 @@ bool ElectronDesktopWindowTreeHostWin::HandleIMEMessage(UINT message,
native_window_view_->NotifyWindowSystemContextMenu(
location.x(), location.y(), &prevent_default);
if (prevent_default) {
// The system menu was suppressed. Replay the deferred ALT+SPACE
// keydown so it reaches before-input-event and the renderer.
if (deferred_alt_space_event_) {
SendEventToSink(deferred_alt_space_event_.get());
deferred_alt_space_event_.reset();
}
return true;
}
// System menu not prevented — discard the deferred event and let
// the default WM_SYSCHAR handling open the system menu.
deferred_alt_space_event_.reset();
return views::DesktopWindowTreeHostWin::HandleIMEMessage(message, w_param,
return prevent_default ||
views::DesktopWindowTreeHostWin::HandleIMEMessage(message, w_param,
l_param, result);
}
}

View File

@@ -7,10 +7,8 @@
#include <windows.h>
#include <memory>
#include <optional>
#include "ui/events/event.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
namespace electron {
@@ -46,7 +44,6 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin,
int frame_thickness) const override;
bool HandleMouseEventForCaption(UINT message) const override;
bool HandleMouseEvent(ui::MouseEvent* event) override;
void HandleKeyEvent(ui::KeyEvent* event) override;
bool HandleIMEMessage(UINT message,
WPARAM w_param,
LPARAM l_param,
@@ -66,10 +63,6 @@ class ElectronDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin,
std::optional<bool> force_should_paint_as_active_;
bool allow_screenshots_ = true;
bool widget_init_done_ = false;
// Stores the ALT+SPACE key event deferred from HandleKeyEvent so it can be
// replayed in HandleIMEMessage if the system-context-menu is prevented.
std::unique_ptr<ui::KeyEvent> deferred_alt_space_event_;
};
} // namespace electron

View File

@@ -567,14 +567,7 @@ describe('command line switches', () => {
});
it('creates startup trace', async () => {
// node.async_hooks relies on %trace builtin to log trace points from JS
// https://github.com/nodejs/node/blob/8b199eef3dd4de910a6521adc42ae611a62a19e1/lib/internal/trace_events_async_hooks.js#L48-L53
// The phase event arg TRACE_EVENT_PHASE_NESTABLE_ASYNC_(BEGIN | END) is not supported in v8_use_perfetto mode
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/builtins/builtins-trace.cc;l=201-216
// and leads to the following error: TypeError: Trace event phase must be a number.
// TODO: Identify why the error started appearing with roll https://github.com/electron/electron/pull/47561
// given both v8_use_perfetto has been enabled before the roll and builtins-trace macro hasn't changed.
const rc = await startRemoteControlApp(['--trace-startup="*,-node.async_hooks"', `--trace-startup-file=${outputFilePath}`, '--trace-startup-duration=1', '--enable-logging']);
const rc = await startRemoteControlApp(['--trace-startup=*', `--trace-startup-file=${outputFilePath}`, '--trace-startup-duration=1', '--enable-logging']);
const stderrComplete = new Promise<string>(resolve => {
let stderr = '';
rc.process.stderr!.on('data', (chunk) => {