Compare commits

..

28 Commits

Author SHA1 Message Date
trop[bot]
95729e64e4 fix: exceptions during function/promise result conversions live in calling world (#37925)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
2023-04-11 09:23:26 -07:00
trop[bot]
6f9cc3ce32 chore: use nested namespaces (#37915)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2023-04-11 12:39:14 +02:00
trop[bot]
55df7a369e fix: broken buttons in PDF viewer (#37918)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-04-11 12:00:17 +02:00
trop[bot]
a6d934f2a2 chore: use emplace when possible (#37910)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2023-04-11 11:53:18 +02:00
trop[bot]
78404eb23f chore: change some for loops to range-based (#37913)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2023-04-11 11:49:30 +02:00
David Sanders
59fe35388d chore: enforce consistent Markdown style for strong and emphasis (#37845) 2023-04-11 16:22:57 +09:00
trop[bot]
81e3cef294 fix: showAboutPanel also on linux (#37872)
showAboutPanel also on linux

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Mikael Finstad <finstaden@gmail.com>
2023-04-11 16:20:47 +09:00
trop[bot]
ae54e3768c test: support 'latest'/'latest@X' Electron version strings (#37867)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2023-04-11 15:21:55 +09:00
trop[bot]
4de542d524 fix: exceptions in nested conversions live in the target world (#37897)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
2023-04-10 18:00:58 -07:00
Keeley Hammond
9f1bb29528 docs: update 24-x-y breaking changes (#37880) 2023-04-06 21:23:54 -07:00
trop[bot]
309cd19d19 docs: update 21-x-y EOL dates (#37870)
* docs: update 21-x-y EOL dates

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

* doc: update node versions

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
2023-04-06 13:01:36 -07:00
Fedor Indutny
14985e29e0 feat: session.resolveHost (#37847)
feat: session.resolveHost (#37690)

* feat: session.resolveHost

Expose Chromium's host resolution API through the Session object.

* Update shell/browser/api/electron_api_session.cc



* address feedback

* fix tests

* address feedback

* Add options

* Update shell/browser/api/electron_api_session.cc



* Update shell/browser/net/resolve_host_function.cc



* lint

* return object

* add missing file

* fix crash

* handle scope

* links

---------

Co-authored-by: Fedor Indutny <indutny@signal.org>
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
Co-authored-by: Cheng Zhao <github@zcbenz.com>
2023-04-06 10:23:44 -04:00
David Sanders
4fc59bcbef test: remove workaround for fixed Menu.closePopup issue (#37846)
test: remove workaround for fixed Menu.closePopup issue (#37802)
2023-04-06 18:52:18 +09:00
electron-roller[bot]
0a153e7dca chore: bump chromium to 112.0.5615.50 (24-x-y) (#37833)
chore: bump chromium in DEPS to 112.0.5615.50

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2023-04-05 15:50:00 -04:00
Jeremy Rose
5517655962 fix: apply csp correctly when contextIsolation: false (#37839) 2023-04-05 12:11:40 -07:00
trop[bot]
663b741d3f docs: fix app.getPreferredSystemLanguages() return type (#37835)
docs: fix app.getPreferredSystemLanguages() return type

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-04-05 11:08:38 -04:00
trop[bot]
ba6ac2a087 fix: Fn+F fullscreen transitioning on macOS (#37823)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-04-05 16:37:59 +09:00
trop[bot]
3c131923f3 chore: combine parallel/test-v8-stats v8 patches (#37818)
* chore: combine parallel/test-v8-stats v8 patches

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

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2023-04-04 15:16:28 +02:00
trop[bot]
0c2cb44976 fix: record helper error messages in electron_main_mac (#37810)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2023-04-03 15:25:21 -07:00
Keeley Hammond
a2d0af4bc3 build: strip warnings from stdout in get_release (#37795)
build: strip warnings from stdout in `get_release` (#37277)

build: strip warnings from stdout in get_release

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-04-03 12:40:35 +02:00
Keeley Hammond
82869b88f1 refactor: createThumbnailFromPath takes size not maxSize (#37796)
refactor: `createThumbnailFromPath` takes `size` not `maxSize` (#37362)

refactor: createThumbnailFromPath takes size not maxSize
2023-04-03 12:39:50 +02:00
trop[bot]
ff01742f8e fix: set background color for menu bar on Windows (#37785)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2023-04-01 10:44:26 +02:00
trop[bot]
0fc69fcaf4 build: fix build-tools schema in config (#37777)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2023-03-31 17:59:34 +02:00
trop[bot]
505e98e694 docs: update references to @electron/rebuild (#37776)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2023-03-31 17:46:26 +02:00
electron-roller[bot]
5c6dd3fdef chore: bump chromium to 112.0.5615.49 (24-x-y) (#37767)
* chore: bump chromium in DEPS to 112.0.5615.49

* fix: Store the thread stack start in TLS.

https://chromium-review.googlesource.com/c/v8/v8/+/4338176

Fixes #37454

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2023-03-30 20:06:12 -04:00
trop[bot]
5f68f374d8 docs: remove save-to-disk disposition (#37769)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2023-03-30 20:38:36 +02:00
trop[bot]
0af7d3c99b docs: add links to IPC event structures (#37770)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2023-03-30 20:38:13 +02:00
trop[bot]
b6070c34d4 docs: update docs.microsoft.com links to learn.microsoft.com (#37751)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2023-03-30 09:41:35 -04:00
86 changed files with 1241 additions and 339 deletions

View File

@@ -35,8 +35,13 @@ if [ ! -f $buildtools/configs/evm.testing.json ]; then
write_config() {
echo "
{
\"root\": \"/workspaces/gclient\",
\"goma\": \"$1\",
\"root\": \"/workspaces/gclient\",
\"remotes\": {
\"electron\": {
\"origin\": \"https://github.com/electron/electron.git\"
}
}
\"gen\": {
\"args\": [
\"import(\\\"//electron/build/args/testing.gn\\\")\",
@@ -48,11 +53,7 @@ if [ ! -f $buildtools/configs/evm.testing.json ]; then
\"CHROMIUM_BUILDTOOLS_PATH\": \"/workspaces/gclient/src/buildtools\",
\"GIT_CACHE_PATH\": \"/workspaces/gclient/.git-cache\"
},
\"remotes\": {
\"electron\": {
\"origin\": \"https://github.com/electron/electron.git\"
}
}
\"$schema\": \"file:///home/builduser/.electron_build_tools/evm-config.schema.json\"
}
" >$buildtools/configs/evm.testing.json
}

View File

@@ -1,29 +1,27 @@
{
"commands-show-output": false,
"first-line-h1": false,
"header-increment": false,
"line-length": {
"code_blocks": false,
"tables": false,
"stern": true,
"line_length": -1
},
"no-bare-urls": false,
"no-blanks-blockquote": false,
"no-duplicate-header": {
"allow_different_nesting": true
},
"no-emphasis-as-header": false,
"no-hard-tabs": {
"code_blocks": false
},
"no-space-in-emphasis": false,
"no-trailing-punctuation": false,
"no-trailing-spaces": {
"br_spaces": 0
},
"single-h1": false,
"no-inline-html": false,
"emphasis-style": false,
"strong-style": false
}
{
"commands-show-output": false,
"first-line-h1": false,
"header-increment": false,
"line-length": {
"code_blocks": false,
"tables": false,
"stern": true,
"line_length": -1
},
"no-bare-urls": false,
"no-blanks-blockquote": false,
"no-duplicate-header": {
"allow_different_nesting": true
},
"no-emphasis-as-header": false,
"no-hard-tabs": {
"code_blocks": false
},
"no-space-in-emphasis": false,
"no-trailing-punctuation": false,
"no-trailing-spaces": {
"br_spaces": 0
},
"single-h1": false,
"no-inline-html": false
}

View File

@@ -28,7 +28,7 @@ _If an issue has been closed and you still feel it's relevant, feel free to ping
### Languages
We accept issues in *any* language.
We accept issues in _any_ language.
When an issue is posted in a language besides English, it is acceptable and encouraged to post an English-translated copy as a reply.
Anyone may post the translated reply.
In most cases, a quick pass through translation software is sufficient.

2
DEPS
View File

@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'112.0.5615.39',
'112.0.5615.50',
'node_version':
'v18.14.0',
'nan_version':

View File

@@ -63,7 +63,7 @@ Calling `event.preventDefault()` will prevent the default behavior, which is
terminating the application.
**Note:** If application quit was initiated by `autoUpdater.quitAndInstall()`,
then `before-quit` is emitted *after* emitting `close` event on all windows and
then `before-quit` is emitted _after_ emitting `close` event on all windows and
closing them.
**Note:** On Windows, this event will not be emitted if the app is closed due
@@ -751,14 +751,21 @@ This API can be used for purposes such as deciding what language to present the
Here are some examples of return values of the various language and locale APIs with different configurations:
* For Windows, where the application locale is German, the regional format is Finnish (Finland), and the preferred system languages from most to least preferred are French (Canada), English (US), Simplified Chinese (China), Finnish, and Spanish (Latin America):
* `app.getLocale()` returns `'de'`
* `app.getSystemLocale()` returns `'fi-FI'`
* `app.getPreferredSystemLanguages()` returns `['fr-CA', 'en-US', 'zh-Hans-CN', 'fi', 'es-419']`
* On macOS, where the application locale is German, the region is Finland, and the preferred system languages from most to least preferred are French (Canada), English (US), Simplified Chinese, and Spanish (Latin America):
* `app.getLocale()` returns `'de'`
* `app.getSystemLocale()` returns `'fr-FI'`
* `app.getPreferredSystemLanguages()` returns `['fr-CA', 'en-US', 'zh-Hans-FI', 'es-419']`
On Windows, given application locale is German, the regional format is Finnish (Finland), and the preferred system languages from most to least preferred are French (Canada), English (US), Simplified Chinese (China), Finnish, and Spanish (Latin America):
```js
app.getLocale() // 'de'
app.getSystemLocale() // 'fi-FI'
app.getPreferredSystemLanguages() // ['fr-CA', 'en-US', 'zh-Hans-CN', 'fi', 'es-419']
```
On macOS, given the application locale is German, the region is Finland, and the preferred system languages from most to least preferred are French (Canada), English (US), Simplified Chinese, and Spanish (Latin America):
```js
app.getLocale() // 'de'
app.getSystemLocale() // 'fr-FI'
app.getPreferredSystemLanguages() // ['fr-CA', 'en-US', 'zh-Hans-FI', 'es-419']
```
Both the available languages and regions and the possible return values differ between the two operating systems.
@@ -804,7 +811,7 @@ editor. Please refer to [Apple's documentation][CFBundleURLTypes] for details.
**Note:** In a Windows Store environment (when packaged as an `appx`) this API
will return `true` for all calls but the registry key it sets won't be accessible
by other applications. In order to register your Windows Store application
as a default protocol handler you must [declare the protocol in your manifest](https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol).
as a default protocol handler you must [declare the protocol in your manifest](https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol).
The API uses the Windows Registry and `LSSetDefaultHandlerForURLScheme` internally.

View File

@@ -595,7 +595,7 @@ Emitted when the window is being moved to a new position.
Emitted once when the window is moved to a new position.
__Note__: On macOS this event is an alias of `move`.
**Note**: On macOS this event is an alias of `move`.
#### Event: 'enter-full-screen'

View File

@@ -115,7 +115,7 @@ The following additional roles are available on _macOS_:
* `moveTabToNewWindow` - Map to the `moveTabToNewWindow` action.
* `window` - The submenu is a "Window" menu.
* `help` - The submenu is a "Help" menu.
* `services` - The submenu is a ["Services"](https://developer.apple.com/documentation/appkit/nsapplication/1428608-servicesmenu?language=objc) menu. This is only intended for use in the Application Menu and is *not* the same as the "Services" submenu used in context menus in macOS apps, which is not implemented in Electron.
* `services` - The submenu is a ["Services"](https://developer.apple.com/documentation/appkit/nsapplication/1428608-servicesmenu?language=objc) menu. This is only intended for use in the Application Menu and is _not_ the same as the "Services" submenu used in context menus in macOS apps, which is not implemented in Electron.
* `recentDocuments` - The submenu is an "Open Recent" menu.
* `clearRecentDocuments` - Map to the `clearRecentDocuments` action.
* `shareMenu` - The submenu is [share menu][ShareMenu]. The `sharingItem` property must also be set to indicate the item to share.

View File

@@ -317,7 +317,7 @@ name, no matter what label you set. To change it, modify your app bundle's
[About Information Property List Files][AboutInformationPropertyListFiles]
for more information.
## Setting Menu for Specific Browser Window (*Linux* *Windows*)
## Setting Menu for Specific Browser Window (_Linux_ _Windows_)
The [`setMenu` method][setMenu] of browser windows can set the menu of certain
browser windows.

View File

@@ -47,7 +47,7 @@ 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 _Size requirements_ section in [this article][icons].
[icons]: https://learn.microsoft.com/en-us/windows/win32/uxguide/vis-icons
@@ -119,13 +119,15 @@ Returns `NativeImage`
Creates an empty `NativeImage` instance.
### `nativeImage.createThumbnailFromPath(path, maxSize)` _macOS_ _Windows_
### `nativeImage.createThumbnailFromPath(path, size)` _macOS_ _Windows_
* `path` string - path to a file that we intend to construct a thumbnail out of.
* `maxSize` [Size](structures/size.md) - the maximum width and height (positive numbers) the thumbnail returned can be. The Windows implementation will ignore `maxSize.height` and scale the height according to `maxSize.width`.
* `size` [Size](structures/size.md) - the desired width and height (positive numbers) of the thumbnail.
Returns `Promise<NativeImage>` - fulfilled with the file's thumbnail preview image, which is a [NativeImage](native-image.md).
Note: The Windows implementation will ignore `size.height` and scale the height according to `size.width`.
### `nativeImage.createFromPath(path)`
* `path` string

View File

@@ -644,8 +644,8 @@ The `proxyBypassRules` is a comma separated list of rules described below:
Match all hostnames that match the pattern HOSTNAME_PATTERN.
Examples:
"foobar.com", "*foobar.com", "*.foobar.com", "*foobar.com:99",
"https://x.*.y.com:99"
"foobar.com", "\*foobar.com", "\*.foobar.com", "\*foobar.com:99",
"https://x.\*.y.com:99"
* `"." HOSTNAME_SUFFIX_PATTERN [ ":" PORT ]`
@@ -674,6 +674,41 @@ The `proxyBypassRules` is a comma separated list of rules described below:
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.
* `options` Object (optional)
* `queryType` string (optional) - Requested DNS query type. If unspecified,
resolver will pick A or AAAA (or both) based on IPv4/IPv6 settings:
* `A` - Fetch only A records
* `AAAA` - Fetch only AAAA records.
* `source` string (optional) - The source to use for resolved addresses.
Default allows the resolver to pick an appropriate source. Only affects use
of big external sources (e.g. calling the system for resolution or using
DNS). Even if a source is specified, results can still come from cache,
resolving "localhost" or IP literals, etc. One of the following values:
* `any` (default) - Resolver will pick an appropriate source. Results could
come from DNS, MulticastDNS, HOSTS file, etc
* `system` - Results will only be retrieved from the system or OS, e.g. via
the `getaddrinfo()` system call
* `dns` - Results will only come from DNS queries
* `mdns` - Results will only come from Multicast DNS queries
* `localOnly` - No external sources will be used. Results will only come
from fast local sources that are available no matter the source setting,
e.g. cache, hosts file, IP literal resolution, etc.
* `cacheUsage` string (optional) - Indicates what DNS cache entries, if any,
can be used to provide a response. One of the following values:
* `allowed` (default) - Results may come from the host cache if non-stale
* `staleAllowed` - Results may come from the host cache even if stale (by
expiration or network changes)
* `disallowed` - Results will not come from the host cache.
* `secureDnsPolicy` string (optional) - Controls the resolver's Secure DNS
behavior for this request. One of the following values:
* `allow` (default)
* `disable`
Returns [`Promise<ResolvedHost>`](structures/resolved-host.md) - Resolves with the resolved IP addresses for the `host`.
#### `ses.resolveProxy(url)`
* `url` URL

View File

@@ -3,9 +3,9 @@
* `processId` Integer - The internal ID of the renderer process that sent this message
* `frameId` Integer - The ID of the renderer frame that sent this message
* `returnValue` any - Set this to the value to be returned in a synchronous message
* `sender` WebContents - Returns the `webContents` that sent the message
* `senderFrame` WebFrameMain _Readonly_ - The frame that sent this message
* `ports` MessagePortMain[] - A list of MessagePorts that were transferred with this message
* `sender` [WebContents](../web-contents.md) - Returns the `webContents` that sent the message
* `senderFrame` [WebFrameMain](../web-frame-main.md) _Readonly_ - The frame that sent this message
* `ports` [MessagePortMain](../message-port-main.md)[] - A list of MessagePorts that were transferred with this message
* `reply` Function - A function that will send an IPC message to the renderer frame that sent the original message that you are currently handling. You should use this method to "reply" to the sent message in order to guarantee the reply will go to the correct process and frame.
* `channel` string
* `...args` any[]

View File

@@ -2,5 +2,5 @@
* `processId` Integer - The internal ID of the renderer process that sent this message
* `frameId` Integer - The ID of the renderer frame that sent this message
* `sender` WebContents - Returns the `webContents` that sent the message
* `senderFrame` WebFrameMain _Readonly_ - The frame that sent this message
* `sender` [WebContents](../web-contents.md) - Returns the `webContents` that sent the message
* `senderFrame` [WebFrameMain](../web-frame-main.md) _Readonly_ - The frame that sent this message

View File

@@ -1,7 +1,8 @@
# IpcRendererEvent Object extends `Event`
* `sender` IpcRenderer - The `IpcRenderer` instance that emitted the event originally
* `sender` [IpcRenderer](../ipc-renderer.md) - The `IpcRenderer` instance that emitted the event originally
* `senderId` Integer - The `webContents.id` that sent the message, you can call `event.sender.sendTo(event.senderId, ...)` to reply to the message, see [ipcRenderer.sendTo][ipc-renderer-sendto] for more information. This only applies to messages sent from a different renderer. Messages sent directly from the main process set `event.senderId` to `0`.
* `ports` MessagePort[] - A list of MessagePorts that were transferred with this message
* `ports` [MessagePort][][] - A list of MessagePorts that were transferred with this message
[ipc-renderer-sendto]: ../ipc-renderer.md#ipcrenderersendtowebcontentsid-channel-args
[MessagePort]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort

View File

@@ -7,7 +7,7 @@
* `isDefault` boolean - whether or not a given printer is set as the default printer on the OS.
* `options` Object - an object containing a variable number of platform-specific printer information.
The number represented by `status` means different things on different platforms: on Windows its potential values can be found [here](https://docs.microsoft.com/en-us/windows/win32/printdocs/printer-info-2), and on Linux and macOS they can be found [here](https://www.cups.org/doc/cupspm.html).
The number represented by `status` means different things on different platforms: on Windows its potential values can be found [here](https://learn.microsoft.com/en-us/windows/win32/printdocs/printer-info-2), and on Linux and macOS they can be found [here](https://www.cups.org/doc/cupspm.html).
## Example

View File

@@ -0,0 +1,7 @@
# ResolvedEndpoint Object
* `address` string
* `family` string - One of the following:
* `ipv4` - Corresponds to `AF_INET`
* `ipv6` - Corresponds to `AF_INET6`
* `unspec` - Corresponds to `AF_UNSPEC`

View File

@@ -0,0 +1,3 @@
# ResolvedHost Object
* `endpoints` [ResolvedEndpoint[]](resolved-endpoint.md) - resolved DNS entries for the hostname

View File

@@ -25,9 +25,9 @@ app.whenReady().then(() => {
})
```
__Platform Considerations__
**Platform Considerations**
__Linux__
**Linux**
* Tray icon uses [StatusNotifierItem](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/)
by default, when it is not available in user's desktop environment the
@@ -58,14 +58,14 @@ app.whenReady().then(() => {
})
```
__MacOS__
**MacOS**
* Icons passed to the Tray constructor should be [Template Images](native-image.md#template-image).
* 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.
__Windows__
**Windows**
* It is recommended to use `ICO` icons to get best visual effects.
@@ -269,9 +269,9 @@ Returns `boolean` - Whether double click events will be ignored.
Displays a tray balloon.
[NIIF_NOSOUND]: https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#niif_nosound-0x00000010
[NIIF_LARGE_ICON]: https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#niif_large_icon-0x00000020
[NIIF_RESPECT_QUIET_TIME]: https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#niif_respect_quiet_time-0x00000080
[NIIF_NOSOUND]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#niif_nosound-0x00000010
[NIIF_LARGE_ICON]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#niif_large_icon-0x00000020
[NIIF_RESPECT_QUIET_TIME]: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#niif_respect_quiet_time-0x00000080
#### `tray.removeBalloon()` _Windows_

View File

@@ -195,7 +195,7 @@ Returns:
Only defined when the window is being created by a form that set
`target=_blank`.
* `disposition` string - Can be `default`, `foreground-tab`,
`background-tab`, `new-window`, `save-to-disk` and `other`.
`background-tab`, `new-window` or `other`.
Emitted _after_ successful creation of a window via `window.open` in the renderer.
Not emitted if the creation of the window is canceled from
@@ -1176,7 +1176,7 @@ Ignore application menu shortcuts while this web contents is focused.
* `frameName` string - Name of the window provided in `window.open()`
* `features` string - Comma separated list of window features provided to `window.open()`.
* `disposition` string - Can be `default`, `foreground-tab`, `background-tab`,
`new-window`, `save-to-disk` or `other`.
`new-window` or `other`.
* `referrer` [Referrer](structures/referrer.md) - The referrer that will be
passed to the new window. May or may not result in the `Referer` header being
sent, depending on the referrer policy.
@@ -1834,36 +1834,36 @@ Shows pop-up dictionary that searches the selected word on the page.
#### `contents.isOffscreen()`
Returns `boolean` - Indicates whether *offscreen rendering* is enabled.
Returns `boolean` - Indicates whether _offscreen rendering_ is enabled.
#### `contents.startPainting()`
If *offscreen rendering* is enabled and not painting, start painting.
If _offscreen rendering_ is enabled and not painting, start painting.
#### `contents.stopPainting()`
If *offscreen rendering* is enabled and painting, stop painting.
If _offscreen rendering_ is enabled and painting, stop painting.
#### `contents.isPainting()`
Returns `boolean` - If *offscreen rendering* is enabled returns whether it is currently painting.
Returns `boolean` - If _offscreen rendering_ is enabled returns whether it is currently painting.
#### `contents.setFrameRate(fps)`
* `fps` Integer
If *offscreen rendering* is enabled sets the frame rate to the specified number.
If _offscreen rendering_ is enabled sets the frame rate to the specified number.
Only values between 1 and 240 are accepted.
#### `contents.getFrameRate()`
Returns `Integer` - If *offscreen rendering* is enabled returns the current frame rate.
Returns `Integer` - If _offscreen rendering_ is enabled returns the current frame rate.
#### `contents.invalidate()`
Schedules a full repaint of the window this web contents is in.
If *offscreen rendering* is enabled invalidates the frame and generates a new
If _offscreen rendering_ is enabled invalidates the frame and generates a new
one through the `'paint'` event.
#### `contents.getWebRTCIPHandlingPolicy()`
@@ -2004,7 +2004,7 @@ The zoom factor is the zoom percent divided by 100, so 300% = 3.0.
An `Integer` property that sets the frame rate of the web contents to the specified number.
Only values between 1 and 240 are accepted.
Only applicable if *offscreen rendering* is enabled.
Only applicable if _offscreen rendering_ is enabled.
#### `contents.id` _Readonly_

View File

@@ -821,7 +821,7 @@ It is also not emitted during in-page navigation, such as clicking anchor links
or updating the `window.location.hash`. Use `did-navigate-in-page` event for
this purpose.
Calling `event.preventDefault()` does __NOT__ have any effect.
Calling `event.preventDefault()` does **NOT** have any effect.
### Event: 'did-start-navigation'

View File

@@ -12,6 +12,43 @@ This document uses the following convention to categorize breaking changes:
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
* **Removed:** An API or feature was removed, and is no longer supported by Electron.
## Planned Breaking API Changes (24.0)
### API Changed: `nativeImage.createThumbnailFromPath(path, size)`
The `maxSize` parameter has been changed to `size` to reflect that the size passed in will be the size the thumbnail created. Previously, Windows would not scale the image up if it were smaller than `maxSize`, and
macOS would always set the size to `maxSize`. Behavior is now the same across platforms.
Updated Behavior:
```js
// a 128x128 image.
const imagePath = path.join('path', 'to', 'capybara.png')
// Scaling up a smaller image.
const upSize = { width: 256, height: 256 }
nativeImage.createThumbnailFromPath(imagePath, upSize).then(result => {
console.log(result.getSize()) // { width: 256, height: 256 }
})
// Scaling down a larger image.
const downSize = { width: 64, height: 64 }
nativeImage.createThumbnailFromPath(imagePath, downSize).then(result => {
console.log(result.getSize()) // { width: 64, height: 64 }
})
```
Previous Behavior (on Windows):
```js
// a 128x128 image
const imagePath = path.join('path', 'to', 'capybara.png')
const size = { width: 256, height: 256 }
nativeImage.createThumbnailFromPath(imagePath, size).then(result => {
console.log(result.getSize()) // { width: 128, height: 128 }
})
```
## Planned Breaking API Changes (23.0)
### Behavior Changed: Draggable Regions on macOS

View File

@@ -1,6 +1,6 @@
# Updating an Appveyor Azure Image
Electron CI on Windows uses AppVeyor, which in turn uses Azure VM images to run. Occasionally, these VM images need to be updated due to changes in Chromium requirements. In order to update you will need [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-6) and the [Azure PowerShell module](https://docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-1.8.0&viewFallbackFrom=azurermps-6.13.0).
Electron CI on Windows uses AppVeyor, which in turn uses Azure VM images to run. Occasionally, these VM images need to be updated due to changes in Chromium requirements. In order to update you will need [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.3&viewFallbackFrom=powershell-6) and the [Azure PowerShell module](https://learn.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-9.5.0&viewFallbackFrom=azps-1.8.0).
Occasionally we need to update these images owing to changes in Chromium or other miscellaneous build requirement changes.
@@ -8,8 +8,8 @@ Example Use Case:
* We need `VS15.9` and we have `VS15.7` installed; this would require us to update an Azure image.
1. Identify the image you wish to modify.
* In [appveyor.yml](https://github.com/electron/electron/blob/main/appveyor.yml), the image is identified by the property *image*.
* The names used correspond to the *"images"* defined for a build cloud, eg the [libcc-20 cloud](https://windows-ci.electronjs.org/build-clouds/8).
* In [appveyor.yml](https://github.com/electron/electron/blob/main/appveyor.yml), the image is identified by the property _image_.
* The names used correspond to the _"images"_ defined for a build cloud, eg the [libcc-20 cloud](https://windows-ci.electronjs.org/build-clouds/8).
* Find the image you wish to modify in the build cloud and make note of the **VHD Blob Path** for that image, which is the value for that corresponding key.
* You will need this URI path to copy into a new image.
* You will also need the storage account name which is labeled in AppVeyor as the **Disk Storage Account Name**

View File

@@ -94,7 +94,7 @@ out [this video tutorial][procmon-instructions] provided by Microsoft.
## Using WinDbg
<!-- TODO(@codebytere): add images and more information here? -->
It's possible to debug crashes and issues in the Renderer process with [WinDbg](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg).
It's possible to debug crashes and issues in the Renderer process with [WinDbg](https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg).
To attach to a debug a process with WinDbg:

View File

@@ -44,7 +44,7 @@ machine you can monitor compile jobs as they flow through the goma system.
For security and cost reasons, access to Electron's Goma cluster is currently restricted
to Electron Maintainers. If you want access please head to `#access-requests` in
Slack and ping `@goma-squad` to ask for access. Please be aware that being a
maintainer does not *automatically* grant access and access is determined on a
maintainer does not _automatically_ grant access and access is determined on a
case by case basis.
## Uptime / Support

View File

@@ -57,7 +57,7 @@ unfriendly.
Contributors are encouraged to solve issues collaboratively and help one
another make progress. If you encounter an issue that you feel is invalid, or
which contains incorrect information, explain *why* you feel that way with
which contains incorrect information, explain _why_ you feel that way with
additional supporting context, and be willing to be convinced that you may
be wrong. By doing so, we can often reach the correct outcome faster.

View File

@@ -219,7 +219,7 @@ of the area you modified in order to land. Whenever a maintainer reviews a pull
request they may request changes. These may be small, such as fixing a typo, or
may involve substantive changes. Such requests are intended to be helpful, but
at times may come across as abrupt or unhelpful, especially if they do not include
concrete suggestions on *how* to change them.
concrete suggestions on _how_ to change them.
Try not to be discouraged. If you feel that a review is unfair, say so or seek
the input of another project contributor. Often such comments are the result of

View File

@@ -79,7 +79,7 @@ the Node.js source tree.
#### Missing fonts
[Some Windows 10 devices](https://docs.microsoft.com/en-us/typography/fonts/windows_10_font_list) do not ship with the Meiryo font installed, which may cause a font fallback test to fail. To install Meiryo:
[Some Windows 10 devices](https://learn.microsoft.com/en-us/typography/fonts/windows_10_font_list) do not ship with the Meiryo font installed, which may cause a font fallback test to fail. To install Meiryo:
1. Push the Windows key and search for _Manage optional features_.
2. Click _Add a feature_.

View File

@@ -234,7 +234,7 @@ embedded content.
[context isolation]: tutorial/context-isolation.md
[mac app store submission guide]: tutorial/mac-app-store-submission-guide.md
[main]: #main-process
[msi]: https://docs.microsoft.com/en-us/windows/win32/msi/windows-installer-portal
[msi]: https://learn.microsoft.com/en-us/windows/win32/msi/windows-installer-portal
[Native Node Modules]: tutorial/using-native-node-modules.md
[offscreen rendering]: tutorial/offscreen-rendering.md
[process sandboxing]: tutorial/sandbox.md

View File

@@ -21,8 +21,8 @@ can clone and customize to your heart's content.
A command line tool on the other hand continues to support you throughout the
development and release. They are more helpful and supportive but enforce
guidelines on how your code should be structured and built. *Especially for
beginners, using a command line tool is likely to be helpful*.
guidelines on how your code should be structured and built. _Especially for
beginners, using a command line tool is likely to be helpful_.
## Electron Forge

View File

@@ -200,4 +200,4 @@ 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://docs.microsoft.com/en-us/dotnet/framework/tools/signtool-exe
[signtool.exe]: https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe

View File

@@ -9,11 +9,11 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
| 25.0.0 | 2023-Apr-10 | 2023-May-02 | 2023-May-30 | TBD | M114 | TBD | TBD |
| 24.0.0 | 2022-Feb-09 | 2023-Mar-07 | 2023-Apr-08 | TBD | M112 | TBD | ✅ |
| 23.0.0 | 2022-Dec-01 | 2023-Jan-10 | 2023-Feb-07 | TBD | M110 | TBD | ✅ |
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | TBD | M108 | v16.17 | ✅ |
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | TBD | M106 | v16.16 | |
| 25.0.0 | 2023-Apr-10 | 2023-May-02 | 2023-May-30 | 2023-Dec-05 | M114 | TBD | |
| 24.0.0 | 2022-Feb-09 | 2023-Mar-07 | 2023-Apr-04 | 2023-Oct-03 | M112 | v18.14 | ✅ |
| 23.0.0 | 2022-Dec-01 | 2023-Jan-10 | 2023-Feb-07 | 2023-Aug-08 | M110 | v18.12 | ✅ |
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | 2023-May-30 | M108 | v16.17 | ✅ |
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | 2023-Apr-04 | M106 | v16.16 | 🚫 |
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | 2023-Feb-07 | M104 | v16.15 | 🚫 |
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | 2022-Nov-29 | M102 | v16.14 | 🚫 |
| 18.0.0 | 2022-Feb-03 | 2022-Mar-03 | 2022-Mar-29 | 2022-Sep-27 | M100 | v16.13 | 🚫 |
@@ -65,7 +65,7 @@ the Electron team will drop support back to the latest three stable major versio
:::
The latest three *stable* major versions are supported by the Electron team.
The latest three _stable_ major versions are supported by the Electron team.
For example, if the latest release is 6.1.x, then the 5.0.x as well
as the 4.2.x series are supported. We only support the latest minor release
for each stable release series. This means that in the case of a security fix,

View File

@@ -143,7 +143,7 @@ The `electron/electron` repository also enforces squash merging, so you only nee
## Historical versioning (Electron 1.X)
Electron versions *< 2.0* did not conform to the [SemVer](https://semver.org) spec: major versions corresponded to end-user API changes, minor versions corresponded to Chromium major releases, and patch versions corresponded to new features and bug fixes. While convenient for developers merging features, it creates problems for developers of client-facing applications. The QA testing cycles of major apps like Slack, Teams, Skype, VS Code, and GitHub Desktop can be lengthy and stability is a highly desired outcome. There is a high risk in adopting new features while trying to absorb bug fixes.
Electron versions _< 2.0_ did not conform to the [SemVer](https://semver.org) spec: major versions corresponded to end-user API changes, minor versions corresponded to Chromium major releases, and patch versions corresponded to new features and bug fixes. While convenient for developers merging features, it creates problems for developers of client-facing applications. The QA testing cycles of major apps like Slack, Teams, Skype, VS Code, and GitHub Desktop can be lengthy and stability is a highly desired outcome. There is a high risk in adopting new features while trying to absorb bug fixes.
Here is an example of the 1.x strategy:

View File

@@ -15,7 +15,7 @@ icon as the entry point for cross-platform features like
The custom dock is commonly used to add shortcuts to tasks the user wouldn't
want to open the whole app window for.
__Dock menu of Terminal.app:__
**Dock menu of Terminal.app:**
![Dock Menu][dock-menu-image]

View File

@@ -8,7 +8,7 @@ The offscreen rendering in Electron uses a similar approach to that of the
[Chromium Embedded Framework](https://bitbucket.org/chromiumembedded/cef)
project.
*Notes*:
_Notes_:
* There are two rendering modes that can be used (see the section below) and only
the dirty area is passed to the `paint` event to be more efficient.

View File

@@ -295,13 +295,13 @@ browsers apply to Electron's renderers, too. The two primary tools at your
disposal are currently `requestIdleCallback()` for small operations and
`Web Workers` for long-running operations.
*`requestIdleCallback()`* allows developers to queue up a function to be
_`requestIdleCallback()`_ allows developers to queue up a function to be
executed as soon as the process is entering an idle period. It enables you to
perform low-priority or background work without impacting the user experience.
For more information about how to use it,
[check out its documentation on MDN][request-idle-callback].
*Web Workers* are a powerful tool to run code on a separate thread. There are
_Web Workers_ are a powerful tool to run code on a separate thread. There are
some caveats to consider  consult Electron's
[multithreading documentation][multithreading] and the
[MDN documentation for Web Workers][web-workers]. They're an ideal solution

View File

@@ -12,11 +12,11 @@ hide_title: true
Windows and macOS provide access to a list of recent documents opened by
the application via JumpList or dock menu, respectively.
__JumpList:__
**JumpList:**
![JumpList Recent Files][jumplist-image]
__Application dock menu:__
**Application dock menu:**
![macOS Dock Menu][dock-menu-image]

View File

@@ -44,7 +44,7 @@ your responsibility to ensure that the code is not malicious.
It is important to remember that the security of your Electron application is
the result of the overall security of the framework foundation
(*Chromium*, *Node.js*), Electron itself, all NPM dependencies and
(_Chromium_, _Node.js_), Electron itself, all NPM dependencies and
your code. As such, it is your responsibility to follow a few important best
practices:
@@ -52,7 +52,7 @@ practices:
When releasing your product, youre also shipping a bundle composed of Electron,
Chromium shared library and Node.js. Vulnerabilities affecting these components
may impact the security of your application. By updating Electron to the latest
version, you ensure that critical vulnerabilities (such as *nodeIntegration bypasses*)
version, you ensure that critical vulnerabilities (such as _nodeIntegration bypasses_)
are already patched and cannot be exploited in your application. For more information,
see "[Use a current version of Electron](#16-use-a-current-version-of-electron)".

View File

@@ -469,7 +469,7 @@ privileged APIs and how to communicate between processes.
[repl]: ./repl.md
[webpack]: https://webpack.js.org
[window-all-closed]: ../api/app.md#event-window-all-closed
[wsl]: https://docs.microsoft.com/en-us/windows/wsl/about#what-is-wsl-2
[wsl]: https://learn.microsoft.com/en-us/windows/wsl/about#what-is-wsl-2
<!-- Tutorial links -->

View File

@@ -21,17 +21,17 @@ There are several different ways to install native modules:
### Installing modules and rebuilding for Electron
You can install modules like other Node projects, and then rebuild the modules
for Electron with the [`electron-rebuild`][electron-rebuild] package. This
for Electron with the [`@electron/rebuild`][@electron/rebuild] package. This
module can automatically determine the version of Electron and handle the
manual steps of downloading headers and rebuilding native modules for your app.
If you are using [Electron Forge][electron-forge], this tool is used automatically
in both development mode and when making distributables.
For example, to install the standalone `electron-rebuild` tool and then rebuild
For example, to install the standalone `@electron/rebuild` tool and then rebuild
modules with it via the command line:
```sh
npm install --save-dev electron-rebuild
npm install --save-dev @electron/rebuild
# Every time you run "npm install", run this:
./node_modules/.bin/electron-rebuild
@@ -53,8 +53,7 @@ For example, to install all dependencies for Electron:
```sh
# Electron's version.
export npm_config_target=1.2.3
# The architecture of Electron, see https://electronjs.org/docs/tutorial/support#supported-platforms
# for supported architectures.
# The architecture of your machine
export npm_config_arch=x64
export npm_config_target_arch=x64
# Download headers for Electron.
@@ -98,7 +97,7 @@ npm rebuild --nodedir=/path/to/src/out/Default/gen/node_headers
If you installed a native module and found it was not working, you need to check
the following things:
* When in doubt, run `electron-rebuild` first.
* When in doubt, run `@electron/rebuild` first.
* Make sure the native module is compatible with the target platform and
architecture for your Electron app.
* Make sure `win_delay_load_hook` is not set to `false` in the module's `binding.gyp`.
@@ -161,14 +160,14 @@ modules with prebuilt binaries, and many popular modules are using it.
Sometimes those modules work fine under Electron, but when there are no
Electron-specific binaries available, you'll need to build from source.
Because of this, it is recommended to use `electron-rebuild` for these modules.
Because of this, it is recommended to use `@electron/rebuild` for these modules.
If you are following the `npm` way of installing modules, you'll need to pass
`--build-from-source` to `npm`, or set the `npm_config_build_from_source`
environment variable.
[abi]: https://en.wikipedia.org/wiki/Application_binary_interface
[electron-rebuild]: https://github.com/electron/electron-rebuild
[@electron/rebuild]: https://github.com/electron/rebuild
[electron-forge]: https://electronforge.io/
[electron-packager]: https://github.com/electron/electron-packager
[node-pre-gyp]: https://github.com/mapbox/node-pre-gyp

View File

@@ -88,7 +88,7 @@ After completing all of the above, open your cross-compilation command prompt an
## Debugging native modules
Debugging native modules can be done with Visual Studio 2017 (running on your development machine) and corresponding [Visual Studio Remote Debugger](https://docs.microsoft.com/en-us/visualstudio/debugger/remote-debugging-cpp?view=vs-2019) running on the target device. To debug:
Debugging native modules can be done with Visual Studio 2017 (running on your development machine) and corresponding [Visual Studio Remote Debugger](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-cpp?view=vs-2019) running on the target device. To debug:
1. Launch your app `.exe` on the target device via the _Command Prompt_ (passing `--inspect-brk` to pause it before any native modules are loaded).
2. Launch Visual Studio 2017 on your development machine.

View File

@@ -89,7 +89,7 @@ app.setUserTasks([])
> NOTE: The user tasks will still be displayed even after closing your
application, so the icon and program path specified for a task should exist until your application is uninstalled.
[msdn-jumplist]: https://docs.microsoft.com/en-us/windows/win32/shell/taskbar-extensions#tasks
[msdn-jumplist]: https://learn.microsoft.com/en-us/windows/win32/shell/taskbar-extensions#tasks
### Thumbnail Toolbars
@@ -156,7 +156,7 @@ const win = new BrowserWindow()
win.setThumbarButtons([])
```
[msdn-thumbnail]: https://docs.microsoft.com/en-us/windows/win32/shell/taskbar-extensions#thumbnail-toolbars
[msdn-thumbnail]: https://learn.microsoft.com/en-us/windows/win32/shell/taskbar-extensions#thumbnail-toolbars
### Icon Overlays in Taskbar
@@ -196,7 +196,7 @@ const win = new BrowserWindow()
win.setOverlayIcon('path/to/overlay.png', 'Description for overlay')
```
[msdn-icon-overlay]: https://docs.microsoft.com/en-us/windows/win32/shell/taskbar-extensions#icon-overlays
[msdn-icon-overlay]: https://learn.microsoft.com/en-us/windows/win32/shell/taskbar-extensions#icon-overlays
### Flash Frame
@@ -230,7 +230,7 @@ win.flashFrame(true)
In the above example, it is called when the window comes into focus,
but you might use a timeout or some other event to disable it.
[msdn-flash-frame]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-flashwindow#remarks
[msdn-flash-frame]: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-flashwindow#remarks
[setthumbarbuttons]: ../api/browser-window.md#winsetthumbarbuttonsbuttons-windows
[setusertaskstasks]: ../api/app.md#appsetusertaskstasks-windows

View File

@@ -115,6 +115,8 @@ auto_filenames = {
"docs/api/structures/protocol-response.md",
"docs/api/structures/rectangle.md",
"docs/api/structures/referrer.md",
"docs/api/structures/resolved-endpoint.md",
"docs/api/structures/resolved-host.md",
"docs/api/structures/scrubber-item.md",
"docs/api/structures/segmented-control-segment.md",
"docs/api/structures/serial-port.md",

View File

@@ -437,6 +437,8 @@ filenames = {
"shell/browser/net/proxying_url_loader_factory.h",
"shell/browser/net/proxying_websocket.cc",
"shell/browser/net/proxying_websocket.h",
"shell/browser/net/resolve_host_function.cc",
"shell/browser/net/resolve_host_function.h",
"shell/browser/net/resolve_proxy_helper.cc",
"shell/browser/net/resolve_proxy_helper.h",
"shell/browser/net/system_network_context_manager.cc",

View File

@@ -26,7 +26,7 @@ export const roleList: Record<RoleId, Role> = {
get label () {
return isLinux ? 'About' : `About ${app.name}`;
},
...(isWindows && { appMethod: () => app.showAboutPanel() })
...((isWindows || isLinux) && { appMethod: () => app.showAboutPanel() })
},
close: {
label: isMac ? 'Close Window' : 'Close',

View File

@@ -27,7 +27,6 @@ v8_api_advance_api_deprecation.patch
fixup_for_error_declaration_shadows_a_local_variable.patch
fix_parallel_test-v8-stats.patch
fix_expose_the_built-in_electron_module_via_the_esm_loader.patch
heap_remove_allocationspace_map_space_enum_constant.patch
api_pass_oomdetails_to_oomerrorcallback.patch
fix_expose_lookupandcompile_with_parameters.patch
fix_prevent_changing_functiontemplateinfo_after_publish.patch

View File

@@ -3,15 +3,24 @@ From: Jeremy Rose <japthorp@slack-corp.com>
Date: Thu, 29 Sep 2022 16:30:17 -0700
Subject: fix parallel/test-v8-stats
new heap spaces were added in v8, this updates the expectations to
match. node should eventually have this too once they roll up the newer
v8.
Refs https://chromium-review.googlesource.com/c/v8/v8/+/3967841
Refs https://chromium-review.googlesource.com/c/v8/v8/+/3892950.
Heap spaces were added/removed in v8 - this patch updates test expectations
to match.
This patch can be removed when Electron updates to Node.js v20.
diff --git a/test/parallel/test-v8-stats.js b/test/parallel/test-v8-stats.js
index 2eaa3c5b0609149271afb85d7ecc33272e0ada2e..3af7ea1e4a4598dc4125ff78e426d6dc6a025c66 100644
index 2eaa3c5b0609149271afb85d7ecc33272e0ada2e..83b375bd3c5b5dbd5189d48ad560580883ac91f6 100644
--- a/test/parallel/test-v8-stats.js
+++ b/test/parallel/test-v8-stats.js
@@ -47,6 +47,8 @@ const expectedHeapSpaces = [
@@ -42,11 +42,12 @@ const expectedHeapSpaces = [
'code_large_object_space',
'code_space',
'large_object_space',
- 'map_space',
'new_large_object_space',
'new_space',
'old_space',
'read_only_space',

View File

@@ -1,20 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: John Kleinschmidt <jkleinsc@electronjs.org>
Date: Wed, 26 Oct 2022 16:14:20 -0400
Subject: Remove AllocationSpace::MAP_SPACE enum constant
This was removed in:
https://chromium-review.googlesource.com/c/v8/v8/+/3967841
diff --git a/test/parallel/test-v8-stats.js b/test/parallel/test-v8-stats.js
index 3af7ea1e4a4598dc4125ff78e426d6dc6a025c66..83b375bd3c5b5dbd5189d48ad560580883ac91f6 100644
--- a/test/parallel/test-v8-stats.js
+++ b/test/parallel/test-v8-stats.js
@@ -42,7 +42,6 @@ const expectedHeapSpaces = [
'code_large_object_space',
'code_space',
'large_object_space',
- 'map_space',
'new_large_object_space',
'new_space',
'old_space',

View File

@@ -8,3 +8,4 @@ fix_build_deprecated_attribute_for_older_msvc_versions.patch
fix_disable_implies_dcheck_for_node_stream_array_buffers.patch
force_cppheapcreateparams_to_be_noncopyable.patch
chore_allow_customizing_microtask_policy_per_context.patch
store_the_thread_stack_start_in_tls.patch

View File

@@ -0,0 +1,193 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Victor Polevoy <fx@thefx.co>
Date: Thu, 9 Mar 2023 12:06:55 +0100
Subject: Store the thread stack start in TLS.
Stores the thread's stack start address in the Thread Local Storage.
This should allow for faster enter of isolates. Previously it was
obtained each time whenever an isolate is entered what was costly
in terms of performance and unnecessary in general. This change
reverts the high performance back.
Bug: v8:13772
Change-Id: I0a5bb29579cfb407e7d129c4434192d2a6962296
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4338176
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#86578}
(cherry picked from commit 2902d182818fa2790c2ff2b60979039fae4fc636)
diff --git a/AUTHORS b/AUTHORS
index 515bb0d6ccaf7e82b8feace2a49bbfcae29ecc8c..5f14dd472616aa17327fc3b44df4e13aa9dae078 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -258,6 +258,7 @@ Ujjwal Sharma <usharma1998@gmail.com>
Vadim Gorbachev <bmsdave@gmail.com>
Varun Varada <varuncvarada@gmail.com>
Victor Costan <costan@gmail.com>
+Victor Polevoy <fx@thefx.co>
Vlad Burlik <vladbph@gmail.com>
Vladimir Krivosheev <develar@gmail.com>
Vladimir Shutoff <vovan@shutoff.ru>
diff --git a/BUILD.bazel b/BUILD.bazel
index 08247f86115254220b954a0b7eddf22ff33002b1..99bd667de0007b87fd12ba3b6477e14e585dd2b9 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -663,6 +663,7 @@ filegroup(
"src/base/platform/mutex.cc",
"src/base/platform/mutex.h",
"src/base/platform/platform.h",
+ "src/base/platform/platform.cc",
"src/base/platform/semaphore.cc",
"src/base/platform/semaphore.h",
"src/base/platform/time.cc",
diff --git a/BUILD.gn b/BUILD.gn
index 1f8f89e1876ca7a02a8762c5e88d6fcf96037dc0..347471a52ce817618a3e9b7addc17e94b1e5619c 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -5648,6 +5648,7 @@ v8_component("v8_libbase") {
"src/base/platform/memory.h",
"src/base/platform/mutex.cc",
"src/base/platform/mutex.h",
+ "src/base/platform/platform.cc",
"src/base/platform/platform.h",
"src/base/platform/semaphore.cc",
"src/base/platform/semaphore.h",
diff --git a/src/base/platform/platform-aix.cc b/src/base/platform/platform-aix.cc
index 9c9adda3897b80ded70e8ddb3b6195eba5c98c47..e6421f37ac32a03e455c60e9ac58466e4447eca9 100644
--- a/src/base/platform/platform-aix.cc
+++ b/src/base/platform/platform-aix.cc
@@ -136,7 +136,7 @@ std::vector<OS::MemoryRange> OS::GetFreeMemoryRangesWithin(
}
// static
-Stack::StackSlot Stack::GetStackStart() {
+Stack::StackSlot Stack::ObtainCurrentThreadStackStart() {
// pthread_getthrds_np creates 3 values:
// __pi_stackaddr, __pi_stacksize, __pi_stackend
diff --git a/src/base/platform/platform-darwin.cc b/src/base/platform/platform-darwin.cc
index b6260dfaf93e430f584c25eb37366fc94cad81e5..7fde32b22bf291b78bdc7e897995c662625794c4 100644
--- a/src/base/platform/platform-darwin.cc
+++ b/src/base/platform/platform-darwin.cc
@@ -136,7 +136,7 @@ std::vector<OS::MemoryRange> OS::GetFreeMemoryRangesWithin(
}
// static
-Stack::StackSlot Stack::GetStackStart() {
+Stack::StackSlot Stack::ObtainCurrentThreadStackStart() {
return pthread_get_stackaddr_np(pthread_self());
}
diff --git a/src/base/platform/platform-freebsd.cc b/src/base/platform/platform-freebsd.cc
index 55d600283f6029ae28edf80eb68d405d079385a4..4314b1070928859e8a67b913ccfb334bb8737c2f 100644
--- a/src/base/platform/platform-freebsd.cc
+++ b/src/base/platform/platform-freebsd.cc
@@ -104,7 +104,7 @@ std::vector<OS::MemoryRange> OS::GetFreeMemoryRangesWithin(
}
// static
-Stack::StackSlot Stack::GetStackStart() {
+Stack::StackSlot Stack::ObtainCurrentThreadStackStart() {
pthread_attr_t attr;
int error;
pthread_attr_init(&attr);
diff --git a/src/base/platform/platform-posix.cc b/src/base/platform/platform-posix.cc
index 540cc8bdff0e6c9a0365ea0e8cc27571472fe46d..40e870a346f885b9009d19f8567f28ad535432e3 100644
--- a/src/base/platform/platform-posix.cc
+++ b/src/base/platform/platform-posix.cc
@@ -1241,7 +1241,7 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
!defined(V8_OS_SOLARIS)
// static
-Stack::StackSlot Stack::GetStackStart() {
+Stack::StackSlot Stack::ObtainCurrentThreadStackStart() {
pthread_attr_t attr;
int error = pthread_getattr_np(pthread_self(), &attr);
if (!error) {
diff --git a/src/base/platform/platform-solaris.cc b/src/base/platform/platform-solaris.cc
index 8f5dd0c9f12284d5197695a9636e442f35fb4c93..6ff02e3278f9a486c36c4c6d3350c8e15f63ce74 100644
--- a/src/base/platform/platform-solaris.cc
+++ b/src/base/platform/platform-solaris.cc
@@ -72,7 +72,7 @@ std::vector<OS::MemoryRange> OS::GetFreeMemoryRangesWithin(
}
// static
-Stack::StackSlot Stack::GetStackStart() {
+Stack::StackSlot Stack::ObtainCurrentThreadStackStart() {
pthread_attr_t attr;
int error;
pthread_attr_init(&attr);
diff --git a/src/base/platform/platform-win32.cc b/src/base/platform/platform-win32.cc
index 0eedaba0e512eea37fdbfdc109f82788851b5897..280fe18f7f2281884e7ced547762cda6feb98302 100644
--- a/src/base/platform/platform-win32.cc
+++ b/src/base/platform/platform-win32.cc
@@ -1739,7 +1739,7 @@ std::vector<OS::MemoryRange> OS::GetFreeMemoryRangesWithin(
}
// static
-Stack::StackSlot Stack::GetStackStart() {
+Stack::StackSlot Stack::ObtainCurrentThreadStackStart() {
#if defined(V8_TARGET_ARCH_X64)
return reinterpret_cast<void*>(
reinterpret_cast<NT_TIB64*>(NtCurrentTeb())->StackBase);
@@ -1753,7 +1753,7 @@ Stack::StackSlot Stack::GetStackStart() {
::GetCurrentThreadStackLimits(&lowLimit, &highLimit);
return reinterpret_cast<void*>(highLimit);
#else
-#error Unsupported GetStackStart.
+#error Unsupported ObtainCurrentThreadStackStart.
#endif
}
diff --git a/src/base/platform/platform.cc b/src/base/platform/platform.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3914b7dabab3fd32a85237cb1e7d97ad14f94d31
--- /dev/null
+++ b/src/base/platform/platform.cc
@@ -0,0 +1,29 @@
+// Copyright 2023 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "src/base/platform/platform.h"
+
+namespace v8 {
+
+namespace base {
+
+namespace {
+
+// A pointer to current thread's stack beginning.
+thread_local void* thread_stack_start = nullptr;
+
+} // namespace
+
+// static
+Stack::StackSlot Stack::GetStackStart() {
+ DCHECK_IMPLIES(thread_stack_start,
+ thread_stack_start == ObtainCurrentThreadStackStart());
+
+ if (!thread_stack_start) {
+ thread_stack_start = ObtainCurrentThreadStackStart();
+ }
+ return thread_stack_start;
+}
+
+} // namespace base
+} // namespace v8
diff --git a/src/base/platform/platform.h b/src/base/platform/platform.h
index b9b734dac8042b67bf68e8bace550c38130b5e7f..850f447185825cdce688e2687a2d0702904fb7e6 100644
--- a/src/base/platform/platform.h
+++ b/src/base/platform/platform.h
@@ -660,6 +660,10 @@ class V8_BASE_EXPORT Stack {
#endif // V8_USE_ADDRESS_SANITIZER
return slot;
}
+
+ private:
+ // Returns the current thread stack start pointer.
+ static Stack::StackSlot ObtainCurrentThreadStackStart();
};
} // namespace base

View File

@@ -383,7 +383,10 @@ def upload_sha256_checksum(version, file_path, key_prefix=None):
def get_release(version):
script_path = os.path.join(
ELECTRON_DIR, 'script', 'release', 'find-github-release.js')
release_info = execute(['node', script_path, version])
# Strip warnings from stdout to ensure the only output is the desired object
release_env = os.environ.copy()
release_env['NODE_NO_WARNINGS'] = '1'
release_info = execute(['node', script_path, version], release_env)
release = json.loads(release_info)
return release

View File

@@ -64,10 +64,24 @@ if (args.runners !== undefined) {
async function main () {
if (args.electronVersion) {
const versions = await ElectronVersions.create();
if (!versions.isVersion(args.electronVersion)) {
if (args.electronVersion === 'latest') {
args.electronVersion = versions.latest.version;
} else if (args.electronVersion.startsWith('latest@')) {
const majorVersion = parseInt(args.electronVersion.slice('latest@'.length));
const ver = versions.inMajor(majorVersion).slice(-1)[0];
if (ver) {
args.electronVersion = ver.version;
} else {
console.log(`${fail} '${majorVersion}' is not a recognized Electron major version`);
process.exit(1);
}
} else if (!versions.isVersion(args.electronVersion)) {
console.log(`${fail} '${args.electronVersion}' is not a recognized Electron version`);
process.exit(1);
}
const versionString = `v${args.electronVersion}`;
console.log(`Running against Electron ${versionString.green}`);
}
const [lastSpecHash, lastSpecInstallHash] = loadLastSpecHash();

View File

@@ -18,6 +18,14 @@
#include "sandbox/mac/seatbelt_exec.h" // nogncheck
#endif
extern "C" {
// abort_report_np() records the message in a special section that both the
// system CrashReporter and Crashpad collect in crash reports. Using a Crashpad
// Annotation would be preferable, but this executable cannot depend on
// Crashpad directly.
void abort_report_np(const char* fmt, ...);
}
namespace {
[[maybe_unused]] bool IsEnvSet(const char* name) {
@@ -25,6 +33,20 @@ namespace {
return indicator && indicator[0] != '\0';
}
#if defined(HELPER_EXECUTABLE) && !IS_MAS_BUILD()
[[noreturn]] void FatalError(const char* format, ...) {
va_list valist;
va_start(valist, format);
char message[4096];
if (vsnprintf(message, sizeof(message), format, valist) >= 0) {
fputs(message, stderr);
abort_report_np("%s", message);
}
va_end(valist);
abort();
}
#endif
} // namespace
int main(int argc, char* argv[]) {
@@ -42,27 +64,23 @@ int main(int argc, char* argv[]) {
uint32_t exec_path_size = 0;
int rv = _NSGetExecutablePath(NULL, &exec_path_size);
if (rv != -1) {
fprintf(stderr, "_NSGetExecutablePath: get length failed\n");
abort();
FatalError("_NSGetExecutablePath: get length failed.");
}
auto exec_path = std::make_unique<char[]>(exec_path_size);
rv = _NSGetExecutablePath(exec_path.get(), &exec_path_size);
if (rv != 0) {
fprintf(stderr, "_NSGetExecutablePath: get path failed\n");
abort();
FatalError("_NSGetExecutablePath: get path failed.");
}
sandbox::SeatbeltExecServer::CreateFromArgumentsResult seatbelt =
sandbox::SeatbeltExecServer::CreateFromArguments(exec_path.get(), argc,
argv);
if (seatbelt.sandbox_required) {
if (!seatbelt.server) {
fprintf(stderr, "Failed to create seatbelt sandbox server.\n");
abort();
FatalError("Failed to create seatbelt sandbox server.");
}
if (!seatbelt.server->InitializeSandbox()) {
fprintf(stderr, "Failed to initialize sandbox.\n");
abort();
FatalError("Failed to initialize sandbox.");
}
}
#endif // defined(HELPER_EXECUTABLE) && !IS_MAS_BUILD

View File

@@ -10,9 +10,7 @@
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/node_includes.h"
namespace electron {
namespace api {
namespace electron::api {
PushNotifications* g_push_notifications = nullptr;
@@ -55,9 +53,7 @@ const char* PushNotifications::GetTypeName() {
return "PushNotifications";
}
} // namespace api
} // namespace electron
} // namespace electron::api
namespace {

View File

@@ -15,9 +15,7 @@
#include "shell/browser/event_emitter_mixin.h"
#include "shell/common/gin_helper/promise.h"
namespace electron {
namespace api {
namespace electron::api {
class PushNotifications
: public ElectronBrowserClient::Delegate,
@@ -57,8 +55,6 @@ class PushNotifications
#endif
};
} // namespace api
} // namespace electron
} // namespace electron::api
#endif // ELECTRON_SHELL_BROWSER_API_ELECTRON_API_PUSH_NOTIFICATIONS_H_

View File

@@ -12,9 +12,7 @@
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/promise.h"
namespace electron {
namespace api {
namespace electron::api {
v8::Local<v8::Promise> PushNotifications::RegisterForAPNSNotifications(
v8::Isolate* isolate) {
@@ -57,6 +55,4 @@ void PushNotifications::OnDidReceiveAPNSNotification(
Emit("received-apns-notification", user_info);
}
} // namespace api
} // namespace electron
} // namespace electron::api

View File

@@ -115,10 +115,10 @@ v8::Local<v8::Value> ServiceWorkerContext::GetAllRunningWorkerInfo(
gin::DataObjectBuilder builder(isolate);
const base::flat_map<int64_t, content::ServiceWorkerRunningInfo>& info_map =
service_worker_context_->GetRunningServiceWorkerInfos();
for (auto iter = info_map.begin(); iter != info_map.end(); ++iter) {
for (const auto& iter : info_map) {
builder.Set(
std::to_string(iter->first),
ServiceWorkerRunningInfoToDict(isolate, std::move(iter->second)));
std::to_string(iter.first),
ServiceWorkerRunningInfoToDict(isolate, std::move(iter.second)));
}
return builder.Build();
}

View File

@@ -63,6 +63,7 @@
#include "shell/browser/javascript_environment.h"
#include "shell/browser/media/media_device_id_salt.h"
#include "shell/browser/net/cert_verifier_client.h"
#include "shell/browser/net/resolve_host_function.h"
#include "shell/browser/session_preferences.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/content_converter.h"
@@ -424,6 +425,37 @@ v8::Local<v8::Promise> Session::ResolveProxy(gin::Arguments* args) {
return handle;
}
v8::Local<v8::Promise> Session::ResolveHost(
std::string host,
absl::optional<network::mojom::ResolveHostParametersPtr> params) {
gin_helper::Promise<gin_helper::Dictionary> promise(isolate_);
v8::Local<v8::Promise> handle = promise.GetHandle();
auto fn = base::MakeRefCounted<ResolveHostFunction>(
browser_context_, std::move(host),
params ? std::move(params.value()) : nullptr,
base::BindOnce(
[](gin_helper::Promise<gin_helper::Dictionary> promise,
int64_t net_error, const absl::optional<net::AddressList>& addrs) {
if (net_error < 0) {
promise.RejectWithErrorMessage(net::ErrorToString(net_error));
} else {
DCHECK(addrs.has_value() && !addrs->empty());
v8::HandleScope handle_scope(promise.isolate());
gin_helper::Dictionary dict =
gin::Dictionary::CreateEmpty(promise.isolate());
dict.Set("endpoints", addrs->endpoints());
promise.Resolve(dict);
}
},
std::move(promise)));
fn->Run();
return handle;
}
v8::Local<v8::Promise> Session::GetCacheSize() {
gin_helper::Promise<int64_t> promise(isolate_);
auto handle = promise.GetHandle();
@@ -1210,6 +1242,7 @@ gin::ObjectTemplateBuilder Session::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin_helper::EventEmitterMixin<Session>::GetObjectTemplateBuilder(
isolate)
.SetMethod("resolveHost", &Session::ResolveHost)
.SetMethod("resolveProxy", &Session::ResolveProxy)
.SetMethod("getCacheSize", &Session::GetCacheSize)
.SetMethod("clearCache", &Session::ClearCache)

View File

@@ -13,6 +13,7 @@
#include "electron/buildflags/buildflags.h"
#include "gin/handle.h"
#include "gin/wrappable.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/ssl_config.mojom.h"
#include "shell/browser/event_emitter_mixin.h"
#include "shell/browser/net/resolve_proxy_helper.h"
@@ -88,6 +89,9 @@ class Session : public gin::Wrappable<Session>,
const char* GetTypeName() override;
// Methods.
v8::Local<v8::Promise> ResolveHost(
std::string host,
absl::optional<network::mojom::ResolveHostParametersPtr> params);
v8::Local<v8::Promise> ResolveProxy(gin::Arguments* args);
v8::Local<v8::Promise> GetCacheSize();
v8::Local<v8::Promise> ClearCache();

View File

@@ -106,10 +106,10 @@ UtilityProcessWrapper::UtilityProcessWrapper(
return;
}
if (io_handle == IOHandle::STDOUT) {
fds_to_remap.push_back(std::make_pair(pipe_fd[1], STDOUT_FILENO));
fds_to_remap.emplace_back(pipe_fd[1], STDOUT_FILENO);
stdout_read_fd_ = pipe_fd[0];
} else if (io_handle == IOHandle::STDERR) {
fds_to_remap.push_back(std::make_pair(pipe_fd[1], STDERR_FILENO));
fds_to_remap.emplace_back(pipe_fd[1], STDERR_FILENO);
stderr_read_fd_ = pipe_fd[0];
}
#endif
@@ -135,9 +135,9 @@ UtilityProcessWrapper::UtilityProcessWrapper(
return;
}
if (io_handle == IOHandle::STDOUT) {
fds_to_remap.push_back(std::make_pair(devnull, STDOUT_FILENO));
fds_to_remap.emplace_back(devnull, STDOUT_FILENO);
} else if (io_handle == IOHandle::STDERR) {
fds_to_remap.push_back(std::make_pair(devnull, STDERR_FILENO));
fds_to_remap.emplace_back(devnull, STDERR_FILENO);
}
#endif
}

View File

@@ -33,9 +33,7 @@ namespace base {
class Process;
} // namespace base
namespace electron {
namespace api {
namespace electron::api {
class UtilityProcessWrapper
: public gin::Wrappable<UtilityProcessWrapper>,
@@ -93,8 +91,6 @@ class UtilityProcessWrapper
base::WeakPtrFactory<UtilityProcessWrapper> weak_factory_{this};
};
} // namespace api
} // namespace electron
} // namespace electron::api
#endif // ELECTRON_SHELL_BROWSER_API_ELECTRON_API_UTILITY_PROCESS_H_

View File

@@ -7,6 +7,8 @@
#include "shell/browser/ui/cocoa/event_dispatching_window.h"
#include "shell/browser/web_contents_preferences.h"
#include "ui/base/cocoa/command_dispatcher.h"
#include "ui/base/cocoa/nsmenu_additions.h"
#include "ui/base/cocoa/nsmenuitem_additions.h"
#include "ui/events/keycodes/keyboard_codes.h"
#import <Cocoa/Cocoa.h>
@@ -52,9 +54,26 @@ bool WebContents::PlatformHandleKeyboardEvent(
return false;
// Send the event to the menu before sending it to the window
if (event.os_event.type == NSEventTypeKeyDown &&
[[NSApp mainMenu] performKeyEquivalent:event.os_event])
return true;
if (event.os_event.type == NSEventTypeKeyDown) {
// If the keyboard event is a system shortcut, it's already sent to the
// NSMenu instance in
// content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm via
// keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv. If we let the
// NSMenu handle it here as well, it'll be sent twice with unexpected side
// effects.
bool is_a_keyboard_shortcut_event =
[[NSApp mainMenu] cr_menuItemForKeyEquivalentEvent:event.os_event] !=
nil;
bool is_a_system_shortcut_event =
is_a_keyboard_shortcut_event &&
(ui::cocoa::ModifierMaskForKeyEvent(event.os_event) &
NSEventModifierFlagFunction) != 0;
if (is_a_system_shortcut_event)
return false;
if ([[NSApp mainMenu] performKeyEquivalent:event.os_event])
return true;
}
// Let the window redispatch the OS event
if (event.os_event.window &&

View File

@@ -240,8 +240,8 @@ std::vector<blink::MessagePortChannel> MessagePort::DisentanglePorts(
// Passed-in ports passed validity checks, so we can disentangle them.
std::vector<blink::MessagePortChannel> channels;
channels.reserve(ports.size());
for (unsigned i = 0; i < ports.size(); ++i)
channels.push_back(ports[i]->Disentangle());
for (auto port : ports)
channels.push_back(port->Disentangle());
return channels;
}

View File

@@ -10,6 +10,7 @@
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/pdf/pdf_extension_util.h"
#include "chrome/common/extensions/api/resources_private.h"
#include "chrome/grit/generated_resources.h"
#include "components/strings/grit/components_strings.h"
@@ -31,45 +32,6 @@
namespace extensions {
namespace {
void AddStringsForPdf(base::Value::Dict* dict) {
#if BUILDFLAG(ENABLE_PDF)
static constexpr webui::LocalizedString kPdfResources[] = {
{"passwordDialogTitle", IDS_PDF_PASSWORD_DIALOG_TITLE},
{"passwordPrompt", IDS_PDF_NEED_PASSWORD},
{"passwordSubmit", IDS_PDF_PASSWORD_SUBMIT},
{"thumbnailPageAriaLabel", IDS_PDF_THUMBNAIL_PAGE_ARIA_LABEL},
{"passwordInvalid", IDS_PDF_PASSWORD_INVALID},
{"pageLoading", IDS_PDF_PAGE_LOADING},
{"pageLoadFailed", IDS_PDF_PAGE_LOAD_FAILED},
{"errorDialogTitle", IDS_PDF_ERROR_DIALOG_TITLE},
{"pageReload", IDS_PDF_PAGE_RELOAD_BUTTON},
{"bookmarks", IDS_PDF_BOOKMARKS},
{"labelPageNumber", IDS_PDF_LABEL_PAGE_NUMBER},
{"tooltipDownload", IDS_PDF_TOOLTIP_DOWNLOAD},
{"tooltipPrint", IDS_PDF_TOOLTIP_PRINT},
{"tooltipFitToPage", IDS_PDF_TOOLTIP_FIT_PAGE},
{"tooltipFitToWidth", IDS_PDF_TOOLTIP_FIT_WIDTH},
{"tooltipZoomIn", IDS_PDF_TOOLTIP_ZOOM_IN},
{"tooltipZoomOut", IDS_PDF_TOOLTIP_ZOOM_OUT},
};
for (const auto& resource : kPdfResources)
dict->Set(resource.name, l10n_util::GetStringUTF16(resource.id));
dict->Set("presetZoomFactors", zoom::GetPresetZoomFactorsAsJSON());
#endif // BUILDFLAG(ENABLE_PDF)
}
void AddAdditionalDataForPdf(base::Value::Dict* dict) {
#if BUILDFLAG(ENABLE_PDF)
dict->Set("pdfAnnotationsEnabled", base::Value(false));
dict->Set("printingEnabled", base::Value(true));
#endif // BUILDFLAG(ENABLE_PDF)
}
} // namespace
namespace get_strings = api::resources_private::GetStrings;
ResourcesPrivateGetStringsFunction::ResourcesPrivateGetStringsFunction() =
@@ -87,8 +49,11 @@ ExtensionFunction::ResponseAction ResourcesPrivateGetStringsFunction::Run() {
switch (component) {
case api::resources_private::COMPONENT_PDF:
AddStringsForPdf(&dict);
AddAdditionalDataForPdf(&dict);
#if BUILDFLAG(ENABLE_PDF)
pdf_extension_util::AddStrings(
pdf_extension_util::PdfViewerContext::kPdfViewer, &dict);
pdf_extension_util::AddAdditionalData(true, false, &dict);
#endif
break;
case api::resources_private::COMPONENT_IDENTITY:
NOTREACHED();

View File

@@ -0,0 +1,78 @@
// Copyright (c) 2023 Signal Messenger, LLC
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/net/resolve_host_function.h"
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/values.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_errors.h"
#include "net/base/network_isolation_key.h"
#include "net/dns/public/resolve_error_info.h"
#include "shell/browser/electron_browser_context.h"
#include "url/origin.h"
using content::BrowserThread;
namespace electron {
ResolveHostFunction::ResolveHostFunction(
ElectronBrowserContext* browser_context,
std::string host,
network::mojom::ResolveHostParametersPtr params,
ResolveHostCallback callback)
: browser_context_(browser_context),
host_(std::move(host)),
params_(std::move(params)),
callback_(std::move(callback)) {}
ResolveHostFunction::~ResolveHostFunction() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!receiver_.is_bound());
}
void ResolveHostFunction::Run() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!receiver_.is_bound());
// Start the request.
net::HostPortPair host_port_pair(host_, 0);
mojo::PendingRemote<network::mojom::ResolveHostClient> resolve_host_client =
receiver_.BindNewPipeAndPassRemote();
receiver_.set_disconnect_handler(base::BindOnce(
&ResolveHostFunction::OnComplete, this, net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_FAILED),
/*resolved_addresses=*/absl::nullopt,
/*endpoint_results_with_metadata=*/absl::nullopt));
browser_context_->GetDefaultStoragePartition()
->GetNetworkContext()
->ResolveHost(network::mojom::HostResolverHost::NewHostPortPair(
std::move(host_port_pair)),
net::NetworkAnonymizationKey(), std::move(params_),
std::move(resolve_host_client));
}
void ResolveHostFunction::OnComplete(
int result,
const net::ResolveErrorInfo& resolve_error_info,
const absl::optional<net::AddressList>& resolved_addresses,
const absl::optional<net::HostResolverEndpointResults>&
endpoint_results_with_metadata) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Ensure that we outlive the `receiver_.reset()` call.
scoped_refptr<ResolveHostFunction> self(this);
receiver_.reset();
std::move(callback_).Run(resolve_error_info.error, resolved_addresses);
}
} // namespace electron

View File

@@ -0,0 +1,69 @@
// Copyright (c) 2023 Signal Messenger, LLC
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ELECTRON_SHELL_BROWSER_NET_RESOLVE_HOST_FUNCTION_H_
#define ELECTRON_SHELL_BROWSER_NET_RESOLVE_HOST_FUNCTION_H_
#include <deque>
#include <string>
#include <vector>
#include "base/memory/ref_counted.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "net/base/address_list.h"
#include "net/dns/public/host_resolver_results.h"
#include "services/network/public/cpp/resolve_host_client_base.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace electron {
class ElectronBrowserContext;
class ResolveHostFunction
: public base::RefCountedThreadSafe<ResolveHostFunction>,
network::ResolveHostClientBase {
public:
using ResolveHostCallback = base::OnceCallback<void(
int64_t,
const absl::optional<net::AddressList>& resolved_addresses)>;
explicit ResolveHostFunction(ElectronBrowserContext* browser_context,
std::string host,
network::mojom::ResolveHostParametersPtr params,
ResolveHostCallback callback);
void Run();
// disable copy
ResolveHostFunction(const ResolveHostFunction&) = delete;
ResolveHostFunction& operator=(const ResolveHostFunction&) = delete;
protected:
~ResolveHostFunction() override;
private:
friend class base::RefCountedThreadSafe<ResolveHostFunction>;
// network::mojom::ResolveHostClient implementation
void OnComplete(int result,
const net::ResolveErrorInfo& resolve_error_info,
const absl::optional<net::AddressList>& resolved_addresses,
const absl::optional<net::HostResolverEndpointResults>&
endpoint_results_with_metadata) override;
// Receiver for the currently in-progress request, if any.
mojo::Receiver<network::mojom::ResolveHostClient> receiver_{this};
// Weak Ref
ElectronBrowserContext* browser_context_;
std::string host_;
network::mojom::ResolveHostParametersPtr params_;
ResolveHostCallback callback_;
};
} // namespace electron
#endif // ELECTRON_SHELL_BROWSER_NET_RESOLVE_HOST_FUNCTION_H_

View File

@@ -99,8 +99,8 @@ bool RelaunchAppWithHelper(const base::FilePath& helper,
base::LaunchOptions options;
#if BUILDFLAG(IS_POSIX)
options.fds_to_remap.push_back(
std::make_pair(pipe_write_fd.get(), internal::kRelauncherSyncFD));
options.fds_to_remap.emplace_back(pipe_write_fd.get(),
internal::kRelauncherSyncFD);
base::Process process = base::LaunchProcess(relaunch_argv, options);
#elif BUILDFLAG(IS_WIN)
base::Process process = base::LaunchProcess(

View File

@@ -82,8 +82,8 @@ int LaunchProgram(const StringVector& relauncher_args,
base::LaunchOptions options;
options.new_process_group = true; // detach
options.fds_to_remap.push_back(std::make_pair(devnull.get(), STDERR_FILENO));
options.fds_to_remap.push_back(std::make_pair(devnull.get(), STDOUT_FILENO));
options.fds_to_remap.emplace_back(devnull.get(), STDERR_FILENO);
options.fds_to_remap.emplace_back(devnull.get(), STDOUT_FILENO);
base::Process process = base::LaunchProcess(argv, options);
return process.IsValid() ? 0 : 1;

View File

@@ -9,9 +9,7 @@
#include "shell/browser/ui/gtk/menu_util.h"
#include "ui/base/models/menu_model.h"
namespace electron {
namespace gtkui {
namespace electron::gtkui {
MenuGtk::MenuGtk(ui::MenuModel* model)
: menu_model_(model), gtk_menu_(TakeGObject(gtk_menu_new())) {
@@ -65,6 +63,4 @@ void MenuGtk::OnMenuItemActivated(GtkWidget* menu_item) {
ExecuteCommand(model, id);
}
} // namespace gtkui
} // namespace electron
} // namespace electron::gtkui

View File

@@ -17,9 +17,7 @@ namespace ui {
class MenuModel;
}
namespace electron {
namespace gtkui {
namespace electron::gtkui {
class MenuGtk {
public:
@@ -41,8 +39,6 @@ class MenuGtk {
bool block_activation_ = false;
};
} // namespace gtkui
} // namespace electron
} // namespace electron::gtkui
#endif // ELECTRON_SHELL_BROWSER_UI_GTK_MENU_GTK_H_

View File

@@ -35,12 +35,11 @@ const char MenuBar::kViewClassName[] = "ElectronMenuBar";
MenuBar::MenuBar(NativeWindow* window, RootView* root_view)
: background_color_(kDefaultColor), window_(window), root_view_(root_view) {
const ui::NativeTheme* theme = root_view_->GetNativeTheme();
RefreshColorCache(theme);
UpdateViewColors();
#if BUILDFLAG(IS_WIN)
SetBackground(views::CreateThemedSolidBackground(ui::kColorMenuBackground));
background_color_ = GetBackground()->get_color();
#endif
RefreshColorCache(theme);
UpdateViewColors();
SetFocusBehavior(FocusBehavior::ALWAYS);
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal));
@@ -209,6 +208,14 @@ void MenuBar::ButtonPressed(size_t id, const ui::Event& event) {
menu_delegate->AddObserver(this);
}
void MenuBar::ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) {
views::AccessiblePaneView::ViewHierarchyChanged(details);
#if BUILDFLAG(IS_WIN)
background_color_ = GetBackground()->get_color();
#endif
}
void MenuBar::RefreshColorCache(const ui::NativeTheme* theme) {
if (theme) {
#if BUILDFLAG(IS_LINUX)
@@ -217,6 +224,8 @@ void MenuBar::RefreshColorCache(const ui::NativeTheme* theme) {
gtk::GetFgColor("GtkMenuBar#menubar GtkMenuItem#menuitem GtkLabel");
disabled_color_ = gtk::GetFgColor(
"GtkMenuBar#menubar GtkMenuItem#menuitem:disabled GtkLabel");
#elif BUILDFLAG(IS_WIN)
background_color_ = GetBackground()->get_color();
#endif
}
}

View File

@@ -50,6 +50,9 @@ class MenuBar : public views::AccessiblePaneView,
ElectronMenuModel** menu_model,
views::MenuButton** button);
void ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) override;
private:
// MenuDelegate::Observer:
void OnBeforeExecuteCommand() override;

View File

@@ -43,7 +43,7 @@ v8::Local<v8::Promise> NativeImage::CreateThumbnailFromPath(
if (FAILED(hr)) {
promise.RejectWithErrorMessage(
"failed to create IShellItem from the given path");
"Failed to create IShellItem from the given path");
return handle;
}
@@ -53,21 +53,20 @@ v8::Local<v8::Promise> NativeImage::CreateThumbnailFromPath(
IID_PPV_ARGS(&pThumbnailCache));
if (FAILED(hr)) {
promise.RejectWithErrorMessage(
"failed to acquire local thumbnail cache reference");
"Failed to acquire local thumbnail cache reference");
return handle;
}
// Populate the IShellBitmap
Microsoft::WRL::ComPtr<ISharedBitmap> pThumbnail;
WTS_CACHEFLAGS flags;
WTS_THUMBNAILID thumbId;
hr = pThumbnailCache->GetThumbnail(pItem.Get(), size.width(),
WTS_FLAGS::WTS_NONE, &pThumbnail, &flags,
&thumbId);
hr = pThumbnailCache->GetThumbnail(
pItem.Get(), size.width(),
WTS_FLAGS::WTS_SCALETOREQUESTEDSIZE | WTS_FLAGS::WTS_SCALEUP, &pThumbnail,
NULL, NULL);
if (FAILED(hr)) {
promise.RejectWithErrorMessage(
"failed to get thumbnail from local thumbnail cache reference");
"Failed to get thumbnail from local thumbnail cache reference");
return handle;
}
@@ -75,14 +74,14 @@ v8::Local<v8::Promise> NativeImage::CreateThumbnailFromPath(
HBITMAP hBitmap = NULL;
hr = pThumbnail->GetSharedBitmap(&hBitmap);
if (FAILED(hr)) {
promise.RejectWithErrorMessage("failed to extract bitmap from thumbnail");
promise.RejectWithErrorMessage("Failed to extract bitmap from thumbnail");
return handle;
}
// convert HBITMAP to gfx::Image
BITMAP bitmap;
if (!GetObject(hBitmap, sizeof(bitmap), &bitmap)) {
promise.RejectWithErrorMessage("could not convert HBITMAP to BITMAP");
promise.RejectWithErrorMessage("Could not convert HBITMAP to BITMAP");
return handle;
}

View File

@@ -318,8 +318,7 @@ bool Converter<scoped_refptr<network::ResourceRequestBody>>::FromV8(
return false;
base::Value::List& list = list_value.GetList();
*out = base::MakeRefCounted<network::ResourceRequestBody>();
for (size_t i = 0; i < list.size(); ++i) {
base::Value& dict_value = list[i];
for (base::Value& dict_value : list) {
if (!dict_value.is_dict())
return false;
base::Value::Dict& dict = dict_value.GetDict();
@@ -403,4 +402,154 @@ v8::Local<v8::Value> Converter<net::RedirectInfo>::ToV8(
return ConvertToV8(isolate, dict);
}
// static
v8::Local<v8::Value> Converter<net::IPEndPoint>::ToV8(
v8::Isolate* isolate,
const net::IPEndPoint& val) {
gin::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("address", val.ToStringWithoutPort());
switch (val.GetFamily()) {
case net::ADDRESS_FAMILY_IPV4: {
dict.Set("family", "ipv4");
break;
}
case net::ADDRESS_FAMILY_IPV6: {
dict.Set("family", "ipv6");
break;
}
case net::ADDRESS_FAMILY_UNSPECIFIED: {
dict.Set("family", "unspec");
break;
}
}
return ConvertToV8(isolate, dict);
}
// static
bool Converter<net::DnsQueryType>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
net::DnsQueryType* out) {
std::string query_type;
if (!ConvertFromV8(isolate, val, &query_type))
return false;
if (query_type == "A") {
*out = net::DnsQueryType::A;
return true;
}
if (query_type == "AAAA") {
*out = net::DnsQueryType::AAAA;
return true;
}
return false;
}
// static
bool Converter<net::HostResolverSource>::FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
net::HostResolverSource* out) {
std::string query_type;
if (!ConvertFromV8(isolate, val, &query_type))
return false;
if (query_type == "any") {
*out = net::HostResolverSource::ANY;
return true;
}
if (query_type == "system") {
*out = net::HostResolverSource::SYSTEM;
return true;
}
if (query_type == "dns") {
*out = net::HostResolverSource::DNS;
return true;
}
if (query_type == "mdns") {
*out = net::HostResolverSource::MULTICAST_DNS;
return true;
}
if (query_type == "localOnly") {
*out = net::HostResolverSource::LOCAL_ONLY;
return true;
}
return false;
}
// static
bool Converter<network::mojom::ResolveHostParameters::CacheUsage>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
network::mojom::ResolveHostParameters::CacheUsage* out) {
std::string query_type;
if (!ConvertFromV8(isolate, val, &query_type))
return false;
if (query_type == "allowed") {
*out = network::mojom::ResolveHostParameters::CacheUsage::ALLOWED;
return true;
}
if (query_type == "staleAllowed") {
*out = network::mojom::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
return true;
}
if (query_type == "disallowed") {
*out = network::mojom::ResolveHostParameters::CacheUsage::DISALLOWED;
return true;
}
return false;
}
// static
bool Converter<network::mojom::SecureDnsPolicy>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
network::mojom::SecureDnsPolicy* out) {
std::string query_type;
if (!ConvertFromV8(isolate, val, &query_type))
return false;
if (query_type == "allow") {
*out = network::mojom::SecureDnsPolicy::ALLOW;
return true;
}
if (query_type == "disable") {
*out = network::mojom::SecureDnsPolicy::DISABLE;
return true;
}
return false;
}
// static
bool Converter<network::mojom::ResolveHostParametersPtr>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
network::mojom::ResolveHostParametersPtr* out) {
gin::Dictionary dict(nullptr);
if (!ConvertFromV8(isolate, val, &dict))
return false;
network::mojom::ResolveHostParametersPtr params =
network::mojom::ResolveHostParameters::New();
dict.Get("queryType", &(params->dns_query_type));
dict.Get("source", &(params->source));
dict.Get("cacheUsage", &(params->cache_usage));
dict.Get("secureDnsPolicy", &(params->secure_dns_policy));
*out = std::move(params);
return true;
}
} // namespace gin

42
shell/common/gin_converters/net_converter.h Executable file → Normal file
View File

@@ -11,6 +11,7 @@
#include "gin/converter.h"
#include "services/network/public/mojom/fetch_api.mojom.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "services/network/public/mojom/url_request.mojom.h"
#include "shell/browser/net/cert_verifier_client.h"
@@ -109,6 +110,47 @@ struct Converter<net::RedirectInfo> {
const net::RedirectInfo& val);
};
template <>
struct Converter<net::IPEndPoint> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const net::IPEndPoint& val);
};
template <>
struct Converter<net::DnsQueryType> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
net::DnsQueryType* out);
};
template <>
struct Converter<net::HostResolverSource> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
net::HostResolverSource* out);
};
template <>
struct Converter<network::mojom::ResolveHostParameters::CacheUsage> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
network::mojom::ResolveHostParameters::CacheUsage* out);
};
template <>
struct Converter<network::mojom::SecureDnsPolicy> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
network::mojom::SecureDnsPolicy* out);
};
template <>
struct Converter<network::mojom::ResolveHostParametersPtr> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
network::mojom::ResolveHostParametersPtr* out);
};
template <typename K, typename V>
struct Converter<std::vector<std::pair<K, V>>> {
static bool FromV8(v8::Isolate* isolate,

View File

@@ -184,9 +184,10 @@ v8::ModifyCodeGenerationFromStringsResult ModifyCodeGenerationFromStrings(
v8::Local<v8::Context> context,
v8::Local<v8::Value> source,
bool is_code_like) {
// If we're running with contextIsolation enabled in the renderer process,
// fall back to Blink's logic.
if (node::Environment::GetCurrent(context) == nullptr) {
// No node environment means we're in the renderer process, either in a
// sandboxed renderer or in an unsandboxed renderer with context isolation
// enabled.
if (gin_helper::Locker::IsBrowserProcess()) {
NOTREACHED();
return {false, {}};
@@ -195,6 +196,22 @@ v8::ModifyCodeGenerationFromStringsResult ModifyCodeGenerationFromStrings(
context, source, is_code_like);
}
// If we get here then we have a node environment, so either a) we're in the
// main process, or b) we're in the renderer process in a context that has
// both node and blink, i.e. contextIsolation disabled.
// If we're in the main process, delegate to node.
if (gin_helper::Locker::IsBrowserProcess()) {
return node::ModifyCodeGenerationFromStrings(context, source, is_code_like);
}
// If we're in the renderer with contextIsolation disabled, ask blink first
// (for CSP), and iff that allows codegen, delegate to node.
v8::ModifyCodeGenerationFromStringsResult result =
blink::V8Initializer::CodeGenerationCheckCallbackInMainThread(
context, source, is_code_like);
if (!result.codegen_allowed)
return result;
return node::ModifyCodeGenerationFromStrings(context, source, is_code_like);
}

View File

@@ -19,7 +19,7 @@ void ObjectCache::CacheProxiedObject(v8::Local<v8::Value> from,
auto obj = from.As<v8::Object>();
int hash = obj->GetIdentityHash();
proxy_map_[hash].push_front(std::make_pair(from, proxy_value));
proxy_map_[hash].emplace_front(from, proxy_value);
}
}

View File

@@ -241,10 +241,29 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
global_destination_context.IsEmpty())
return;
context_bridge::ObjectCache object_cache;
auto val =
PassValueToOtherContext(global_source_context.Get(isolate),
global_destination_context.Get(isolate),
result, &object_cache, false, 0);
v8::MaybeLocal<v8::Value> val;
{
v8::TryCatch try_catch(isolate);
val = PassValueToOtherContext(
global_source_context.Get(isolate),
global_destination_context.Get(isolate), result, &object_cache,
false, 0, BridgeErrorTarget::kDestination);
if (try_catch.HasCaught()) {
if (try_catch.Message().IsEmpty()) {
proxied_promise->RejectWithErrorMessage(
"An error was thrown while sending a promise result over "
"the context bridge but it was not actually an Error "
"object. This normally means that a promise was resolved "
"with a value that is not supported by the Context "
"Bridge.");
} else {
proxied_promise->Reject(
v8::Exception::Error(try_catch.Message()->Get()));
}
return;
}
}
DCHECK(!val.IsEmpty());
if (!val.IsEmpty())
proxied_promise->Resolve(val.ToLocalChecked());
},
@@ -268,10 +287,28 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
global_destination_context.IsEmpty())
return;
context_bridge::ObjectCache object_cache;
auto val =
PassValueToOtherContext(global_source_context.Get(isolate),
global_destination_context.Get(isolate),
result, &object_cache, false, 0);
v8::MaybeLocal<v8::Value> val;
{
v8::TryCatch try_catch(isolate);
val = PassValueToOtherContext(
global_source_context.Get(isolate),
global_destination_context.Get(isolate), result, &object_cache,
false, 0, BridgeErrorTarget::kDestination);
if (try_catch.HasCaught()) {
if (try_catch.Message().IsEmpty()) {
proxied_promise->RejectWithErrorMessage(
"An error was thrown while sending a promise rejection "
"over the context bridge but it was not actually an Error "
"object. This normally means that a promise was rejected "
"with a value that is not supported by the Context "
"Bridge.");
} else {
proxied_promise->Reject(
v8::Exception::Error(try_catch.Message()->Get()));
}
return;
}
}
if (!val.IsEmpty())
proxied_promise->Reject(val.ToLocalChecked());
},
@@ -324,7 +361,7 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
auto value_for_array = PassValueToOtherContext(
source_context, destination_context,
arr->Get(source_context, i).ToLocalChecked(), object_cache,
support_dynamic_properties, recursion_depth + 1);
support_dynamic_properties, recursion_depth + 1, error_target);
if (value_for_array.IsEmpty())
return v8::MaybeLocal<v8::Value>();
@@ -358,7 +395,7 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
auto object_value = value.As<v8::Object>();
auto passed_value = CreateProxyForAPI(
object_value, source_context, destination_context, object_cache,
support_dynamic_properties, recursion_depth + 1);
support_dynamic_properties, recursion_depth + 1, error_target);
if (passed_value.IsEmpty())
return v8::MaybeLocal<v8::Value>();
return v8::MaybeLocal<v8::Value>(passed_value.ToLocalChecked());
@@ -372,8 +409,9 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
: destination_context;
v8::Context::Scope error_scope(error_context);
// V8 serializer will throw an error if required
if (!gin::ConvertFromV8(error_context->GetIsolate(), value, &ret))
if (!gin::ConvertFromV8(error_context->GetIsolate(), value, &ret)) {
return v8::MaybeLocal<v8::Value>();
}
}
{
@@ -420,9 +458,9 @@ void ProxyFunctionWrapper(const v8::FunctionCallbackInfo<v8::Value>& info) {
args.GetRemaining(&original_args);
for (auto value : original_args) {
auto arg =
PassValueToOtherContext(calling_context, func_owning_context, value,
&object_cache, support_dynamic_properties, 0);
auto arg = PassValueToOtherContext(
calling_context, func_owning_context, value, &object_cache,
support_dynamic_properties, 0, BridgeErrorTarget::kSource);
if (arg.IsEmpty())
return;
proxied_args.push_back(arg.ToLocalChecked());
@@ -469,10 +507,50 @@ void ProxyFunctionWrapper(const v8::FunctionCallbackInfo<v8::Value>& info) {
if (maybe_return_value.IsEmpty())
return;
auto ret = PassValueToOtherContext(
func_owning_context, calling_context,
maybe_return_value.ToLocalChecked(), &object_cache,
support_dynamic_properties, 0, BridgeErrorTarget::kDestination);
// In the case where we encounted an exception converting the return value
// of the function we need to ensure that the exception / thrown value is
// safely transferred from the function_owning_context (where it was thrown)
// into the calling_context (where it needs to be thrown) To do this we pull
// the message off the exception and later re-throw it in the right context.
// In some cases the caught thing is not an exception i.e. it's technically
// valid to `throw 123`. In these cases to avoid infinite
// PassValueToOtherContext recursion we bail early as being unable to send
// the value from one context to the other.
// TODO(MarshallOfSound): In this case and other cases where the error can't
// be sent _across_ worlds we should probably log it globally in some way to
// allow easier debugging. This is not trivial though so is left to a
// future change.
bool did_error_converting_result = false;
v8::MaybeLocal<v8::Value> ret;
v8::Local<v8::String> exception;
{
v8::TryCatch try_catch(args.isolate());
ret = PassValueToOtherContext(func_owning_context, calling_context,
maybe_return_value.ToLocalChecked(),
&object_cache, support_dynamic_properties,
0, BridgeErrorTarget::kDestination);
if (try_catch.HasCaught()) {
did_error_converting_result = true;
if (!try_catch.Message().IsEmpty()) {
exception = try_catch.Message()->Get();
}
}
}
if (did_error_converting_result) {
v8::Context::Scope calling_context_scope(calling_context);
if (exception.IsEmpty()) {
const char err_msg[] =
"An unknown exception occurred while sending a function return "
"value over the context bridge, an error "
"occurred but a valid exception was not thrown.";
args.isolate()->ThrowException(v8::Exception::Error(
gin::StringToV8(args.isolate(), err_msg).As<v8::String>()));
} else {
args.isolate()->ThrowException(v8::Exception::Error(exception));
}
return;
}
DCHECK(!ret.IsEmpty());
if (ret.IsEmpty())
return;
info.GetReturnValue().Set(ret.ToLocalChecked());
@@ -485,7 +563,8 @@ v8::MaybeLocal<v8::Object> CreateProxyForAPI(
const v8::Local<v8::Context>& destination_context,
context_bridge::ObjectCache* object_cache,
bool support_dynamic_properties,
int recursion_depth) {
int recursion_depth,
BridgeErrorTarget error_target) {
gin_helper::Dictionary api(source_context->GetIsolate(), api_object);
{
@@ -526,14 +605,16 @@ v8::MaybeLocal<v8::Object> CreateProxyForAPI(
if (!getter.IsEmpty()) {
if (!PassValueToOtherContext(source_context, destination_context,
getter, object_cache,
support_dynamic_properties, 1)
support_dynamic_properties, 1,
error_target)
.ToLocal(&getter_proxy))
continue;
}
if (!setter.IsEmpty()) {
if (!PassValueToOtherContext(source_context, destination_context,
setter, object_cache,
support_dynamic_properties, 1)
support_dynamic_properties, 1,
error_target)
.ToLocal(&setter_proxy))
continue;
}
@@ -551,7 +632,7 @@ v8::MaybeLocal<v8::Object> CreateProxyForAPI(
auto passed_value = PassValueToOtherContext(
source_context, destination_context, value, object_cache,
support_dynamic_properties, recursion_depth + 1);
support_dynamic_properties, recursion_depth + 1, error_target);
if (passed_value.IsEmpty())
return v8::MaybeLocal<v8::Object>();
proxy.Set(key, passed_value.ToLocalChecked());
@@ -597,9 +678,9 @@ void ExposeAPIInWorld(v8::Isolate* isolate,
context_bridge::ObjectCache object_cache;
v8::Context::Scope target_context_scope(target_context);
v8::MaybeLocal<v8::Value> maybe_proxy =
PassValueToOtherContext(electron_isolated_context, target_context, api,
&object_cache, false, 0);
v8::MaybeLocal<v8::Value> maybe_proxy = PassValueToOtherContext(
electron_isolated_context, target_context, api, &object_cache, false, 0,
BridgeErrorTarget::kSource);
if (maybe_proxy.IsEmpty())
return;
auto proxy = maybe_proxy.ToLocalChecked();
@@ -649,7 +730,7 @@ void OverrideGlobalValueFromIsolatedWorld(
context_bridge::ObjectCache object_cache;
v8::MaybeLocal<v8::Value> maybe_proxy = PassValueToOtherContext(
value->GetCreationContextChecked(), main_context, value, &object_cache,
support_dynamic_properties, 1);
support_dynamic_properties, 1, BridgeErrorTarget::kSource);
DCHECK(!maybe_proxy.IsEmpty());
auto proxy = maybe_proxy.ToLocalChecked();
@@ -685,14 +766,14 @@ bool OverrideGlobalPropertyFromIsolatedWorld(
if (!getter->IsNullOrUndefined()) {
v8::MaybeLocal<v8::Value> maybe_getter_proxy = PassValueToOtherContext(
getter->GetCreationContextChecked(), main_context, getter,
&object_cache, false, 1);
&object_cache, false, 1, BridgeErrorTarget::kSource);
DCHECK(!maybe_getter_proxy.IsEmpty());
getter_proxy = maybe_getter_proxy.ToLocalChecked();
}
if (!setter->IsNullOrUndefined() && setter->IsObject()) {
v8::MaybeLocal<v8::Value> maybe_setter_proxy = PassValueToOtherContext(
getter->GetCreationContextChecked(), main_context, setter,
&object_cache, false, 1);
&object_cache, false, 1, BridgeErrorTarget::kSource);
DCHECK(!maybe_setter_proxy.IsEmpty());
setter_proxy = maybe_setter_proxy.ToLocalChecked();
}

View File

@@ -39,7 +39,7 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
context_bridge::ObjectCache* object_cache,
bool support_dynamic_properties,
int recursion_depth,
BridgeErrorTarget error_target = BridgeErrorTarget::kSource);
BridgeErrorTarget error_target);
v8::MaybeLocal<v8::Object> CreateProxyForAPI(
const v8::Local<v8::Object>& api_object,
@@ -47,7 +47,8 @@ v8::MaybeLocal<v8::Object> CreateProxyForAPI(
const v8::Local<v8::Context>& destination_context,
context_bridge::ObjectCache* object_cache,
bool support_dynamic_properties,
int recursion_depth);
int recursion_depth,
BridgeErrorTarget error_target);
} // namespace electron::api

View File

@@ -142,7 +142,7 @@ class ScriptExecutionCallback {
context_bridge::ObjectCache object_cache;
maybe_result = PassValueToOtherContext(
result->GetCreationContextChecked(), promise_.GetContext(), result,
&object_cache, false, 0);
&object_cache, false, 0, BridgeErrorTarget::kSource);
if (maybe_result.IsEmpty() || try_catch.HasCaught()) {
success = false;
}

View File

@@ -608,7 +608,8 @@ void RendererClientBase::SetupMainWorldOverrides(
if (global.GetHidden("guestViewInternal", &guest_view_internal)) {
api::context_bridge::ObjectCache object_cache;
auto result = api::PassValueToOtherContext(
source_context, context, guest_view_internal, &object_cache, false, 0);
source_context, context, guest_view_internal, &object_cache, false, 0,
api::BridgeErrorTarget::kSource);
if (!result.IsEmpty()) {
isolated_api.Set("guestViewInternal", result.ToLocalChecked());
}

View File

@@ -802,10 +802,29 @@ describe('contextBridge', () => {
throwNotClonable: () => {
return Object(Symbol('foo'));
},
argumentConvert: () => {}
throwNotClonableNestedArray: () => {
return [Object(Symbol('foo'))];
},
throwNotClonableNestedObject: () => {
return {
bad: Object(Symbol('foo'))
};
},
throwDynamic: () => {
return {
get bad () {
throw new Error('damm');
}
};
},
argumentConvert: () => {},
rejectNotClonable: async () => {
throw Object(Symbol('foo'));
},
resolveNotClonable: async () => Object(Symbol('foo'))
});
});
const result = await callWithBindings((root: any) => {
const result = await callWithBindings(async (root: any) => {
const getError = (fn: Function) => {
try {
fn();
@@ -814,13 +833,26 @@ describe('contextBridge', () => {
}
return null;
};
const getAsyncError = async (fn: Function) => {
try {
await fn();
} catch (e) {
return e;
}
return null;
};
const normalIsError = Object.getPrototypeOf(getError(root.example.throwNormal)) === Error.prototype;
const weirdIsError = Object.getPrototypeOf(getError(root.example.throwWeird)) === Error.prototype;
const notClonableIsError = Object.getPrototypeOf(getError(root.example.throwNotClonable)) === Error.prototype;
const notClonableNestedArrayIsError = Object.getPrototypeOf(getError(root.example.throwNotClonableNestedArray)) === Error.prototype;
const notClonableNestedObjectIsError = Object.getPrototypeOf(getError(root.example.throwNotClonableNestedObject)) === Error.prototype;
const dynamicIsError = Object.getPrototypeOf(getError(root.example.throwDynamic)) === Error.prototype;
const argumentConvertIsError = Object.getPrototypeOf(getError(() => root.example.argumentConvert(Object(Symbol('test'))))) === Error.prototype;
return [normalIsError, weirdIsError, notClonableIsError, argumentConvertIsError];
const rejectNotClonableIsError = Object.getPrototypeOf(await getAsyncError(root.example.rejectNotClonable)) === Error.prototype;
const resolveNotClonableIsError = Object.getPrototypeOf(await getAsyncError(root.example.resolveNotClonable)) === Error.prototype;
return [normalIsError, weirdIsError, notClonableIsError, notClonableNestedArrayIsError, notClonableNestedObjectIsError, dynamicIsError, argumentConvertIsError, rejectNotClonableIsError, resolveNotClonableIsError];
});
expect(result).to.deep.equal([true, true, true, true], 'should all be errors in the current context');
expect(result).to.deep.equal([true, true, true, true, true, true, true, true, true], 'should all be errors in the current context');
});
it('should not leak prototypes', async () => {

View File

@@ -816,10 +816,7 @@ describe('Menu module', function () {
it('should emit menu-will-close event', (done) => {
menu.on('menu-will-close', () => { done(); });
menu.popup({ window: w });
// https://github.com/electron/electron/issues/19411
setTimeout(() => {
menu.closePopup();
});
menu.closePopup();
});
it('returns immediately', () => {
@@ -848,18 +845,12 @@ describe('Menu module', function () {
expect(x).to.equal(100);
expect(y).to.equal(101);
// https://github.com/electron/electron/issues/19411
setTimeout(() => {
menu.closePopup();
});
menu.closePopup();
});
it('works with a given BrowserWindow, no options, and a callback', (done) => {
menu.popup({ window: w, callback: () => done() });
// https://github.com/electron/electron/issues/19411
setTimeout(() => {
menu.closePopup();
});
menu.closePopup();
});
it('prevents menu from getting garbage-collected when popuping', async () => {

View File

@@ -447,6 +447,30 @@ describe('nativeImage module', () => {
const result = await nativeImage.createThumbnailFromPath(goodPath, goodSize);
expect(result.isEmpty()).to.equal(false);
});
it('returns the correct size if larger than the initial image', async () => {
// capybara.png is a 128x128 image.
const imgPath = path.join(fixturesPath, 'assets', 'capybara.png');
const size = { width: 256, height: 256 };
const result = await nativeImage.createThumbnailFromPath(imgPath, size);
expect(result.getSize()).to.deep.equal(size);
});
it('returns the correct size if is the same as the initial image', async () => {
// capybara.png is a 128x128 image.
const imgPath = path.join(fixturesPath, 'assets', 'capybara.png');
const size = { width: 128, height: 128 };
const result = await nativeImage.createThumbnailFromPath(imgPath, size);
expect(result.getSize()).to.deep.equal(size);
});
it('returns the correct size if smaller than the initial image', async () => {
// capybara.png is a 128x128 image.
const imgPath = path.join(fixturesPath, 'assets', 'capybara.png');
const maxSize = { width: 64, height: 64 };
const result = await nativeImage.createThumbnailFromPath(imgPath, maxSize);
expect(result.getSize()).to.deep.equal(maxSize);
});
});
describe('addRepresentation()', () => {

View File

@@ -482,6 +482,53 @@ describe('session module', () => {
});
});
describe('ses.resolveHost(host)', () => {
let customSession: Electron.Session;
beforeEach(async () => {
customSession = session.fromPartition('resolvehost');
});
afterEach(() => {
customSession = null as any;
});
it('resolves ipv4.localhost2', async () => {
const { endpoints } = await customSession.resolveHost('ipv4.localhost2');
expect(endpoints).to.be.a('array');
expect(endpoints).to.have.lengthOf(1);
expect(endpoints[0].family).to.equal('ipv4');
expect(endpoints[0].address).to.equal('10.0.0.1');
});
it('fails to resolve AAAA record for ipv4.localhost2', async () => {
await expect(customSession.resolveHost('ipv4.localhost2', {
queryType: 'AAAA'
}))
.to.eventually.be.rejectedWith(/net::ERR_NAME_NOT_RESOLVED/);
});
it('resolves ipv6.localhost2', async () => {
const { endpoints } = await customSession.resolveHost('ipv6.localhost2');
expect(endpoints).to.be.a('array');
expect(endpoints).to.have.lengthOf(1);
expect(endpoints[0].family).to.equal('ipv6');
expect(endpoints[0].address).to.equal('::1');
});
it('fails to resolve A record for ipv6.localhost2', async () => {
await expect(customSession.resolveHost('notfound.localhost2', {
queryType: 'A'
}))
.to.eventually.be.rejectedWith(/net::ERR_NAME_NOT_RESOLVED/);
});
it('fails to resolve notfound.localhost2', async () => {
await expect(customSession.resolveHost('notfound.localhost2'))
.to.eventually.be.rejectedWith(/net::ERR_NAME_NOT_RESOLVED/);
});
});
describe('ses.getBlobData()', () => {
const scheme = 'cors-blob';
const protocol = session.defaultSession.protocol;

View File

@@ -355,28 +355,74 @@ describe('web security', () => {
});
});
describe('csp in sandbox: false', () => {
it('is correctly applied', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: { sandbox: false }
describe('csp', () => {
for (const sandbox of [true, false]) {
describe(`when sandbox: ${sandbox}`, () => {
for (const contextIsolation of [true, false]) {
describe(`when contextIsolation: ${contextIsolation}`, () => {
it('prevents eval from running in an inline script', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: { sandbox, contextIsolation }
});
w.loadURL(`data:text/html,<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'">
</head>
<script>
try {
// We use console.log here because it is easier than making a
// preload script, and the behavior under test changes when
// contextIsolation: false
console.log(eval('true'))
} catch (e) {
console.log(e.message)
}
</script>`);
const [,, message] = await emittedOnce(w.webContents, 'console-message');
expect(message).to.match(/Refused to evaluate a string/);
});
it('does not prevent eval from running in an inline script when there is no csp', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: { sandbox, contextIsolation }
});
w.loadURL(`data:text/html,
<script>
try {
// We use console.log here because it is easier than making a
// preload script, and the behavior under test changes when
// contextIsolation: false
console.log(eval('true'))
} catch (e) {
console.log(e.message)
}
</script>`);
const [,, message] = await emittedOnce(w.webContents, 'console-message');
expect(message).to.equal('true');
});
it('prevents eval from running in executeJavaScript', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: { sandbox, contextIsolation }
});
w.loadURL('data:text/html,<head><meta http-equiv="Content-Security-Policy" content="default-src \'self\'; script-src \'self\' \'unsafe-inline\'"></meta></head>');
await expect(w.webContents.executeJavaScript('eval("true")')).to.be.rejected();
});
it('does not prevent eval from running in executeJavaScript when there is no csp', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: { sandbox, contextIsolation }
});
w.loadURL('data:text/html,');
expect(await w.webContents.executeJavaScript('eval("true")')).to.be.true();
});
});
}
});
w.loadURL(`data:text/html,<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'">
</head>
<script>
try {
// We use console.log here because it is easier than making a
// preload script, and the behavior under test changes when
// contextIsolation: false
console.log(eval('failure'))
} catch (e) {
console.log('success')
}
</script>`);
const [,, message] = await emittedOnce(w.webContents, 'console-message');
expect(message).to.equal('success');
});
}
});
it('does not crash when multiple WebContent are created with web security disabled', () => {

BIN
spec/fixtures/assets/capybara.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -21,6 +21,11 @@ app.on('window-all-closed', () => null);
// Use fake device for Media Stream to replace actual camera and microphone.
app.commandLine.appendSwitch('use-fake-device-for-media-stream');
app.commandLine.appendSwitch('host-rules', 'MAP localhost2 127.0.0.1');
app.commandLine.appendSwitch('host-resolver-rules', [
'MAP ipv4.localhost2 10.0.0.1',
'MAP ipv6.localhost2 [::1]',
'MAP notfound.localhost2 ~NOTFOUND'
].join(', '));
global.standardScheme = 'app';
global.zoomScheme = 'zoom';