Compare commits

...

33 Commits

Author SHA1 Message Date
Keeley Hammond
1e34cdda96 fix: remove DXDiag telemetry code (#41574)
* fix: remove dxdiag telemetry

* fix: update methods in GPU info manager

* fix: update gpu_notify_when_dxdiag_request_fails
2024-03-12 21:51:20 -07:00
Calvin
880aee0aa7 chore: cherry-pick 2607ddacd643 from chromium (#41573)
* chore: cherry-pick 2607ddacd643 from chromium

* chore: update patches

---------

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2024-03-12 16:17:11 -07:00
trop[bot]
c00489396b fix: chrome://process-internals failing to load (#41542)
fix: chrome://process-internals failing to load

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2024-03-12 20:35:47 +01:00
trop[bot]
30587d9700 docs: nativeImage api cleanup (#41569)
* docs: `nativeImage` api cleanup

Co-authored-by: Erick Zhao <erick@hotmail.ca>

* Update docs/api/native-image.md

Co-authored-by: Erick Zhao <erick@hotmail.ca>

* Update native-image.md

Co-authored-by: Erick Zhao <erick@hotmail.ca>

* Update docs/api/native-image.md

Co-authored-by: Felix Rieseberg <fr@makenotion.com>

Co-authored-by: Erick Zhao <erick@hotmail.ca>

* Update link to app icon

Co-authored-by: Alice Zhao <66543449+alicelovescake@users.noreply.github.com>

Co-authored-by: Erick Zhao <erick@hotmail.ca>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2024-03-12 19:41:48 +01:00
trop[bot]
ede0ebcc73 docs: Update code signing documentation (#41554)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Felix Rieseberg <fr@makenotion.com>
2024-03-12 12:45:38 -04:00
electron-roller[bot]
5966b42ac5 chore: bump chromium to 122.0.6261.112 (29-x-y) (#41552)
chore: bump chromium in DEPS to 122.0.6261.112

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2024-03-08 14:31:35 -05:00
John Kleinschmidt
33e61a19ef test: disable CapturableScreen tests on Windows x64 (#41544)
* test: disable CapturableScreen tests on Windows x64

(cherry picked from commit 60a288a2ca)

* test: disable js-execute-iframe" case should not crash on win 32-bit

(cherry picked from commit d545ae049b)
2024-03-08 09:29:28 -05:00
trop[bot]
a90c5b1b08 docs: correct release timeline inaccuracy (#41516)
docs: correct timeline inaccuracy

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-03-07 11:38:05 +01:00
electron-roller[bot]
06e01e5b76 chore: bump chromium to 122.0.6261.111 (29-x-y) (#41532)
chore: bump chromium in DEPS to 122.0.6261.111

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2024-03-07 10:17:44 +01:00
trop[bot]
96d053677d chore: add missing gin::Wrappable GetTypeName overrides (#41530)
chore: add missing gin::Wrappable GetTypeName overrides

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-03-06 11:02:30 -05:00
trop[bot]
8fe14665a0 fix: user-did-{resign|become}-active events on macOS (#41527)
fix: user-did-{resign|become}-active events on macOS

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-03-06 15:57:59 +01:00
Cheng Zhao
c9384a609e chore: update src_preload_function_for_environment.patch (#41501) 2024-03-04 10:00:39 -05:00
trop[bot]
fd2620bda4 fix: webContents.print options should be optional (#41479)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-03-01 17:08:55 -05:00
electron-roller[bot]
dc04802296 chore: bump chromium to 122.0.6261.95 (29-x-y) (#41489)
chore: bump chromium in DEPS to 122.0.6261.95

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2024-03-01 16:52:04 -05:00
electron-roller[bot]
90ca4e5f80 chore: bump chromium to 122.0.6261.94 (29-x-y) (#41451)
* chore: bump chromium in DEPS to 122.0.6261.94

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2024-02-28 11:32:54 -08:00
trop[bot]
5013150cfd ci: add logging to uploading to GitHub releases (#41458)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2024-02-28 12:54:24 +09:00
electron-roller[bot]
2d9c5a62c6 chore: bump chromium to 122.0.6261.70 (29-x-y) (#41446)
chore: bump chromium in DEPS to 122.0.6261.70

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2024-02-27 15:12:14 -05:00
electron-roller[bot]
23f690ffd0 chore: bump chromium to 122.0.6261.69 (29-x-y) (#41425)
* chore: bump chromium in DEPS to 122.0.6261.69

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2024-02-26 11:35:30 +01:00
trop[bot]
8f4e94694e chore: fix import from patches.py in script/lib/git.py (#41437)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2024-02-26 10:05:30 +01:00
trop[bot]
af47434dc8 feat: add support for configuring system network context proxies (#41416)
* feat: add support for configuring system network context proxies

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

* chore: add specs

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

* chore: fix lint

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

* fix: address review feedback

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2024-02-23 14:56:30 -06:00
trop[bot]
8ab99e2d8e refactor: prefer using base::NoDestructor to base::{Singleton,LazyInstance} (#41423)
refactor: prefer using base::NoDestructor to base::{Singleton,LazyInstance}

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-02-23 09:02:58 -06:00
trop[bot]
ffcccdcf37 perf: omit unnecessary work from ElectronRenderFrameObserver::ShouldNotifyClient() (#41381)
perf: omit unnecessary work from ElectronRenderFrameObserver::ShouldNotifyClient()

- (perf) GetBlinkPreferences() returns a const&, so we can use that
  reference instead of making a temporary copy

- (perf) Don't create url object unless it's needed.

- (refactor) Move is_main_world() and is_isolated_world() from the
  header into an anonymous namespace in the .cc file so they can
  be inlined and made constexpr

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-02-22 11:51:37 -05:00
trop[bot]
ce2ac1c0c2 fix: use ScreenCaptureKit exclusively on macOS 14.4 and higher (#41403)
This fixes a nasty warning / permission dialog that pops up to end-users
when consuming legacy APIs.  Chrome has flipped these flags via field trials
as have other Electron apps. It should just be the default.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
2024-02-22 14:31:53 +01:00
trop[bot]
1c3feddef8 docs: update breaking changes language (#41398)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
2024-02-21 14:17:57 -08:00
electron-roller[bot]
c779f19ee5 chore: bump chromium to 122.0.6261.57 (29-x-y) (#41390)
* chore: bump chromium in DEPS to 122.0.6261.57

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2024-02-21 11:26:48 -05:00
Keeley Hammond
09fbee9998 fix: check for draggable regions outside of main frame (#41388)
* fix: check for draggable regions outside of main frame

* fix: add nut-js to optional spec deps

Co-authored-by: samuelmaddock <samuelmaddock@electronjs.org>

---------

Co-authored-by: samuelmaddock <samuelmaddock@electronjs.org>
2024-02-21 10:52:02 -05:00
trop[bot]
69d371fc41 fix: revert to legacyMainResolve in JavaScript for asar compatibility (#41371)
* fix: revert to legacyMainResolve in JavaScript for asar compatibility

Co-authored-by: VerteDinde <vertedinde@electronjs.org>

* chore: update patch harder

* fix: export legacyMainResolve

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: VerteDinde <vertedinde@electronjs.org>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2024-02-19 15:56:16 -08:00
trop[bot]
b6db80c1c4 fix: properly stream uploadData in protocol.handle() (#41359)
* refactor(protocol): extract file stream factory

Increase readability by moving the file stream creation logic out of the
`uploadData` to request body conversion function.

Co-authored-by: Henrik S. Gaßmann <BurningEnlightenment@users.noreply.github.com>

* fix: properly flatten streams in `protocol.handle()`

Refs: electron/electron#39658

Co-authored-by: Henrik S. Gaßmann <BurningEnlightenment@users.noreply.github.com>

* fix: `protocol.handle()` filter null origin header

Refs: electron/electron#40754

Co-authored-by: Henrik S. Gaßmann <BurningEnlightenment@users.noreply.github.com>

* fix: remove obsolete TODO comment

Refs: electron/electron#38929

Co-authored-by: Henrik S. Gaßmann <BurningEnlightenment@users.noreply.github.com>

* fix: forward `Blob` parts in `protocol.handle()`

Refs: electron/electron#40826

Co-authored-by: Henrik S. Gaßmann <BurningEnlightenment@users.noreply.github.com>

* fix: explicitly error out on unknown chunk parts

Co-authored-by: Henrik S. Gaßmann <BurningEnlightenment@users.noreply.github.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Henrik S. Gaßmann <BurningEnlightenment@users.noreply.github.com>
2024-02-19 12:23:14 -08:00
trop[bot]
b87cf56b09 ci: fix helperPath calls in ci configs (#41365)
* ci: fix helperPath calls in ci configs

Co-authored-by: codebytere <codebytere@electronjs.org>

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* ci: fix helperPaths harder

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2024-02-17 20:32:12 -05:00
electron-roller[bot]
bc40a1aa0c chore: bump chromium to 122.0.6261.39 (29-x-y) (#41349)
* chore: bump chromium in DEPS to 122.0.6261.39

* chore: update patches

* fix: restore MessagePort close event

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2024-02-16 10:31:11 -06:00
trop[bot]
d8606efe94 fix: Ignore -webkit-app-region: drag; when window is in full screen mode. (#41332)
fix: Ignore `-webkit-app-region: drag;` when window is in full screen mode. (#41307)

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Mikhail Leliakin <leliakin@canva.com>
2024-02-15 14:41:46 -05:00
Charles Kerr
523e0d4574 refactor: inline simple getters, pt . 2 (#41254) (#41341)
* refactor: inline AutofillPopup::line_count()

refactor: inline AutofillPopup::value_at()

refactor: inline AutofillPopup::label_at()

* refactor: inline NativeWindow::aspect_ratio()

refactor: inline NativeWindow::aspect_ratio_extra_size()

* refactor: inline BrowserProcessImpl::linux_storage_backend()

* refactor: inline ElectronMenuModel::sharing_item()

* refactor: inline Browser::badge_count()

* refactor: inline WebContents::is_guest()

refactor: inline InspectableWebContents::is_guest()

* refactor: inline InspectableWebContents::dev_tool_bounds()

* refactor: inline WebContents::type()
2024-02-15 11:00:32 -05:00
electron-roller[bot]
3b23911121 chore: bump chromium to 122.0.6261.29 (29-x-y) (#41279)
* chore: bump chromium in DEPS to 122.0.6261.29

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2024-02-15 08:32:44 +09:00
106 changed files with 3818 additions and 647 deletions

View File

@@ -353,10 +353,10 @@ step-setup-rbe-for-build: &step-setup-rbe-for-build
mkdir third_party
# Pull down credential helper and print status
node -e "require('./src/utils/reclient.js').downloadAndPrepare({})"
HELPER=$(node -p "require('./src/utils/reclient.js').helperPath")
HELPER=$(node -p "require('./src/utils/reclient.js').helperPath({})")
$HELPER login
echo 'export RBE_service='`node -e "console.log(require('./src/utils/reclient.js').serviceAddress)"` >> $BASH_ENV
echo 'export RBE_experimental_credentials_helper='`node -e "console.log(require('./src/utils/reclient.js').helperPath)"` >> $BASH_ENV
echo 'export RBE_experimental_credentials_helper='`node -e "console.log(require('./src/utils/reclient.js').helperPath({}))"` >> $BASH_ENV
echo 'export RBE_experimental_credentials_helper_args="print"' >> $BASH_ENV
step-restore-brew-cache: &step-restore-brew-cache

2
DEPS
View File

@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'122.0.6261.18',
'122.0.6261.112',
'node_version':
'v20.9.0',
'nan_version':

View File

@@ -106,7 +106,7 @@ for:
- mkdir third_party
- ps: >-
node -e "require('./src/utils/reclient.js').downloadAndPrepare({})"
- ps: $env:RECLIENT_HELPER = node -p "require('./src/utils/reclient.js').helperPath"
- ps: $env:RECLIENT_HELPER = node -p "require('./src/utils/reclient.js').helperPath({})"
- ps: >-
& $env:RECLIENT_HELPER login
- ps: >-

View File

@@ -104,7 +104,7 @@ for:
- mkdir third_party
- ps: >-
node -e "require('./src/utils/reclient.js').downloadAndPrepare({})"
- ps: $env:RECLIENT_HELPER = node -p "require('./src/utils/reclient.js').helperPath"
- ps: $env:RECLIENT_HELPER = node -p "require('./src/utils/reclient.js').helperPath({})"
- ps: >-
& $env:RECLIENT_HELPER login
- ps: >-

View File

@@ -1468,6 +1468,24 @@ details.
**Note:** Enable `Secure Keyboard Entry` only when it is needed and disable it when it is no longer needed.
### `app.setProxy(config)`
* `config` [ProxyConfig](structures/proxy-config.md)
Returns `Promise<void>` - Resolves when the proxy setting process is complete.
Sets the proxy settings for networks requests made without an associated [Session](session.md).
Currently this will affect requests made with [Net](net.md) in the [utility process](../glossary.md#utility-process)
and internal requests made by the runtime (ex: geolocation queries).
This method can only be called after app is ready.
#### `app.resolveProxy(url)`
* `url` URL
Returns `Promise<string>` - Resolves with the proxy information for `url` that will be used when attempting to make requests using [Net](net.md) in the [utility process](../glossary.md#utility-process).
## Properties
### `app.accessibilitySupportEnabled` _macOS_ _Windows_

View File

@@ -4,36 +4,41 @@
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
In Electron, for the APIs that take images, you can pass either file paths or
`NativeImage` instances. An empty image will be used when `null` is passed.
The `nativeImage` module provides a unified interface for manipulating
system images. These can be handy if you want to provide multiple scaled
versions of the same icon or take advantage of macOS [template images][template-image].
For example, when creating a tray or setting a window's icon, you can pass an
image file path as a `string`:
Electron APIs that take image files accept either file paths or
`NativeImage` instances. An empty and transparent image will be used when `null` is passed.
```js
For example, when creating a [Tray](../api/tray.md) or setting a [BrowserWindow](../api/browser-window.md)'s
icon, you can either pass an image file path as a string:
```js title='Main Process'
const { BrowserWindow, Tray } = require('electron')
const appIcon = new Tray('/Users/somebody/images/icon.png')
const tray = new Tray('/Users/somebody/images/icon.png')
const win = new BrowserWindow({ icon: '/Users/somebody/images/window.png' })
console.log(appIcon, win)
```
Or read the image from the clipboard, which returns a `NativeImage`:
or generate a `NativeImage` instance from the same file:
```js
const { clipboard, Tray } = require('electron')
const image = clipboard.readImage()
const appIcon = new Tray(image)
console.log(appIcon)
```js title='Main Process'
const { BrowserWindow, nativeImage, Tray } = require('electron')
const trayIcon = nativeImage.createFromPath('/Users/somebody/images/icon.png')
const appIcon = nativeImage.createFromPath('/Users/somebody/images/window.png')
const tray = new Tray(trayIcon)
const win = new BrowserWindow({ icon: appIcon })
```
## Supported Formats
Currently `PNG` and `JPEG` image formats are supported. `PNG` is recommended
because of its support for transparency and lossless compression.
Currently, `PNG` and `JPEG` image formats are supported across all platforms.
`PNG` is recommended because of its support for transparency and lossless compression.
On Windows, you can also load `ICO` icons from file paths. For best visual
quality, it is recommended to include at least the following sizes in the:
quality, we recommend including at least the following sizes:
* Small icon
* 16x16 (100% DPI scale)
@@ -47,9 +52,9 @@ quality, it is recommended to include at least the following sizes in the:
* 64x64 (200% DPI scale)
* 256x256
Check the _Size requirements_ section in [this article][icons].
Check the _Icon Scaling_ section in the Windows [App Icon Construction][icons] reference.
[icons]: https://learn.microsoft.com/en-us/windows/win32/uxguide/vis-icons
[icons]: https://learn.microsoft.com/en-us/windows/apps/design/style/iconography/app-icon-construction#icon-scaling
:::note
@@ -60,16 +65,17 @@ image encoding and decoding.
## High Resolution Image
On platforms that have high-DPI support such as Apple Retina displays, you can
append `@2x` after image's base filename to mark it as a high resolution image.
On platforms that support high pixel density displays (such as Apple Retina),
you can append `@2x` after image's base filename to mark it as a 2x scale
high resolution image.
For example, if `icon.png` is a normal image that has standard resolution, then
`icon@2x.png` will be treated as a high resolution image that has double DPI
density.
`icon@2x.png` will be treated as a high resolution image that has double
Dots per Inch (DPI) density.
If you want to support displays with different DPI densities at the same time,
you can put images with different sizes in the same folder and use the filename
without DPI suffixes. For example:
without DPI suffixes within Electron. For example:
```plaintext
images/
@@ -78,10 +84,9 @@ images/
└── icon@3x.png
```
```js
```js title='Main Process'
const { Tray } = require('electron')
const appIcon = new Tray('/Users/somebody/images/icon.png')
console.log(appIcon)
const appTray = new Tray('/Users/somebody/images/icon.png')
```
The following suffixes for DPI are also supported:
@@ -98,27 +103,23 @@ The following suffixes for DPI are also supported:
* `@4x`
* `@5x`
## Template Image
## Template Image _macOS_
Template images consist of black and an alpha channel.
On macOS, [template images][template-image] consist of black and an alpha channel.
Template images are not intended to be used as standalone images and are usually
mixed with other content to create the desired final appearance.
The most common case is to use template images for a menu bar icon, so it can
The most common case is to use template images for a menu bar (Tray) icon, so it can
adapt to both light and dark menu bars.
**Note:** Template image is only supported on macOS.
To mark an image as a template image, its filename should end with the word
`Template`. For example:
* `xxxTemplate.png`
* `xxxTemplate@2x.png`
To mark an image as a template image, its base filename should end with the word
`Template` (e.g. `xxxTemplate.png`). You can also specify template images at
different DPI densities (e.g. `xxxTemplate@2x.png`).
## Methods
The `nativeImage` module has the following methods, all of which return
an instance of the `NativeImage` class:
an instance of the [`NativeImage`](#class-nativeimage) class:
### `nativeImage.createEmpty()`
@@ -137,7 +138,7 @@ Note: The Windows implementation will ignore `size.height` and scale the height
### `nativeImage.createFromPath(path)`
* `path` string
* `path` string - path to a file that we intend to construct an image out of.
Returns `NativeImage`
@@ -146,7 +147,7 @@ returns an empty image if the `path` does not exist, cannot be read, or is not
a valid image.
```js
const nativeImage = require('electron').nativeImage
const { nativeImage } = require('electron')
const image = nativeImage.createFromPath('/Users/somebody/images/icon.png')
console.log(image)
@@ -183,7 +184,7 @@ Creates a new `NativeImage` instance from `buffer`. Tries to decode as PNG or JP
Returns `NativeImage`
Creates a new `NativeImage` instance from `dataURL`.
Creates a new `NativeImage` instance from `dataUrl`, a base 64 encoded [Data URL][data-url] string.
### `nativeImage.createFromNamedImage(imageName[, hslShift])` _macOS_
@@ -192,14 +193,14 @@ Creates a new `NativeImage` instance from `dataURL`.
Returns `NativeImage`
Creates a new `NativeImage` instance from the NSImage that maps to the
given image name. See [`System Icons`](https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/system-icons/)
for a list of possible values.
Creates a new `NativeImage` instance from the `NSImage` that maps to the
given image name. See Apple's [`NSImageName`](https://developer.apple.com/documentation/appkit/nsimagename#2901388)
documentation for a list of possible values.
The `hslShift` is applied to the image with the following rules:
* `hsl_shift[0]` (hue): The absolute hue value for the image - 0 and 1 map
to 0 and 360 on the hue color wheel (red).
to 0 and 360 on the hue color wheel (red).
* `hsl_shift[1]` (saturation): A saturation shift for the image, with the
following key values:
0 = remove all color.
@@ -216,7 +217,9 @@ This means that `[-1, 0, 1]` will make the image completely white and
In some cases, the `NSImageName` doesn't match its string representation; one example of this is `NSFolderImageName`, whose string representation would actually be `NSFolder`. Therefore, you'll need to determine the correct string representation for your image before passing it in. This can be done with the following:
`echo -e '#import <Cocoa/Cocoa.h>\nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME); }' | clang -otest -x objective-c -framework Cocoa - && ./test`
```sh
echo -e '#import <Cocoa/Cocoa.h>\nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME); }' | clang -otest -x objective-c -framework Cocoa - && ./test
```
where `SYSTEM_IMAGE_NAME` should be replaced with any value from [this list](https://developer.apple.com/documentation/appkit/nsimagename?language=objc).
@@ -257,7 +260,7 @@ data.
* `options` Object (optional)
* `scaleFactor` Number (optional) - Defaults to 1.0.
Returns `string` - The data URL of the image.
Returns `string` - The [Data URL][data-url] of the image.
#### `image.getBitmap([options])`
@@ -273,7 +276,7 @@ current event loop tick; otherwise the data might be changed or destroyed.
#### `image.getNativeHandle()` _macOS_
Returns `Buffer` - A [Buffer][buffer] that stores C pointer to underlying native handle of
the image. On macOS, a pointer to `NSImage` instance would be returned.
the image. On macOS, a pointer to `NSImage` instance is returned.
Notice that the returned pointer is a weak pointer to the underlying native
image instead of a copy, so you _must_ ensure that the associated
@@ -295,11 +298,11 @@ If `scaleFactor` is passed, this will return the size corresponding to the image
* `option` boolean
Marks the image as a template image.
Marks the image as a macOS [template image][template-image].
#### `image.isTemplateImage()`
Returns `boolean` - Whether the image is a template image.
Returns `boolean` - Whether the image is a macOS [template image][template-image].
#### `image.crop(rect)`
@@ -328,13 +331,13 @@ will be preserved in the resized image.
* `scaleFactor` Number (optional) - Defaults to 1.0.
Returns `Number` - The image's aspect ratio.
Returns `Number` - The image's aspect ratio (width divided by height).
If `scaleFactor` is passed, this will return the aspect ratio corresponding to the image representation most closely matching the passed value.
#### `image.getScaleFactors()`
Returns `Number[]` - An array of all scale factors corresponding to representations for a given nativeImage.
Returns `Number[]` - An array of all scale factors corresponding to representations for a given `NativeImage`.
#### `image.addRepresentation(options)`
@@ -349,15 +352,17 @@ Returns `Number[]` - An array of all scale factors corresponding to representati
encoded PNG or JPEG image.
Add an image representation for a specific scale factor. This can be used
to explicitly add different scale factor representations to an image. This
to programmatically add different scale factor representations to an image. This
can be called on empty images.
[buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer
### Instance Properties
#### `nativeImage.isMacTemplateImage` _macOS_
A `boolean` property that determines whether the image is considered a [template image](https://developer.apple.com/documentation/appkit/nsimage/1520017-template).
A `boolean` property that determines whether the image is considered a [template image][template-image].
Please note that this property only has an effect on macOS.
[buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer
[data-url]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
[template-image]: https://developer.apple.com/documentation/appkit/nsimage/1520017-template

View File

@@ -589,105 +589,15 @@ Writes any unwritten DOMStorage data to disk.
#### `ses.setProxy(config)`
* `config` Object
* `mode` string (optional) - The proxy mode. Should be one of `direct`,
`auto_detect`, `pac_script`, `fixed_servers` or `system`. If it's
unspecified, it will be automatically determined based on other specified
options.
* `direct`
In direct mode all connections are created directly, without any proxy involved.
* `auto_detect`
In auto_detect mode the proxy configuration is determined by a PAC script that can
be downloaded at http://wpad/wpad.dat.
* `pac_script`
In pac_script mode the proxy configuration is determined by a PAC script that is
retrieved from the URL specified in the `pacScript`. This is the default mode
if `pacScript` is specified.
* `fixed_servers`
In fixed_servers mode the proxy configuration is specified in `proxyRules`.
This is the default mode if `proxyRules` is specified.
* `system`
In system mode the proxy configuration is taken from the operating system.
Note that the system mode is different from setting no proxy configuration.
In the latter case, Electron falls back to the system settings
only if no command-line options influence the proxy configuration.
* `pacScript` string (optional) - The URL associated with the PAC file.
* `proxyRules` string (optional) - Rules indicating which proxies to use.
* `proxyBypassRules` string (optional) - Rules indicating which URLs should
bypass the proxy settings.
* `config` [ProxyConfig](structures/proxy-config.md)
Returns `Promise<void>` - Resolves when the proxy setting process is complete.
Sets the proxy settings.
When `mode` is unspecified, `pacScript` and `proxyRules` are provided together, the `proxyRules`
option is ignored and `pacScript` configuration is applied.
You may need `ses.closeAllConnections` to close currently in flight connections to prevent
pooled sockets using previous proxy from being reused by future requests.
The `proxyRules` has to follow the rules below:
```sh
proxyRules = schemeProxies[";"<schemeProxies>]
schemeProxies = [<urlScheme>"="]<proxyURIList>
urlScheme = "http" | "https" | "ftp" | "socks"
proxyURIList = <proxyURL>[","<proxyURIList>]
proxyURL = [<proxyScheme>"://"]<proxyHost>[":"<proxyPort>]
```
For example:
* `http=foopy:80;ftp=foopy2` - Use HTTP proxy `foopy:80` for `http://` URLs, and
HTTP proxy `foopy2:80` for `ftp://` URLs.
* `foopy:80` - Use HTTP proxy `foopy:80` for all URLs.
* `foopy:80,bar,direct://` - Use HTTP proxy `foopy:80` for all URLs, failing
over to `bar` if `foopy:80` is unavailable, and after that using no proxy.
* `socks4://foopy` - Use SOCKS v4 proxy `foopy:1080` for all URLs.
* `http=foopy,socks5://bar.com` - Use HTTP proxy `foopy` for http URLs, and fail
over to the SOCKS5 proxy `bar.com` if `foopy` is unavailable.
* `http=foopy,direct://` - Use HTTP proxy `foopy` for http URLs, and use no
proxy if `foopy` is unavailable.
* `http=foopy;socks=foopy2` - Use HTTP proxy `foopy` for http URLs, and use
`socks4://foopy2` for all other URLs.
The `proxyBypassRules` is a comma separated list of rules described below:
* `[ URL_SCHEME "://" ] HOSTNAME_PATTERN [ ":" <port> ]`
Match all hostnames that match the pattern HOSTNAME_PATTERN.
Examples:
"foobar.com", "\*foobar.com", "\*.foobar.com", "\*foobar.com:99",
"https://x.\*.y.com:99"
* `"." HOSTNAME_SUFFIX_PATTERN [ ":" PORT ]`
Match a particular domain suffix.
Examples:
".google.com", ".com", "http://.google.com"
* `[ SCHEME "://" ] IP_LITERAL [ ":" PORT ]`
Match URLs which are IP address literals.
Examples:
"127.0.1", "\[0:0::1]", "\[::1]", "http://\[::1]:99"
* `IP_LITERAL "/" PREFIX_LENGTH_IN_BITS`
Match any URL that is to an IP literal that falls between the
given range. IP range is specified using CIDR notation.
Examples:
"192.168.1.1/16", "fefe:13::abc/33".
* `<local>`
Match local addresses. The meaning of `<local>` is whether the
host matches one of: "127.0.0.1", "::1", "localhost".
#### `ses.resolveHost(host, [options])`
* `host` string - Hostname to resolve.

View File

@@ -0,0 +1,86 @@
# ProxyConfig Object
* `mode` string (optional) - The proxy mode. Should be one of `direct`,
`auto_detect`, `pac_script`, `fixed_servers` or `system`.
Defaults to `pac_script` proxy mode if `pacScript` option is specified
otherwise defaults to `fixed_servers`.
* `direct` - In direct mode all connections are created directly, without any proxy involved.
* `auto_detect` - In auto_detect mode the proxy configuration is determined by a PAC script that can
be downloaded at http://wpad/wpad.dat.
* `pac_script` - In pac_script mode the proxy configuration is determined by a PAC script that is
retrieved from the URL specified in the `pacScript`. This is the default mode if `pacScript` is specified.
* `fixed_servers` - In fixed_servers mode the proxy configuration is specified in `proxyRules`.
This is the default mode if `proxyRules` is specified.
* `system` - In system mode the proxy configuration is taken from the operating system.
Note that the system mode is different from setting no proxy configuration.
In the latter case, Electron falls back to the system settings only if no
command-line options influence the proxy configuration.
* `pacScript` string (optional) - The URL associated with the PAC file.
* `proxyRules` string (optional) - Rules indicating which proxies to use.
* `proxyBypassRules` string (optional) - Rules indicating which URLs should
bypass the proxy settings.
When `mode` is unspecified, `pacScript` and `proxyRules` are provided together, the `proxyRules`
option is ignored and `pacScript` configuration is applied.
The `proxyRules` has to follow the rules below:
```sh
proxyRules = schemeProxies[";"<schemeProxies>]
schemeProxies = [<urlScheme>"="]<proxyURIList>
urlScheme = "http" | "https" | "ftp" | "socks"
proxyURIList = <proxyURL>[","<proxyURIList>]
proxyURL = [<proxyScheme>"://"]<proxyHost>[":"<proxyPort>]
```
For example:
* `http=foopy:80;ftp=foopy2` - Use HTTP proxy `foopy:80` for `http://` URLs, and
HTTP proxy `foopy2:80` for `ftp://` URLs.
* `foopy:80` - Use HTTP proxy `foopy:80` for all URLs.
* `foopy:80,bar,direct://` - Use HTTP proxy `foopy:80` for all URLs, failing
over to `bar` if `foopy:80` is unavailable, and after that using no proxy.
* `socks4://foopy` - Use SOCKS v4 proxy `foopy:1080` for all URLs.
* `http=foopy,socks5://bar.com` - Use HTTP proxy `foopy` for http URLs, and fail
over to the SOCKS5 proxy `bar.com` if `foopy` is unavailable.
* `http=foopy,direct://` - Use HTTP proxy `foopy` for http URLs, and use no
proxy if `foopy` is unavailable.
* `http=foopy;socks=foopy2` - Use HTTP proxy `foopy` for http URLs, and use
`socks4://foopy2` for all other URLs.
The `proxyBypassRules` is a comma separated list of rules described below:
* `[ URL_SCHEME "://" ] HOSTNAME_PATTERN [ ":" <port> ]`
Match all hostnames that match the pattern HOSTNAME_PATTERN.
Examples:
"foobar.com", "\*foobar.com", "\*.foobar.com", "\*foobar.com:99",
"https://x.\*.y.com:99"
* `"." HOSTNAME_SUFFIX_PATTERN [ ":" PORT ]`
Match a particular domain suffix.
Examples:
".google.com", ".com", "http://.google.com"
* `[ SCHEME "://" ] IP_LITERAL [ ":" PORT ]`
Match URLs which are IP address literals.
Examples:
"127.0.1", "\[0:0::1]", "\[::1]", "http://\[::1]:99"
* `IP_LITERAL "/" PREFIX_LENGTH_IN_BITS`
Match any URL that is to an IP literal that falls between the
given range. IP range is specified using CIDR notation.
Examples:
"192.168.1.1/16", "fefe:13::abc/33".
* `<local>`
Match local addresses. The meaning of `<local>` is whether the
host matches one of: "127.0.0.1", "::1", "localhost".

View File

@@ -60,7 +60,7 @@ app.whenReady().then(() => {
**MacOS**
* Icons passed to the Tray constructor should be [Template Images](native-image.md#template-image).
* Icons passed to the Tray constructor should be [Template Images](native-image.md#template-image-macos).
* To make sure your icon isn't grainy on retina monitors, be sure your `@2x` image is 144dpi.
* If you are bundling your application (e.g., with webpack for development), be sure that the file names are not being mangled or hashed. The filename needs to end in Template, and the `@2x` image needs to have the same filename as the standard image, or MacOS will not magically invert your image's colors or use the high density image.
* 16x16 (72dpi) and 32x32@2x (144dpi) work well for most icons.

View File

@@ -30,10 +30,10 @@ This switch was never formally documented but it's removal is being noted here r
### Behavior Changed: `ipcRenderer` can no longer be sent over the `contextBridge`
Attempting to send `ipcRenderer` as an object over the `contextBridge` will now result in
Attempting to send the entire `ipcRenderer` module as an object over the `contextBridge` will now result in
an empty object on the receiving side of the bridge. This change was made to remove / mitigate
a security footgun, you should not directly expose ipcRenderer or it's methods over the bridge.
Instead provide a safe wrapper like below:
a security footgun. You should not directly expose ipcRenderer or its methods over the bridge.
Instead, provide a safe wrapper like below:
```js
contextBridge.exposeInMainWorld('app', {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 4.2 MiB

View File

@@ -5,34 +5,25 @@ slug: code-signing
hide_title: false
---
Code signing is a security technology that you use to certify that an app was
created by you. You should sign your application so it does not trigger any
operating system security checks.
Code signing is a security technology to certify that an app was created by you.
You should sign your application so it does not trigger any operating system
security warnings.
On macOS, the system can detect any change to the app, whether the change is
introduced accidentally or by malicious code.
![macOS Sonoma Gatekeeper warning: The app is damaged](../images/gatekeeper.png)
On Windows, the system assigns a trust level to your code signing certificate
which if you don't have, or if your trust level is low, will cause security
dialogs to appear when users start using your application. Trust level builds
over time so it's better to start code signing as early as possible.
While it is possible to distribute unsigned apps, it is not recommended. Both
Windows and macOS will, by default, prevent either the download or the execution
of unsigned applications. Starting with macOS Catalina (version 10.15), users
have to go through multiple manual steps to open unsigned applications.
![macOS Catalina Gatekeeper warning: The app cannot be opened because the developer cannot be verified](../images/gatekeeper.png)
As you can see, users get two options: Move the app straight to the trash or
cancel running it. You don't want your users to see that dialog.
Both Windows and macOS prevent users from running unsigned applications. It is
possible to distribute applications without codesigning them - but in order to
run them, users need to go through multiple advanced and manual steps to run
them.
If you are building an Electron app that you intend to package and distribute,
it should be code signed.
it should be code signed. The Electron ecosystem tooling makes codesigning your
apps straightforward - this documentation explains how sign your apps on both
Windows and macOS.
## Signing & notarizing macOS builds
Properly preparing macOS applications for release requires two steps. First, the
Preparing macOS applications for release requires two steps: First, the
app needs to be code signed. Then, the app needs to be uploaded to Apple for a
process called **notarization**, where automated systems will further verify that
your app isn't doing anything to endanger its users.
@@ -65,7 +56,9 @@ are likely using [`@electron/packager`][], which includes [`@electron/osx-sign`]
[`@electron/notarize`][].
If you're using Packager's API, you can pass [in configuration that both signs
and notarizes your application](https://electron.github.io/packager/main/interfaces/electronpackager.options.html).
and notarizes your application](https://electron.github.io/packager/main/modules.html).
If the example below does not meet your needs, please see [`@electron/osx-sign`][] and
[`@electron/notarize`][] for the many possible configuration options.
```js @ts-nocheck
const packager = require('@electron/packager')
@@ -86,35 +79,81 @@ See the [Mac App Store Guide][].
## Signing Windows builds
Before signing Windows builds, you must do the following:
Before you can code sign your application, you need to acquire a code signing
certificate. Unlike Apple, Microsoft allows developers to purchase those
certificates on the open market. They are usually sold by the same companies
also offering HTTPS certificates. Prices vary, so it may be worth your time to
shop around. Popular resellers include:
1. Get a Windows Authenticode code signing certificate (requires an annual fee)
2. Install Visual Studio to get the signing utility (the free [Community
Edition](https://visualstudio.microsoft.com/vs/community/) is enough)
- [Certum EV code signing certificate](https://shop.certum.eu/data-safety/code-signing-certificates/certum-ev-code-sigining.html)
- [DigiCert EV code signing certificate](https://www.digicert.com/signing/code-signing-certificates)
- [Entrust EV code signing certificate](https://www.entrustdatacard.com/products/digital-signing-certificates/code-signing-certificates)
- [GlobalSign EV code signing certificate](https://www.globalsign.com/en/code-signing-certificate/ev-code-signing-certificates)
- [IdenTrust EV code signing certificate](https://www.identrust.com/digital-certificates/trustid-ev-code-signing)
- [Sectigo (formerly Comodo) EV code signing certificate](https://sectigo.com/ssl-certificates-tls/code-signing)
- [SSL.com EV code signing certificate](https://www.ssl.com/certificates/ev-code-signing/)
You can get a code signing certificate from a lot of resellers. Prices vary, so
it may be worth your time to shop around. Popular resellers include:
It is important to call out that since June 2023, Microsoft requires software to
be signed with an "extended validation" certificate, also called an "EV code signing
certificate". In the past, developers could sign software with a simpler and cheaper
certificate called "authenticode code signing certificate" or "software-based OV certificate".
These simpler certificates no longer provide benefits: Windows will treat your app as
completely unsigned and display the equivalent warning dialogs.
- [digicert](https://www.digicert.com/dc/code-signing/microsoft-authenticode.htm)
- [Sectigo](https://sectigo.com/ssl-certificates-tls/code-signing)
- Amongst others, please shop around to find one that suits your needs! 😄
The new EV certificates are required to be stored on a hardware storage module
compliant with FIPS 140 Level 2, Common Criteria EAL 4+ or equivalent. In other words,
the certificate cannot be simply downloaded onto a CI infrastructure. In practice,
those storage modules look like fancy USB thumb drives.
:::caution Keep your certificate password private
Your certificate password should be a **secret**. Do not share it publicly or
commit it to your source code.
:::
Many certificate providers now offer "cloud-based signing" - the entire signing hardware
is in their data center and you can use it to remotely sign code. This approach is
popular with Electron maintainers since it makes signing your applications in CI (like
GitHub Actions, CircleCI, etc) relatively easy.
At the time of writing, Electron's own apps use [DigiCert KeyLocker](https://docs.digicert.com/en/digicert-keylocker.html), but any provider that provides a command line tool for
signing files will be compatible with Electron's tooling.
All tools in the Electron ecosystem use [`@electron/windows-sign`][] and typically
expose configuration options through a `windowsSign` property. You can either use it
to sign files directly - or use the same `windowsSign` configuration across Electron
Forge, [`@electron/packager`][], [`electron-winstaller`][], and [`electron-wix-msi`][].
### Using Electron Forge
Electron Forge is the recommended way to sign your `Squirrel.Windows` and `WiX MSI` installers. Detailed instructions on how to configure your application can be found in the [Electron Forge Code Signing Tutorial](https://www.electronforge.io/guides/code-signing/code-signing-macos).
Electron Forge is the recommended way to sign your app as well as your `Squirrel.Windows`
and `WiX MSI` installers. Detailed instructions on how to configure your application can
be found in the [Electron Forge Code Signing Tutorial](https://www.electronforge.io/guides/code-signing/code-signing-windows).
### Using Electron Packager
If you're not using an integrated build pipeline like Forge, you
are likely using [`@electron/packager`][], which includes [`@electron/windows-sign`][].
If you're using Packager's API, you can pass [in configuration that signs
your application](https://electron.github.io/packager/main/modules.html). If the
example below does not meet your needs, please see [`@electron/windows-sign`][]
for the many possible configuration options.
```js @ts-nocheck
const packager = require('@electron/packager')
packager({
dir: '/path/to/my/app',
windowsSign: {
signWithParams: '--my=custom --parameters',
// If signtool.exe does not work for you, customize!
signToolPath: 'C:\\Path\\To\\my-custom-tool.exe'
}
})
```
### Using electron-winstaller (Squirrel.Windows)
[`electron-winstaller`][] is a package that can generate Squirrel.Windows installers for your
Electron app. This is the tool used under the hood by Electron Forge's
[Squirrel.Windows Maker][maker-squirrel]. If you're not using Electron Forge and want to use
`electron-winstaller` directly, use the `certificateFile` and `certificatePassword` configuration
options when creating your installer.
[Squirrel.Windows Maker][maker-squirrel]. Just like `@electron/packager`, it uses
[`@electron/windows-sign`][] under the hood and supports the same `windowsSign`
options.
```js {10-11} @ts-nocheck
const electronInstaller = require('electron-winstaller')
@@ -126,8 +165,11 @@ try {
outputDirectory: '/tmp/build/installer64',
authors: 'My App Inc.',
exe: 'myapp.exe',
certificateFile: './cert.pfx',
certificatePassword: 'this-is-a-secret'
windowsSign: {
signWithParams: '--my=custom --parameters',
// If signtool.exe does not work for you, customize!
signToolPath: 'C:\\Path\\To\\my-custom-tool.exe'
}
})
console.log('It worked!')
} catch (e) {
@@ -141,10 +183,8 @@ For full configuration options, check out the [`electron-winstaller`][] reposito
[`electron-wix-msi`][] is a package that can generate MSI installers for your
Electron app. This is the tool used under the hood by Electron Forge's [MSI Maker][maker-msi].
If you're not using Electron Forge and want to use `electron-wix-msi` directly, use the
`certificateFile` and `certificatePassword` configuration options
or pass in parameters directly to [SignTool.exe][] with the `signWithParams` option.
Just like `@electron/packager`, it uses [`@electron/windows-sign`][] under the hood
and supports the same `windowsSign` options.
```js {12-13} @ts-nocheck
import { MSICreator } from 'electron-wix-msi'
@@ -158,8 +198,11 @@ const msiCreator = new MSICreator({
manufacturer: 'Kitten Technologies',
version: '1.1.2',
outputDirectory: '/path/to/output/folder',
certificateFile: './cert.pfx',
certificatePassword: 'this-is-a-secret'
windowsSign: {
signWithParams: '--my=custom --parameters',
// If signtool.exe does not work for you, customize!
signToolPath: 'C:\\Path\\To\\my-custom-tool.exe'
}
})
// Step 2: Create a .wxs template file
@@ -192,6 +235,7 @@ See the [Windows Store Guide][].
[`@electron/osx-sign`]: https://github.com/electron/osx-sign
[`@electron/packager`]: https://github.com/electron/packager
[`@electron/notarize`]: https://github.com/electron/notarize
[`@electron/windows-sign`]: https://github.com/electron/windows-sign
[`electron-winstaller`]: https://github.com/electron/windows-installer
[`electron-wix-msi`]: https://github.com/electron-userland/electron-wix-msi
[xcode]: https://developer.apple.com/xcode
@@ -200,4 +244,3 @@ See the [Windows Store Guide][].
[windows store guide]: ./windows-store-guide.md
[maker-squirrel]: https://www.electronforge.io/config/makers/squirrel.windows
[maker-msi]: https://www.electronforge.io/config/makers/wix-msi
[signtool.exe]: https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe

View File

@@ -12,7 +12,7 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
| 29.0.0 | 2023-Dec-07 | 2024-Jan-24 | 2024-Feb-20 | 2024-Aug-20 | M122 | v18.19 | ✅ |
| 28.0.0 | 2023-Oct-11 | 2023-Nov-06 | 2023-Dec-05 | 2024-Jun-11 | M120 | v18.18 | ✅ |
| 27.0.0 | 2023-Aug-17 | 2023-Sep-13 | 2023-Oct-10 | 2024-Apr-16 | M118 | v18.17 | ✅ |
| 26.0.0 | 2023-Jun-01 | 2023-Jun-27 | 2023-Aug-15 | 2024-Feb-20 | M116 | v18.16 | |
| 26.0.0 | 2023-Jun-01 | 2023-Jun-27 | 2023-Aug-15 | 2024-Feb-20 | M116 | v18.16 | 🚫 |
| 25.0.0 | 2023-Apr-10 | 2023-May-02 | 2023-May-30 | 2023-Dec-05 | M114 | v18.15 | 🚫 |
| 24.0.0 | 2023-Feb-09 | 2023-Mar-07 | 2023-Apr-04 | 2023-Oct-10 | M112 | v18.14 | 🚫 |
| 23.0.0 | 2022-Dec-01 | 2023-Jan-10 | 2023-Feb-07 | 2023-Aug-15 | M110 | v18.12 | 🚫 |

View File

@@ -77,6 +77,7 @@ template("electron_extra_paks") {
"//content:content_resources",
"//content/browser/resources/gpu:resources",
"//content/browser/resources/media:resources",
"//content/browser/resources/process:resources",
"//content/browser/tracing:resources",
"//content/browser/webrtc/resources",
"//electron:resources",
@@ -96,6 +97,7 @@ template("electron_extra_paks") {
# New paks should be added here by default.
sources += [
"$root_gen_dir/content/browser/devtools/devtools_resources.pak",
"$root_gen_dir/content/process_resources.pak",
"$root_gen_dir/ui/resources/webui_resources.pak",
]
deps += [ "//content/browser/devtools:devtools_resources" ]

View File

@@ -114,6 +114,7 @@ auto_filenames = {
"docs/api/structures/protocol-request.md",
"docs/api/structures/protocol-response-upload-data.md",
"docs/api/structures/protocol-response.md",
"docs/api/structures/proxy-config.md",
"docs/api/structures/rectangle.md",
"docs/api/structures/referrer.md",
"docs/api/structures/render-process-gone-details.md",

View File

@@ -385,6 +385,7 @@ filenames = {
"shell/browser/extended_web_contents_observer.h",
"shell/browser/feature_list.cc",
"shell/browser/feature_list.h",
"shell/browser/feature_list_mac.mm",
"shell/browser/file_select_helper.cc",
"shell/browser/file_select_helper.h",
"shell/browser/file_select_helper_mac.mm",

View File

@@ -29,6 +29,21 @@ function makeStreamFromPipe (pipe: any): ReadableStream {
});
}
function makeStreamFromFileInfo ({
filePath,
offset = 0,
length = -1
}: {
filePath: string;
offset?: number;
length?: number;
}): ReadableStream {
return Readable.toWeb(createReadStream(filePath, {
start: offset,
end: length >= 0 ? offset + length : undefined
}));
}
function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): RequestInit['body'] {
if (!uploadData) return null;
// Optimization: skip creating a stream if the request is just a single buffer.
@@ -37,30 +52,42 @@ function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): Reque
const chunks = [...uploadData] as any[]; // TODO: types are wrong
let current: ReadableStreamDefaultReader | null = null;
return new ReadableStream({
pull (controller) {
async pull (controller) {
if (current) {
current.read().then(({ done, value }) => {
const { done, value } = await current.read();
// (done => value === undefined) as per WHATWG spec
if (done) {
current = null;
return this.pull!(controller);
} else {
controller.enqueue(value);
if (done) current = null;
}, (err) => {
controller.error(err);
});
}
} else {
if (!chunks.length) { return controller.close(); }
const chunk = chunks.shift()!;
if (chunk.type === 'rawData') { controller.enqueue(chunk.bytes); } else if (chunk.type === 'file') {
current = Readable.toWeb(createReadStream(chunk.filePath, { start: chunk.offset ?? 0, end: chunk.length >= 0 ? chunk.offset + chunk.length : undefined })).getReader();
this.pull!(controller);
if (chunk.type === 'rawData') {
controller.enqueue(chunk.bytes);
} else if (chunk.type === 'file') {
current = makeStreamFromFileInfo(chunk).getReader();
return this.pull!(controller);
} else if (chunk.type === 'stream') {
current = makeStreamFromPipe(chunk.body).getReader();
this.pull!(controller);
return this.pull!(controller);
} else if (chunk.type === 'blob') {
// Note that even though `getBlobData()` is a `Session` API, it doesn't
// actually use the `Session` context. Its implementation solely relies
// on global variables which allows us to implement this feature without
// knowledge of the `Session` associated with the current request by
// always pulling `Blob` data out of the default `Session`.
controller.enqueue(await session.defaultSession.getBlobData(chunk.blobUUID));
} else {
throw new Error(`Unknown upload data chunk type: ${chunk.type}`);
}
}
}
}) as RequestInit['body'];
}
// TODO(codebytere): Use Object.hasOwn() once we update to ECMAScript 2022.
function validateResponse (res: Response) {
if (!res || typeof res !== 'object') return false;
@@ -85,8 +112,12 @@ Protocol.prototype.handle = function (this: Electron.Protocol, scheme: string, h
const success = register.call(this, scheme, async (preq: ProtocolRequest, cb: any) => {
try {
const body = convertToRequestBody(preq.uploadData);
const headers = new Headers(preq.headers);
if (headers.get('origin') === 'null') {
headers.delete('origin');
}
const req = new Request(preq.url, {
headers: preq.headers,
headers,
method: preq.method,
referrer: preq.referrer,
body,

View File

@@ -263,13 +263,11 @@ WebContents.prototype.printToPDF = async function (options) {
// TODO(codebytere): deduplicate argument sanitization by moving rest of
// print param logic into new file shared between printToPDF and print
WebContents.prototype.print = function (options: ElectronInternal.WebContentsPrintOptions, callback) {
if (typeof options !== 'object') {
WebContents.prototype.print = function (options: ElectronInternal.WebContentsPrintOptions = {}, callback) {
if (typeof options !== 'object' || options == null) {
throw new TypeError('webContents.print(): Invalid print settings specified.');
}
const printSettings: Record<string, any> = { ...options };
const pageSize = options.pageSize ?? 'A4';
if (typeof pageSize === 'object') {
if (!pageSize.height || !pageSize.width) {
@@ -283,7 +281,7 @@ WebContents.prototype.print = function (options: ElectronInternal.WebContentsPri
throw new RangeError('height and width properties must be minimum 352 microns.');
}
printSettings.mediaSize = {
options.mediaSize = {
name: 'CUSTOM',
custom_display_name: 'Custom',
height_microns: height,
@@ -295,7 +293,7 @@ WebContents.prototype.print = function (options: ElectronInternal.WebContentsPri
};
} else if (typeof pageSize === 'string' && PDFPageSizes[pageSize]) {
const mediaSize = PDFPageSizes[pageSize];
printSettings.mediaSize = {
options.mediaSize = {
...mediaSize,
imageable_area_left_microns: 0,
imageable_area_bottom_microns: 0,
@@ -308,9 +306,9 @@ WebContents.prototype.print = function (options: ElectronInternal.WebContentsPri
if (this._print) {
if (callback) {
this._print(printSettings, callback);
this._print(options, callback);
} else {
this._print(printSettings);
this._print(options);
}
} else {
console.error('Error: Printing feature is disabled.');

View File

@@ -131,3 +131,5 @@ fix_suppress_clang_-wimplicit-const-int-float-conversion_in.patch
cherry-pick-e7ffe20ebfac.patch
fix_getcursorscreenpoint_wrongly_returns_0_0.patch
fix_add_support_for_skipping_first_2_no-op_refreshes_in_thumb_cap.patch
remove_dxdiag_telemetry_code.patch
cherry-pick-2607ddacd643.patch

View File

@@ -92,10 +92,10 @@ index 2709519d0bbf33548704c14a99324b504d27ccbf..aa3c2d3c1ea73da128616fe676ac09e2
int32_t world_id) = 0;
virtual bool AllowScriptExtensions() = 0;
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
index 6139aed9ebbb459d4d7027312c0f15b669fedfb6..db566ba272b1eff5e67547c5d82bf7420def7285 100644
index 070f61ef364eec98080f29d089d39f74222e9759..a6d2f3bbe61486187d23d20fecb01749e1d897b7 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
@@ -289,6 +289,13 @@ void LocalFrameClientImpl::DidCreateScriptContext(
@@ -290,6 +290,13 @@ void LocalFrameClientImpl::DidCreateScriptContext(
web_frame_->Client()->DidCreateScriptContext(context, world_id);
}

View File

@@ -151,7 +151,7 @@ index 794efdb773422ddc12ccbe013a13aadeb980b487..a60bbd76141f06202343c68e78688a95
// In GTK4, there's no way to obtain the frame thickness from CSS values
// directly, so we must determine it experimentally based on the drawn
diff --git a/ui/gtk/window_frame_provider_gtk.h b/ui/gtk/window_frame_provider_gtk.h
index 91236ec07c01ca14248b997577ae887c0c396cd2..d70639d2ba40e325bbbbf6117741c13354984ed5 100644
index bed28192daffe032fde3a74ca70f1298fb12b1b7..268acade8bd1075f3ce756cdf29bf50905ccb433 100644
--- a/ui/gtk/window_frame_provider_gtk.h
+++ b/ui/gtk/window_frame_provider_gtk.h
@@ -18,7 +18,7 @@ namespace gtk {

View File

@@ -33,10 +33,10 @@ index d09e7aeb788550e7ecefb4b9c177dd26ecc5ad4c..c894dc421f55a94e541d00e05e2f05bf
"//base",
"//build:branding_buildflags",
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 81dceec0b449c6fa90984f0bede8d3b94a93910a..32e5194b60f7db1d076e2ba3a884cce4e3133b02 100644
index 0811d62fd215de0231021c88c6083493b0a6b1ca..502275e7adf9388afeeaeca692784f9e9a060f8c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4755,7 +4755,7 @@ static_library("browser") {
@@ -4763,7 +4763,7 @@ static_library("browser") {
# On Windows, the hashes are embedded in //chrome:chrome_initial rather
# than here in :chrome_dll.
@@ -46,10 +46,10 @@ index 81dceec0b449c6fa90984f0bede8d3b94a93910a..32e5194b60f7db1d076e2ba3a884cce4
sources += [ "certificate_viewer_stub.cc" ]
}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 005e321b191e69422c892c2001ffc98f9e198d14..e02717f068247e0c971e19374b8c2d2d0809fc5b 100644
index 84ea91c781a2f777461a4e99d1c8c2cdf138dc22..0d8a1ed6cebd86de4efc36a93e20cb6034a16c10 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7220,7 +7220,6 @@ if (!is_fuchsia) {
@@ -7221,7 +7221,6 @@ if (!is_fuchsia) {
deps += [
"//chrome:other_version",
@@ -57,7 +57,7 @@ index 005e321b191e69422c892c2001ffc98f9e198d14..e02717f068247e0c971e19374b8c2d2d
"//chrome//services/util_win:unit_tests",
"//chrome/app:chrome_dll_resources",
"//chrome/app:win_unit_tests",
@@ -7241,6 +7240,10 @@ if (!is_fuchsia) {
@@ -7242,6 +7241,10 @@ if (!is_fuchsia) {
"//ui/resources",
]
@@ -68,7 +68,7 @@ index 005e321b191e69422c892c2001ffc98f9e198d14..e02717f068247e0c971e19374b8c2d2d
ldflags = [
"/DELAYLOAD:api-ms-win-core-winrt-error-l1-1-0.dll",
"/DELAYLOAD:api-ms-win-core-winrt-l1-1-0.dll",
@@ -8259,7 +8262,6 @@ if (!is_fuchsia) {
@@ -8260,7 +8263,6 @@ if (!is_fuchsia) {
}
deps += [
@@ -76,7 +76,7 @@ index 005e321b191e69422c892c2001ffc98f9e198d14..e02717f068247e0c971e19374b8c2d2d
"//chrome/browser/apps:icon_standardizer",
"//chrome/browser/apps/app_service",
"//chrome/browser/apps/app_service:app_registry_cache_waiter",
@@ -8352,6 +8354,10 @@ if (!is_fuchsia) {
@@ -8353,6 +8355,10 @@ if (!is_fuchsia) {
"//ui/webui/resources/js/browser_command:mojo_bindings",
]

View File

@@ -0,0 +1,38 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Calvin Watford <watfordcalvin@gmail.com>
Date: Tue, 12 Mar 2024 20:37:32 +0000
Subject: Fix primary display race condition crash on Windows
In rare cases, it's possible for the OS to provide us a list of displays
that doesn't contain the primary display. This situation causes
undefined behavior (dereference past vector end) and a crash to occur in
|display::win::(anon)::DisplayInfosToScreenWinDisplays| on builds
without DCHECK enabled.
Bug: 40265302
Change-Id: I2154bedea84478a84147c380610c85d4ea3f703a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5353255
Reviewed-by: David Bienvenu <davidbienvenu@chromium.org>
Reviewed-by: Robert Liao <robliao@chromium.org>
Commit-Queue: David Bienvenu <davidbienvenu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1271793}
diff --git a/ui/display/win/screen_win.cc b/ui/display/win/screen_win.cc
index 6b6189a124e3fde423b129ef34f2c96186b4e86d..1040e583c6c50ba01efc44faa1882657ff8f63b2 100644
--- a/ui/display/win/screen_win.cc
+++ b/ui/display/win/screen_win.cc
@@ -324,7 +324,13 @@ std::vector<ScreenWinDisplay> DisplayInfosToScreenWinDisplays(
display_infos_remaining, [](const internal::DisplayInfo& display_info) {
return display_info.screen_rect().origin().IsOrigin();
});
- DCHECK(primary_display_iter != display_infos_remaining.end());
+
+ // If we can't find the primary display, we likely witnessed a race condition
+ // when querying the OS for display info. We expect another OS notification to
+ // trigger this lookup again soon, so just return an empty list for now.
+ if (primary_display_iter == display_infos_remaining.end()) {
+ return {};
+ }
// Build the tree and determine DisplayPlacements along the way.
DisplayLayoutBuilder builder(primary_display_iter->id());

View File

@@ -13,10 +13,10 @@ uses internally for things like menus and devtools.
We can remove this patch once it has in some shape been upstreamed.
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
index fbbc35818cfa1709b30520e2336eeab2d96693d0..814230e423a44bfc71b6e028fb870f2a1d15e15f 100644
index 17c3b9f659a3b3e7f61463e5900ed53276454a7b..93848cea0c62b5457c9033c477a3fb81120f33cb 100644
--- a/ui/native_theme/native_theme.cc
+++ b/ui/native_theme/native_theme.cc
@@ -198,6 +198,8 @@ NativeTheme::NativeTheme(bool should_use_dark_colors,
@@ -191,6 +191,8 @@ NativeTheme::NativeTheme(bool should_use_dark_colors,
NativeTheme::~NativeTheme() = default;
bool NativeTheme::ShouldUseDarkColors() const {
@@ -26,10 +26,10 @@ index fbbc35818cfa1709b30520e2336eeab2d96693d0..814230e423a44bfc71b6e028fb870f2a
}
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 772c7a34a97588dd20c040be5ca482696990c880..fe1c04d6f9c9e9ca6a3aeacbaf897f1df5783248 100644
index 14f3667521de110e6b9cd884ef9311878bb8bc8a..c88da6b43b9caefa50e8606e5a641c7c3dd6287e 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -442,6 +442,23 @@ class NATIVE_THEME_EXPORT NativeTheme {
@@ -441,6 +441,23 @@ class NATIVE_THEME_EXPORT NativeTheme {
scoped_refptr<ColorProviderKey::ThemeInitializerSupplier> custom_theme,
bool use_custom_frame = true) const;
@@ -53,7 +53,7 @@ index 772c7a34a97588dd20c040be5ca482696990c880..fe1c04d6f9c9e9ca6a3aeacbaf897f1d
// Returns a shared instance of the native theme that should be used for web
// rendering. Do not use it in a normal application context (i.e. browser).
// The returned object should not be deleted by the caller. This function is
@@ -658,6 +675,7 @@ class NATIVE_THEME_EXPORT NativeTheme {
@@ -655,6 +672,7 @@ class NATIVE_THEME_EXPORT NativeTheme {
bool inverted_colors_ = false;
PreferredColorScheme preferred_color_scheme_ = PreferredColorScheme::kLight;
PreferredContrast preferred_contrast_ = PreferredContrast::kNoPreference;
@@ -62,10 +62,10 @@ index 772c7a34a97588dd20c040be5ca482696990c880..fe1c04d6f9c9e9ca6a3aeacbaf897f1d
SEQUENCE_CHECKER(sequence_checker_);
};
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc
index 6af4df92820d5a9d247a927244522f4c4acd164d..92af8c4a933b8462dce2524b0623a6843abd2600 100644
index 50647269ec84f1a543132b3d102152a40e1e65e1..41a7df7e873a7d3300fd48db0ffa5f1fc8e43198 100644
--- a/ui/native_theme/native_theme_win.cc
+++ b/ui/native_theme/native_theme_win.cc
@@ -655,6 +655,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const {
@@ -664,6 +664,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const {
// ...unless --force-dark-mode was specified in which case caveat emptor.
if (InForcedColorsMode() && !IsForcedDarkMode())
return false;

View File

@@ -13,13 +13,13 @@ messages in the legacy window handle layer.
These conditions are regularly hit with WCO-enabled windows on Windows.
diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.cc b/content/browser/renderer_host/legacy_render_widget_host_win.cc
index 2635d8967c42c85fb6830036e6de694b5736a97b..17bb1932f3a01c5027113061276f7050bb61f80e 100644
index 70c19054022dd8ebc28657bb9ec94c0ee3e7ad87..ed9bea21b4ee6d6b9a7b979fc63ccc43d1926184 100644
--- a/content/browser/renderer_host/legacy_render_widget_host_win.cc
+++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc
@@ -322,12 +322,12 @@ LRESULT LegacyRenderWidgetHostHWND::OnKeyboardRange(UINT message,
LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
@@ -320,12 +320,12 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
WPARAM w_param,
LPARAM l_param) {
LPARAM l_param,
BOOL& handled) {
- if (message == WM_MOUSEMOVE) {
+ if (message == WM_MOUSEMOVE || message == WM_NCMOUSEMOVE) {
if (!mouse_tracking_enabled_) {
@@ -31,27 +31,27 @@ index 2635d8967c42c85fb6830036e6de694b5736a97b..17bb1932f3a01c5027113061276f7050
tme.hwndTrack = hwnd();
tme.dwHoverTime = 0;
TrackMouseEvent(&tme);
@@ -359,7 +359,10 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
@@ -356,7 +356,10 @@ LRESULT LegacyRenderWidgetHostHWND::OnMouseRange(UINT message,
// out of the picture.
if (!msg_handled_ &&
if (!handled &&
(message >= WM_NCMOUSEMOVE && message <= WM_NCXBUTTONDBLCLK)) {
- ret = ::DefWindowProc(GetParent(), message, w_param, l_param);
+ // Send WM_NCMOUSEMOVE messages using the LegacyRenderWidgetHostHWND's
+ // handle so mouse tracking on non-client areas doesn't break.
+ HWND target = message == WM_NCMOUSEMOVE ? hwnd() : GetParent();
+ ret = ::DefWindowProc(target, message, w_param, l_param);
msg_handled_ = TRUE;
handled = TRUE;
}
}
diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.h b/content/browser/renderer_host/legacy_render_widget_host_win.h
index c478d6351ba160c76871ad657ede69b05b4e09ca..77631423937f7df7c52b4d3d309aa9335ab05bbb 100644
index f22af1f3e24033688a4f59666346075831df2243..50c66051efb0bfcb3c13e4ccb37dddfade9abb82 100644
--- a/content/browser/renderer_host/legacy_render_widget_host_win.h
+++ b/content/browser/renderer_host/legacy_render_widget_host_win.h
@@ -91,6 +91,7 @@ class CONTENT_EXPORT LegacyRenderWidgetHostHWND
CR_MESSAGE_HANDLER_EX(WM_NCHITTEST, OnNCHitTest)
CR_MESSAGE_RANGE_HANDLER_EX(WM_NCMOUSEMOVE, WM_NCXBUTTONDBLCLK,
OnMouseRange)
+ CR_MESSAGE_HANDLER_EX(WM_NCMOUSELEAVE, OnMouseLeave)
CR_MESSAGE_HANDLER_EX(WM_NCCALCSIZE, OnNCCalcSize)
CR_MESSAGE_HANDLER_EX(WM_SIZE, OnSize)
CR_MESSAGE_HANDLER_EX(WM_DESTROY, OnDestroy)
@@ -102,6 +102,7 @@ class CONTENT_EXPORT LegacyRenderWidgetHostHWND
MESSAGE_HANDLER_EX(WM_VSCROLL, OnScroll)
MESSAGE_HANDLER_EX(WM_NCHITTEST, OnNCHitTest)
MESSAGE_RANGE_HANDLER(WM_NCMOUSEMOVE, WM_NCXBUTTONDBLCLK, OnMouseRange)
+ MESSAGE_HANDLER_EX(WM_NCMOUSELEAVE, OnMouseLeave)
MESSAGE_HANDLER_EX(WM_NCCALCSIZE, OnNCCalcSize)
MESSAGE_HANDLER_EX(WM_SIZE, OnSize)
MESSAGE_HANDLER_EX(WM_DESTROY, OnDestroy)

View File

@@ -40,18 +40,16 @@ index 25896ab0f3ca233ae17bf509f2a6ae5a6cc3c54b..5a8e6d184e276833034c604be8c48e01
// Called from BrowserMainLoop::PostCreateThreads().
// TODO(content/browser/gpu/OWNERS): This should probably use a
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index 4fa857fed71485898f4ba7c6e78cc8be37d8dbf7..3306a36044eed8395b3a13cffa4754cadee1048d 100644
index 4fa857fed71485898f4ba7c6e78cc8be37d8dbf7..c12a96390350b5bd14a43506d8a35e4b0dec4924 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -1222,6 +1222,12 @@ void GpuDataManagerImplPrivate::TerminateInfoCollectionGpuProcess() {
@@ -1222,6 +1222,10 @@ void GpuDataManagerImplPrivate::TerminateInfoCollectionGpuProcess() {
if (host)
host->ForceShutdown();
}
+
+bool GpuDataManagerImplPrivate::DxdiagDx12VulkanRequested() const {
+ return !(gpu_info_vulkan_request_failed_ ||
+ gpu_info_dx12_request_failed_ ||
+ gpu_info_dx_diag_request_failed_);
+ return !(gpu_info_vulkan_request_failed_ || gpu_info_dx12_request_failed_);
+}
#endif

View File

@@ -1278,7 +1278,7 @@ index eb81a70e4d5d5cd3e6ae9b45f8cd1c795ea76c51..dc30306f2c5d20503399fc3a8860773a
} // namespace sandbox
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index b39c4b213855c1028e0d8149a9f3d46080023d2f..5b844fca5705299df8c91c5e3fb9060d714782e9 100644
index d6a3fe5c93ab4dec202fa5bf83c55896b1bd6423..e046eb1b3e60fb8ef99922a6dff6c3701677bc22 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -312,6 +312,7 @@ component("core") {

View File

@@ -133,10 +133,10 @@ index 38c8cf36fdf9366121c7ada96c167a4c9664952e..03b37fb62655a355e104870a088e4222
const GURL& document_url,
const WeakDocumentPtr& weak_document_ptr,
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index b656054d342f53d655b8c068b127250a8e8af6c3..5f6267cce7c59eab684dda3f8bfc7982b6407841 100644
index 545c860afff13c6aa5030128f508f325de0ab507..938844e3f653344b97dd8e59faacdb8eded561a9 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2045,7 +2045,7 @@ void RenderProcessHostImpl::CreateNotificationService(
@@ -2048,7 +2048,7 @@ void RenderProcessHostImpl::CreateNotificationService(
case RenderProcessHost::NotificationServiceCreatorType::kSharedWorker:
case RenderProcessHost::NotificationServiceCreatorType::kDedicatedWorker: {
storage_partition_impl_->GetPlatformNotificationContext()->CreateService(
@@ -145,7 +145,7 @@ index b656054d342f53d655b8c068b127250a8e8af6c3..5f6267cce7c59eab684dda3f8bfc7982
creator_type, std::move(receiver));
break;
}
@@ -2053,7 +2053,7 @@ void RenderProcessHostImpl::CreateNotificationService(
@@ -2056,7 +2056,7 @@ void RenderProcessHostImpl::CreateNotificationService(
CHECK(rfh);
storage_partition_impl_->GetPlatformNotificationContext()->CreateService(

File diff suppressed because it is too large Load Diff

View File

@@ -22,13 +22,13 @@ However, the patch would need to be reviewed by the security team, as it
does touch a security-sensitive class.
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 5f6267cce7c59eab684dda3f8bfc7982b6407841..4551601cf77d57e74c725d4352b128c0ac8b09b7 100644
index 938844e3f653344b97dd8e59faacdb8eded561a9..ae21df015a9e43d4c324bd79ff3bf00c23b5225e 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1727,9 +1727,15 @@ bool RenderProcessHostImpl::Init() {
@@ -1730,9 +1730,15 @@ bool RenderProcessHostImpl::Init() {
std::unique_ptr<SandboxedProcessLauncherDelegate> sandbox_delegate =
std::make_unique<RendererSandboxedProcessLauncherDelegateWin>(
*cmd_line, IsPdf(), IsJitDisabled());
*cmd_line, IsPdf(), /*is_jit_disabled=*/IsPdf());
+#else
+#if BUILDFLAG(USE_ZYGOTE)
+ bool use_zygote = !cmd_line->HasSwitch(switches::kNoZygote);

View File

@@ -10,10 +10,10 @@ to handle this without patching, but this is fairly clean for now and no longer
patching legacy devtools code.
diff --git a/front_end/entrypoints/main/MainImpl.ts b/front_end/entrypoints/main/MainImpl.ts
index e5beed66e2c81c918b23287a190a628b416b5f6a..1d9b58c2ac3429e704df3b5f6d3dd3af405e8cb4 100644
index df915df956604163a9a7dda5dacb628899fe1015..1e449b06d96341ac8d1411b2760a7e2aeb087dca 100644
--- a/front_end/entrypoints/main/MainImpl.ts
+++ b/front_end/entrypoints/main/MainImpl.ts
@@ -729,6 +729,8 @@ export class MainImpl {
@@ -734,6 +734,8 @@ export class MainImpl {
globalThis.Main = globalThis.Main || {};
// @ts-ignore Exported for Tests.js
globalThis.Main.Main = MainImpl;

View File

@@ -50,4 +50,5 @@ src_update_default_v8_platform_to_override_functions_with_location.patch
fix_capture_embedder_exceptions_before_entering_v8.patch
spec_add_iterator_to_global_intrinsics.patch
fix_undici_incorrectly_copies_headers_onto_fetches.patch
fix_revert_src_lb_reducing_c_calls_of_esm_legacy_main_resolve.patch
src_preload_function_for_environment.patch

View File

@@ -0,0 +1,572 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Keeley Hammond <khammond@slack-corp.com>
Date: Mon, 19 Feb 2024 12:05:42 -0800
Subject: fix: revert "src,lb: reducing C++ calls of esm legacy main resolve"
This switch to native legacyMainResolve doesn't take asar into account, and can
cause errors when a project using ESM and asar tries to load a dependency which
uses commonJS. This will need to be fixed forward, but revert for Electron 29's
stable release to avoid potentially breaking apps with a riskier fix.
This patch can be removed when node's
native implementation has been patched
to recognize asar files.
This reverts commit 9cf2e1f55b8446a7cde23699d00a3be73aa0c8f1.
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
index ce8092b96aee8d09ff382110db4be62dcd760cce..fe0b6591e4c86b5fcbda4a1aac9c116e17920f05 100644
--- a/lib/internal/modules/esm/resolve.js
+++ b/lib/internal/modules/esm/resolve.js
@@ -36,10 +36,9 @@ const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const experimentalNetworkImports =
getOptionValue('--experimental-network-imports');
const typeFlag = getOptionValue('--input-type');
-const { URL, pathToFileURL, fileURLToPath, isURL } = require('internal/url');
+const { URL, pathToFileURL, fileURLToPath, isURL, toPathIfFileURL } = require('internal/url');
const { getCWDURL } = require('internal/util');
const { canParse: URLCanParse } = internalBinding('url');
-const { legacyMainResolve: FSLegacyMainResolve } = internalBinding('fs');
const {
ERR_INPUT_TYPE_NOT_ALLOWED,
ERR_INVALID_ARG_TYPE,
@@ -136,34 +135,13 @@ function emitLegacyIndexDeprecation(url, packageJSONUrl, base, main) {
const realpathCache = new SafeMap();
-const legacyMainResolveExtensions = [
- '',
- '.js',
- '.json',
- '.node',
- '/index.js',
- '/index.json',
- '/index.node',
- './index.js',
- './index.json',
- './index.node',
-];
-
-const legacyMainResolveExtensionsIndexes = {
- // 0-6: when packageConfig.main is defined
- kResolvedByMain: 0,
- kResolvedByMainJs: 1,
- kResolvedByMainJson: 2,
- kResolvedByMainNode: 3,
- kResolvedByMainIndexJs: 4,
- kResolvedByMainIndexJson: 5,
- kResolvedByMainIndexNode: 6,
- // 7-9: when packageConfig.main is NOT defined,
- // or when the previous case didn't found the file
- kResolvedByPackageAndJs: 7,
- kResolvedByPackageAndJson: 8,
- kResolvedByPackageAndNode: 9,
-};
+/**
+ * @param {string | URL} url
+ * @returns {boolean}
+ */
+function fileExists(url) {
+ return internalModuleStat(toNamespacedPath(toPathIfFileURL(url))) === 0;
+}
/**
* Legacy CommonJS main resolution:
@@ -178,22 +156,44 @@ const legacyMainResolveExtensionsIndexes = {
* @returns {URL}
*/
function legacyMainResolve(packageJSONUrl, packageConfig, base) {
- const packageJsonUrlString = packageJSONUrl.href;
-
- if (typeof packageJsonUrlString !== 'string') {
- throw new ERR_INVALID_ARG_TYPE('packageJSONUrl', ['URL'], packageJSONUrl);
+ let guess;
+ if (packageConfig.main !== undefined) {
+ // Note: fs check redundances will be handled by Descriptor cache here.
+ if (fileExists(guess = new URL(`./${packageConfig.main}`,
+ packageJSONUrl))) {
+ return guess;
+ } else if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
+ packageJSONUrl)));
+ else if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
+ packageJSONUrl)));
+ else if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
+ packageJSONUrl)));
+ else if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
+ packageJSONUrl)));
+ else if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
+ packageJSONUrl)));
+ else if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
+ packageJSONUrl)));
+ else guess = undefined;
+ if (guess) {
+ emitLegacyIndexDeprecation(guess, packageJSONUrl, base,
+ packageConfig.main);
+ return guess;
+ }
+ // Fallthrough.
}
-
- const baseStringified = isURL(base) ? base.href : base;
-
- const resolvedOption = FSLegacyMainResolve(packageJsonUrlString, packageConfig.main, baseStringified);
-
- const baseUrl = resolvedOption <= legacyMainResolveExtensionsIndexes.kResolvedByMainIndexNode ? `./${packageConfig.main}` : '';
- const resolvedUrl = new URL(baseUrl + legacyMainResolveExtensions[resolvedOption], packageJSONUrl);
-
- emitLegacyIndexDeprecation(resolvedUrl, packageJSONUrl, base, packageConfig.main);
-
- return resolvedUrl;
+ if (fileExists(guess = new URL('./index.js', packageJSONUrl)));
+ // So fs.
+ else if (fileExists(guess = new URL('./index.json', packageJSONUrl)));
+ else if (fileExists(guess = new URL('./index.node', packageJSONUrl)));
+ else guess = undefined;
+ if (guess) {
+ emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main);
+ return guess;
+ }
+ // Not found.
+ throw new ERR_MODULE_NOT_FOUND(
+ fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base));
}
const encodedSepRegEx = /%2F|%5C/i;
diff --git a/src/node_file.cc b/src/node_file.cc
index 59780dec1c4b6d157d2b04fea8c57cacce73ec3a..8f8629ed0b8cbc08a544211b63675ea0dcca1828 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -19,14 +19,11 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "node_file.h" // NOLINT(build/include_inline)
-#include "ada.h"
#include "aliased_buffer-inl.h"
#include "memory_tracker-inl.h"
#include "node_buffer.h"
-#include "node_errors.h"
#include "node_external_reference.h"
#include "node_file-inl.h"
-#include "node_metadata.h"
#include "node_process-inl.h"
#include "node_stat_watcher.h"
#include "permission/permission.h"
@@ -3013,293 +3010,6 @@ static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
}
}
-static bool FileURLToPath(
- Environment* env,
- const ada::url_aggregator& file_url,
- /* The linter can't detect the assign for result_file_path
- So we need to ignore since it suggest to put const */
- // NOLINTNEXTLINE(runtime/references)
- std::string& result_file_path) {
- if (file_url.type != ada::scheme::FILE) {
- env->isolate()->ThrowException(ERR_INVALID_URL_SCHEME(env->isolate()));
-
- return false;
- }
-
- std::string_view pathname = file_url.get_pathname();
-#ifdef _WIN32
- size_t first_percent = std::string::npos;
- size_t pathname_size = pathname.size();
- std::string pathname_escaped_slash;
-
- for (size_t i = 0; i < pathname_size; i++) {
- if (pathname[i] == '/') {
- pathname_escaped_slash += '\\';
- } else {
- pathname_escaped_slash += pathname[i];
- }
-
- if (pathname[i] != '%') continue;
-
- if (first_percent == std::string::npos) {
- first_percent = i;
- }
-
- // just safe-guard against access the pathname
- // outside the bounds
- if ((i + 2) >= pathname_size) continue;
-
- char third = pathname[i + 2] | 0x20;
-
- bool is_slash = pathname[i + 1] == '2' && third == 102;
- bool is_forward_slash = pathname[i + 1] == '5' && third == 99;
-
- if (!is_slash && !is_forward_slash) continue;
-
- env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
- env->isolate(),
- "File URL path must not include encoded \\ or / characters"));
-
- return false;
- }
-
- std::string_view hostname = file_url.get_hostname();
- std::string decoded_pathname = ada::unicode::percent_decode(
- std::string_view(pathname_escaped_slash), first_percent);
-
- if (hostname.size() > 0) {
- // If hostname is set, then we have a UNC path
- // Pass the hostname through domainToUnicode just in case
- // it is an IDN using punycode encoding. We do not need to worry
- // about percent encoding because the URL parser will have
- // already taken care of that for us. Note that this only
- // causes IDNs with an appropriate `xn--` prefix to be decoded.
- result_file_path =
- "\\\\" + ada::unicode::to_unicode(hostname) + decoded_pathname;
-
- return true;
- }
-
- char letter = decoded_pathname[1] | 0x20;
- char sep = decoded_pathname[2];
-
- // a..z A..Z
- if (letter < 'a' || letter > 'z' || sep != ':') {
- env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
- env->isolate(), "File URL path must be absolute"));
-
- return false;
- }
-
- result_file_path = decoded_pathname.substr(1);
-
- return true;
-#else // _WIN32
- std::string_view hostname = file_url.get_hostname();
-
- if (hostname.size() > 0) {
- std::string error_message =
- std::string("File URL host must be \"localhost\" or empty on ") +
- std::string(per_process::metadata.platform);
- env->isolate()->ThrowException(
- ERR_INVALID_FILE_URL_HOST(env->isolate(), error_message.c_str()));
-
- return false;
- }
-
- size_t first_percent = std::string::npos;
- for (size_t i = 0; (i + 2) < pathname.size(); i++) {
- if (pathname[i] != '%') continue;
-
- if (first_percent == std::string::npos) {
- first_percent = i;
- }
-
- if (pathname[i + 1] == '2' && (pathname[i + 2] | 0x20) == 102) {
- env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
- env->isolate(),
- "File URL path must not include encoded / characters"));
-
- return false;
- }
- }
-
- result_file_path = ada::unicode::percent_decode(pathname, first_percent);
-
- return true;
-#endif // _WIN32
-}
-
-BindingData::FilePathIsFileReturnType BindingData::FilePathIsFile(
- Environment* env, const std::string& file_path) {
- THROW_IF_INSUFFICIENT_PERMISSIONS(
- env,
- permission::PermissionScope::kFileSystemRead,
- file_path,
- BindingData::FilePathIsFileReturnType::kThrowInsufficientPermissions);
-
- uv_fs_t req;
-
- int rc = uv_fs_stat(env->event_loop(), &req, file_path.c_str(), nullptr);
-
- if (rc == 0) {
- const uv_stat_t* const s = static_cast<const uv_stat_t*>(req.ptr);
- rc = !!(s->st_mode & S_IFDIR);
- }
-
- uv_fs_req_cleanup(&req);
-
- // rc is 0 if the path refers to a file
- if (rc == 0) return BindingData::FilePathIsFileReturnType::kIsFile;
-
- return BindingData::FilePathIsFileReturnType::kIsNotFile;
-}
-
-// the possible file extensions that should be tested
-// 0-6: when packageConfig.main is defined
-// 7-9: when packageConfig.main is NOT defined,
-// or when the previous case didn't found the file
-const std::array<std::string, 10> BindingData::legacy_main_extensions = {
- "",
- ".js",
- ".json",
- ".node",
- "/index.js",
- "/index.json",
- "/index.node",
- ".js",
- ".json",
- ".node"};
-
-void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
- CHECK_GE(args.Length(), 1);
- CHECK(args[0]->IsString());
-
- Environment* env = Environment::GetCurrent(args);
-
- Utf8Value utf8_package_json_url(env->isolate(), args[0].As<String>());
- auto package_json_url =
- ada::parse<ada::url_aggregator>(utf8_package_json_url.ToStringView());
-
- if (!package_json_url) {
- env->isolate()->ThrowException(
- ERR_INVALID_URL(env->isolate(), "Invalid URL"));
-
- return;
- }
-
- ada::result<ada::url_aggregator> file_path_url;
- std::string initial_file_path;
- std::string file_path;
-
- if (args.Length() >= 2 && !args[1]->IsNullOrUndefined() &&
- args[1]->IsString()) {
- std::string package_config_main =
- Utf8Value(env->isolate(), args[1].As<String>()).ToString();
-
- file_path_url = ada::parse<ada::url_aggregator>(
- std::string("./") + package_config_main, &package_json_url.value());
-
- if (!file_path_url) {
- env->isolate()->ThrowException(
- ERR_INVALID_URL(env->isolate(), "Invalid URL"));
-
- return;
- }
-
- if (!FileURLToPath(env, file_path_url.value(), initial_file_path)) return;
-
- FromNamespacedPath(&initial_file_path);
-
- for (int i = 0; i < BindingData::legacy_main_extensions_with_main_end;
- i++) {
- file_path = initial_file_path + BindingData::legacy_main_extensions[i];
-
- switch (FilePathIsFile(env, file_path)) {
- case BindingData::FilePathIsFileReturnType::kIsFile:
- return args.GetReturnValue().Set(i);
- case BindingData::FilePathIsFileReturnType::kIsNotFile:
- continue;
- case BindingData::FilePathIsFileReturnType::
- kThrowInsufficientPermissions:
- // the default behavior when do not have permission is to return
- // and exit the execution of the method as soon as possible
- // the internal function will throw the exception
- return;
- default:
- UNREACHABLE();
- }
- }
- }
-
- file_path_url =
- ada::parse<ada::url_aggregator>("./index", &package_json_url.value());
-
- if (!file_path_url) {
- env->isolate()->ThrowException(
- ERR_INVALID_URL(env->isolate(), "Invalid URL"));
-
- return;
- }
-
- if (!FileURLToPath(env, file_path_url.value(), initial_file_path)) return;
-
- FromNamespacedPath(&initial_file_path);
-
- for (int i = BindingData::legacy_main_extensions_with_main_end;
- i < BindingData::legacy_main_extensions_package_fallback_end;
- i++) {
- file_path = initial_file_path + BindingData::legacy_main_extensions[i];
-
- switch (FilePathIsFile(env, file_path)) {
- case BindingData::FilePathIsFileReturnType::kIsFile:
- return args.GetReturnValue().Set(i);
- case BindingData::FilePathIsFileReturnType::kIsNotFile:
- continue;
- case BindingData::FilePathIsFileReturnType::kThrowInsufficientPermissions:
- // the default behavior when do not have permission is to return
- // and exit the execution of the method as soon as possible
- // the internal function will throw the exception
- return;
- default:
- UNREACHABLE();
- }
- }
-
- std::string module_path;
- std::string module_base;
-
- if (!FileURLToPath(env, package_json_url.value(), module_path)) return;
-
- if (args.Length() >= 3 && !args[2]->IsNullOrUndefined() &&
- args[2]->IsString()) {
- Utf8Value utf8_base_path(env->isolate(), args[2].As<String>());
- auto base_url =
- ada::parse<ada::url_aggregator>(utf8_base_path.ToStringView());
-
- if (!base_url) {
- env->isolate()->ThrowException(
- ERR_INVALID_URL(env->isolate(), "Invalid URL"));
-
- return;
- }
-
- if (!FileURLToPath(env, base_url.value(), module_base)) return;
- } else {
- std::string err_arg_message =
- "The \"base\" argument must be of type string or an instance of URL.";
- env->isolate()->ThrowException(
- ERR_INVALID_ARG_TYPE(env->isolate(), err_arg_message.c_str()));
- return;
- }
-
- env->isolate()->ThrowException(
- ERR_MODULE_NOT_FOUND(env->isolate(),
- "Cannot find package '%s' imported from %s",
- module_path,
- module_base));
-}
-
void BindingData::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("stats_field_array", stats_field_array);
tracker->TrackField("stats_field_bigint_array", stats_field_bigint_array);
@@ -3399,19 +3109,6 @@ InternalFieldInfoBase* BindingData::Serialize(int index) {
return info;
}
-void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
- Local<ObjectTemplate> target) {
- Isolate* isolate = isolate_data->isolate();
-
- SetMethod(
- isolate, target, "legacyMainResolve", BindingData::LegacyMainResolve);
-}
-
-void BindingData::RegisterExternalReferences(
- ExternalReferenceRegistry* registry) {
- registry->Register(BindingData::LegacyMainResolve);
-}
-
static void CreatePerIsolateProperties(IsolateData* isolate_data,
Local<ObjectTemplate> target) {
Isolate* isolate = isolate_data->isolate();
@@ -3468,7 +3165,6 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
SetMethod(isolate, target, "mkdtemp", Mkdtemp);
StatWatcher::CreatePerIsolateProperties(isolate_data, target);
- BindingData::CreatePerIsolateProperties(isolate_data, target);
target->Set(
FIXED_ONE_BYTE_STRING(isolate, "kFsStatsFieldsNumber"),
@@ -3542,7 +3238,6 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(Access);
registry->Register(AccessSync);
StatWatcher::RegisterExternalReferences(registry);
- BindingData::RegisterExternalReferences(registry);
registry->Register(Close);
registry->Register(CloseSync);
diff --git a/src/node_file.h b/src/node_file.h
index 4599546c5245300346557b68070c60292daaed23..7b43d027a2e6524f3ec6b7bccdb6e49a3c8790ea 100644
--- a/src/node_file.h
+++ b/src/node_file.h
@@ -63,13 +63,6 @@ class BindingData : public SnapshotableObject {
AliasedBufferIndex statfs_field_array;
AliasedBufferIndex statfs_field_bigint_array;
};
-
- enum class FilePathIsFileReturnType {
- kIsFile = 0,
- kIsNotFile,
- kThrowInsufficientPermissions
- };
-
explicit BindingData(Realm* realm,
v8::Local<v8::Object> wrap,
InternalFieldInfo* info = nullptr);
@@ -86,30 +79,12 @@ class BindingData : public SnapshotableObject {
SERIALIZABLE_OBJECT_METHODS()
SET_BINDING_ID(fs_binding_data)
- static void LegacyMainResolve(
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
- static void CreatePerIsolateProperties(IsolateData* isolate_data,
- v8::Local<v8::ObjectTemplate> ctor);
- static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
-
void MemoryInfo(MemoryTracker* tracker) const override;
SET_SELF_SIZE(BindingData)
SET_MEMORY_INFO_NAME(BindingData)
private:
InternalFieldInfo* internal_field_info_ = nullptr;
-
- static FilePathIsFileReturnType FilePathIsFile(Environment* env,
- const std::string& file_path);
-
- static const std::array<std::string, 10> legacy_main_extensions;
- // define the final index of the algorithm resolution
- // when packageConfig.main is defined.
- static const uint8_t legacy_main_extensions_with_main_end = 7;
- // define the final index of the algorithm resolution
- // when packageConfig.main is NOT defined
- static const uint8_t legacy_main_extensions_package_fallback_end = 10;
};
// structure used to store state during a complex operation, e.g., mkdirp.
diff --git a/test/es-module/test-cjs-legacyMainResolve.js b/test/es-module/test-cjs-legacyMainResolve.js
index 1dc7d8faafe6eb5cea7e43e9783041f2a994be0d..d86d501689b2b72f2b964d6e2a91c5d36b6b62f5 100644
--- a/test/es-module/test-cjs-legacyMainResolve.js
+++ b/test/es-module/test-cjs-legacyMainResolve.js
@@ -82,7 +82,7 @@ describe('legacyMainResolve', () => {
{},
''
),
- { message: /instance of URL/, code: 'ERR_INVALID_ARG_TYPE' },
+ { message: 'Invalid URL', code: 'ERR_INVALID_URL' },
);
});
@@ -129,7 +129,7 @@ describe('legacyMainResolve', () => {
);
assert.throws(
() => legacyMainResolve(packageJsonUrl, { main: null }, packageJsonUrl),
- { code: 'ERR_MODULE_NOT_FOUND' },
+ { code: 'ERR_INTERNAL_ASSERTION' },
);
});
@@ -137,7 +137,7 @@ describe('legacyMainResolve', () => {
const packageJsonUrl = pathToFileURL('/c/file%20with%20percents/package.json');
assert.throws(
() => legacyMainResolve(packageJsonUrl, { main: null }, packageJsonUrl),
- { code: 'ERR_MODULE_NOT_FOUND' },
+ { code: 'ERR_INTERNAL_ASSERTION' },
);
});
@@ -150,7 +150,7 @@ describe('legacyMainResolve', () => {
);
assert.throws(
() => legacyMainResolve(packageJsonUrl, { main: null }, undefined),
- { message: /"base" argument must be/, code: 'ERR_INVALID_ARG_TYPE' },
+ { message: 'The "path" argument must be of type string or an instance of URL. Received undefined', code: 'ERR_INVALID_ARG_TYPE' },
);
});
});

View File

@@ -1,23 +1,9 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
Date: Mon, 22 Jan 2024 13:45:55 +0900
Date: Mon, 4 Mar 2024 11:41:18 +0900
Subject: src: preload function for Environment
https://github.com/nodejs/node/pull/51539
This PR adds a |preload| arg to the node::CreateEnvironment to allow
embedders to set a preload function for the environment, which will run
after the environment is loaded and before the main script runs.
This is similiar to the --require CLI option, but runs a C++ function,
and can only be set by embedders.
The preload function can be used by embedders to inject scripts before
running the main script, for example:
1. In Electron it is used to initialize the ASAR virtual filesystem,
inject custom process properties, etc.
2. In VS Code it can be used to reset the module search paths for
extensions.
Backport https://github.com/nodejs/node/pull/51539
diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
index cbb89d76621de6a85b8e8697078d74c6bde0a742..4b1c0405a3bc7fb66d138b273cab05589b1a7360 100644
@@ -45,92 +31,80 @@ index cbb89d76621de6a85b8e8697078d74c6bde0a742..4b1c0405a3bc7fb66d138b273cab0558
// For user code, we preload modules if `-r` is passed
const preloadModules = getOptionValue('--require');
diff --git a/src/api/environment.cc b/src/api/environment.cc
index 74b4e15b8230c6380d41e84aa504824bb79b2ee5..e033760193fcd4fedd645c9259f58bc6b06250f4 100644
index 74b4e15b8230c6380d41e84aa504824bb79b2ee5..5ef231ba77b187ff4df3b3d2f3f4eec2b7667c92 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -437,7 +437,8 @@ Environment* CreateEnvironment(
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags,
ThreadId thread_id,
- std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
+ std::unique_ptr<InspectorParentHandle> inspector_parent_handle,
+ EmbedderPreloadCallback preload) {
Isolate* isolate = isolate_data->isolate();
HandleScope handle_scope(isolate);
@@ -549,25 +549,31 @@ NODE_EXTERN std::unique_ptr<InspectorParentHandle> GetInspectorParentHandle(
#endif
}
@@ -456,7 +457,8 @@ Environment* CreateEnvironment(
exec_args,
env_snapshot_info,
flags,
- thread_id);
+ thread_id,
+ std::move(preload));
CHECK_NOT_NULL(env);
-MaybeLocal<Value> LoadEnvironment(
- Environment* env,
- StartExecutionCallback cb) {
+MaybeLocal<Value> LoadEnvironment(Environment* env,
+ StartExecutionCallback cb,
+ EmbedderPreloadCallback preload) {
env->InitializeLibuv();
env->InitializeDiagnostics();
+ if (preload) {
+ env->set_embedder_preload(std::move(preload));
+ }
if (use_snapshot) {
return StartExecution(env, cb);
}
MaybeLocal<Value> LoadEnvironment(Environment* env,
- std::string_view main_script_source_utf8) {
+ std::string_view main_script_source_utf8,
+ EmbedderPreloadCallback preload) {
CHECK_NOT_NULL(main_script_source_utf8.data());
return LoadEnvironment(
- env, [&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {
+ env,
+ [&](const StartExecutionCallbackInfo& info) -> MaybeLocal<Value> {
Local<Value> main_script =
ToV8Value(env->context(), main_script_source_utf8).ToLocalChecked();
return info.run_cjs->Call(
env->context(), Null(env->isolate()), 1, &main_script);
- });
+ },
+ std::move(preload));
}
Environment* GetCurrentEnvironment(Local<Context> context) {
diff --git a/src/env-inl.h b/src/env-inl.h
index 524a9633ef16e48797dc6a1e507ca0be2bfffe7e..b5ec98128d4e08ef26121be070ac2ebe5e339534 100644
index 524a9633ef16e48797dc6a1e507ca0be2bfffe7e..9f30ebb821dfcbfe4c7c55332a28d076b09b9177 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -438,6 +438,10 @@ inline void Environment::set_embedder_entry_point(StartExecutionCallback&& fn) {
@@ -438,6 +438,14 @@ inline void Environment::set_embedder_entry_point(StartExecutionCallback&& fn) {
embedder_entry_point_ = std::move(fn);
}
+inline const EmbedderPreloadCallback& Environment::embedder_preload() const {
+ return embedder_preload_;
+}
+
+inline void Environment::set_embedder_preload(EmbedderPreloadCallback fn) {
+ embedder_preload_ = std::move(fn);
+}
+
inline double Environment::new_async_id() {
async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter] += 1;
return async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter];
diff --git a/src/env.cc b/src/env.cc
index 25b81dee18aeeb1bd0452ba0b66085d451723709..1b4e783ca1f4ac6e09ce9fc1caad30de85e3be91 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -763,7 +763,8 @@ Environment::Environment(IsolateData* isolate_data,
const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags,
- ThreadId thread_id)
+ ThreadId thread_id,
+ EmbedderPreloadCallback preload)
: isolate_(isolate),
isolate_data_(isolate_data),
async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)),
@@ -789,7 +790,8 @@ Environment::Environment(IsolateData* isolate_data,
flags_(flags),
thread_id_(thread_id.id == static_cast<uint64_t>(-1)
? AllocateEnvironmentThreadId().id
- : thread_id.id) {
+ : thread_id.id),
+ embedder_preload_(std::move(preload)) {
constexpr bool is_shared_ro_heap =
#ifdef NODE_V8_SHARED_RO_HEAP
true;
diff --git a/src/env.h b/src/env.h
index 448075e354c760a2dbd1dd763f40b7a645730250..a5aad9596953536b0a1f741dfbc4f21f6a961404 100644
index 448075e354c760a2dbd1dd763f40b7a645730250..d6956873b1b7bdf49ed0217587729aaa974ae89f 100644
--- a/src/env.h
+++ b/src/env.h
@@ -635,7 +635,8 @@ class Environment : public MemoryRetainer {
const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags,
- ThreadId thread_id);
+ ThreadId thread_id,
+ EmbedderPreloadCallback preload);
void InitializeMainContext(v8::Local<v8::Context> context,
const EnvSerializeInfo* env_info);
~Environment() override;
@@ -986,6 +987,8 @@ class Environment : public MemoryRetainer {
@@ -985,6 +985,8 @@ class Environment : public MemoryRetainer {
inline const StartExecutionCallback& embedder_entry_point() const;
inline void set_embedder_entry_point(StartExecutionCallback&& fn);
+ inline const EmbedderPreloadCallback& embedder_preload() const;
+
+ inline void set_embedder_preload(EmbedderPreloadCallback fn);
inline void set_process_exit_handler(
std::function<void(Environment*, ExitCode)>&& handler);
@@ -1186,6 +1189,7 @@ class Environment : public MemoryRetainer {
@@ -1186,6 +1188,7 @@ class Environment : public MemoryRetainer {
builtins::BuiltinLoader builtin_loader_;
StartExecutionCallback embedder_entry_point_;
@@ -139,43 +113,45 @@ index 448075e354c760a2dbd1dd763f40b7a645730250..a5aad9596953536b0a1f741dfbc4f21f
// Used by allocate_managed_buffer() and release_managed_buffer() to keep
// track of the BackingStore for a given pointer.
diff --git a/src/node.h b/src/node.h
index 3ffc51783b0b6dee1c0f0a37d2f52cb1aec2fa3f..4d88bbedb2fc2776d32cbaa755fd8657f17f78bc 100644
index 3ffc51783b0b6dee1c0f0a37d2f52cb1aec2fa3f..400a6b91ccb9875352012bffc21bc842f6febb9c 100644
--- a/src/node.h
+++ b/src/node.h
@@ -676,11 +676,23 @@ struct InspectorParentHandle {
virtual ~InspectorParentHandle() = default;
};
@@ -716,12 +716,33 @@ struct StartExecutionCallbackInfo {
using StartExecutionCallback =
std::function<v8::MaybeLocal<v8::Value>(const StartExecutionCallbackInfo&)>;
+using EmbedderPreloadCallback =
+ std::function<void(Environment* env,
+ v8::Local<v8::Value> process,
+ v8::Local<v8::Value> require)>;
+
// TODO(addaleax): Maybe move per-Environment options parsing here.
// Returns nullptr when the Environment cannot be created e.g. there are
// pending JavaScript exceptions.
// `context` may be empty if an `EmbedderSnapshotData` instance was provided
// to `NewIsolate()` and `CreateIsolateData()`.
+//
+// The |preload| function will run before executing the entry point, which
+// is usually used by embedders to inject scripts. The function is executed
+// with preload(process, require), and the passed require function has access
+// to internal Node.js modules. The |preload| function is inherited by worker
+// threads and thus will run in work threads, so make sure the function is
+// thread-safe.
NODE_EXTERN Environment* CreateEnvironment(
IsolateData* isolate_data,
v8::Local<v8::Context> context,
@@ -688,7 +700,8 @@ NODE_EXTERN Environment* CreateEnvironment(
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags = EnvironmentFlags::kDefaultFlags,
ThreadId thread_id = {} /* allocates a thread id automatically */,
- std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {});
+ std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {},
+ EmbedderPreloadCallback preload = nullptr);
// Returns a handle that can be passed to `LoadEnvironment()`, making the
// child Environment accessible to the inspector as if it were a Node.js Worker.
+// Run initialization for the environment.
+//
+// The |preload| function, usually used by embedders to inject scripts,
+// will be run by Node.js before Node.js executes the entry point.
+// The function is guaranteed to run before the user land module loader running
+// any user code, so it is safe to assume that at this point, no user code has
+// been run yet.
+// The function will be executed with preload(process, require), and the passed
+// require function has access to internal Node.js modules. There is no
+// stability guarantee about the internals exposed to the internal require
+// function. Expect breakages when updating Node.js versions if the embedder
+// imports internal modules with the internal require function.
+// Worker threads created in the environment will also respect The |preload|
+// function, so make sure the function is thread-safe.
NODE_EXTERN v8::MaybeLocal<v8::Value> LoadEnvironment(
Environment* env,
- StartExecutionCallback cb);
+ StartExecutionCallback cb,
+ EmbedderPreloadCallback preload = nullptr);
NODE_EXTERN v8::MaybeLocal<v8::Value> LoadEnvironment(
- Environment* env, std::string_view main_script_source_utf8);
+ Environment* env,
+ std::string_view main_script_source_utf8,
+ EmbedderPreloadCallback preload = nullptr);
NODE_EXTERN void FreeEnvironment(Environment* env);
// Set a callback that is called when process.exit() is called from JS,
diff --git a/src/node_options.cc b/src/node_options.cc
index 6eb2c137e1dd05b05e781820905cf6778107275d..b098837338c2c0d435ee8e659433f168c453dde5 100644
--- a/src/node_options.cc
@@ -194,24 +170,28 @@ index 6eb2c137e1dd05b05e781820905cf6778107275d..b098837338c2c0d435ee8e659433f168
}
diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc
index 562a47ddcc9c8e61590b7b09d84dc08ab4b3653d..325bebc1df9ad2e8b0bad468951cf1563ecefc14 100644
index 562a47ddcc9c8e61590b7b09d84dc08ab4b3653d..431cbe1c2cb77669ceb10602a7b3ef1c2f7e8718 100644
--- a/src/node_snapshotable.cc
+++ b/src/node_snapshotable.cc
@@ -1369,6 +1369,13 @@ static void RunEmbedderEntryPoint(const FunctionCallbackInfo<Value>& args) {
@@ -1369,6 +1369,17 @@ static void RunEmbedderEntryPoint(const FunctionCallbackInfo<Value>& args) {
}
}
+static void RunEmbedderPreload(const FunctionCallbackInfo<Value>& args) {
+void RunEmbedderPreload(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ CHECK(env->embedder_preload());
+ CHECK_EQ(args.Length(), 2);
+ env->embedder_preload()(env, args[0], args[1]);
+ Local<Value> process_obj = args[0];
+ Local<Value> require_fn = args[1];
+ CHECK(process_obj->IsObject());
+ CHECK(require_fn->IsFunction());
+ env->embedder_preload()(env, process_obj, require_fn);
+}
+
void CompileSerializeMain(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsString());
Local<String> filename = args[0].As<String>();
@@ -1493,6 +1500,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
@@ -1493,6 +1504,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
Local<ObjectTemplate> target) {
Isolate* isolate = isolate_data->isolate();
SetMethod(isolate, target, "runEmbedderEntryPoint", RunEmbedderEntryPoint);
@@ -219,7 +199,7 @@ index 562a47ddcc9c8e61590b7b09d84dc08ab4b3653d..325bebc1df9ad2e8b0bad468951cf156
SetMethod(isolate, target, "compileSerializeMain", CompileSerializeMain);
SetMethod(isolate, target, "setSerializeCallback", SetSerializeCallback);
SetMethod(isolate, target, "setDeserializeCallback", SetDeserializeCallback);
@@ -1506,6 +1514,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
@@ -1506,6 +1518,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(RunEmbedderEntryPoint);
@@ -228,7 +208,7 @@ index 562a47ddcc9c8e61590b7b09d84dc08ab4b3653d..325bebc1df9ad2e8b0bad468951cf156
registry->Register(SetSerializeCallback);
registry->Register(SetDeserializeCallback);
diff --git a/src/node_worker.cc b/src/node_worker.cc
index 900674bbe4c90e9aeb2013c06c9979864b06dcd5..2a22d986585e93ea00c6dcdca1f7b783ef0723f8 100644
index 900674bbe4c90e9aeb2013c06c9979864b06dcd5..52d7473b05ccb49e5fc915224b6d2972a14191da 100644
--- a/src/node_worker.cc
+++ b/src/node_worker.cc
@@ -63,6 +63,7 @@ Worker::Worker(Environment* env,
@@ -239,16 +219,20 @@ index 900674bbe4c90e9aeb2013c06c9979864b06dcd5..2a22d986585e93ea00c6dcdca1f7b783
snapshot_data_(snapshot_data) {
Debug(this, "Creating new worker instance with thread id %llu",
thread_id_.id);
@@ -360,7 +361,8 @@ void Worker::Run() {
std::move(exec_argv_),
static_cast<EnvironmentFlags::Flags>(environment_flags_),
thread_id_,
- std::move(inspector_parent_handle_)));
+ std::move(inspector_parent_handle_),
+ std::move(embedder_preload_)));
if (is_stopped()) return;
CHECK_NOT_NULL(env_);
env_->set_env_vars(std::move(env_vars_));
@@ -381,8 +382,12 @@ void Worker::Run() {
}
Debug(this, "Created message port for worker %llu", thread_id_.id);
- if (LoadEnvironment(env_.get(), StartExecutionCallback{}).IsEmpty())
+ if (LoadEnvironment(env_.get(),
+ StartExecutionCallback{},
+ std::move(embedder_preload_))
+ .IsEmpty()) {
return;
+ }
Debug(this, "Loaded environment for worker %llu", thread_id_.id);
}
diff --git a/src/node_worker.h b/src/node_worker.h
index 531e2b5287010f9206ab4fd7f4dd0f3dec9fe55c..07fd7b460654e169e8b6822474dc3cc70fcec4c0 100644
--- a/src/node_worker.h
@@ -262,7 +246,7 @@ index 531e2b5287010f9206ab4fd7f4dd0f3dec9fe55c..07fd7b460654e169e8b6822474dc3cc7
// A raw flag that is used by creator and worker threads to
// sync up on pre-mature termination of worker - while in the
diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc
index 2e747c7be58922897abd0424b797f3f12a89ada1..658f8df4b01d60759e858cf5283b9be9467dd142 100644
index 2e747c7be58922897abd0424b797f3f12a89ada1..fcffaca89cf5aa24be6e539bfb4d9d6df690a709 100644
--- a/test/cctest/test_environment.cc
+++ b/test/cctest/test_environment.cc
@@ -773,3 +773,31 @@ TEST_F(EnvironmentTest, RequestInterruptAtExit) {
@@ -280,20 +264,20 @@ index 2e747c7be58922897abd0424b797f3f12a89ada1..658f8df4b01d60759e858cf5283b9be9
+ v8::Local<v8::Value> require) {
+ CHECK(process->IsObject());
+ CHECK(require->IsFunction());
+ process.As<v8::Object>()->Set(
+ env->context(),
+ v8::String::NewFromUtf8Literal(env->isolate(), "prop"),
+ v8::String::NewFromUtf8Literal(env->isolate(), "preload")).Check();
+ process.As<v8::Object>()
+ ->Set(env->context(),
+ v8::String::NewFromUtf8Literal(env->isolate(), "prop"),
+ v8::String::NewFromUtf8Literal(env->isolate(), "preload"))
+ .Check();
+ };
+
+ std::unique_ptr<node::Environment, decltype(&node::FreeEnvironment)> env(
+ node::CreateEnvironment(isolate_data_, context, {}, {},
+ node::EnvironmentFlags::kDefaultFlags, {}, {},
+ preload),
+ node::CreateEnvironment(isolate_data_, context, {}, {}),
+ node::FreeEnvironment);
+
+ v8::Local<v8::Value> main_ret =
+ node::LoadEnvironment(env.get(), "return process.prop;").ToLocalChecked();
+ node::LoadEnvironment(env.get(), "return process.prop;", preload)
+ .ToLocalChecked();
+ node::Utf8Value main_ret_str(isolate_, main_ret);
+ EXPECT_EQ(std::string(*main_ret_str), "preload");
+}

View File

@@ -13,7 +13,10 @@ import re
import subprocess
import sys
from .patches import PATCH_FILENAME_PREFIX, is_patch_location_line
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(SCRIPT_DIR)
from patches import PATCH_FILENAME_PREFIX, is_patch_location_line
UPSTREAM_HEAD='refs/patches/upstream-head'

View File

@@ -27,7 +27,9 @@ const getHeaders = (filePath: string, fileName: string) => {
if (!extension) {
throw new Error(`Failed to get headers for extensionless file: ${fileName}`);
}
console.log(`About to get size of ${filePath}`);
const size = fs.statSync(filePath).size;
console.log(`Got size of ${filePath}: ${size}`);
const options: Record<string, string> = {
json: 'text/json',
zip: 'application/zip',
@@ -46,10 +48,13 @@ const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/relea
let retry = 0;
function uploadToGitHub () {
console.log(`in uploadToGitHub for ${filePath}, ${fileName}`);
const fileData = fs.createReadStream(filePath);
console.log(`in uploadToGitHub, created readstream for ${filePath}`);
octokit.repos.uploadReleaseAsset({
url: uploadUrl,
headers: getHeaders(filePath, fileName),
data: fs.createReadStream(filePath) as any,
data: fileData as any,
name: fileName,
owner: 'electron',
repo: targetRepo,

View File

@@ -362,10 +362,13 @@ def upload_io_to_github(release, filename, filepath, version):
(filename))
script_path = os.path.join(
ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-to-github.ts')
upload_gh_output = execute([TS_NODE, script_path, filepath, filename,
str(release['id']), version])
upload_process = subprocess.Popen([TS_NODE, script_path, filepath, filename,
str(release['id']), version], stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
if is_verbose_mode():
print(upload_gh_output)
for c in iter(lambda: upload_process.stdout.read(1), b""):
sys.stdout.buffer.write(c)
sys.stdout.flush()
def upload_sha256_checksum(version, file_path, key_prefix=None):

View File

@@ -259,8 +259,7 @@ int NodeMain(int argc, char* argv[]) {
env = node::CreateEnvironment(
isolate_data, isolate->GetCurrentContext(), result->args(),
result->exec_args(),
static_cast<node::EnvironmentFlags::Flags>(env_flags), {}, {},
&OnNodePreload);
static_cast<node::EnvironmentFlags::Flags>(env_flags));
CHECK_NE(nullptr, env);
node::SetIsolateUpForNode(isolate);
@@ -285,7 +284,7 @@ int NodeMain(int argc, char* argv[]) {
}
v8::HandleScope scope(isolate);
node::LoadEnvironment(env, node::StartExecutionCallback{});
node::LoadEnvironment(env, node::StartExecutionCallback{}, &OnNodePreload);
// Potential reasons we get Nothing here may include: the env
// is stopping, or the user hooks process.emit('exit').

View File

@@ -26,6 +26,9 @@
#include "chrome/browser/icon_manager.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/proxy_config/proxy_prefs.h"
#include "content/browser/gpu/compositor_util.h" // nogncheck
#include "content/browser/gpu/gpu_data_manager_impl.h" // nogncheck
#include "content/public/browser/browser_accessibility_state.h"
@@ -1472,6 +1475,98 @@ void App::EnableSandbox(gin_helper::ErrorThrower thrower) {
command_line->AppendSwitch(switches::kEnableSandbox);
}
v8::Local<v8::Promise> App::SetProxy(gin::Arguments* args) {
v8::Isolate* isolate = args->isolate();
gin_helper::Promise<void> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
gin_helper::Dictionary options;
args->GetNext(&options);
if (!Browser::Get()->is_ready()) {
promise.RejectWithErrorMessage(
"app.setProxy() can only be called after app is ready.");
return handle;
}
if (!g_browser_process->local_state()) {
promise.RejectWithErrorMessage(
"app.setProxy() failed due to internal error.");
return handle;
}
std::string mode, proxy_rules, bypass_list, pac_url;
options.Get("pacScript", &pac_url);
options.Get("proxyRules", &proxy_rules);
options.Get("proxyBypassRules", &bypass_list);
ProxyPrefs::ProxyMode proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS;
if (!options.Get("mode", &mode)) {
// pacScript takes precedence over proxyRules.
if (!pac_url.empty()) {
proxy_mode = ProxyPrefs::MODE_PAC_SCRIPT;
}
} else if (!ProxyPrefs::StringToProxyMode(mode, &proxy_mode)) {
promise.RejectWithErrorMessage(
"Invalid mode, must be one of direct, auto_detect, pac_script, "
"fixed_servers or system");
return handle;
}
base::Value::Dict proxy_config;
switch (proxy_mode) {
case ProxyPrefs::MODE_DIRECT:
proxy_config = ProxyConfigDictionary::CreateDirect();
break;
case ProxyPrefs::MODE_SYSTEM:
proxy_config = ProxyConfigDictionary::CreateSystem();
break;
case ProxyPrefs::MODE_AUTO_DETECT:
proxy_config = ProxyConfigDictionary::CreateAutoDetect();
break;
case ProxyPrefs::MODE_PAC_SCRIPT:
proxy_config = ProxyConfigDictionary::CreatePacScript(pac_url, true);
break;
case ProxyPrefs::MODE_FIXED_SERVERS:
proxy_config =
ProxyConfigDictionary::CreateFixedServers(proxy_rules, bypass_list);
break;
default:
NOTIMPLEMENTED();
}
static_cast<BrowserProcessImpl*>(g_browser_process)
->in_memory_pref_store()
->SetValue(proxy_config::prefs::kProxy,
base::Value{std::move(proxy_config)},
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
g_browser_process->system_network_context_manager()
->GetContext()
->ForceReloadProxyConfig(base::BindOnce(
gin_helper::Promise<void>::ResolvePromise, std::move(promise)));
return handle;
}
v8::Local<v8::Promise> App::ResolveProxy(gin::Arguments* args) {
v8::Isolate* isolate = args->isolate();
gin_helper::Promise<std::string> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
GURL url;
args->GetNext(&url);
static_cast<BrowserProcessImpl*>(g_browser_process)
->GetResolveProxyHelper()
->ResolveProxy(
url, base::BindOnce(gin_helper::Promise<std::string>::ResolvePromise,
std::move(promise)));
return handle;
}
void App::SetUserAgentFallback(const std::string& user_agent) {
ElectronBrowserClient::Get()->SetUserAgent(user_agent);
}
@@ -1681,7 +1776,7 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
.SetMethod("setBadgeCount",
base::BindRepeating(&Browser::SetBadgeCount, browser))
.SetMethod("getBadgeCount",
base::BindRepeating(&Browser::GetBadgeCount, browser))
base::BindRepeating(&Browser::badge_count, browser))
.SetMethod("getLoginItemSettings", &App::GetLoginItemSettings)
.SetMethod("setLoginItemSettings",
base::BindRepeating(&Browser::SetLoginItemSettings, browser))
@@ -1776,7 +1871,9 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
.SetProperty("userAgentFallback", &App::GetUserAgentFallback,
&App::SetUserAgentFallback)
.SetMethod("configureHostResolver", &ConfigureHostResolver)
.SetMethod("enableSandbox", &App::EnableSandbox);
.SetMethod("enableSandbox", &App::EnableSandbox)
.SetMethod("setProxy", &App::SetProxy)
.SetMethod("resolveProxy", &App::ResolveProxy);
}
const char* App::GetTypeName() {

View File

@@ -222,6 +222,8 @@ class App : public ElectronBrowserClient::Delegate,
void EnableSandbox(gin_helper::ErrorThrower thrower);
void SetUserAgentFallback(const std::string& user_agent);
std::string GetUserAgentFallback();
v8::Local<v8::Promise> SetProxy(gin::Arguments* args);
v8::Local<v8::Promise> ResolveProxy(gin::Arguments* args);
#if BUILDFLAG(IS_MAC)
void SetActivationPolicy(gin_helper::ErrorThrower thrower,

View File

@@ -162,6 +162,10 @@ v8::Local<v8::Promise> DataPipeHolder::ReadAll(v8::Isolate* isolate) {
return handle;
}
const char* DataPipeHolder::GetTypeName() {
return "DataPipeHolder";
}
// static
gin::Handle<DataPipeHolder> DataPipeHolder::Create(
v8::Isolate* isolate,

View File

@@ -18,7 +18,9 @@ namespace electron::api {
// Retains reference to the data pipe.
class DataPipeHolder : public gin::Wrappable<DataPipeHolder> {
public:
// gin::Wrappable
static gin::WrapperInfo kWrapperInfo;
const char* GetTypeName() override;
static gin::Handle<DataPipeHolder> Create(
v8::Isolate* isolate,

View File

@@ -22,41 +22,45 @@
- (id)init {
if ((self = [super init])) {
NSDistributedNotificationCenter* distCenter =
NSDistributedNotificationCenter* distributed_center =
[NSDistributedNotificationCenter defaultCenter];
// A notification that the screen was locked.
[distCenter addObserver:self
selector:@selector(onScreenLocked:)
name:@"com.apple.screenIsLocked"
object:nil];
[distributed_center addObserver:self
selector:@selector(onScreenLocked:)
name:@"com.apple.screenIsLocked"
object:nil];
// A notification that the screen was unlocked by the user.
[distCenter addObserver:self
selector:@selector(onScreenUnlocked:)
name:@"com.apple.screenIsUnlocked"
object:nil];
[distributed_center addObserver:self
selector:@selector(onScreenUnlocked:)
name:@"com.apple.screenIsUnlocked"
object:nil];
// A notification that the workspace posts before the machine goes to sleep.
[distCenter addObserver:self
selector:@selector(isSuspending:)
name:NSWorkspaceWillSleepNotification
object:nil];
[distributed_center addObserver:self
selector:@selector(isSuspending:)
name:NSWorkspaceWillSleepNotification
object:nil];
// A notification that the workspace posts when the machine wakes from
// sleep.
[distCenter addObserver:self
selector:@selector(isResuming:)
name:NSWorkspaceDidWakeNotification
object:nil];
[distributed_center addObserver:self
selector:@selector(isResuming:)
name:NSWorkspaceDidWakeNotification
object:nil];
NSNotificationCenter* shared_center =
[[NSWorkspace sharedWorkspace] notificationCenter];
// A notification that the workspace posts when the user session becomes
// active.
[distCenter addObserver:self
selector:@selector(onUserDidBecomeActive:)
name:NSWorkspaceSessionDidBecomeActiveNotification
object:nil];
[shared_center addObserver:self
selector:@selector(onUserDidBecomeActive:)
name:NSWorkspaceSessionDidBecomeActiveNotification
object:nil];
// A notification that the workspace posts when the user session becomes
// inactive.
[distCenter addObserver:self
selector:@selector(onUserDidResignActive:)
name:NSWorkspaceSessionDidResignActiveNotification
object:nil];
[shared_center addObserver:self
selector:@selector(onUserDidResignActive:)
name:NSWorkspaceSessionDidResignActiveNotification
object:nil];
}
return self;
}

View File

@@ -124,6 +124,10 @@ gin::ObjectTemplateBuilder PowerSaveBlocker::GetObjectTemplateBuilder(
.SetMethod("isStarted", &PowerSaveBlocker::IsStarted);
}
const char* PowerSaveBlocker::GetTypeName() {
return "PowerSaveBlocker";
}
} // namespace electron::api
namespace {

View File

@@ -20,10 +20,10 @@ class PowerSaveBlocker : public gin::Wrappable<PowerSaveBlocker> {
static gin::Handle<PowerSaveBlocker> Create(v8::Isolate* isolate);
// gin::Wrappable
static gin::WrapperInfo kWrapperInfo;
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
static gin::WrapperInfo kWrapperInfo;
const char* GetTypeName() override;
// disable copy
PowerSaveBlocker(const PowerSaveBlocker&) = delete;

View File

@@ -31,7 +31,7 @@ bool IsEncryptionAvailable() {
return OSCrypt::IsEncryptionAvailable() ||
(use_password_v10 &&
static_cast<BrowserProcessImpl*>(g_browser_process)
->GetLinuxStorageBackend() == "basic_text");
->linux_storage_backend() == "basic_text");
#else
return OSCrypt::IsEncryptionAvailable();
#endif
@@ -46,7 +46,7 @@ std::string GetSelectedLinuxBackend() {
if (!Browser::Get()->is_ready())
return "unknown";
return static_cast<BrowserProcessImpl*>(g_browser_process)
->GetLinuxStorageBackend();
->linux_storage_backend();
}
#endif

View File

@@ -859,7 +859,7 @@ WebContents::WebContents(v8::Isolate* isolate,
session_.Reset(isolate, session.ToV8());
std::unique_ptr<content::WebContents> web_contents;
if (IsGuest()) {
if (is_guest()) {
scoped_refptr<content::SiteInstance> site_instance =
content::SiteInstance::CreateForURL(session->browser_context(),
GURL("chrome-guest://fake-host"));
@@ -929,7 +929,7 @@ void WebContents::InitWithSessionAndOptions(
const gin_helper::Dictionary& options) {
Observe(owned_web_contents.get());
InitWithWebContents(std::move(owned_web_contents), session->browser_context(),
IsGuest());
is_guest());
inspectable_web_contents_->GetView()->SetDelegate(this);
@@ -989,7 +989,7 @@ void WebContents::InitWithSessionAndOptions(
SetUserAgent(GetBrowserContext()->GetUserAgent());
if (IsGuest()) {
if (is_guest()) {
NativeWindow* owner_window = nullptr;
if (embedder_) {
// New WebContents's owner_window is the embedder's owner_window.
@@ -1024,7 +1024,7 @@ void WebContents::InitWithExtensionView(v8::Isolate* isolate,
// Allow toggling DevTools for background pages
Observe(web_contents);
InitWithWebContents(std::unique_ptr<content::WebContents>(web_contents),
GetBrowserContext(), IsGuest());
GetBrowserContext(), is_guest());
inspectable_web_contents_->GetView()->SetDelegate(this);
}
#endif
@@ -1073,7 +1073,7 @@ WebContents::~WebContents() {
// For guest view based on OOPIF, the WebContents is released by the embedder
// frame, and we need to clear the reference to the memory.
bool not_owned_by_this = IsGuest() && attached_;
bool not_owned_by_this = is_guest() && attached_;
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
// And background pages are owned by extensions::ExtensionHost.
if (type_ == Type::kBackgroundPage)
@@ -1103,7 +1103,7 @@ void WebContents::DeleteThisIfAlive() {
void WebContents::Destroy() {
// The content::WebContents should be destroyed asynchronously when possible
// as user may choose to destroy WebContents during an event of it.
if (Browser::Get()->is_shutting_down() || IsGuest()) {
if (Browser::Get()->is_shutting_down() || is_guest()) {
DeleteThisIfAlive();
} else {
content::GetUIThreadTaskRunner({})->PostTask(
@@ -1317,7 +1317,7 @@ void WebContents::CloseContents(content::WebContents* source) {
observer.OnCloseContents();
// This is handled by the embedder frame.
if (!IsGuest())
if (!is_guest())
Destroy();
}
@@ -1631,7 +1631,7 @@ void WebContents::HandleNewRenderFrame(
auto* web_preferences = WebContentsPreferences::From(web_contents());
if (web_preferences) {
auto maybe_color = web_preferences->GetBackgroundColor();
bool guest = IsGuest() || type_ == Type::kBrowserView;
bool guest = is_guest() || type_ == Type::kBrowserView;
// If webPreferences has no color stored we need to explicitly set guest
// webContents background color to transparent.
@@ -2134,7 +2134,7 @@ void WebContents::DidFinishNavigation(
Emit("did-navigate", url, http_response_code, http_status_text);
}
}
if (IsGuest())
if (is_guest())
Emit("load-commit", url, is_main_frame);
} else {
auto url = navigation_handle->GetURL();
@@ -2348,10 +2348,6 @@ base::ProcessId WebContents::GetOSProcessID() const {
return base::GetProcId(process_handle);
}
WebContents::Type WebContents::GetType() const {
return type_;
}
bool WebContents::Equal(const WebContents* web_contents) const {
return ID() == web_contents->ID();
}
@@ -3345,7 +3341,7 @@ bool WebContents::IsFocused() const {
if (!view)
return false;
if (GetType() != Type::kBackgroundPage) {
if (type() != Type::kBackgroundPage) {
auto* window = web_contents()->GetNativeView()->GetToplevelWindow();
if (window && !window->IsVisible())
return false;
@@ -3541,10 +3537,6 @@ void WebContents::OnCursorChanged(const ui::Cursor& cursor) {
}
}
bool WebContents::IsGuest() const {
return type_ == Type::kWebView;
}
void WebContents::AttachToIframe(content::WebContents* embedder_web_contents,
int embedder_frame_id) {
attached_ = true;
@@ -4260,7 +4252,7 @@ void WebContents::UpdateHtmlApiFullscreen(bool fullscreen) {
}
// Make sure all child webviews quit html fullscreen.
if (!fullscreen && !IsGuest()) {
if (!fullscreen && !is_guest()) {
auto* manager = WebViewManager::GetWebViewManager(web_contents());
manager->ForEachGuest(web_contents(), [&](content::WebContents* guest) {
WebContents* api_web_contents = WebContents::From(guest);
@@ -4372,7 +4364,7 @@ void WebContents::FillObjectTemplate(v8::Isolate* isolate,
.SetMethod("getZoomLevel", &WebContents::GetZoomLevel)
.SetMethod("setZoomFactor", &WebContents::SetZoomFactor)
.SetMethod("getZoomFactor", &WebContents::GetZoomFactor)
.SetMethod("getType", &WebContents::GetType)
.SetMethod("getType", &WebContents::type)
.SetMethod("_getPreloadPaths", &WebContents::GetPreloadPaths)
.SetMethod("getLastWebPreferences", &WebContents::GetLastWebPreferences)
.SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)

View File

@@ -169,7 +169,7 @@ class WebContents : public ExclusiveAccessContext,
void SetBackgroundThrottling(bool allowed);
int GetProcessID() const;
base::ProcessId GetOSProcessID() const;
Type GetType() const;
[[nodiscard]] Type type() const { return type_; }
bool Equal(const WebContents* web_contents) const;
void LoadURL(const GURL& url, const gin_helper::Dictionary& options);
void Reload();
@@ -288,7 +288,7 @@ class WebContents : public ExclusiveAccessContext,
v8::Local<v8::Promise> CapturePage(gin::Arguments* args);
// Methods for creating <webview>.
bool IsGuest() const;
[[nodiscard]] bool is_guest() const { return type_ == Type::kWebView; }
void AttachToIframe(content::WebContents* embedder_web_contents,
int embedder_frame_id);
void DetachFromOuterFrame();

View File

@@ -24,7 +24,7 @@ bool WebContents::IsFocused() const {
if (!view)
return false;
if (GetType() != Type::kBackgroundPage) {
if (type() != Type::kBackgroundPage) {
auto window = [web_contents()->GetNativeView().GetNativeNSView() window];
// On Mac the render widget host view does not lose focus when the window
// loses focus so check if the top level window is the key window.

View File

@@ -24,8 +24,6 @@ namespace electron::api {
class WebRequest : public gin::Wrappable<WebRequest>, public WebRequestAPI {
public:
static gin::WrapperInfo kWrapperInfo;
// Return the WebRequest object attached to |browser_context|, create if there
// is no one.
// Note that the lifetime of WebRequest object is managed by Session, instead
@@ -44,6 +42,7 @@ class WebRequest : public gin::Wrappable<WebRequest>, public WebRequestAPI {
content::BrowserContext* browser_context);
// gin::Wrappable:
static gin::WrapperInfo kWrapperInfo;
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
const char* GetTypeName() override;

View File

@@ -32,8 +32,7 @@ GPUInfoManager::~GPUInfoManager() {
// https://chromium.googlesource.com/chromium/src.git/+/69.0.3497.106/content/browser/gpu/gpu_data_manager_impl_private.cc#838
bool GPUInfoManager::NeedsCompleteGpuInfoCollection() const {
#if BUILDFLAG(IS_WIN)
return gpu_data_manager_->DxdiagDx12VulkanRequested() &&
gpu_data_manager_->GetGPUInfo().dx_diagnostics.IsEmpty();
return gpu_data_manager_->DxdiagDx12VulkanRequested();
#else
return false;
#endif
@@ -51,9 +50,6 @@ void GPUInfoManager::ProcessCompleteInfo() {
}
void GPUInfoManager::OnGpuInfoUpdate() {
// Ignore if called when not asked for complete GPUInfo
if (NeedsCompleteGpuInfoCollection())
return;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&GPUInfoManager::ProcessCompleteInfo,
base::Unretained(this)));
@@ -64,12 +60,8 @@ void GPUInfoManager::CompleteInfoFetcher(
gin_helper::Promise<base::Value> promise) {
complete_info_promise_set_.emplace_back(std::move(promise));
if (NeedsCompleteGpuInfoCollection()) {
gpu_data_manager_->RequestDxdiagDx12VulkanVideoGpuInfoIfNeeded(
content::GpuDataManagerImpl::kGpuInfoRequestAll, /* delayed */ false);
} else {
GPUInfoManager::OnGpuInfoUpdate();
}
gpu_data_manager_->RequestDx12VulkanVideoGpuInfoIfNeeded(
content::GpuDataManagerImpl::kGpuInfoRequestAll, /* delayed */ false);
}
void GPUInfoManager::FetchCompleteInfo(

View File

@@ -53,9 +53,9 @@ class MessagePort : public gin::Wrappable<MessagePort>,
bool* threw_exception);
// gin::Wrappable
static gin::WrapperInfo kWrapperInfo;
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
static gin::WrapperInfo kWrapperInfo;
const char* GetTypeName() override;
private:

View File

@@ -21,7 +21,8 @@ BadgeManager* BadgeManagerFactory::GetForBrowserContext(
// static
BadgeManagerFactory* BadgeManagerFactory::GetInstance() {
return base::Singleton<BadgeManagerFactory>::get();
static base::NoDestructor<BadgeManagerFactory> instance;
return instance.get();
}
BadgeManagerFactory::BadgeManagerFactory()

View File

@@ -9,7 +9,7 @@
namespace base {
template <typename T>
struct DefaultSingletonTraits;
class NoDestructor;
}
namespace badging {
@@ -30,7 +30,7 @@ class BadgeManagerFactory : public BrowserContextKeyedServiceFactory {
BadgeManagerFactory& operator=(const BadgeManagerFactory&) = delete;
private:
friend struct base::DefaultSingletonTraits<BadgeManagerFactory>;
friend base::NoDestructor<BadgeManagerFactory>;
BadgeManagerFactory();
~BadgeManagerFactory() override;

View File

@@ -159,10 +159,6 @@ void Browser::SetName(const std::string& name) {
OverriddenApplicationName() = name;
}
int Browser::GetBadgeCount() {
return badge_count_;
}
bool Browser::OpenFile(const std::string& file_path) {
bool prevent_default = false;
for (BrowserObserver& observer : observers_)

View File

@@ -110,7 +110,7 @@ class Browser : public WindowListObserver {
// Set/Get the badge count.
bool SetBadgeCount(std::optional<int> count);
int GetBadgeCount();
[[nodiscard]] int badge_count() const { return badge_count_; }
#if BUILDFLAG(IS_WIN)
struct LaunchItem {

View File

@@ -37,6 +37,7 @@
#include "net/proxy_resolution/proxy_config_with_annotation.h"
#include "services/device/public/cpp/geolocation/geolocation_manager.h"
#include "services/network/public/cpp/network_switches.h"
#include "shell/browser/net/resolve_proxy_helper.h"
#include "shell/common/electron_paths.h"
#include "shell/common/thread_restrictions.h"
@@ -100,9 +101,9 @@ void BrowserProcessImpl::PostEarlyInitialization() {
OSCrypt::RegisterLocalPrefs(pref_registry.get());
#endif
auto pref_store = base::MakeRefCounted<ValueMapPrefStore>();
ApplyProxyModeFromCommandLine(pref_store.get());
prefs_factory.set_command_line_prefs(std::move(pref_store));
in_memory_pref_store_ = base::MakeRefCounted<ValueMapPrefStore>();
ApplyProxyModeFromCommandLine(in_memory_pref_store());
prefs_factory.set_command_line_prefs(in_memory_pref_store());
// Only use a persistent prefs store when cookie encryption is enabled as that
// is the only key that needs it
@@ -316,6 +317,14 @@ const std::string& BrowserProcessImpl::GetSystemLocale() const {
return system_locale_;
}
electron::ResolveProxyHelper* BrowserProcessImpl::GetResolveProxyHelper() {
if (!resolve_proxy_helper_) {
resolve_proxy_helper_ = base::MakeRefCounted<electron::ResolveProxyHelper>(
system_network_context_manager()->GetContext());
}
return resolve_proxy_helper_.get();
}
#if BUILDFLAG(IS_LINUX)
void BrowserProcessImpl::SetLinuxStorageBackend(
os_crypt::SelectedLinuxBackend selected_backend) {
@@ -340,10 +349,6 @@ void BrowserProcessImpl::SetLinuxStorageBackend(
break;
}
}
const std::string& BrowserProcessImpl::GetLinuxStorageBackend() const {
return selected_linux_storage_backend_;
}
#endif // BUILDFLAG(IS_LINUX)
void BrowserProcessImpl::SetApplicationLocale(const std::string& locale) {

View File

@@ -31,6 +31,10 @@ namespace printing {
class PrintJobManager;
}
namespace electron {
class ResolveProxyHelper;
}
// Empty definition for std::unique_ptr, rather than a forward declaration
class BackgroundModeManager {};
@@ -53,13 +57,15 @@ class BrowserProcessImpl : public BrowserProcess {
void PreMainMessageLoopRun();
void PostDestroyThreads() {}
void PostMainMessageLoopRun();
void SetSystemLocale(const std::string& locale);
const std::string& GetSystemLocale() const;
electron::ResolveProxyHelper* GetResolveProxyHelper();
#if BUILDFLAG(IS_LINUX)
void SetLinuxStorageBackend(os_crypt::SelectedLinuxBackend selected_backend);
const std::string& GetLinuxStorageBackend() const;
[[nodiscard]] const std::string& linux_storage_backend() const {
return selected_linux_storage_backend_;
}
#endif
void EndSession() override {}
@@ -121,6 +127,10 @@ class BrowserProcessImpl : public BrowserProcess {
printing::PrintJobManager* print_job_manager() override;
StartupData* startup_data() override;
ValueMapPrefStore* in_memory_pref_store() const {
return in_memory_pref_store_.get();
}
private:
void CreateNetworkQualityObserver();
void CreateOSCryptAsync();
@@ -137,6 +147,8 @@ class BrowserProcessImpl : public BrowserProcess {
#endif
embedder_support::OriginTrialsSettingsStorage origin_trials_settings_storage_;
scoped_refptr<ValueMapPrefStore> in_memory_pref_store_;
scoped_refptr<electron::ResolveProxyHelper> resolve_proxy_helper_;
std::unique_ptr<network::NetworkQualityTracker> network_quality_tracker_;
std::unique_ptr<
network::NetworkQualityTracker::RTTAndThroughputEstimatesObserver>

View File

@@ -527,7 +527,8 @@ ElectronBrowserContext::GetReduceAcceptLanguageControllerDelegate() {
ResolveProxyHelper* ElectronBrowserContext::GetResolveProxyHelper() {
if (!resolve_proxy_helper_) {
resolve_proxy_helper_ = base::MakeRefCounted<ResolveProxyHelper>(this);
resolve_proxy_helper_ = base::MakeRefCounted<ResolveProxyHelper>(
GetDefaultStoragePartition()->GetNetworkContext());
}
return resolve_proxy_helper_.get();
}

View File

@@ -4,6 +4,7 @@
#include "shell/browser/extensions/electron_extension_system_factory.h"
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "extensions/browser/extension_prefs_factory.h"
#include "extensions/browser/extension_registry_factory.h"
@@ -21,7 +22,8 @@ ExtensionSystem* ElectronExtensionSystemFactory::GetForBrowserContext(
// static
ElectronExtensionSystemFactory* ElectronExtensionSystemFactory::GetInstance() {
return base::Singleton<ElectronExtensionSystemFactory>::get();
static base::NoDestructor<ElectronExtensionSystemFactory> instance;
return instance.get();
}
ElectronExtensionSystemFactory::ElectronExtensionSystemFactory()

View File

@@ -5,9 +5,13 @@
#ifndef ELECTRON_SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_FACTORY_H_
#define ELECTRON_SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_FACTORY_H_
#include "base/memory/singleton.h"
#include "extensions/browser/extension_system_provider.h"
namespace base {
template <typename T>
class NoDestructor;
} // namespace base
namespace extensions {
// A factory that provides ElectronExtensionSystem.
@@ -26,7 +30,7 @@ class ElectronExtensionSystemFactory : public ExtensionSystemProvider {
const ElectronExtensionSystemFactory&) = delete;
private:
friend struct base::DefaultSingletonTraits<ElectronExtensionSystemFactory>;
friend base::NoDestructor<ElectronExtensionSystemFactory>;
ElectronExtensionSystemFactory();
~ElectronExtensionSystemFactory() override;

View File

@@ -49,6 +49,11 @@ void InitializeFeatureList() {
// 'custom dictionary word list API' spec to crash.
std::string(",") + spellcheck::kWinDelaySpellcheckServiceInit.name;
#endif
std::string platform_specific_enable_features =
EnablePlatformSpecificFeatures();
if (platform_specific_enable_features.size() > 0) {
enable_features += std::string(",") + platform_specific_enable_features;
}
base::FeatureList::InitInstance(enable_features, disable_features);
}
@@ -60,4 +65,10 @@ void InitializeFieldTrials() {
base::FieldTrialList::CreateTrialsFromString(force_fieldtrials);
}
#if !BUILDFLAG(IS_MAC)
std::string EnablePlatformSpecificFeatures() {
return "";
}
#endif
} // namespace electron

View File

@@ -5,9 +5,12 @@
#ifndef ELECTRON_SHELL_BROWSER_FEATURE_LIST_H_
#define ELECTRON_SHELL_BROWSER_FEATURE_LIST_H_
#include <string>
namespace electron {
void InitializeFeatureList();
void InitializeFieldTrials();
std::string EnablePlatformSpecificFeatures();
} // namespace electron
#endif // ELECTRON_SHELL_BROWSER_FEATURE_LIST_H_

View File

@@ -0,0 +1,28 @@
// Copyright (c) 2024 Salesforce, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "electron/shell/browser/feature_list.h"
#include <string>
namespace electron {
std::string EnablePlatformSpecificFeatures() {
if (@available(macOS 14.4, *)) {
// These flags aren't exported so reference them by name directly, they are
// used to ensure that screen and window capture exclusive use
// ScreenCaptureKit APIs to avoid warning dialogs on macOS 14.4 and higher.
// kScreenCaptureKitPickerScreen,
// chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
// kScreenCaptureKitStreamPickerSonoma,
// chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
// kThumbnailCapturerMac,
// chrome/browser/media/webrtc/thumbnail_capturer_mac.mm
return "ScreenCaptureKitPickerScreen,ScreenCaptureKitStreamPickerSonoma,"
"ThumbnailCapturerMac:capture_mode/sc_screenshot_manager";
}
return "";
}
} // namespace electron

View File

@@ -3,7 +3,7 @@
// found in the LICENSE-CHROMIUM file.
#include "shell/browser/media/media_capture_devices_dispatcher.h"
// #include "base/no_destructor.h"
#include "base/logging.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/media_capture_devices.h"
@@ -13,7 +13,8 @@ using content::BrowserThread;
namespace electron {
MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
return base::Singleton<MediaCaptureDevicesDispatcher>::get();
static base::NoDestructor<MediaCaptureDevicesDispatcher> instance;
return instance.get();
}
MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() {

View File

@@ -5,7 +5,6 @@
#ifndef ELECTRON_SHELL_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
#define ELECTRON_SHELL_BROWSER_MEDIA_MEDIA_CAPTURE_DEVICES_DISPATCHER_H_
#include "base/memory/singleton.h"
#include "components/webrtc/media_stream_device_enumerator_impl.h"
#include "content/public/browser/media_observer.h"
#include "content/public/browser/media_stream_request.h"
@@ -45,7 +44,7 @@ class MediaCaptureDevicesDispatcher
const MediaCaptureDevicesDispatcher&) = delete;
private:
friend struct base::DefaultSingletonTraits<MediaCaptureDevicesDispatcher>;
friend base::NoDestructor<MediaCaptureDevicesDispatcher>;
MediaCaptureDevicesDispatcher();
~MediaCaptureDevicesDispatcher() override;

View File

@@ -518,14 +518,6 @@ bool NativeWindow::IsMenuBarVisible() const {
return true;
}
double NativeWindow::GetAspectRatio() const {
return aspect_ratio_;
}
gfx::Size NativeWindow::GetAspectRatioExtraSize() const {
return aspect_ratio_extraSize_;
}
void NativeWindow::SetAspectRatio(double aspect_ratio,
const gfx::Size& extra_size) {
aspect_ratio_ = aspect_ratio;
@@ -763,6 +755,11 @@ int NativeWindow::NonClientHitTest(const gfx::Point& point) {
}
#endif
// This is to disable dragging in HTML5 full screen mode.
// Details: https://github.com/electron/electron/issues/41002
if (GetWidget()->IsFullscreen())
return HTNOWHERE;
for (auto* provider : draggable_region_providers_) {
int hit = provider->NonClientHitTest(point);
if (hit != HTNOWHERE)

View File

@@ -265,8 +265,10 @@ class NativeWindow : public base::SupportsUserData,
virtual bool IsMenuBarVisible() const;
// Set the aspect ratio when resizing window.
double GetAspectRatio() const;
gfx::Size GetAspectRatioExtraSize() const;
[[nodiscard]] double aspect_ratio() const { return aspect_ratio_; }
[[nodiscard]] gfx::Size aspect_ratio_extra_size() const {
return aspect_ratio_extraSize_;
}
virtual void SetAspectRatio(double aspect_ratio, const gfx::Size& extra_size);
// File preview APIs.

View File

@@ -649,7 +649,7 @@ bool NativeWindowMac::IsMaximized() const {
if (HasStyleMask(NSWindowStyleMaskResizable) != 0)
return [window_ isZoomed];
NSRect rectScreen = GetAspectRatio() > 0.0
NSRect rectScreen = aspect_ratio() > 0.0
? default_frame_for_zoom()
: [[NSScreen mainScreen] visibleFrame];

View File

@@ -4,6 +4,7 @@
#include "shell/browser/net/network_context_service_factory.h"
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "shell/browser/net/network_context_service.h"
@@ -16,7 +17,8 @@ NetworkContextService* NetworkContextServiceFactory::GetForContext(
}
NetworkContextServiceFactory* NetworkContextServiceFactory::GetInstance() {
return base::Singleton<NetworkContextServiceFactory>::get();
static base::NoDestructor<NetworkContextServiceFactory> instance;
return instance.get();
}
NetworkContextServiceFactory::NetworkContextServiceFactory()

View File

@@ -14,6 +14,11 @@ namespace content {
class BrowserContext;
}
namespace base {
template <typename T>
class NoDestructor;
}
namespace electron {
class NetworkContextService;
@@ -33,7 +38,7 @@ class NetworkContextServiceFactory : public BrowserContextKeyedServiceFactory {
delete;
private:
friend struct base::DefaultSingletonTraits<NetworkContextServiceFactory>;
friend base::NoDestructor<NetworkContextServiceFactory>;
NetworkContextServiceFactory();
~NetworkContextServiceFactory() override;

View File

@@ -8,19 +8,17 @@
#include "base/functional/bind.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/base/network_anonymization_key.h"
#include "net/proxy_resolution/proxy_info.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "shell/browser/electron_browser_context.h"
using content::BrowserThread;
namespace electron {
ResolveProxyHelper::ResolveProxyHelper(ElectronBrowserContext* browser_context)
: browser_context_(browser_context) {}
ResolveProxyHelper::ResolveProxyHelper(
network::mojom::NetworkContext* network_context)
: network_context_(network_context) {}
ResolveProxyHelper::~ResolveProxyHelper() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -54,11 +52,9 @@ void ResolveProxyHelper::StartPendingRequest() {
receiver_.set_disconnect_handler(
base::BindOnce(&ResolveProxyHelper::OnProxyLookupComplete,
base::Unretained(this), net::ERR_ABORTED, std::nullopt));
browser_context_->GetDefaultStoragePartition()
->GetNetworkContext()
->LookUpProxyForURL(pending_requests_.front().url,
net::NetworkAnonymizationKey(),
std::move(proxy_lookup_client));
network_context_->LookUpProxyForURL(pending_requests_.front().url,
net::NetworkAnonymizationKey(),
std::move(proxy_lookup_client));
}
void ResolveProxyHelper::OnProxyLookupComplete(

View File

@@ -12,20 +12,19 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/proxy_lookup_client.mojom.h"
#include "url/gurl.h"
namespace electron {
class ElectronBrowserContext;
class ResolveProxyHelper
: public base::RefCountedThreadSafe<ResolveProxyHelper>,
network::mojom::ProxyLookupClient {
public:
using ResolveProxyCallback = base::OnceCallback<void(std::string)>;
explicit ResolveProxyHelper(ElectronBrowserContext* browser_context);
explicit ResolveProxyHelper(network::mojom::NetworkContext* network_context);
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
@@ -71,7 +70,7 @@ class ResolveProxyHelper
mojo::Receiver<network::mojom::ProxyLookupClient> receiver_{this};
// Weak Ref
raw_ptr<ElectronBrowserContext> browser_context_;
raw_ptr<network::mojom::NetworkContext> network_context_ = nullptr;
};
} // namespace electron

View File

@@ -4,6 +4,7 @@
#include "shell/browser/serial/serial_chooser_context_factory.h"
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "shell/browser/electron_browser_context.h"
#include "shell/browser/serial/serial_chooser_context.h"
@@ -26,7 +27,8 @@ KeyedService* SerialChooserContextFactory::BuildServiceInstanceFor(
// static
SerialChooserContextFactory* SerialChooserContextFactory::GetInstance() {
return base::Singleton<SerialChooserContextFactory>::get();
static base::NoDestructor<SerialChooserContextFactory> instance;
return instance.get();
}
// static

View File

@@ -5,10 +5,14 @@
#ifndef ELECTRON_SHELL_BROWSER_SERIAL_SERIAL_CHOOSER_CONTEXT_FACTORY_H_
#define ELECTRON_SHELL_BROWSER_SERIAL_SERIAL_CHOOSER_CONTEXT_FACTORY_H_
#include "base/memory/singleton.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "shell/browser/serial/serial_chooser_context.h"
namespace base {
template <typename T>
class NoDestructor;
} // namespace base
namespace electron {
class SerialChooserContext;
@@ -20,7 +24,7 @@ class SerialChooserContextFactory : public BrowserContextKeyedServiceFactory {
static SerialChooserContextFactory* GetInstance();
private:
friend struct base::DefaultSingletonTraits<SerialChooserContextFactory>;
friend base::NoDestructor<SerialChooserContextFactory>;
SerialChooserContextFactory();
~SerialChooserContextFactory() override;

View File

@@ -231,7 +231,7 @@ void AutofillPopup::SetItems(const std::vector<std::u16string>& values,
void AutofillPopup::AcceptSuggestion(int index) {
mojo::AssociatedRemote<mojom::ElectronAutofillAgent> autofill_agent;
frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(&autofill_agent);
autofill_agent->AcceptDataListSuggestion(GetValueAt(index));
autofill_agent->AcceptDataListSuggestion(value_at(index));
}
void AutofillPopup::UpdatePopupBounds() {
@@ -272,11 +272,10 @@ int AutofillPopup::GetDesiredPopupWidth() {
int popup_width = element_bounds_.width();
for (size_t i = 0; i < values_.size(); ++i) {
int row_size =
kEndPadding + 2 * kPopupBorderThickness +
gfx::GetStringWidth(GetValueAt(i), GetValueFontListForRow(i)) +
gfx::GetStringWidth(GetLabelAt(i), GetLabelFontListForRow(i));
if (!GetLabelAt(i).empty())
int row_size = kEndPadding + 2 * kPopupBorderThickness +
gfx::GetStringWidth(value_at(i), GetValueFontListForRow(i)) +
gfx::GetStringWidth(label_at(i), GetLabelFontListForRow(i));
if (!label_at(i).empty())
row_size += kNamePadding + kEndPadding;
popup_width = std::max(popup_width, row_size);
@@ -307,18 +306,6 @@ ui::ColorId AutofillPopup::GetBackgroundColorIDForRow(int index) const {
: ui::kColorResultsTableNormalBackground;
}
int AutofillPopup::GetLineCount() {
return values_.size();
}
std::u16string AutofillPopup::GetValueAt(int i) {
return values_.at(i);
}
std::u16string AutofillPopup::GetLabelAt(int i) {
return labels_.at(i);
}
int AutofillPopup::LineFromY(int y) const {
int current_height = kPopupBorderThickness;

View File

@@ -57,9 +57,9 @@ class AutofillPopup : public views::ViewObserver {
const gfx::FontList& GetLabelFontListForRow(int index) const;
ui::ColorId GetBackgroundColorIDForRow(int index) const;
int GetLineCount();
std::u16string GetValueAt(int i);
std::u16string GetLabelAt(int i);
int line_count() const { return values_.size(); }
const std::u16string& value_at(int i) const { return values_.at(i); }
const std::u16string& label_at(int i) const { return labels_.at(i); }
int LineFromY(int y) const;
int selected_index_;

View File

@@ -27,7 +27,7 @@
devtools_is_first_responder_ = NO;
attached_to_window_ = NO;
if (inspectableWebContentsView_->inspectable_web_contents()->IsGuest()) {
if (inspectableWebContentsView_->inspectable_web_contents()->is_guest()) {
fake_view_ = [[NSView alloc] init];
[fake_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[self addSubview:fake_view_];

View File

@@ -493,8 +493,8 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
if (menu_)
return menu_;
if (model_ && model_->GetSharingItem()) {
NSMenu* menu = [self createShareMenuForItem:*model_->GetSharingItem()];
if (model_ && model_->sharing_item()) {
NSMenu* menu = [self createShareMenuForItem:*model_->sharing_item()];
menu_ = menu;
} else {
menu_ = [[NSMenu alloc] initWithTitle:@""];

View File

@@ -76,7 +76,7 @@ using FullScreenTransitionState =
- (NSRect)windowWillUseStandardFrame:(NSWindow*)window
defaultFrame:(NSRect)frame {
if (!shell_->zoom_to_page_width()) {
if (shell_->GetAspectRatio() > 0.0)
if (shell_->aspect_ratio() > 0.0)
shell_->set_default_frame_for_zoom(frame);
return frame;
}
@@ -104,7 +104,7 @@ using FullScreenTransitionState =
// Set the width. Don't touch y or height.
frame.size.width = zoomed_width;
if (shell_->GetAspectRatio() > 0.0)
if (shell_->aspect_ratio() > 0.0)
shell_->set_default_frame_for_zoom(frame);
return frame;
@@ -139,13 +139,12 @@ using FullScreenTransitionState =
- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize {
NSSize newSize = frameSize;
double aspectRatio = shell_->GetAspectRatio();
NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow();
if (aspectRatio > 0.0) {
gfx::Size windowSize = shell_->GetSize();
gfx::Size contentSize = shell_->GetContentSize();
gfx::Size extraSize = shell_->GetAspectRatioExtraSize();
if (const double aspectRatio = shell_->aspect_ratio(); aspectRatio > 0.0) {
const gfx::Size windowSize = shell_->GetSize();
const gfx::Size contentSize = shell_->GetContentSize();
const gfx::Size extraSize = shell_->aspect_ratio_extra_size();
double titleBarHeight = windowSize.height() - contentSize.height();
double extraWidthPlusFrame =

View File

@@ -99,11 +99,6 @@ bool ElectronMenuModel::GetSharingItemAt(size_t index,
void ElectronMenuModel::SetSharingItem(SharingItem item) {
sharing_item_.emplace(std::move(item));
}
const std::optional<ElectronMenuModel::SharingItem>&
ElectronMenuModel::GetSharingItem() const {
return sharing_item_;
}
#endif
void ElectronMenuModel::MenuWillClose() {

View File

@@ -98,7 +98,10 @@ class ElectronMenuModel : public ui::SimpleMenuModel {
bool GetSharingItemAt(size_t index, SharingItem* item) const;
// Set/Get the SharingItem of this menu.
void SetSharingItem(SharingItem item);
const std::optional<SharingItem>& GetSharingItem() const;
[[nodiscard]] const std::optional<SharingItem>& sharing_item() const {
return sharing_item_;
}
#endif
// ui::SimpleMenuModel:

View File

@@ -385,10 +385,6 @@ InspectableWebContentsDelegate* InspectableWebContents::GetDelegate() const {
return delegate_;
}
bool InspectableWebContents::IsGuest() const {
return is_guest_;
}
void InspectableWebContents::ReleaseWebContents() {
web_contents_.release();
WebContentsDestroyed();
@@ -455,7 +451,7 @@ void InspectableWebContents::CloseDevTools() {
managed_devtools_web_contents_.reset();
}
embedder_message_dispatcher_.reset();
if (!IsGuest())
if (!is_guest())
web_contents_->Focus();
}
}
@@ -517,10 +513,6 @@ void InspectableWebContents::CallClientFunction(
std::move(arguments), std::move(cb));
}
gfx::Rect InspectableWebContents::GetDevToolsBounds() const {
return devtools_bounds_;
}
void InspectableWebContents::SaveDevToolsBounds(const gfx::Rect& bounds) {
pref_service_->Set(kDevToolsBoundsPref,
base::Value{RectToDictionary(bounds)});

View File

@@ -55,7 +55,7 @@ class InspectableWebContents
void SetDelegate(InspectableWebContentsDelegate* delegate);
InspectableWebContentsDelegate* GetDelegate() const;
bool IsGuest() const;
[[nodiscard]] bool is_guest() const { return is_guest_; }
void ReleaseWebContents();
void SetDevToolsWebContents(content::WebContents* devtools);
void SetDockState(const std::string& state);
@@ -76,7 +76,9 @@ class InspectableWebContents
void InspectElement(int x, int y);
// Return the last position and size of devtools window.
gfx::Rect GetDevToolsBounds() const;
[[nodiscard]] const gfx::Rect& dev_tools_bounds() const {
return devtools_bounds_;
}
void SaveDevToolsBounds(const gfx::Rect& bounds);
// Return the last set zoom level of devtools window.

View File

@@ -124,7 +124,7 @@ void AutofillPopupView::OnSuggestionsChanged() {
return;
CreateChildViews();
if (popup_->GetLineCount() == 0) {
if (popup_->line_count() == 0) {
popup_->Hide();
return;
}
@@ -177,28 +177,28 @@ void AutofillPopupView::DrawAutofillEntry(gfx::Canvas* canvas,
int x_align_left = value_rect.x();
const int value_width = gfx::GetStringWidth(
popup_->GetValueAt(index), popup_->GetValueFontListForRow(index));
popup_->value_at(index), popup_->GetValueFontListForRow(index));
int value_x_align_left = x_align_left;
value_x_align_left =
is_rtl ? value_rect.right() - value_width : value_rect.x();
canvas->DrawStringRectWithFlags(
popup_->GetValueAt(index), popup_->GetValueFontListForRow(index),
popup_->value_at(index), popup_->GetValueFontListForRow(index),
GetColorProvider()->GetColor(ui::kColorResultsTableNormalText),
gfx::Rect(value_x_align_left, value_rect.y(), value_width,
value_rect.height()),
text_align);
// Draw the label text, if one exists.
if (!popup_->GetLabelAt(index).empty()) {
const int label_width = gfx::GetStringWidth(
popup_->GetLabelAt(index), popup_->GetLabelFontListForRow(index));
if (auto const& label = popup_->label_at(index); !label.empty()) {
const int label_width =
gfx::GetStringWidth(label, popup_->GetLabelFontListForRow(index));
int label_x_align_left = x_align_left;
label_x_align_left =
is_rtl ? value_rect.x() : value_rect.right() - label_width;
canvas->DrawStringRectWithFlags(
popup_->GetLabelAt(index), popup_->GetLabelFontListForRow(index),
label, popup_->GetLabelFontListForRow(index),
GetColorProvider()->GetColor(ui::kColorResultsTableDimmedText),
gfx::Rect(label_x_align_left, entry_rect.y(), label_width,
entry_rect.height()),
@@ -212,8 +212,8 @@ void AutofillPopupView::CreateChildViews() {
RemoveAllChildViews();
for (int i = 0; i < popup_->GetLineCount(); ++i) {
auto* child_view = new AutofillPopupChildView(popup_->GetValueAt(i));
for (int i = 0; i < popup_->line_count(); ++i) {
auto* child_view = new AutofillPopupChildView(popup_->value_at(i));
child_view->set_drag_controller(this);
AddChildView(child_view);
}
@@ -234,8 +234,7 @@ void AutofillPopupView::DoUpdateBoundsAndRedrawPopup() {
}
void AutofillPopupView::OnPaint(gfx::Canvas* canvas) {
if (!popup_ ||
static_cast<size_t>(popup_->GetLineCount()) != children().size())
if (!popup_ || static_cast<size_t>(popup_->line_count()) != children().size())
return;
gfx::Canvas* draw_canvas = canvas;
SkBitmap bitmap;
@@ -252,7 +251,7 @@ void AutofillPopupView::OnPaint(gfx::Canvas* canvas) {
GetColorProvider()->GetColor(ui::kColorResultsTableNormalBackground));
OnPaintBorder(draw_canvas);
for (int i = 0; i < popup_->GetLineCount(); ++i) {
for (int i = 0; i < popup_->line_count(); ++i) {
gfx::Rect line_rect = popup_->GetRowBounds(i);
DrawAutofillEntry(draw_canvas, i, line_rect);
@@ -381,7 +380,7 @@ bool AutofillPopupView::HandleKeyPressEvent(
SetSelectedLine(0);
return true;
case ui::VKEY_NEXT: // Page down.
SetSelectedLine(popup_->GetLineCount() - 1);
SetSelectedLine(popup_->line_count() - 1);
return true;
case ui::VKEY_ESCAPE:
popup_->Hide();
@@ -421,7 +420,7 @@ void AutofillPopupView::AcceptSuggestion(int index) {
}
bool AutofillPopupView::AcceptSelectedLine() {
if (!selected_line_ || selected_line_.value() >= popup_->GetLineCount())
if (!selected_line_ || selected_line_.value() >= popup_->line_count())
return false;
AcceptSuggestion(selected_line_.value());
@@ -441,7 +440,7 @@ void AutofillPopupView::SetSelectedLine(std::optional<int> selected_line) {
return;
if (selected_line_ == selected_line)
return;
if (selected_line && selected_line.value() >= popup_->GetLineCount())
if (selected_line && selected_line.value() >= popup_->line_count())
return;
auto previous_selected_line(selected_line_);
@@ -461,7 +460,7 @@ void AutofillPopupView::SelectNextLine() {
return;
int new_selected_line = selected_line_ ? *selected_line_ + 1 : 0;
if (new_selected_line >= popup_->GetLineCount())
if (new_selected_line >= popup_->line_count())
new_selected_line = 0;
SetSelectedLine(new_selected_line);
@@ -473,7 +472,7 @@ void AutofillPopupView::SelectPreviousLine() {
int new_selected_line = selected_line_.value_or(0) - 1;
if (new_selected_line < 0)
new_selected_line = popup_->GetLineCount() - 1;
new_selected_line = popup_->line_count() - 1;
SetSelectedLine(new_selected_line);
}

View File

@@ -83,7 +83,7 @@ InspectableWebContentsViewViews::InspectableWebContentsViewViews(
: InspectableWebContentsView(inspectable_web_contents),
devtools_web_view_(new views::WebView(nullptr)),
title_(u"Developer Tools") {
if (!inspectable_web_contents_->IsGuest() &&
if (!inspectable_web_contents_->is_guest() &&
inspectable_web_contents_->GetWebContents()->GetNativeView()) {
auto* contents_web_view = new views::WebView(nullptr);
contents_web_view->SetWebContents(
@@ -116,8 +116,7 @@ void InspectableWebContentsViewViews::ShowDevTools(bool activate) {
if (devtools_window_) {
devtools_window_web_view_->SetWebContents(
inspectable_web_contents_->GetDevToolsWebContents());
devtools_window_->SetBounds(
inspectable_web_contents()->GetDevToolsBounds());
devtools_window_->SetBounds(inspectable_web_contents()->dev_tools_bounds());
if (activate) {
devtools_window_->Show();
} else {
@@ -182,7 +181,7 @@ void InspectableWebContentsViewViews::SetIsDocked(bool docked, bool activate) {
views::Widget::InitParams params;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.delegate = devtools_window_delegate_;
params.bounds = inspectable_web_contents()->GetDevToolsBounds();
params.bounds = inspectable_web_contents()->dev_tools_bounds();
#if BUILDFLAG(IS_LINUX)
params.wm_role_name = "devtools";

View File

@@ -4,6 +4,7 @@
#include "shell/browser/usb/usb_chooser_context_factory.h"
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "shell/browser/electron_browser_context.h"
#include "shell/browser/usb/usb_chooser_context.h"
@@ -26,7 +27,8 @@ KeyedService* UsbChooserContextFactory::BuildServiceInstanceFor(
// static
UsbChooserContextFactory* UsbChooserContextFactory::GetInstance() {
return base::Singleton<UsbChooserContextFactory>::get();
static base::NoDestructor<UsbChooserContextFactory> instance;
return instance.get();
}
// static

View File

@@ -5,9 +5,13 @@
#ifndef ELECTRON_SHELL_BROWSER_USB_USB_CHOOSER_CONTEXT_FACTORY_H_
#define ELECTRON_SHELL_BROWSER_USB_USB_CHOOSER_CONTEXT_FACTORY_H_
#include "base/memory/singleton.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
namespace base {
template <typename T>
class NoDestructor;
} // namespace base
namespace electron {
class UsbChooserContext;
@@ -24,7 +28,7 @@ class UsbChooserContextFactory : public BrowserContextKeyedServiceFactory {
UsbChooserContextFactory& operator=(const UsbChooserContextFactory&) = delete;
private:
friend struct base::DefaultSingletonTraits<UsbChooserContextFactory>;
friend base::NoDestructor<UsbChooserContextFactory>;
UsbChooserContextFactory();
~UsbChooserContextFactory() override;

View File

@@ -182,6 +182,8 @@ class JSChunkedDataPipeGetter : public gin::Wrappable<JSChunkedDataPipeGetter>,
.SetMethod("done", &JSChunkedDataPipeGetter::Done);
}
const char* GetTypeName() override { return "JSChunkedDataPipeGetter"; }
static gin::WrapperInfo kWrapperInfo;
~JSChunkedDataPipeGetter() override = default;

View File

@@ -271,6 +271,8 @@ class ChunkedDataPipeReadableStream
.SetMethod("read", &ChunkedDataPipeReadableStream::Read);
}
const char* GetTypeName() override { return "ChunkedDataPipeReadableStream"; }
static gin::WrapperInfo kWrapperInfo;
private:

View File

@@ -28,4 +28,8 @@ Event::~Event() = default;
gin::WrapperInfo Event::kWrapperInfo = {gin::kEmbedderNativeGin};
const char* Event::GetTypeName() {
return GetClassName();
}
} // namespace gin_helper::internal

View File

@@ -31,6 +31,7 @@ class Event : public gin::Wrappable<Event>,
// gin::Wrappable
static gin::WrapperInfo kWrapperInfo;
const char* GetTypeName() override;
~Event() override;

View File

@@ -659,8 +659,7 @@ std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
v8::TryCatch try_catch(isolate);
env = node::CreateEnvironment(
static_cast<node::IsolateData*>(isolate_data), context, args, exec_args,
static_cast<node::EnvironmentFlags::Flags>(env_flags), {}, {},
&OnNodePreload);
static_cast<node::EnvironmentFlags::Flags>(env_flags));
if (try_catch.HasCaught()) {
std::string err_msg =
@@ -792,7 +791,7 @@ std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
}
void NodeBindings::LoadEnvironment(node::Environment* env) {
node::LoadEnvironment(env, node::StartExecutionCallback{});
node::LoadEnvironment(env, node::StartExecutionCallback{}, &OnNodePreload);
gin_helper::EmitEvent(env->isolate(), env->process_object(), "loaded");
}

View File

@@ -46,6 +46,14 @@ scoped_refptr<base::RefCountedMemory> NetResourceProvider(int key) {
return nullptr;
}
[[nodiscard]] constexpr bool is_main_world(int world_id) {
return world_id == WorldIDs::MAIN_WORLD_ID;
}
[[nodiscard]] constexpr bool is_isolated_world(int world_id) {
return world_id == WorldIDs::ISOLATED_WORLD_ID;
}
} // namespace
ElectronRenderFrameObserver::ElectronRenderFrameObserver(
@@ -57,10 +65,10 @@ ElectronRenderFrameObserver::ElectronRenderFrameObserver(
// Initialise resource for directory listing.
net::NetModule::SetResourceProvider(NetResourceProvider);
// App regions are only supported in the main frame.
auto* main_frame = frame->GetMainRenderFrame();
if (main_frame && main_frame == frame)
render_frame_->GetWebView()->SetSupportsAppRegion(true);
// In Chrome, app regions are only supported in the main frame.
// However, we need to support draggable regions on other
// local frames/windows, so extend support beyond the main frame.
render_frame_->GetWebView()->SetSupportsAppRegion(true);
}
void ElectronRenderFrameObserver::DidClearWindowObject() {
@@ -129,7 +137,7 @@ void ElectronRenderFrameObserver::DidInstallConditionalFeatures(
// This logic matches the EXPLAINED logic in electron_renderer_client.cc
// to avoid explaining it twice go check that implementation in
// DidCreateScriptContext();
bool is_main_world = IsMainWorld(world_id);
bool is_main_world = electron::is_main_world(world_id);
bool is_main_frame = render_frame_->IsMainFrame();
bool allow_node_in_sub_frames = prefs.node_integration_in_sub_frames;
@@ -203,16 +211,8 @@ void ElectronRenderFrameObserver::CreateIsolatedWorldContext() {
blink::BackForwardCacheAware::kPossiblyDisallow);
}
bool ElectronRenderFrameObserver::IsMainWorld(int world_id) {
return world_id == WorldIDs::MAIN_WORLD_ID;
}
bool ElectronRenderFrameObserver::IsIsolatedWorld(int world_id) {
return world_id == WorldIDs::ISOLATED_WORLD_ID;
}
bool ElectronRenderFrameObserver::ShouldNotifyClient(int world_id) {
auto prefs = render_frame_->GetBlinkPreferences();
bool ElectronRenderFrameObserver::ShouldNotifyClient(int world_id) const {
const auto& prefs = render_frame_->GetBlinkPreferences();
// This is necessary because if an iframe is created and a source is not
// set, the iframe loads about:blank and creates a script context for the
@@ -220,17 +220,17 @@ bool ElectronRenderFrameObserver::ShouldNotifyClient(int world_id) {
// is later set, the JS necessary to do that triggers illegal access errors
// when the initial about:blank Node.js environment is cleaned up. See:
// https://source.chromium.org/chromium/chromium/src/+/main:content/renderer/render_frame_impl.h;l=870-892;drc=4b6001440a18740b76a1c63fa2a002cc941db394
GURL url = render_frame_->GetWebFrame()->GetDocument().Url();
bool allow_node_in_sub_frames = prefs.node_integration_in_sub_frames;
if (allow_node_in_sub_frames && url.IsAboutBlank() &&
!render_frame_->IsMainFrame())
return false;
const bool allow_node_in_sub_frames = prefs.node_integration_in_sub_frames;
if (allow_node_in_sub_frames && !render_frame_->IsMainFrame()) {
if (GURL{render_frame_->GetWebFrame()->GetDocument().Url()}.IsAboutBlank())
return false;
}
if (prefs.context_isolation &&
(render_frame_->IsMainFrame() || allow_node_in_sub_frames))
return IsIsolatedWorld(world_id);
return is_isolated_world(world_id);
return IsMainWorld(world_id);
return is_main_world(world_id);
}
} // namespace electron

View File

@@ -37,10 +37,9 @@ class ElectronRenderFrameObserver : public content::RenderFrameObserver {
void DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) override;
private:
bool ShouldNotifyClient(int world_id);
[[nodiscard]] bool ShouldNotifyClient(int world_id) const;
void CreateIsolatedWorldContext();
bool IsMainWorld(int world_id);
bool IsIsolatedWorld(int world_id);
void OnTakeHeapSnapshot(IPC::PlatformFileForTransit file_handle,
const std::string& channel);

View File

@@ -37,6 +37,7 @@
#include "shell/renderer/electron_autofill_agent.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "third_party/blink/public/platform/web_runtime_features.h"
#include "third_party/blink/public/web/blink.h"
#include "third_party/blink/public/web/web_custom_element.h" // NOLINT(build/include_alpha)
#include "third_party/blink/public/web/web_frame_widget.h"
@@ -225,6 +226,14 @@ bool RendererClientBase::ShouldLoadPreload(
void RendererClientBase::RenderThreadStarted() {
auto* command_line = base::CommandLine::ForCurrentProcess();
// Enable MessagePort close event by default.
// The feature got reverted from stable to test in
// https://chromium-review.googlesource.com/c/chromium/src/+/5276821
// We had the event supported through patch before upstream support,
// this is an alternative option than restoring our patch.
blink::WebRuntimeFeatures::EnableFeatureFromString("MessagePortCloseEvent",
true);
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
auto* thread = content::RenderThread::Get();

View File

@@ -6,9 +6,10 @@ import * as net from 'node:net';
import * as fs from 'fs-extra';
import * as path from 'node:path';
import { promisify } from 'node:util';
import { app, BrowserWindow, Menu, session, net as electronNet, WebContents } from 'electron/main';
import { app, BrowserWindow, Menu, session, net as electronNet, WebContents, utilityProcess } from 'electron/main';
import { closeWindow, closeAllWindows } from './lib/window-helpers';
import { ifdescribe, ifit, listen, waitUntil } from './lib/spec-helpers';
import { collectStreamBody, getResponse } from './lib/net-helpers';
import { once } from 'node:events';
import split = require('split')
import * as semver from 'semver';
@@ -1895,6 +1896,154 @@ describe('app module', () => {
app.showAboutPanel();
});
});
describe('app.setProxy(options)', () => {
let server: http.Server;
afterEach(async () => {
if (server) {
server.close();
}
await app.setProxy({ mode: 'direct' as const });
});
it('allows configuring proxy settings', async () => {
const config = { proxyRules: 'http=myproxy:80' };
await app.setProxy(config);
const proxy = await app.resolveProxy('http://example.com/');
expect(proxy).to.equal('PROXY myproxy:80');
});
it('allows removing the implicit bypass rules for localhost', async () => {
const config = {
proxyRules: 'http=myproxy:80',
proxyBypassRules: '<-loopback>'
};
await app.setProxy(config);
const proxy = await app.resolveProxy('http://localhost');
expect(proxy).to.equal('PROXY myproxy:80');
});
it('allows configuring proxy settings with pacScript', async () => {
server = http.createServer((req, res) => {
const pac = `
function FindProxyForURL(url, host) {
return "PROXY myproxy:8132";
}
`;
res.writeHead(200, {
'Content-Type': 'application/x-ns-proxy-autoconfig'
});
res.end(pac);
});
const { url } = await listen(server);
{
const config = { pacScript: url };
await app.setProxy(config);
const proxy = await app.resolveProxy('https://google.com');
expect(proxy).to.equal('PROXY myproxy:8132');
}
{
const config = { mode: 'pac_script' as any, pacScript: url };
await app.setProxy(config);
const proxy = await app.resolveProxy('https://google.com');
expect(proxy).to.equal('PROXY myproxy:8132');
}
});
it('allows bypassing proxy settings', async () => {
const config = {
proxyRules: 'http=myproxy:80',
proxyBypassRules: '<local>'
};
await app.setProxy(config);
const proxy = await app.resolveProxy('http://example/');
expect(proxy).to.equal('DIRECT');
});
it('allows configuring proxy settings with mode `direct`', async () => {
const config = { mode: 'direct' as const, proxyRules: 'http=myproxy:80' };
await app.setProxy(config);
const proxy = await app.resolveProxy('http://example.com/');
expect(proxy).to.equal('DIRECT');
});
it('allows configuring proxy settings with mode `auto_detect`', async () => {
const config = { mode: 'auto_detect' as const };
await app.setProxy(config);
});
it('allows configuring proxy settings with mode `pac_script`', async () => {
const config = { mode: 'pac_script' as const };
await app.setProxy(config);
const proxy = await app.resolveProxy('http://example.com/');
expect(proxy).to.equal('DIRECT');
});
it('allows configuring proxy settings with mode `fixed_servers`', async () => {
const config = { mode: 'fixed_servers' as const, proxyRules: 'http=myproxy:80' };
await app.setProxy(config);
const proxy = await app.resolveProxy('http://example.com/');
expect(proxy).to.equal('PROXY myproxy:80');
});
it('allows configuring proxy settings with mode `system`', async () => {
const config = { mode: 'system' as const };
await app.setProxy(config);
});
it('disallows configuring proxy settings with mode `invalid`', async () => {
const config = { mode: 'invalid' as any };
await expect(app.setProxy(config)).to.eventually.be.rejectedWith(/Invalid mode/);
});
it('impacts proxy for requests made from utility process', async () => {
const utilityFixturePath = path.resolve(__dirname, 'fixtures', 'api', 'utility-process', 'api-net-spec.js');
const fn = async () => {
const urlRequest = electronNet.request('http://example.com/');
const response = await getResponse(urlRequest);
expect(response.statusCode).to.equal(200);
const message = await collectStreamBody(response);
expect(message).to.equal('ok from proxy\n');
};
server = http.createServer((req, res) => {
res.writeHead(200);
res.end('ok from proxy\n');
});
const { port, hostname } = await listen(server);
const config = { mode: 'fixed_servers' as const, proxyRules: `http=${hostname}:${port}` };
await app.setProxy(config);
const proxy = await app.resolveProxy('http://example.com/');
expect(proxy).to.equal(`PROXY ${hostname}:${port}`);
const child = utilityProcess.fork(utilityFixturePath, [], {
execArgv: ['--expose-gc']
});
child.postMessage({ fn: `(${fn})()` });
const [data] = await once(child, 'message');
expect(data.ok).to.be.true(data.message);
// Cleanup.
const [code] = await once(child, 'exit');
expect(code).to.equal(0);
});
it('does not impact proxy for requests made from main process', async () => {
server = http.createServer((req, res) => {
res.writeHead(200);
res.end('ok from server\n');
});
const { url } = await listen(server);
const config = { mode: 'fixed_servers' as const, proxyRules: 'http=myproxy:80' };
await app.setProxy(config);
const proxy = await app.resolveProxy('http://example.com/');
expect(proxy).to.equal('PROXY myproxy:80');
const urlRequest = electronNet.request(url);
const response = await getResponse(urlRequest);
expect(response.statusCode).to.equal(200);
const message = await collectStreamBody(response);
expect(message).to.equal('ok from server\n');
});
});
});
describe('default behavior', () => {

View File

@@ -11,7 +11,7 @@ import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersLi
import { emittedUntil, emittedNTimes } from './lib/events-helpers';
import { ifit, ifdescribe, defer, listen } from './lib/spec-helpers';
import { closeWindow, closeAllWindows } from './lib/window-helpers';
import { areColorsSimilar, captureScreen, HexColors, getPixelColor } from './lib/screen-helpers';
import { areColorsSimilar, captureScreen, HexColors, getPixelColor, hasCapturableScreen } from './lib/screen-helpers';
import { once } from 'node:events';
import { setTimeout } from 'node:timers/promises';
import { setTimeout as syncSetTimeout } from 'node:timers';
@@ -6599,4 +6599,117 @@ describe('BrowserWindow module', () => {
expect(areColorsSimilar(centerColor, HexColors.BLUE)).to.be.true();
});
});
describe('draggable regions', () => {
afterEach(closeAllWindows);
ifit(hasCapturableScreen())('should allow the window to be dragged when enabled', async () => {
// WOA fails to load libnut so we're using require to defer loading only
// on supported platforms.
// "@nut-tree\libnut-win32\build\Release\libnut.node is not a valid Win32 application."
// @ts-ignore: nut-js is an optional dependency so it may not be installed
const { mouse, straightTo, centerOf, Region, Button } = require('@nut-tree/nut-js') as typeof import('@nut-tree/nut-js');
const display = screen.getPrimaryDisplay();
const w = new BrowserWindow({
x: 0,
y: 0,
width: display.bounds.width / 2,
height: display.bounds.height / 2,
frame: false,
titleBarStyle: 'hidden'
});
const overlayHTML = path.join(__dirname, 'fixtures', 'pages', 'overlay.html');
w.loadFile(overlayHTML);
await once(w, 'ready-to-show');
const winBounds = w.getBounds();
const titleBarHeight = 30;
const titleBarRegion = new Region(winBounds.x, winBounds.y, winBounds.width, titleBarHeight);
const screenRegion = new Region(display.bounds.x, display.bounds.y, display.bounds.width, display.bounds.height);
const startPos = w.getPosition();
await mouse.setPosition(await centerOf(titleBarRegion));
await mouse.pressButton(Button.LEFT);
await mouse.drag(straightTo(centerOf(screenRegion)));
// Wait for move to complete
await Promise.race([
once(w, 'move'),
setTimeout(100) // fallback for possible race condition
]);
const endPos = w.getPosition();
expect(startPos).to.not.deep.equal(endPos);
});
ifit(hasCapturableScreen())('should allow the window to be dragged when no WCO and --webkit-app-region: drag enabled', async () => {
// @ts-ignore: nut-js is an optional dependency so it may not be installed
const { mouse, straightTo, centerOf, Region, Button } = require('@nut-tree/nut-js') as typeof import('@nut-tree/nut-js');
const display = screen.getPrimaryDisplay();
const w = new BrowserWindow({
x: 0,
y: 0,
width: display.bounds.width / 2,
height: display.bounds.height / 2,
frame: false
});
const basePageHTML = path.join(__dirname, 'fixtures', 'pages', 'base-page.html');
w.loadFile(basePageHTML);
await once(w, 'ready-to-show');
await w.webContents.executeJavaScript(`
const style = document.createElement('style');
style.innerHTML = \`
#titlebar {
background-color: red;
height: 30px;
width: 100%;
-webkit-user-select: none;
-webkit-app-region: drag;
position: fixed;
top: 0;
left: 0;
z-index: 1000000000000;
}
\`;
const titleBar = document.createElement('title-bar');
titleBar.id = 'titlebar';
titleBar.textContent = 'test-titlebar';
document.body.append(style);
document.body.append(titleBar);
`);
// allow time for titlebar to finish loading
await setTimeout(2000);
const winBounds = w.getBounds();
const titleBarHeight = 30;
const titleBarRegion = new Region(winBounds.x, winBounds.y, winBounds.width, titleBarHeight);
const screenRegion = new Region(display.bounds.x, display.bounds.y, display.bounds.width, display.bounds.height);
const startPos = w.getPosition();
await mouse.setPosition(await centerOf(titleBarRegion));
await mouse.pressButton(Button.LEFT);
await mouse.drag(straightTo(centerOf(screenRegion)));
// Wait for move to complete
await Promise.race([
once(w, 'move'),
setTimeout(1000) // fallback for possible race condition
]);
const endPos = w.getPosition();
expect(startPos).to.not.deep.equal(endPos);
});
});
});

View File

@@ -8,6 +8,8 @@ import * as http from 'node:http';
import * as fs from 'node:fs';
import * as qs from 'node:querystring';
import * as stream from 'node:stream';
import * as streamConsumers from 'node:stream/consumers';
import * as webStream from 'node:stream/web';
import { EventEmitter, once } from 'node:events';
import { closeAllWindows, closeWindow } from './lib/window-helpers';
import { WebmGenerator } from './lib/video-helpers';
@@ -1548,6 +1550,122 @@ describe('protocol module', () => {
}
});
it('does not emit undefined chunks into the request body stream when uploading a stream', async () => {
protocol.handle('cors', async (request) => {
expect(request.body).to.be.an.instanceOf(webStream.ReadableStream);
for await (const value of request.body as webStream.ReadableStream<Uint8Array>) {
expect(value).to.not.be.undefined();
}
return new Response(undefined, { status: 200 });
});
defer(() => { protocol.unhandle('cors'); });
await contents.loadFile(path.resolve(fixturesPath, 'pages', 'base-page.html'));
contents.on('console-message', (e, level, message) => console.log(message));
const ok = await contents.executeJavaScript(`(async () => {
function wait(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
const stream = new ReadableStream({
async start(controller) {
await wait(4);
controller.enqueue('This ');
await wait(4);
controller.enqueue('is ');
await wait(4);
controller.enqueue('a ');
await wait(4);
controller.enqueue('slow ');
await wait(4);
controller.enqueue('request.');
controller.close();
}
}).pipeThrough(new TextEncoderStream());
return (await fetch('cors://url.invalid', { method: 'POST', body: stream, duplex: 'half' })).ok;
})()`);
expect(ok).to.be.true();
});
it('does not emit undefined chunks into the request body stream when uploading a file', async () => {
protocol.handle('cors', async (request) => {
expect(request.body).to.be.an.instanceOf(webStream.ReadableStream);
for await (const value of request.body as webStream.ReadableStream<Uint8Array>) {
expect(value).to.not.be.undefined();
}
return new Response(undefined, { status: 200 });
});
defer(() => { protocol.unhandle('cors'); });
await contents.loadFile(path.resolve(fixturesPath, 'pages', 'file-input.html'));
const { debugger: debug } = contents;
debug.attach();
try {
const { root: { nodeId } } = await debug.sendCommand('DOM.getDocument');
const { nodeId: inputNodeId } = await debug.sendCommand('DOM.querySelector', { nodeId, selector: 'input' });
await debug.sendCommand('DOM.setFileInputFiles', {
files: [path.join(fixturesPath, 'cat-spin.mp4')],
nodeId: inputNodeId
});
const ok = await contents.executeJavaScript(`(async () => {
const formData = new FormData();
formData.append("data", document.getElementById("file").files[0]);
return (await fetch('cors://url.invalid', { method: 'POST', body: formData })).ok;
})()`);
expect(ok).to.be.true();
} finally {
debug.detach();
}
});
it('filters an illegal "origin: null" header', async () => {
protocol.handle('http', (req) => {
expect(new Headers(req.headers).get('origin')).to.not.equal('null');
return new Response();
});
defer(() => { protocol.unhandle('http'); });
const filePath = path.join(fixturesPath, 'pages', 'form-with-data.html');
await contents.loadFile(filePath);
const loadPromise = new Promise((resolve, reject) => {
contents.once('did-finish-load', resolve);
contents.once('did-fail-load', (_, errorCode, errorDescription) =>
reject(new Error(`did-fail-load: ${errorCode} ${errorDescription}. See AssertionError for details.`))
);
});
await contents.executeJavaScript(`
const form = document.querySelector('form');
form.action = 'http://cors.invalid';
form.method = 'POST';
form.submit();
`);
await loadPromise;
});
it('does forward Blob chunks', async () => {
// we register the protocol on a separate session to validate the assumption
// that `getBlobData()` indeed returns the blob data from a global variable
const s = session.fromPartition('protocol-handle-forwards-blob-chunks');
s.protocol.handle('cors', async (request) => {
expect(request.body).to.be.an.instanceOf(webStream.ReadableStream);
return new Response(
`hello to ${await streamConsumers.text(request.body as webStream.ReadableStream<Uint8Array>)}`,
{ status: 200 }
);
});
defer(() => { s.protocol.unhandle('cors'); });
const w = new BrowserWindow({ show: false, webPreferences: { session: s } });
await w.webContents.loadFile(path.resolve(fixturesPath, 'pages', 'base-page.html'));
const response = await w.webContents.executeJavaScript(`(async () => {
const body = new Blob(["it's-a ", 'me! ', 'Mario!'], { type: 'text/plain' });
return await (await fetch('cors://url.invalid', { method: 'POST', body })).text();
})()`);
expect(response).to.be.string('hello to it\'s-a me! Mario!');
});
// TODO(nornagon): this test doesn't pass on Linux currently, investigate.
ifit(process.platform !== 'linux')('is fast', async () => {
// 128 MB of spaces.

View File

@@ -187,11 +187,32 @@ describe('webContents module', () => {
afterEach(closeAllWindows);
it('does not throw when options are not passed', () => {
expect(() => {
w.webContents.print();
}).not.to.throw();
expect(() => {
w.webContents.print(undefined);
}).not.to.throw();
});
it('does not throw when options object is empty', () => {
expect(() => {
w.webContents.print({});
}).not.to.throw();
});
it('throws when invalid settings are passed', () => {
expect(() => {
// @ts-ignore this line is intentionally incorrect
w.webContents.print(true);
}).to.throw('webContents.print(): Invalid print settings specified.');
expect(() => {
// @ts-ignore this line is intentionally incorrect
w.webContents.print(null);
}).to.throw('webContents.print(): Invalid print settings specified.');
});
it('throws when an invalid pageSize is passed', () => {

Some files were not shown because too many files have changed in this diff Show More