mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
28 Commits
v24.0.0-be
...
v24.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95729e64e4 | ||
|
|
6f9cc3ce32 | ||
|
|
55df7a369e | ||
|
|
a6d934f2a2 | ||
|
|
78404eb23f | ||
|
|
59fe35388d | ||
|
|
81e3cef294 | ||
|
|
ae54e3768c | ||
|
|
4de542d524 | ||
|
|
9f1bb29528 | ||
|
|
309cd19d19 | ||
|
|
14985e29e0 | ||
|
|
4fc59bcbef | ||
|
|
0a153e7dca | ||
|
|
5517655962 | ||
|
|
663b741d3f | ||
|
|
ba6ac2a087 | ||
|
|
3c131923f3 | ||
|
|
0c2cb44976 | ||
|
|
a2d0af4bc3 | ||
|
|
82869b88f1 | ||
|
|
ff01742f8e | ||
|
|
0fc69fcaf4 | ||
|
|
505e98e694 | ||
|
|
5c6dd3fdef | ||
|
|
5f68f374d8 | ||
|
|
0af7d3c99b | ||
|
|
b6070c34d4 |
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
2
DEPS
@@ -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':
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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[]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
7
docs/api/structures/resolved-endpoint.md
Normal file
7
docs/api/structures/resolved-endpoint.md
Normal 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`
|
||||
3
docs/api/structures/resolved-host.md
Normal file
3
docs/api/structures/resolved-host.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# ResolvedHost Object
|
||||
|
||||
* `endpoints` [ResolvedEndpoint[]](resolved-endpoint.md) - resolved DNS entries for the hostname
|
||||
@@ -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_
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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**
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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, you’re 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)".
|
||||
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
@@ -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
|
||||
|
||||
193
patches/v8/store_the_thread_stack_start_in_tls.patch
Normal file
193
patches/v8/store_the_thread_stack_start_in_tls.patch
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
78
shell/browser/net/resolve_host_function.cc
Normal file
78
shell/browser/net/resolve_host_function.cc
Normal 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
|
||||
69
shell/browser/net/resolve_host_function.h
Normal file
69
shell/browser/net/resolve_host_function.h
Normal 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_
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
42
shell/common/gin_converters/net_converter.h
Executable file → Normal 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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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()', () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
BIN
spec/fixtures/assets/capybara.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user