Compare commits

..

7 Commits

Author SHA1 Message Date
trop[bot]
f72ec2c45a refactor: have ShowSaveDialogSync() return a std::optional<base::FilePath> (#47453)
* refactor: have ShowSaveDialogSync() return a std::optional<base::FilePath>

Co-authored-by: Charles Kerr <charles@charleskerr.com>

* fixup! refactor: have ShowSaveDialogSync() return a std::optional<base::FilePath>

Co-authored-by: Charles Kerr <charles@charleskerr.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2025-06-13 10:03:03 +02:00
trop[bot]
ba6f0358b0 docs: update remaining references to electron-quick-start (#47448)
* chore: udpate remaining references to electron-quick-start

Co-authored-by: Anny Yang <yangannyx@gmail.com>

* chore: Update docs/tutorial/tutorial-1-prerequisites.md

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

Co-authored-by: Anny Yang <anny@electronjs.org>

* chore: Update docs/tutorial/tutorial-3-preload.md

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

Co-authored-by: Anny Yang <anny@electronjs.org>

* chore: Update docs/tutorial/tutorial-2-first-app.md

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

Co-authored-by: Anny Yang <anny@electronjs.org>

* chore: linebreak

Co-authored-by: Anny Yang <yangannyx@gmail.com>

* chore: swap minimal-repro for npx create-electron-app

Co-authored-by: Anny Yang <yangannyx@gmail.com>

* chore: add back code commands

Co-authored-by: Anny Yang <yangannyx@gmail.com>

* chore: add whitespace

Co-authored-by: Anny Yang <yangannyx@gmail.com>

* chore: remove reference to repo containing old quick start

Co-authored-by: Anny Yang <yangannyx@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Anny Yang <yangannyx@gmail.com>
Co-authored-by: Anny Yang <anny@electronjs.org>
2025-06-12 12:25:24 +02:00
trop[bot]
2f4472f058 fix: crash calling Fetch.continueResponse with WebContentsView (#47444)
fix: crash calling Fetch.continueResponse with WebContentsView

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2025-06-11 12:25:18 -05:00
trop[bot]
32599dda44 docs: mention kwallet6 command line option (#47437)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Niklas Wenzel <dev@nikwen.de>
2025-06-10 18:37:12 -05:00
trop[bot]
d2c95a28bb fix: add back fallback wasm-trap handling (#47345)
* fix: add back fallback wasm-trap handling

Refs https://chromium-review.googlesource.com/c/chromium/src/+/5372409

This change sets up wasm-trap handling for the case where content_shell
has not enabled crash reporting but moves the responsibility to
ElectronRendererClient. The default ContentRendererClient assumes
that crash reporting is enabled (crashpad enabled by default) and does
not set up its own handler.

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* chore: fix build

Co-authored-by: deepak1556 <hop2deep@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2025-06-10 18:02:14 +02:00
trop[bot]
e90404be7d docs: no class inheritance (#47433)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Erick Zhao <ezhao@slack-corp.com>
2025-06-10 13:12:24 +02:00
trop[bot]
b494ae62ba feat: [net] add "priority" option to net.request (#47321)
document the default value of priority option

Update the priority test to not use the httpbin.org as server

Fixed the lint errors

Fixed the build error

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Zeeker <13848632+zeeker999@users.noreply.github.com>
2025-06-10 10:28:46 +02:00
37 changed files with 345 additions and 119 deletions

View File

@@ -99,6 +99,10 @@ Process: [Main](../glossary.md#main-process)
It creates a new `BaseWindow` with native properties as set by the `options`.
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### `new BaseWindow([options])`
* `options` [BaseWindowConstructorOptions](structures/base-window-options.md?inline) (optional)

View File

@@ -38,6 +38,10 @@ Process: [Main](../glossary.md#main-process)
This module cannot be used until the `ready` event of the `app`
module is emitted.
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### Example
```js

View File

@@ -150,6 +150,10 @@ Process: [Main](../glossary.md#main-process)
It creates a new `BrowserWindow` with native properties as set by the `options`.
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### `new BrowserWindow([options])`
* `options` [BrowserWindowConstructorOptions](structures/browser-window-options.md?inline) (optional)

View File

@@ -60,6 +60,10 @@ following properties:
`strict-origin-when-cross-origin`.
* `cache` string (optional) - can be `default`, `no-store`, `reload`,
`no-cache`, `force-cache` or `only-if-cached`.
* `priority` string (optional) - can be `throttled`, `idle`, `lowest`,
`low`, `medium`, or `highest`. Defaults to `idle`.
* `priorityIncremental` boolean (optional) - the incremental loading flag as part
of HTTP extensible priorities (RFC 9218). Default is `true`.
`options` properties such as `protocol`, `host`, `hostname`, `port` and `path`
strictly follow the Node.js model as described in the

View File

@@ -42,6 +42,10 @@ Process: [Main](../glossary.md#main-process)
`ImageView` is an [EventEmitter][event-emitter].
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### `new ImageView()` _Experimental_
Creates an ImageView.

View File

@@ -11,6 +11,10 @@ Process: [Main](../glossary.md#main-process)
<!-- TODO(samuelmaddock): refactor doc gen to allow generics to reduce duplication -->
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### Instance Methods
#### `ipcMainServiceWorker.on(channel, listener)`

View File

@@ -6,6 +6,10 @@ Process: [Main](../glossary.md#main-process)
See [`Menu`](menu.md) for examples.
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### `new MenuItem(options)`
* `options` Object

View File

@@ -6,6 +6,10 @@
Process: [Main](../glossary.md#main-process)
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### `new Menu()`
Creates a new menu.

View File

@@ -33,6 +33,10 @@ ipcRenderer.on('port', (e) => {
})
```
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### Instance Properties
#### `channel.port1`

View File

@@ -4,12 +4,9 @@
Process: [Main](../glossary.md#main-process)
:::info Renderer process notifications
If you want to show notifications from a renderer process you should use the
[web Notifications API](../tutorial/notifications.md)
:::
> [!NOTE]
> If you want to show notifications from a renderer process you should use the
> [web Notifications API](../tutorial/notifications.md)
## Class: Notification
@@ -21,6 +18,10 @@ Process: [Main](../glossary.md#main-process)
It creates a new `Notification` with native properties as set by the `options`.
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### Static Methods
The `Notification` class has the following static methods:

View File

@@ -73,5 +73,6 @@ command line flag is provided `--password-store="basic"`.
is provided `--password-store="kwallet"`.
* `kwallet5` - When the desktop session is `kde5` or if the following command line flag
is provided `--password-store="kwallet5"`.
* `kwallet6` - When the desktop session is `kde6`.
* `kwallet6` - When the desktop session is `kde6` or if the following command line flag
is provided `--password-store="kwallet6"`.
* `unknown` - When the function is called before app has emitted the `ready` event.

View File

@@ -13,6 +13,10 @@ For including the share menu as a submenu of other menus, please use the
Process: [Main](../glossary.md#main-process)
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### `new ShareMenu(sharingItem)`
* `sharingItem` SharingItem - The item to share.

View File

@@ -1,5 +1,9 @@
# TouchBar
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
## Class: TouchBar
> Create TouchBar layouts for native macOS applications

View File

@@ -25,6 +25,10 @@ app.whenReady().then(() => {
})
```
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
**Platform Considerations**
**Linux**

View File

@@ -25,6 +25,10 @@ Process: [Main](../glossary.md#main-process)
`View` is an [EventEmitter][event-emitter].
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### `new View()`
Creates a new `View`.

View File

@@ -32,6 +32,10 @@ Process: [Main](../glossary.md#main-process)
`WebContentsView` is an [EventEmitter][event-emitter].
> [!WARNING]
> Electron's built-in classes cannot be subclassed in user code.
> For more information, see [the FAQ](../faq.md#class-inheritance-does-not-work-with-electron-built-in-modules).
### `new WebContentsView([options])`
* `options` Object (optional)

View File

@@ -152,6 +152,14 @@ The effect is visible only on (some?) LCD screens. Even if you don't see a diffe
Notice that just setting the background in the CSS does not have the desired effect.
## Class inheritance does not work with Electron built-in modules
Electron classes cannot be subclassed with the [`extends`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends)
keyword (also known as class inheritance). This feature was never implemented in Electron due
to the added complexity it would add to C++/JavaScript interop in Electron's internals.
For more information, see [electron/electron#23](https://github.com/electron/electron/issues/23).
[memory-management]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management
[closures]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
[storage]: https://developer.mozilla.org/en-US/docs/Web/API/Storage

View File

@@ -1,6 +1,7 @@
# Debugging in VSCode
This guide goes over how to set up VSCode debugging for both your own Electron project as well as the native Electron codebase.
This guide goes over how to set up VSCode debugging for both your own Electron
project as well as the native Electron codebase.
## Debugging your Electron app
@@ -9,8 +10,8 @@ This guide goes over how to set up VSCode debugging for both your own Electron p
#### 1. Open an Electron project in VSCode.
```sh
$ git clone git@github.com:electron/electron-quick-start.git
$ code electron-quick-start
$ npx create-electron-app@latest my-app
$ code my-app
```
#### 2. Add a file `.vscode/launch.json` with the following configuration:
@@ -37,23 +38,27 @@ $ code electron-quick-start
#### 3. Debugging
Set some breakpoints in `main.js`, and start debugging in the [Debug View](https://code.visualstudio.com/docs/editor/debugging). You should be able to hit the breakpoints.
Here is a pre-configured project that you can download and directly debug in VSCode: https://github.com/octref/vscode-electron-debug/tree/master/electron-quick-start
Set some breakpoints in `main.js`, and start debugging in the
[Debug View](https://code.visualstudio.com/docs/editor/debugging). You should
be able to hit the breakpoints.
## Debugging the Electron codebase
If you want to build Electron from source and modify the native Electron codebase, this section will help you in testing your modifications.
If you want to build Electron from source and modify the native Electron codebase,
this section will help you in testing your modifications.
For those unsure where to acquire this code or how to build it, [Electron's Build Tools](https://github.com/electron/build-tools) automates and explains most of this process. If you wish to manually set up the environment, you can instead use these [build instructions](../development/build-instructions-gn.md).
For those unsure where to acquire this code or how to build it,
[Electron's Build Tools](https://github.com/electron/build-tools) automates and
explains most of this process. If you wish to manually set up the environment,
you can instead use these [build instructions](../development/build-instructions-gn.md).
### Windows (C++)
#### 1. Open an Electron project in VSCode.
```sh
$ git clone git@github.com:electron/electron-quick-start.git
$ code electron-quick-start
$ npx create-electron-app@latest my-app
$ code my-app
```
#### 2. Add a file `.vscode/launch.json` with the following configuration:
@@ -86,14 +91,22 @@ $ code electron-quick-start
**Configuration Notes**
* `cppvsdbg` requires the [built-in C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) be enabled.
* `cppvsdbg` requires the
[built-in C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)
be enabled.
* `${workspaceFolder}` is the full path to Chromium's `src` directory.
* `your-executable-location` will be one of the following depending on a few items:
* `Testing`: If you are using the default settings of [Electron's Build-Tools](https://github.com/electron/build-tools) or the default instructions when [building from source](../development/build-instructions-gn.md#building).
* `Testing`: If you are using the default settings of
[Electron's Build-Tools](https://github.com/electron/build-tools) or the default
instructions when [building from source](../development/build-instructions-gn.md#building).
* `Release`: If you built a Release build rather than a Testing build.
* `your-directory-name`: If you modified this during your build process from the default, this will be whatever you specified.
* The `args` array string `"your-electron-project-path"` should be the absolute path to either the directory or `main.js` file of the Electron project you are using for testing. In this example, it should be your path to `electron-quick-start`.
* `your-directory-name`: If you modified this during your build process from
the default, this will be whatever you specified.
* The `args` array string `"your-electron-project-path"` should be the absolute
path to either the directory or `main.js` file of the Electron project you are
using for testing. In this example, it should be your path to `my-app`.
#### 3. Debugging
Set some breakpoints in the .cc files of your choosing in the native Electron C++ code, and start debugging in the [Debug View](https://code.visualstudio.com/docs/editor/debugging).
Set some breakpoints in the .cc files of your choosing in the native Electron C++
code, and start debugging in the [Debug View](https://code.visualstudio.com/docs/editor/debugging).

View File

@@ -5,8 +5,8 @@ for any Snapcraft environment, including the Ubuntu Software Center.
## Background and Requirements
Together with the broader Linux community, Canonical aims to fix many of the
common software installation problems with the [`snapcraft`](https://snapcraft.io/)
Together with the broader Linux community, Canonical aims to address common
software installation issues through the [`snapcraft`](https://snapcraft.io/)
project. Snaps are containerized software packages that include required
dependencies, auto-update, and work on all major Linux distributions without
system modification.
@@ -83,7 +83,14 @@ snap(options)
### Step 1: Create Sample Snapcraft Project
Create your project directory and add the following to `snap/snapcraft.yaml`:
```sh
$ npx create-electron-app@latest my-app
```
### Step 2: Create Sample Snapcraft Project
Create a `snap` directory in your project root and add the following to
`snap/snapcraft.yaml`:
```yaml
name: electron-packager-hello-world
@@ -97,7 +104,7 @@ grade: stable
apps:
electron-packager-hello-world:
command: electron-quick-start/electron-quick-start --no-sandbox
command: my-app/my-app --no-sandbox
extensions: [gnome]
plugs:
- browser-support
@@ -109,13 +116,13 @@ apps:
TMPDIR: $XDG_RUNTIME_DIR
parts:
electron-quick-start:
my-app:
plugin: nil
source: https://github.com/electron/electron-quick-start.git
source: .
override-build: |
npm install electron @electron/packager
npx electron-packager . --overwrite --platform=linux --output=release-build --prune=true
cp -rv ./electron-quick-start-linux-* $SNAPCRAFT_PART_INSTALL/electron-quick-start
cp -rv ./my-app-linux-* $SNAPCRAFT_PART_INSTALL/my-app
build-snaps:
- node/14/stable
build-packages:
@@ -125,12 +132,10 @@ parts:
- libnspr4
```
If you want to apply this example to an existing project:
If you want to apply this example to an existing project, replace all instances
of `my-app` with your project's name.
- Replace `source: https://github.com/electron/electron-quick-start.git` with `source: .`.
- Replace all instances of `electron-quick-start` with your project's name.
### Step 2: Build the snap
### Step 3: Build the snap
```sh
$ snapcraft
@@ -139,13 +144,13 @@ $ snapcraft
Snapped electron-packager-hello-world_0.1_amd64.snap
```
### Step 3: Install the snap
### Step 4: Install the snap
```sh
sudo snap install electron-packager-hello-world_0.1_amd64.snap --dangerous
```
### Step 4: Run the snap
### Step 5: Run the snap
```sh
electron-packager-hello-world

View File

@@ -1,6 +1,6 @@
---
title: 'Prerequisites'
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.'
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron.'
slug: tutorial-prerequisites
hide_title: false
---

View File

@@ -1,6 +1,6 @@
---
title: 'Building your First App'
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.'
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron.'
slug: tutorial-first-app
hide_title: false
---

View File

@@ -1,6 +1,6 @@
---
title: 'Using Preload Scripts'
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.'
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron.'
slug: tutorial-preload
hide_title: false
---

View File

@@ -288,8 +288,12 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
origin: options.origin,
referrerPolicy: options.referrerPolicy,
cache: options.cache,
allowNonHttpProtocols: Object.hasOwn(options, kAllowNonHttpProtocols)
allowNonHttpProtocols: Object.hasOwn(options, kAllowNonHttpProtocols),
priority: options.priority
};
if ('priorityIncremental' in options) {
urlLoaderOptions.priorityIncremental = options.priorityIncremental;
}
const headers: Record<string, string | string[]> = options.headers || {};
for (const [name, value] of Object.entries(headers)) {
validateHeader(name, value);

View File

@@ -79,7 +79,6 @@ introduce_ozoneplatform_electron_can_call_x11_property.patch
make_gtk_getlibgtk_public.patch
custom_protocols_plzserviceworker.patch
feat_filter_out_non-shareable_windows_in_the_current_application_in.patch
disable_freezing_flags_after_init_in_node.patch
short-circuit_permissions_checks_in_mediastreamdevicescontroller.patch
chore_add_electron_deps_to_gitignores.patch
chore_modify_chromium_handling_of_mouse_events.patch

View File

@@ -1,30 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Rose <japthorp@slack-corp.com>
Date: Mon, 20 Jun 2022 14:53:37 -0700
Subject: disable freezing flags after init in node
This was introduced in
https://chromium-review.googlesource.com/c/chromium/src/+/3687671.
When running node in the renderer, flags are updated after initialization, so
freezing the flags in Blink causes node initialization to fail.
If possible, it would be ideal to do this without a patch.
https://bugs.chromium.org/p/v8/issues/detail?id=12887 suggests that there may
at some point be an API to "unfreeze" the flags, or we may be able to refactor
node initialization to not update flags after V8 initialization.
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index 7a20f5199bd6cb5d13f31ec5db3e3cc03821bc3a..22167f808cb7b27d5b2a8e517cdeee63205ab9ad 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -212,6 +212,9 @@ RenderProcessImpl::RenderProcessImpl()
v8::V8::SetFlagsFromString(kSABPerContextFlag, sizeof(kSABPerContextFlag));
}
+ // Freezing flags after init conflicts with node in the renderer.
+ v8::V8::SetFlagsFromString("--no-freeze-flags-after-init");
+
if (base::FeatureList::IsEnabled(features::kWebAssemblyTrapHandler)) {
content::GetContentClient()->renderer()->SetUpWebAssemblyTrapHandler();
}

View File

@@ -81,7 +81,11 @@ void Debugger::DispatchProtocolMessage(DevToolsAgentHost* agent_host,
void Debugger::RenderFrameHostChanged(content::RenderFrameHost* old_rfh,
content::RenderFrameHost* new_rfh) {
if (agent_host_) {
// ConnectWebContents uses the primary main frame of the webContents,
// so if the new_rfh is not the primary main frame, we don't want to
// reconnect otherwise we'll end up trying to reconnect to a RenderFrameHost
// that already has a DevToolsAgentHost associated with it.
if (agent_host_ && new_rfh->IsInPrimaryMainFrame()) {
agent_host_->DisconnectWebContents();
auto* web_contents = content::WebContents::FromRenderFrameHost(new_rfh);
agent_host_->ConnectWebContents(web_contents);

View File

@@ -71,9 +71,8 @@ v8::Local<v8::Promise> ShowOpenDialog(
void ShowSaveDialogSync(const file_dialog::DialogSettings& settings,
gin::Arguments* args) {
base::FilePath path;
if (file_dialog::ShowSaveDialogSync(settings, &path))
args->Return(path);
if (const auto path = file_dialog::ShowSaveDialogSync(settings))
args->Return(*path);
}
v8::Local<v8::Promise> ShowSaveDialog(

View File

@@ -18,6 +18,7 @@
#include "base/containers/fixed_flat_map.h"
#include "base/containers/flat_set.h"
#include "base/containers/id_map.h"
#include "base/containers/map_util.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/no_destructor.h"
@@ -4013,30 +4014,35 @@ void WebContents::DevToolsSaveToFile(const std::string& url,
const std::string& content,
bool save_as,
bool is_base64) {
base::FilePath path;
auto it = saved_files_.find(url);
if (it != saved_files_.end() && !save_as) {
path = it->second;
} else {
const base::FilePath* path = nullptr;
if (!save_as)
base::FindOrNull(saved_files_, url);
if (path == nullptr) {
file_dialog::DialogSettings settings;
settings.parent_window = owner_window();
settings.force_detached = offscreen_;
settings.title = url;
settings.default_path = base::FilePath::FromUTF8Unsafe(url);
if (!file_dialog::ShowSaveDialogSync(settings, &path)) {
inspectable_web_contents_->CallClientFunction(
"DevToolsAPI", "canceledSaveURL", base::Value(url));
return;
if (auto new_path = file_dialog::ShowSaveDialogSync(settings)) {
auto [iter, _] = saved_files_.try_emplace(url, std::move(*new_path));
path = &iter->second;
}
}
saved_files_[url] = path;
if (path == nullptr) {
inspectable_web_contents_->CallClientFunction(
"DevToolsAPI", "canceledSaveURL", base::Value{url});
return;
}
// Notify DevTools.
inspectable_web_contents_->CallClientFunction(
"DevToolsAPI", "savedURL", base::Value(url),
base::Value(path.AsUTF8Unsafe()));
"DevToolsAPI", "savedURL", base::Value{url},
base::Value{path->AsUTF8Unsafe()});
file_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&WriteToFile, path, content, is_base64));
FROM_HERE, base::BindOnce(&WriteToFile, *path, content, is_base64));
}
void WebContents::DevToolsAppendToFile(const std::string& url,

View File

@@ -5,6 +5,7 @@
#ifndef ELECTRON_SHELL_BROWSER_UI_FILE_DIALOG_H_
#define ELECTRON_SHELL_BROWSER_UI_FILE_DIALOG_H_
#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -72,7 +73,8 @@ bool ShowOpenDialogSync(const DialogSettings& settings,
void ShowOpenDialog(const DialogSettings& settings,
gin_helper::Promise<gin_helper::Dictionary> promise);
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path);
std::optional<base::FilePath> ShowSaveDialogSync(
const DialogSettings& settings);
void ShowSaveDialog(const DialogSettings& settings,
gin_helper::Promise<gin_helper::Dictionary> promise);

View File

@@ -233,20 +233,25 @@ void ShowOpenDialog(const DialogSettings& settings,
dialog->RunOpenDialog(std::move(promise), settings);
}
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
auto cb = base::BindOnce(
[](base::RepeatingClosure cb, base::FilePath* file_path,
gin_helper::Dictionary result) {
result.Get("filePath", file_path);
std::move(cb).Run();
},
run_loop.QuitClosure(), path);
std::optional<base::FilePath> ShowSaveDialogSync(
const DialogSettings& settings) {
std::optional<base::FilePath> path;
FileChooserDialog* dialog = new FileChooserDialog();
dialog->RunSaveDialog(std::move(cb), settings);
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
auto on_chooser_dialog_done = base::BindOnce(
[](base::RepeatingClosure run_loop_closure,
std::optional<base::FilePath>* path, gin_helper::Dictionary result) {
if (base::FilePath val; result.Get("filePath", &val))
*path = std::move(val);
std::move(run_loop_closure).Run();
},
run_loop.QuitClosure(), &path);
auto* const dialog = new FileChooserDialog{};
dialog->RunSaveDialog(std::move(on_chooser_dialog_done), settings);
run_loop.Run();
return !path->empty();
return path;
}
void ShowSaveDialog(const DialogSettings& settings,

View File

@@ -436,19 +436,18 @@ void ShowOpenDialog(const DialogSettings& settings,
}
}
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
DCHECK(path);
std::optional<base::FilePath> ShowSaveDialogSync(
const DialogSettings& settings) {
NSSavePanel* dialog = [NSSavePanel savePanel];
SetupDialog(dialog, settings);
SetupSaveDialogForProperties(dialog, settings.properties);
int chosen = RunModalDialog(dialog, settings);
const int chosen = RunModalDialog(dialog, settings);
if (chosen == NSModalResponseCancel || ![[dialog URL] isFileURL])
return false;
return {};
*path = base::FilePath(base::SysNSStringToUTF8([[dialog URL] path]));
return true;
return base::FilePath{base::SysNSStringToUTF8([[dialog URL] path])};
}
void SaveDialogCompletion(int chosen,

View File

@@ -219,11 +219,12 @@ void ShowOpenDialog(const DialogSettings& settings,
base::BindOnce(done, std::move(promise)));
}
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
std::optional<base::FilePath> ShowSaveDialogSync(
const DialogSettings& settings) {
ATL::CComPtr<IFileSaveDialog> file_save_dialog;
HRESULT hr = file_save_dialog.CoCreateInstance(CLSID_FileSaveDialog);
if (FAILED(hr))
return false;
return {};
DWORD options = FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT;
if (settings.properties & SAVE_DIALOG_SHOW_HIDDEN_FILES)
@@ -236,32 +237,31 @@ bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
hr = ShowFileDialog(file_save_dialog, settings);
if (FAILED(hr))
return false;
return {};
CComPtr<IShellItem> pItem;
hr = file_save_dialog->GetResult(&pItem);
if (FAILED(hr))
return false;
return {};
PWSTR result_path = nullptr;
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &result_path);
if (!SUCCEEDED(hr))
return false;
return {};
*path = base::FilePath(result_path);
auto path = base::FilePath{result_path};
CoTaskMemFree(result_path);
return true;
return path;
}
void ShowSaveDialog(const DialogSettings& settings,
gin_helper::Promise<gin_helper::Dictionary> promise) {
auto done = [](gin_helper::Promise<gin_helper::Dictionary> promise,
bool success, base::FilePath result) {
std::optional<base::FilePath> result) {
v8::HandleScope handle_scope(promise.isolate());
auto dict = gin::Dictionary::CreateEmpty(promise.isolate());
dict.Set("canceled", !success);
dict.Set("filePath", result);
dict.Set("canceled", !result.has_value());
dict.Set("filePath", result.value_or(base::FilePath{}));
promise.Resolve(dict);
};
dialog_thread::Run(base::BindOnce(ShowSaveDialogSync, settings),

View File

@@ -638,6 +638,24 @@ gin::Handle<SimpleURLLoaderWrapper> SimpleURLLoaderWrapper::Create(
break;
}
if (std::string priority; opts.Get("priority", &priority)) {
static constexpr auto Lookup =
base::MakeFixedFlatMap<std::string_view, net::RequestPriority>({
{"throttled", net::THROTTLED},
{"idle", net::IDLE},
{"lowest", net::LOWEST},
{"low", net::LOW},
{"medium", net::MEDIUM},
{"highest", net::HIGHEST},
});
if (auto iter = Lookup.find(priority); iter != Lookup.end())
request->priority = iter->second;
}
if (bool priorityIncremental = request->priority_incremental;
opts.Get("priorityIncremental", &priorityIncremental)) {
request->priority_incremental = priorityIncremental;
}
const bool use_session_cookies =
opts.ValueOrDefault("useSessionCookies", false);
int options = network::mojom::kURLLoadOptionSniffMimeType;

View File

@@ -6,6 +6,7 @@
#include <algorithm>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/debug/stack_trace.h"
@@ -26,6 +27,13 @@
#include "third_party/blink/renderer/core/execution_context/execution_context.h" // nogncheck
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" // nogncheck
#if BUILDFLAG(IS_LINUX) && (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64))
#define ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX
#include "components/crash/core/app/crashpad.h"
#include "content/public/common/content_switches.h"
#include "v8/include/v8-wasm-trap-handler-posix.h"
#endif
namespace electron {
ElectronRendererClient::ElectronRendererClient()
@@ -36,6 +44,14 @@ ElectronRendererClient::ElectronRendererClient()
ElectronRendererClient::~ElectronRendererClient() = default;
void ElectronRendererClient::PostIOThreadCreated(
base::SingleThreadTaskRunner* io_thread_task_runner) {
// Freezing flags after init conflicts with node in the renderer.
// We do this here in order to avoid having to patch the ctor in
// content/renderer/render_process_impl.cc.
v8::V8::SetFlagsFromString("--no-freeze-flags-after-init");
}
void ElectronRendererClient::RenderFrameCreated(
content::RenderFrame* render_frame) {
new ElectronRenderFrameObserver(render_frame, this);
@@ -230,6 +246,48 @@ void ElectronRendererClient::WillDestroyWorkerContextOnWorkerThread(
}
}
void ElectronRendererClient::SetUpWebAssemblyTrapHandler() {
// See CL:5372409 - copied from ShellContentRendererClient.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
// Mac and Windows use the default implementation (where the default v8 trap
// handler gets set up).
ContentRendererClient::SetUpWebAssemblyTrapHandler();
return;
#elif defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX)
const bool crash_reporter_enabled =
crash_reporter::GetHandlerSocket(nullptr, nullptr);
if (crash_reporter_enabled) {
// If either --enable-crash-reporter or --enable-crash-reporter-for-testing
// is enabled it should take care of signal handling for us, use the default
// implementation which doesn't register an additional handler.
ContentRendererClient::SetUpWebAssemblyTrapHandler();
return;
}
const bool use_v8_default_handler =
base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kDisableInProcessStackTraces);
if (use_v8_default_handler) {
// There is no signal handler yet, but it's okay if v8 registers one.
v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/true);
return;
}
if (base::debug::SetStackDumpFirstChanceCallback(
v8::TryHandleWebAssemblyTrapPosix)) {
// Crashpad and Breakpad are disabled, but the in-process stack dump
// handlers are enabled, so set the callback on the stack dump handlers.
v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/false);
return;
}
// As the registration of the callback failed, we don't enable trap
// handlers.
#endif // defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX)
}
node::Environment* ElectronRendererClient::GetEnvironment(
content::RenderFrame* render_frame) const {
if (!injected_frames_.contains(render_frame))

View File

@@ -38,6 +38,8 @@ class ElectronRendererClient : public RendererClientBase {
void UndeferLoad(content::RenderFrame* render_frame);
// content::ContentRendererClient:
void PostIOThreadCreated(
base::SingleThreadTaskRunner* io_thread_task_runner) override;
void RenderFrameCreated(content::RenderFrame*) override;
void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override;
void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override;
@@ -45,6 +47,7 @@ class ElectronRendererClient : public RendererClientBase {
v8::Local<v8::Context> context) override;
void WillDestroyWorkerContextOnWorkerThread(
v8::Local<v8::Context> context) override;
void SetUpWebAssemblyTrapHandler() override;
node::Environment* GetEnvironment(content::RenderFrame* frame) const;

View File

@@ -1,15 +1,19 @@
import { net, ClientRequest, ClientRequestConstructorOptions, utilityProcess } from 'electron/main';
import { net, session, ClientRequest, ClientRequestConstructorOptions, utilityProcess } from 'electron/main';
import { expect } from 'chai';
import { once } from 'node:events';
import * as fs from 'node:fs';
import * as http from 'node:http';
import * as http2 from 'node:http2';
import * as path from 'node:path';
import { setTimeout } from 'node:timers/promises';
import { collectStreamBody, collectStreamBodyBuffer, getResponse, kOneKiloByte, kOneMegaByte, randomBuffer, randomString, respondNTimes, respondOnce } from './lib/net-helpers';
import { listen, defer } from './lib/spec-helpers';
const utilityFixturePath = path.resolve(__dirname, 'fixtures', 'api', 'utility-process', 'api-net-spec.js');
const fixturesPath = path.resolve(__dirname, 'fixtures');
async function itUtility (name: string, fn?: Function, args?: {[key:string]: any}) {
it(`${name} in utility process`, async () => {
@@ -46,6 +50,34 @@ describe('net module', () => {
}
});
let http2URL: string;
const certPath = path.join(fixturesPath, 'certificates');
const h2server = http2.createSecureServer({
key: fs.readFileSync(path.join(certPath, 'server.key')),
cert: fs.readFileSync(path.join(certPath, 'server.pem'))
}, async (req, res) => {
if (req.method === 'POST') {
const chunks = [];
for await (const chunk of req) chunks.push(chunk);
res.end(Buffer.concat(chunks).toString('utf8'));
} else if (req.method === 'GET' && req.headers[':path'] === '/get') {
res.end(JSON.stringify({
headers: req.headers
}));
} else {
res.end('<html></html>');
}
});
before(async () => {
http2URL = (await listen(h2server)).url + '/';
});
after(() => {
h2server.close();
});
for (const test of [itIgnoringArgs, itUtility]) {
describe('HTTP basics', () => {
test('should be able to issue a basic GET request', async () => {
@@ -1615,4 +1647,45 @@ describe('net module', () => {
});
});
}
for (const test of [itIgnoringArgs]) {
describe('ClientRequest API', () => {
for (const [priorityName, urgency] of Object.entries({
throttled: 'u=5',
idle: 'u=4',
lowest: '',
low: 'u=2',
medium: 'u=1',
highest: 'u=0'
})) {
for (const priorityIncremental of [true, false]) {
test(`should set priority to ${priorityName}/${priorityIncremental} if requested`, async () => {
// Priority header is available on HTTP/2, which is only
// supported over TLS, so...
session.defaultSession.setCertificateVerifyProc((req, cb) => cb(0));
defer(() => {
session.defaultSession.setCertificateVerifyProc(null);
});
const urlRequest = net.request({
url: `${http2URL}get`,
priority: priorityName as any,
priorityIncremental
});
const response = await getResponse(urlRequest);
const data = JSON.parse(await collectStreamBody(response));
let expectedPriority = urgency;
if (priorityIncremental) {
expectedPriority = expectedPriority ? expectedPriority + ', i' : 'i';
}
if (expectedPriority === '') {
expect(data.headers.priority).to.be.undefined();
} else {
expect(data.headers.priority).to.be.a('string').and.equal(expectedPriority);
}
}, { priorityName, urgency, priorityIncremental });
}
}
});
}
});

View File

@@ -177,6 +177,8 @@ declare namespace NodeJS {
mode?: string;
destination?: string;
bypassCustomProtocolHandlers?: boolean;
priority?: 'throttled' | 'idle' | 'lowest' | 'low' | 'medium' | 'highest';
priorityIncremental?: boolean;
};
type ResponseHead = {
statusCode: number;