mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
42 Commits
v15.0.0-ni
...
v15.0.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edb65a07f1 | ||
|
|
82b5fbc396 | ||
|
|
19820fc2a7 | ||
|
|
adc3f39a9e | ||
|
|
9fe70c5580 | ||
|
|
ea69da279f | ||
|
|
deb75ceaa5 | ||
|
|
d35fb2a2e3 | ||
|
|
c9ba0d02d7 | ||
|
|
612361c4da | ||
|
|
a3298424b3 | ||
|
|
9441ff747d | ||
|
|
d4b2f69f36 | ||
|
|
cfb2829634 | ||
|
|
0d9e6f29ba | ||
|
|
1bb689e6dd | ||
|
|
063ac19712 | ||
|
|
849a3b6f81 | ||
|
|
341b370213 | ||
|
|
3f38681c55 | ||
|
|
05ba6359d0 | ||
|
|
4b780f9770 | ||
|
|
b2da2f759a | ||
|
|
4931c055a9 | ||
|
|
a855aa34d9 | ||
|
|
bec47f54f4 | ||
|
|
4db7221c7d | ||
|
|
75b4267aa9 | ||
|
|
c0995b8dff | ||
|
|
637ba48b42 | ||
|
|
4d0475c9ce | ||
|
|
1897b14af3 | ||
|
|
eb2efd4b7e | ||
|
|
d267f979b7 | ||
|
|
3582a513ca | ||
|
|
9959f01e4c | ||
|
|
19a6286dfd | ||
|
|
459a8417e3 | ||
|
|
96ff8d7bd7 | ||
|
|
e26901aba4 | ||
|
|
0cb5631b0b | ||
|
|
36079b822a |
@@ -45,7 +45,7 @@ executors:
|
||||
type: enum
|
||||
enum: ["medium", "xlarge", "2xlarge+"]
|
||||
docker:
|
||||
- image: electron.azurecr.io/build:4fc81b50f9c0980699d329bc32062fac20a26701
|
||||
- image: electron.azurecr.io/build:fe71f448c9b00708c7a8a67a0210bcef5055ac64
|
||||
resource_class: << parameters.size >>
|
||||
|
||||
macos:
|
||||
|
||||
@@ -22,7 +22,7 @@ Issues are created [here](https://github.com/electron/electron/issues/new).
|
||||
|
||||
### Issue Closure
|
||||
|
||||
Bug reports will be closed if the issue has been inactive and the latest affected version no longer receives support. At the moment, Electron maintains its three latest major versions, with a new major version being released every 12 weeks. (For more information on Electron's release cadence, see [this blog post](https://electronjs.org/blog/12-week-cadence).)
|
||||
Bug reports will be closed if the issue has been inactive and the latest affected version no longer receives support. At the moment, Electron maintains its three latest major versions, with a new major version being released every 8 weeks. (For more information on Electron's release cadence, see [this blog post](https://electronjs.org/blog/8-week-cadence).)
|
||||
|
||||
_If an issue has been closed and you still feel it's relevant, feel free to ping a maintainer or add a comment!_
|
||||
|
||||
|
||||
2
DEPS
2
DEPS
@@ -17,7 +17,7 @@ vars = {
|
||||
'chromium_version':
|
||||
'93.0.4566.0',
|
||||
'node_version':
|
||||
'v16.4.1',
|
||||
'v16.5.0',
|
||||
'nan_version':
|
||||
# The following commit hash of NAN is v2.14.2 with *only* changes to the
|
||||
# test suite. This should be updated to a specific tag when one becomes
|
||||
|
||||
@@ -1 +1 @@
|
||||
15.0.0-nightly.20210712
|
||||
15.0.0-alpha.1
|
||||
@@ -53,6 +53,16 @@ steps:
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$pwd\src\pdb.zip"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/pdb.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
cd src
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y pdb.zip
|
||||
displayName: 'Download pdb files for detailed stacktraces'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
New-Item src\out\Default\gen\node_headers\Release -Type directory
|
||||
Copy-Item -path src\out\Default\electron.lib -destination src\out\Default\gen\node_headers\Release\node.lib
|
||||
@@ -63,15 +73,30 @@ steps:
|
||||
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
set npm_config_arch=arm64
|
||||
cd electron
|
||||
# CalculateNativeWinOcclusion is disabled due to https://bugs.chromium.org/p/chromium/issues/detail?id=1139022
|
||||
node script/yarn test -- --enable-logging --verbose --disable-features=CalculateNativeWinOcclusion
|
||||
displayName: 'Run Electron tests'
|
||||
node script/yarn test --runners=main --runTestFilesSeperately --enable-logging --disable-features=CalculateNativeWinOcclusion
|
||||
displayName: 'Run Electron Main process tests'
|
||||
env:
|
||||
ELECTRON_ENABLE_STACK_DUMPING: true
|
||||
ELECTRON_OUT_DIR: Default
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
|
||||
- script: |
|
||||
cd src
|
||||
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
set npm_config_arch=arm64
|
||||
cd electron
|
||||
node script/yarn test --runners=remote --enable-logging --disable-features=CalculateNativeWinOcclusion
|
||||
displayName: 'Run Electron Remote based tests'
|
||||
env:
|
||||
ELECTRON_OUT_DIR: Default
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
condition: always()
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Test Results'
|
||||
|
||||
@@ -16,5 +16,5 @@ try:
|
||||
subprocess.check_output(args, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
error_msg = "NPM script '{}' failed with code '{}':\n".format(sys.argv[2], e.returncode)
|
||||
print(error_msg + e.output.decode('ascii'))
|
||||
print(error_msg + e.output.decode('utf8'))
|
||||
sys.exit(e.returncode)
|
||||
|
||||
@@ -31,12 +31,6 @@ PATHS_TO_SKIP = [
|
||||
# //chrome/browser/resources/ssl/ssl_error_assistant, but we don't need to
|
||||
# ship it.
|
||||
'pyproto',
|
||||
# On Windows, this binary doesn't exist (the crashpad handler is built-in).
|
||||
# On MacOS, the binary is called 'chrome_crashpad_handler' and is inside the
|
||||
# app bundle.
|
||||
# On Linux, we don't use crashpad, but this binary is still built for some
|
||||
# reason. Exclude it from the zip.
|
||||
'./crashpad_handler',
|
||||
# Skip because these are outputs that we don't need.
|
||||
'resources/inspector',
|
||||
'gen/third_party/devtools-frontend/src',
|
||||
|
||||
@@ -161,6 +161,8 @@ Returns:
|
||||
[`NSUserActivity.activityType`][activity-type].
|
||||
* `userInfo` unknown - Contains app-specific state stored by the activity on
|
||||
another device.
|
||||
* `details` Object
|
||||
* `webpageURL` String (optional) - A string identifying the URL of the webpage accessed by the activity on another device, if available.
|
||||
|
||||
Emitted during [Handoff][handoff] when an activity from a different device wants
|
||||
to be resumed. You should call `event.preventDefault()` if you want to handle
|
||||
|
||||
@@ -273,6 +273,11 @@ If `browserWindow` is not shown dialog will not be attached to it. In such case
|
||||
will result in one button labeled "OK".
|
||||
* `defaultId` Integer (optional) - Index of the button in the buttons array which will
|
||||
be selected by default when the message box opens.
|
||||
* `signal` AbortSignal (optional) - Pass an instance of [AbortSignal][] to
|
||||
optionally close the message box, the message box will behave as if it was
|
||||
cancelled by the user. On macOS, `signal` does not work with message boxes
|
||||
that do not have a parent window, since those message boxes run
|
||||
synchronously due to platform limitations.
|
||||
* `title` String (optional) - Title of the message box, some platforms will not show it.
|
||||
* `detail` String (optional) - Extra information of the message.
|
||||
* `checkboxLabel` String (optional) - If provided, the message box will
|
||||
@@ -360,3 +365,5 @@ window is provided.
|
||||
|
||||
You can call `BrowserWindow.getCurrentWindow().setSheetOffset(offset)` to change
|
||||
the offset from the window frame where sheets are attached.
|
||||
|
||||
[AbortSignal]: https://nodejs.org/api/globals.html#globals_class_abortsignal
|
||||
|
||||
@@ -78,6 +78,7 @@ The following methods of `chrome.runtime` are supported:
|
||||
- `chrome.runtime.getURL`
|
||||
- `chrome.runtime.connect`
|
||||
- `chrome.runtime.sendMessage`
|
||||
- `chrome.runtime.reload`
|
||||
|
||||
The following events of `chrome.runtime` are supported:
|
||||
|
||||
|
||||
@@ -449,6 +449,8 @@ Returns:
|
||||
* `control` Boolean - Equivalent to [KeyboardEvent.controlKey][keyboardevent].
|
||||
* `alt` Boolean - Equivalent to [KeyboardEvent.altKey][keyboardevent].
|
||||
* `meta` Boolean - Equivalent to [KeyboardEvent.metaKey][keyboardevent].
|
||||
* `location` Number - Equivalent to [KeyboardEvent.location][keyboardevent].
|
||||
* `modifiers` String[] - See [InputEvent.modifiers](structures/input-event.md).
|
||||
|
||||
Emitted before dispatching the `keydown` and `keyup` events in the page.
|
||||
Calling `event.preventDefault` will prevent the page `keydown`/`keyup` events
|
||||
|
||||
@@ -710,6 +710,10 @@ Corresponds to the points in time when the spinner of the tab starts spinning.
|
||||
|
||||
Corresponds to the points in time when the spinner of the tab stops spinning.
|
||||
|
||||
### Event: 'did-attach'
|
||||
|
||||
Fired when attached to the embedder web contents.
|
||||
|
||||
### Event: 'dom-ready'
|
||||
|
||||
Fired when document in the given frame is loaded.
|
||||
|
||||
@@ -133,7 +133,7 @@ are likely using [`electron-packager`], which includes [`electron-osx-sign`] and
|
||||
|
||||
If you're using Packager's API, you can pass [in configuration that both signs
|
||||
and notarizes your
|
||||
application](https://electron.github.io/electron-packager/master/interfaces/electronpackager.options.html).
|
||||
application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html).
|
||||
|
||||
```js
|
||||
const packager = require('electron-packager')
|
||||
|
||||
@@ -200,6 +200,6 @@ Run the example using Electron Fiddle and then click the "Toggle Dark Mode" butt
|
||||
[system-wide-dark-mode]: https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/dark-mode/
|
||||
[electron-forge]: https://www.electronforge.io/
|
||||
[electron-packager]: https://github.com/electron/electron-packager
|
||||
[packager-darwindarkmode-api]: https://electron.github.io/electron-packager/master/interfaces/electronpackager.options.html#darwindarkmodesupport
|
||||
[packager-darwindarkmode-api]: https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html#darwindarkmodesupport
|
||||
[prefers-color-scheme]: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
|
||||
[event-listeners]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
# Electron Release Timelines
|
||||
|
||||
Special notes:
|
||||
|
||||
* The `-beta.1` and `stable` dates are our solid release dates.
|
||||
* We strive for weekly beta releases, however we often release more betas than scheduled.
|
||||
* All dates are our goals but there may be reasons for adjusting the stable deadline, such as security bugs.
|
||||
* Take a look at the [5.0.0 Timeline blog post](https://electronjs.org/blog/electron-5-0-timeline) for info about publicizing our release dates.
|
||||
* Since Electron 6.0, we've been targeting every other Chromium version and releasing our stable on the same day as Chrome stable. You can reference Chromium's release schedule [here](https://chromiumdash.appspot.com/schedule). See [Electron's new release cadence blog post](https://www.electronjs.org/blog/12-week-cadence) for more details on our release schedule.
|
||||
* Electron 15.0 only will include a special Alpha release. Starting in Electron 16.0, we will release on an 8-week cadence. See [Electron's new 8-week cadence blog post](https://www.electronjs.org/blog/8-week-cadence) for more details.
|
||||
|
||||
| Version | -beta.1 | Stable | Chrome | Node |
|
||||
| ------- | ------- | ------ | ------ | ---- |
|
||||
| 2.0.0 | 2018-02-21 | 2018-05-01 | M61 | v8.9 |
|
||||
| 3.0.0 | 2018-06-21 | 2018-09-18 | M66 | v10.2 |
|
||||
| 4.0.0 | 2018-10-11 | 2018-12-20 | M69 | v10.11 |
|
||||
| 5.0.0 | 2019-01-22 | 2019-04-24 | M73 | v12.0 |
|
||||
| 6.0.0 | 2019-05-01 | 2019-07-30 | M76 | v12.4 |
|
||||
| 7.0.0 | 2019-08-01 | 2019-10-22 | M78 | v12.8 |
|
||||
| 8.0.0 | 2019-10-24 | 2020-02-04 | M80 | v12.13 |
|
||||
| 9.0.0 | 2020-02-06 | 2020-05-19 | M83 | v12.14 |
|
||||
| 10.0.0 | 2020-05-21 | 2020-08-25 | M85 | v12.16 |
|
||||
| 11.0.0 | 2020-08-27 | 2020-11-17 | M87 | v12.18 |
|
||||
| 12.0.0 | 2020-11-19 | 2021-03-02 | M89 | v14.16 |
|
||||
| 13.0.0 | 2021-03-04 | 2021-05-25 | M91 | v14.16 |
|
||||
| 14.0.0 | 2021-05-27 | 2021-08-31 | M93 | TBD |
|
||||
| Electron | Alpha | Beta | Stable | Chrome | Node |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- |
|
||||
| 2.0.0 | -- | 2018-Feb-21 | 2018-May-01 | M61 | v8.9 |
|
||||
| 3.0.0 | -- | 2018-Jun-21 | 2018-Sep-18 | M66 | v10.2 |
|
||||
| 4.0.0 | -- | 2018-Oct-11 | 2018-Dec-20 | M69 | v10.11 |
|
||||
| 5.0.0 | -- | 2019-Jan-22 | 2019-Apr-24 | M73 | v12.0 |
|
||||
| 6.0.0 | -- | 2019-May-01 | 2019-Jul-30 | M76 | v12.4 |
|
||||
| 7.0.0 | -- | 2019-Aug-01 | 2019-Oct-22 | M78 | v12.8 |
|
||||
| 8.0.0 | -- | 2019-Oct-24 | 2020-Feb-04 | M80 | v12.13 |
|
||||
| 9.0.0 | -- | 2020-Feb-06 | 2020-May-19 | M83 | v12.14 |
|
||||
| 10.0.0 | -- | 2020-May-21 | 2020-Aug-25 | M85 | v12.16 |
|
||||
| 11.0.0 | -- | 2020-Aug-27 | 2020-Nov-17 | M87 | v12.18 |
|
||||
| 12.0.0 | -- | 2020-Nov-19 | 2021-Mar-02 | M89 | v14.16 |
|
||||
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | M91 | v14.16 |
|
||||
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | M93 | TBD |
|
||||
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | M94 | TBD |
|
||||
|
||||
@@ -168,7 +168,7 @@ After you start your electron app, you can now enter in a URL in your browser th
|
||||
can leave it empty.
|
||||
-->
|
||||
|
||||
```fiddle docs/fiddles/system/protocol-handler/launch-app-from-url-in-another-app
|
||||
```fiddle docs/fiddles/system/protocol-handler/launch-app-from-URL-in-another-app
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ app.on('window-all-closed', function () {
|
||||
|
||||
[node-platform]: https://nodejs.org/api/process.html#process_process_platform
|
||||
[window-all-closed]: ../api/app.md#event-window-all-closed
|
||||
[window-all-closed]: ../api/app.md#appquit
|
||||
[app-quit]: ../api/app.md#appquit
|
||||
|
||||
#### Open a window if none are open (macOS)
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ or [Parcel][parcel].
|
||||
Note that because the environment presented to the `preload` script is substantially
|
||||
more privileged than that of a sandboxed renderer, it is still possible to leak
|
||||
privileged APIs to untrusted code running in the renderer process unless
|
||||
[`contextIsolation`][contextIsolation] is enabled.
|
||||
[`contextIsolation`][context-isolation] is enabled.
|
||||
|
||||
## Configuring the sandbox
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ 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*)
|
||||
are already patched and cannot be exploited in your application. For more information,
|
||||
see "[Use a current version of Electron](#15-use-a-current-version-of-electron)".
|
||||
see "[Use a current version of Electron](#16-use-a-current-version-of-electron)".
|
||||
|
||||
* **Evaluate your dependencies.** While NPM provides half a million reusable packages,
|
||||
it is your responsibility to choose trusted 3rd-party libraries. If you use outdated
|
||||
@@ -88,18 +88,19 @@ You should at least follow these steps to improve the security of your applicati
|
||||
1. [Only load secure content](#1-only-load-secure-content)
|
||||
2. [Disable the Node.js integration in all renderers that display remote content](#2-do-not-enable-nodejs-integration-for-remote-content)
|
||||
3. [Enable context isolation in all renderers that display remote content](#3-enable-context-isolation-for-remote-content)
|
||||
4. [Use `ses.setPermissionRequestHandler()` in all sessions that load remote content](#4-handle-session-permission-requests-from-remote-content)
|
||||
5. [Do not disable `webSecurity`](#5-do-not-disable-websecurity)
|
||||
6. [Define a `Content-Security-Policy`](#6-define-a-content-security-policy) and use restrictive rules (i.e. `script-src 'self'`)
|
||||
7. [Do not set `allowRunningInsecureContent` to `true`](#7-do-not-set-allowrunninginsecurecontent-to-true)
|
||||
8. [Do not enable experimental features](#8-do-not-enable-experimental-features)
|
||||
9. [Do not use `enableBlinkFeatures`](#9-do-not-use-enableblinkfeatures)
|
||||
10. [`<webview>`: Do not use `allowpopups`](#10-do-not-use-allowpopups)
|
||||
11. [`<webview>`: Verify options and params](#11-verify-webview-options-before-creation)
|
||||
12. [Disable or limit navigation](#12-disable-or-limit-navigation)
|
||||
13. [Disable or limit creation of new windows](#13-disable-or-limit-creation-of-new-windows)
|
||||
14. [Do not use `openExternal` with untrusted content](#14-do-not-use-openexternal-with-untrusted-content)
|
||||
15. [Use a current version of Electron](#15-use-a-current-version-of-electron)
|
||||
4. [Enable sandboxing](#4-enable-sandboxing)
|
||||
5. [Use `ses.setPermissionRequestHandler()` in all sessions that load remote content](#5-handle-session-permission-requests-from-remote-content)
|
||||
6. [Do not disable `webSecurity`](#6-do-not-disable-websecurity)
|
||||
7. [Define a `Content-Security-Policy`](#7-define-a-content-security-policy) and use restrictive rules (i.e. `script-src 'self'`)
|
||||
8. [Do not set `allowRunningInsecureContent` to `true`](#8-do-not-set-allowrunninginsecurecontent-to-true)
|
||||
9. [Do not enable experimental features](#9-do-not-enable-experimental-features)
|
||||
10. [Do not use `enableBlinkFeatures`](#10-do-not-use-enableblinkfeatures)
|
||||
11. [`<webview>`: Do not use `allowpopups`](#11-do-not-use-allowpopups)
|
||||
12. [`<webview>`: Verify options and params](#12-verify-webview-options-before-creation)
|
||||
13. [Disable or limit navigation](#13-disable-or-limit-navigation)
|
||||
14. [Disable or limit creation of new windows](#14-disable-or-limit-creation-of-new-windows)
|
||||
15. [Do not use `openExternal` with untrusted content](#15-do-not-use-openexternal-with-untrusted-content)
|
||||
16. [Use a current version of Electron](#16-use-a-current-version-of-electron)
|
||||
|
||||
To automate the detection of misconfigurations and insecure patterns, it is
|
||||
possible to use
|
||||
@@ -239,7 +240,26 @@ and prevent the use of Node primitives `contextIsolation` **must** also be used.
|
||||
For more information on what `contextIsolation` is and how to enable it please
|
||||
see our dedicated [Context Isolation](context-isolation.md) document.
|
||||
|
||||
## 4) Handle Session Permission Requests From Remote Content
|
||||
## 4) Enable Sandboxing
|
||||
|
||||
[Sandboxing](sandbox.md) is a Chromium feature that uses the operating system to
|
||||
significantly limit what renderer processes have access to. You should enable
|
||||
the sandbox in all renderers. Loading, reading or processing any untrusted
|
||||
content in an unsandboxed process, including the main process, is not advised.
|
||||
|
||||
### How?
|
||||
|
||||
When creating a window, pass the `sandbox: true` option in `webPreferences`:
|
||||
|
||||
```js
|
||||
const win = new BrowserWindow({
|
||||
webPreferences: {
|
||||
sandbox: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 5) Handle Session Permission Requests From Remote Content
|
||||
|
||||
You may have seen permission requests while using Chrome: They pop up whenever
|
||||
the website attempts to use a feature that the user has to manually approve (
|
||||
@@ -277,7 +297,7 @@ session
|
||||
})
|
||||
```
|
||||
|
||||
## 5) Do Not Disable WebSecurity
|
||||
## 6) Do Not Disable WebSecurity
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
|
||||
@@ -318,7 +338,7 @@ const mainWindow = new BrowserWindow()
|
||||
<webview src="page.html"></webview>
|
||||
```
|
||||
|
||||
## 6) Define a Content Security Policy
|
||||
## 7) Define a Content Security Policy
|
||||
|
||||
A Content Security Policy (CSP) is an additional layer of protection against
|
||||
cross-site-scripting attacks and data injection attacks. We recommend that they
|
||||
@@ -374,7 +394,7 @@ on a page directly in the markup using a `<meta>` tag:
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'">
|
||||
```
|
||||
|
||||
## 7) Do Not Set `allowRunningInsecureContent` to `true`
|
||||
## 8) Do Not Set `allowRunningInsecureContent` to `true`
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
|
||||
@@ -407,7 +427,7 @@ const mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({})
|
||||
```
|
||||
|
||||
## 8) Do Not Enable Experimental Features
|
||||
## 9) Do Not Enable Experimental Features
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
|
||||
@@ -439,7 +459,7 @@ const mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow({})
|
||||
```
|
||||
|
||||
## 9) Do Not Use `enableBlinkFeatures`
|
||||
## 10) Do Not Use `enableBlinkFeatures`
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
|
||||
@@ -471,7 +491,7 @@ const mainWindow = new BrowserWindow({
|
||||
const mainWindow = new BrowserWindow()
|
||||
```
|
||||
|
||||
## 10) Do Not Use `allowpopups`
|
||||
## 11) Do Not Use `allowpopups`
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
|
||||
@@ -498,7 +518,7 @@ you know it needs that feature.
|
||||
<webview src="page.html"></webview>
|
||||
```
|
||||
|
||||
## 11) Verify WebView Options Before Creation
|
||||
## 12) Verify WebView Options Before Creation
|
||||
|
||||
A WebView created in a renderer process that does not have Node.js integration
|
||||
enabled will not be able to enable integration itself. However, a WebView will
|
||||
@@ -545,7 +565,7 @@ app.on('web-contents-created', (event, contents) => {
|
||||
Again, this list merely minimizes the risk, it does not remove it. If your goal
|
||||
is to display a website, a browser will be a more secure option.
|
||||
|
||||
## 12) Disable or limit navigation
|
||||
## 13) Disable or limit navigation
|
||||
|
||||
If your app has no need to navigate or only needs to navigate to known pages,
|
||||
it is a good idea to limit navigation outright to that known scope, disallowing
|
||||
@@ -589,7 +609,7 @@ app.on('web-contents-created', (event, contents) => {
|
||||
})
|
||||
```
|
||||
|
||||
## 13) Disable or limit creation of new windows
|
||||
## 14) Disable or limit creation of new windows
|
||||
|
||||
If you have a known set of windows, it's a good idea to limit the creation of
|
||||
additional windows in your app.
|
||||
@@ -636,7 +656,7 @@ app.on('web-contents-created', (event, contents) => {
|
||||
})
|
||||
```
|
||||
|
||||
## 14) Do not use `openExternal` with untrusted content
|
||||
## 15) Do not use `openExternal` with untrusted content
|
||||
|
||||
Shell's [`openExternal`][open-external] allows opening a given protocol URI with
|
||||
the desktop's native utilities. On macOS, for instance, this function is similar
|
||||
@@ -663,7 +683,7 @@ const { shell } = require('electron')
|
||||
shell.openExternal('https://example.com/index.html')
|
||||
```
|
||||
|
||||
## 15) Use a current version of Electron
|
||||
## 16) Use a current version of Electron
|
||||
|
||||
You should strive for always using the latest available version of Electron.
|
||||
Whenever a new major version is released, you should attempt to update your
|
||||
|
||||
@@ -37,6 +37,13 @@ tools and resources.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
_**Note:** Beginning in September 2021 with Electron 15, the Electron team
|
||||
will temporarily support the latest **four** stable major versions. This
|
||||
extended support is intended to help Electron developers transition to
|
||||
the [new eight week release cadence](https://electronjs.org/blog/8-week-cadence), and will continue until May 2022, with
|
||||
the release of Electron 19. At that time, the Electron team will drop support
|
||||
back to the latest three stable major versions._
|
||||
|
||||
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
|
||||
@@ -63,6 +70,7 @@ until the maintainers feel the maintenance burden is too high to continue doing
|
||||
|
||||
### Currently supported versions
|
||||
|
||||
* 14.x.y
|
||||
* 13.x.y
|
||||
* 12.x.y
|
||||
* 11.x.y
|
||||
|
||||
@@ -22,6 +22,11 @@ enum OpenFileDialogProperties {
|
||||
dontAddToRecent = 1 << 8 // Windows
|
||||
}
|
||||
|
||||
let nextId = 0;
|
||||
const getNextId = function () {
|
||||
return ++nextId;
|
||||
};
|
||||
|
||||
const normalizeAccessKey = (text: string) => {
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
@@ -157,6 +162,7 @@ const messageBox = (sync: boolean, window: BrowserWindow | null, options?: Messa
|
||||
let {
|
||||
buttons = [],
|
||||
cancelId,
|
||||
signal,
|
||||
checkboxLabel = '',
|
||||
checkboxChecked,
|
||||
defaultId = -1,
|
||||
@@ -196,10 +202,21 @@ const messageBox = (sync: boolean, window: BrowserWindow | null, options?: Messa
|
||||
}
|
||||
}
|
||||
|
||||
// AbortSignal processing.
|
||||
let id: number | undefined;
|
||||
if (signal) {
|
||||
// Generate an ID used for closing the message box.
|
||||
id = getNextId();
|
||||
// Close the message box when signal is aborted.
|
||||
if (signal.aborted) { return Promise.resolve({ cancelId, checkboxChecked }); }
|
||||
signal.addEventListener('abort', () => dialogBinding._closeMessageBox(id));
|
||||
}
|
||||
|
||||
const settings = {
|
||||
window,
|
||||
messageBoxType,
|
||||
buttons,
|
||||
id,
|
||||
defaultId,
|
||||
cancelId,
|
||||
noLink,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "15.0.0-nightly.20210712",
|
||||
"version": "15.0.0-alpha.1",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -101,3 +101,5 @@ build_do_not_depend_on_packed_resource_integrity.patch
|
||||
refactor_restore_base_adaptcallbackforrepeating.patch
|
||||
hack_to_allow_gclient_sync_with_host_os_mac_on_linux_in_ci.patch
|
||||
don_t_run_pcscan_notifythreadcreated_if_pcscan_is_disabled.patch
|
||||
set_svgimage_page_after_document_install.patch
|
||||
add_gin_wrappable_crash_key.patch
|
||||
|
||||
44
patches/chromium/add_gin_wrappable_crash_key.patch
Normal file
44
patches/chromium/add_gin_wrappable_crash_key.patch
Normal file
@@ -0,0 +1,44 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: VerteDinde <khammond@slack-corp.com>
|
||||
Date: Thu, 15 Jul 2021 12:16:50 -0700
|
||||
Subject: chore: add gin::wrappable wrapperinfo crash key
|
||||
|
||||
This patch adds an additional crash key for gin::Wrappable, to help
|
||||
debug a crash that is occurring during garbage collection in SecondWeakCallback.
|
||||
|
||||
The crash seems to be due to a class that is holding a reference to
|
||||
gin::Wrappable even after being deleted. This added crash key compares
|
||||
the soon-to-be-deleted WrapperInfo with known WrapperInfo components to
|
||||
help determine where the crash originated from.
|
||||
|
||||
This patch should not be upstreamed, and can be removed in Electron 15 and
|
||||
beyond once we identify the cause of the crash.
|
||||
|
||||
diff --git a/gin/wrappable.cc b/gin/wrappable.cc
|
||||
index fe07eb94a8e679859bba6d76ff0d6ee86bd0c67e..d0066fca501eae5be4177440b44dbecc8e34c897 100644
|
||||
--- a/gin/wrappable.cc
|
||||
+++ b/gin/wrappable.cc
|
||||
@@ -8,6 +8,10 @@
|
||||
#include "gin/object_template_builder.h"
|
||||
#include "gin/per_isolate_data.h"
|
||||
|
||||
+#if !defined(MAS_BUILD)
|
||||
+#include "electron/shell/common/crash_keys.h"
|
||||
+#endif
|
||||
+
|
||||
namespace gin {
|
||||
|
||||
WrappableBase::WrappableBase() = default;
|
||||
@@ -36,6 +40,12 @@ void WrappableBase::FirstWeakCallback(
|
||||
void WrappableBase::SecondWeakCallback(
|
||||
const v8::WeakCallbackInfo<WrappableBase>& data) {
|
||||
WrappableBase* wrappable = data.GetParameter();
|
||||
+
|
||||
+#if !defined(MAS_BUILD)
|
||||
+ WrapperInfo* info = static_cast<WrapperInfo*>(data.GetInternalField(0));
|
||||
+ electron::crash_keys::SetCrashKeyForGinWrappable(info);
|
||||
+#endif
|
||||
+
|
||||
delete wrappable;
|
||||
}
|
||||
|
||||
@@ -74,6 +74,32 @@ index 39557cce474439238255ecd28030215085db0c81..5b3f980837911c710686ab91a2a81c31
|
||||
#if defined(OS_ANDROID)
|
||||
// Used by WebView to sample crashes without generating the unwanted dumps. If
|
||||
// the returned value is less than 100, crash dumping will be sampled to that
|
||||
diff --git a/components/crash/core/app/crashpad_linux.cc b/components/crash/core/app/crashpad_linux.cc
|
||||
index 5f97c1ef00d9c63a7b16265cc97d9f145adae550..8c3028f228373b5e1145fe3235dc06663f8b087f 100644
|
||||
--- a/components/crash/core/app/crashpad_linux.cc
|
||||
+++ b/components/crash/core/app/crashpad_linux.cc
|
||||
@@ -165,6 +165,7 @@ base::FilePath PlatformCrashpadInitialization(
|
||||
// where crash_reporter provides it's own values for lsb-release.
|
||||
annotations["lsb-release"] = base::GetLinuxDistro();
|
||||
#endif
|
||||
+ crash_reporter_client->GetProcessSimpleAnnotations(&annotations);
|
||||
|
||||
std::vector<std::string> arguments;
|
||||
if (crash_reporter_client->ShouldMonitorCrashHandlerExpensively()) {
|
||||
@@ -186,6 +187,13 @@ base::FilePath PlatformCrashpadInitialization(
|
||||
}
|
||||
#endif
|
||||
|
||||
+ if (!crash_reporter_client->GetShouldRateLimit()) {
|
||||
+ arguments.push_back("--no-rate-limit");
|
||||
+ }
|
||||
+ if (!crash_reporter_client->GetShouldCompressUploads()) {
|
||||
+ arguments.push_back("--no-upload-gzip");
|
||||
+ }
|
||||
+
|
||||
bool result =
|
||||
client.StartHandler(handler_path, database_path, metrics_path, url,
|
||||
annotations, arguments, false, false);
|
||||
diff --git a/components/crash/core/app/crashpad_mac.mm b/components/crash/core/app/crashpad_mac.mm
|
||||
index e3fc1fb2bcab31d6a7cb325a892acb26dc00d4e4..fd654d6e514de416457c283caeb1895dba6286e1 100644
|
||||
--- a/components/crash/core/app/crashpad_mac.mm
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Fredrik=20S=C3=B6derqvist?= <fs@opera.com>
|
||||
Date: Fri, 9 Jul 2021 08:44:55 +0000
|
||||
Subject: Set SVGImage::page_ after document install
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
We can end up having the associated ImageResource call
|
||||
SVGImage::ResetAnimation() before the Document has been associated with
|
||||
the SVGImage's LocalFrame, but after the link to the initial Document
|
||||
was severed, if a GC is triggered within that window and ends up
|
||||
collecting the last observer of the ImageResource.
|
||||
|
||||
By assigning |SVGImage::page_| after the installing the document, we
|
||||
close this hole since SVGImage::RootElement() (called by
|
||||
SVGImage::ResetAnimation()) will now observe a null Page and return null
|
||||
without attempting to dereference the document.
|
||||
|
||||
Bug: 1216190
|
||||
Change-Id: I26e08848e5b9bd52e3377841eee35e4acc03d320
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3010140
|
||||
Reviewed-by: Stephen Chenney <schenney@chromium.org>
|
||||
Commit-Queue: Fredrik Söderquist <fs@opera.com>
|
||||
Cr-Commit-Position: refs/heads/master@{#899922}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.cc b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
|
||||
index b23ad2192bec4d1cac9d704074d12c9e00d4d2f5..ff2bf69be27f0afcb6a9909e716495e8d4a127ef 100644
|
||||
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.cc
|
||||
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
|
||||
@@ -851,12 +851,15 @@ Image::SizeAvailability SVGImage::DataChanged(bool all_data_received) {
|
||||
// SVG Images are transparent.
|
||||
frame->View()->SetBaseBackgroundColor(Color::kTransparent);
|
||||
|
||||
- page_ = page;
|
||||
-
|
||||
TRACE_EVENT0("blink", "SVGImage::dataChanged::load");
|
||||
|
||||
frame->ForceSynchronousDocumentInstall("image/svg+xml", Data());
|
||||
|
||||
+ // Set up our Page reference after installing our document. This avoids
|
||||
+ // tripping on a non-existing (null) Document if a GC is triggered during the
|
||||
+ // set up and ends up collecting the last owner/observer of this image.
|
||||
+ page_ = page;
|
||||
+
|
||||
// Intrinsic sizing relies on computed style (e.g. font-size and
|
||||
// writing-mode).
|
||||
frame->GetDocument()->UpdateStyleAndLayoutTree();
|
||||
@@ -888,10 +888,10 @@ index 0000000000000000000000000000000000000000..2c9d2826c85bdd033f1df1d6188df636
|
||||
+}
|
||||
diff --git a/filenames.json b/filenames.json
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..7225bdb6b281837253978430a665e9ee5a9e0646
|
||||
index 0000000000000000000000000000000000000000..6ce2f7899ffc39e145a767685a51597fa4473e50
|
||||
--- /dev/null
|
||||
+++ b/filenames.json
|
||||
@@ -0,0 +1,530 @@
|
||||
@@ -0,0 +1,544 @@
|
||||
+// This file is automatically generated by generate_gn_filenames_json.py
|
||||
+// DO NOT EDIT
|
||||
+{
|
||||
@@ -926,6 +926,14 @@ index 0000000000000000000000000000000000000000..7225bdb6b281837253978430a665e9ee
|
||||
+ ]
|
||||
+ },
|
||||
+ {
|
||||
+ "dest_dir": "include/node//libplatform/",
|
||||
+ "files": [
|
||||
+ "//v8/include/libplatform/libplatform-export.h",
|
||||
+ "//v8/include/libplatform/libplatform.h",
|
||||
+ "//v8/include/libplatform/v8-tracing.h"
|
||||
+ ]
|
||||
+ },
|
||||
+ {
|
||||
+ "dest_dir": "include/node//uv/",
|
||||
+ "files": [
|
||||
+ "deps/uv/include/uv/aix.h",
|
||||
@@ -953,250 +961,260 @@ index 0000000000000000000000000000000000000000..7225bdb6b281837253978430a665e9ee
|
||||
+ }
|
||||
+ ],
|
||||
+ "library_files": [
|
||||
+ "lib/internal/bootstrap/environment.js",
|
||||
+ "lib/internal/bootstrap/loaders.js",
|
||||
+ "lib/internal/bootstrap/node.js",
|
||||
+ "lib/internal/bootstrap/pre_execution.js",
|
||||
+ "lib/internal/bootstrap/switches/does_own_process_state.js",
|
||||
+ "lib/internal/bootstrap/switches/does_not_own_process_state.js",
|
||||
+ "lib/internal/bootstrap/switches/is_main_thread.js",
|
||||
+ "lib/internal/bootstrap/switches/is_not_main_thread.js",
|
||||
+ "lib/internal/per_context/primordials.js",
|
||||
+ "lib/internal/per_context/domexception.js",
|
||||
+ "lib/internal/per_context/messageport.js",
|
||||
+ "lib/async_hooks.js",
|
||||
+ "lib/assert.js",
|
||||
+ "lib/assert/strict.js",
|
||||
+ "lib/buffer.js",
|
||||
+ "lib/child_process.js",
|
||||
+ "lib/console.js",
|
||||
+ "lib/constants.js",
|
||||
+ "lib/crypto.js",
|
||||
+ "lib/cluster.js",
|
||||
+ "lib/diagnostics_channel.js",
|
||||
+ "lib/dgram.js",
|
||||
+ "lib/dns.js",
|
||||
+ "lib/dns/promises.js",
|
||||
+ "lib/domain.js",
|
||||
+ "lib/events.js",
|
||||
+ "lib/fs.js",
|
||||
+ "lib/fs/promises.js",
|
||||
+ "lib/http.js",
|
||||
+ "lib/http2.js",
|
||||
+ "lib/_http_agent.js",
|
||||
+ "lib/_http_client.js",
|
||||
+ "lib/_http_common.js",
|
||||
+ "lib/_http_incoming.js",
|
||||
+ "lib/_http_outgoing.js",
|
||||
+ "lib/_http_server.js",
|
||||
+ "lib/https.js",
|
||||
+ "lib/inspector.js",
|
||||
+ "lib/module.js",
|
||||
+ "lib/net.js",
|
||||
+ "lib/os.js",
|
||||
+ "lib/path.js",
|
||||
+ "lib/path/posix.js",
|
||||
+ "lib/path/win32.js",
|
||||
+ "lib/perf_hooks.js",
|
||||
+ "lib/process.js",
|
||||
+ "lib/punycode.js",
|
||||
+ "lib/querystring.js",
|
||||
+ "lib/readline.js",
|
||||
+ "lib/repl.js",
|
||||
+ "lib/stream.js",
|
||||
+ "lib/stream/promises.js",
|
||||
+ "lib/_stream_readable.js",
|
||||
+ "lib/_stream_writable.js",
|
||||
+ "lib/_stream_duplex.js",
|
||||
+ "lib/_stream_transform.js",
|
||||
+ "lib/_stream_passthrough.js",
|
||||
+ "lib/_stream_wrap.js",
|
||||
+ "lib/string_decoder.js",
|
||||
+ "lib/sys.js",
|
||||
+ "lib/timers/promises.js",
|
||||
+ "lib/timers.js",
|
||||
+ "lib/tls.js",
|
||||
+ "lib/_tls_common.js",
|
||||
+ "lib/_tls_wrap.js",
|
||||
+ "lib/trace_events.js",
|
||||
+ "lib/tty.js",
|
||||
+ "lib/url.js",
|
||||
+ "lib/events.js",
|
||||
+ "lib/repl.js",
|
||||
+ "lib/util.js",
|
||||
+ "lib/util/types.js",
|
||||
+ "lib/v8.js",
|
||||
+ "lib/dgram.js",
|
||||
+ "lib/vm.js",
|
||||
+ "lib/wasi.js",
|
||||
+ "lib/stream.js",
|
||||
+ "lib/child_process.js",
|
||||
+ "lib/assert.js",
|
||||
+ "lib/_tls_wrap.js",
|
||||
+ "lib/http2.js",
|
||||
+ "lib/inspector.js",
|
||||
+ "lib/os.js",
|
||||
+ "lib/_http_server.js",
|
||||
+ "lib/console.js",
|
||||
+ "lib/perf_hooks.js",
|
||||
+ "lib/readline.js",
|
||||
+ "lib/punycode.js",
|
||||
+ "lib/_http_incoming.js",
|
||||
+ "lib/https.js",
|
||||
+ "lib/_stream_wrap.js",
|
||||
+ "lib/domain.js",
|
||||
+ "lib/dns.js",
|
||||
+ "lib/_http_client.js",
|
||||
+ "lib/diagnostics_channel.js",
|
||||
+ "lib/tty.js",
|
||||
+ "lib/_http_agent.js",
|
||||
+ "lib/timers.js",
|
||||
+ "lib/_http_outgoing.js",
|
||||
+ "lib/querystring.js",
|
||||
+ "lib/_tls_common.js",
|
||||
+ "lib/module.js",
|
||||
+ "lib/_stream_passthrough.js",
|
||||
+ "lib/_stream_transform.js",
|
||||
+ "lib/worker_threads.js",
|
||||
+ "lib/sys.js",
|
||||
+ "lib/_stream_duplex.js",
|
||||
+ "lib/path.js",
|
||||
+ "lib/_http_common.js",
|
||||
+ "lib/string_decoder.js",
|
||||
+ "lib/cluster.js",
|
||||
+ "lib/v8.js",
|
||||
+ "lib/crypto.js",
|
||||
+ "lib/wasi.js",
|
||||
+ "lib/_stream_readable.js",
|
||||
+ "lib/zlib.js",
|
||||
+ "lib/internal/abort_controller.js",
|
||||
+ "lib/internal/assert.js",
|
||||
+ "lib/internal/assert/assertion_error.js",
|
||||
+ "lib/internal/assert/calltracker.js",
|
||||
+ "lib/internal/async_hooks.js",
|
||||
+ "lib/internal/blob.js",
|
||||
+ "lib/internal/blocklist.js",
|
||||
+ "lib/internal/buffer.js",
|
||||
+ "lib/internal/cli_table.js",
|
||||
+ "lib/internal/child_process.js",
|
||||
+ "lib/internal/child_process/serialization.js",
|
||||
+ "lib/internal/cluster/child.js",
|
||||
+ "lib/internal/cluster/primary.js",
|
||||
+ "lib/internal/cluster/round_robin_handle.js",
|
||||
+ "lib/internal/cluster/shared_handle.js",
|
||||
+ "lib/internal/cluster/utils.js",
|
||||
+ "lib/internal/cluster/worker.js",
|
||||
+ "lib/internal/console/constructor.js",
|
||||
+ "lib/internal/console/global.js",
|
||||
+ "lib/internal/crypto/aes.js",
|
||||
+ "lib/internal/crypto/certificate.js",
|
||||
+ "lib/internal/crypto/cipher.js",
|
||||
+ "lib/internal/crypto/diffiehellman.js",
|
||||
+ "lib/internal/crypto/dsa.js",
|
||||
+ "lib/internal/crypto/ec.js",
|
||||
+ "lib/internal/crypto/hash.js",
|
||||
+ "lib/internal/crypto/hashnames.js",
|
||||
+ "lib/internal/crypto/hkdf.js",
|
||||
+ "lib/internal/crypto/keygen.js",
|
||||
+ "lib/internal/crypto/keys.js",
|
||||
+ "lib/internal/crypto/mac.js",
|
||||
+ "lib/internal/crypto/pbkdf2.js",
|
||||
+ "lib/internal/crypto/random.js",
|
||||
+ "lib/internal/crypto/rsa.js",
|
||||
+ "lib/internal/crypto/scrypt.js",
|
||||
+ "lib/internal/crypto/sig.js",
|
||||
+ "lib/internal/crypto/util.js",
|
||||
+ "lib/internal/crypto/webcrypto.js",
|
||||
+ "lib/internal/crypto/x509.js",
|
||||
+ "lib/url.js",
|
||||
+ "lib/tls.js",
|
||||
+ "lib/_stream_writable.js",
|
||||
+ "lib/async_hooks.js",
|
||||
+ "lib/process.js",
|
||||
+ "lib/http.js",
|
||||
+ "lib/buffer.js",
|
||||
+ "lib/fs.js",
|
||||
+ "lib/util/types.js",
|
||||
+ "lib/timers/promises.js",
|
||||
+ "lib/path/win32.js",
|
||||
+ "lib/path/posix.js",
|
||||
+ "lib/stream/promises.js",
|
||||
+ "lib/stream/web.js",
|
||||
+ "lib/internal/constants.js",
|
||||
+ "lib/internal/debugger/_inspect.js",
|
||||
+ "lib/internal/debugger/inspect_client.js",
|
||||
+ "lib/internal/debugger/inspect_repl.js",
|
||||
+ "lib/internal/dgram.js",
|
||||
+ "lib/internal/dns/promises.js",
|
||||
+ "lib/internal/dns/utils.js",
|
||||
+ "lib/internal/dtrace.js",
|
||||
+ "lib/internal/encoding.js",
|
||||
+ "lib/internal/errors.js",
|
||||
+ "lib/internal/error_serdes.js",
|
||||
+ "lib/internal/event_target.js",
|
||||
+ "lib/internal/fixed_queue.js",
|
||||
+ "lib/internal/freelist.js",
|
||||
+ "lib/internal/freeze_intrinsics.js",
|
||||
+ "lib/internal/fs/dir.js",
|
||||
+ "lib/internal/fs/promises.js",
|
||||
+ "lib/internal/fs/read_file_context.js",
|
||||
+ "lib/internal/fs/rimraf.js",
|
||||
+ "lib/internal/fs/streams.js",
|
||||
+ "lib/internal/fs/sync_write_stream.js",
|
||||
+ "lib/internal/fs/utils.js",
|
||||
+ "lib/internal/fs/watchers.js",
|
||||
+ "lib/internal/http.js",
|
||||
+ "lib/internal/heap_utils.js",
|
||||
+ "lib/internal/histogram.js",
|
||||
+ "lib/internal/idna.js",
|
||||
+ "lib/internal/inspector_async_hook.js",
|
||||
+ "lib/internal/js_stream_socket.js",
|
||||
+ "lib/internal/legacy/processbinding.js",
|
||||
+ "lib/internal/linkedlist.js",
|
||||
+ "lib/internal/main/check_syntax.js",
|
||||
+ "lib/internal/main/eval_string.js",
|
||||
+ "lib/internal/main/eval_stdin.js",
|
||||
+ "lib/internal/main/inspect.js",
|
||||
+ "lib/internal/main/print_help.js",
|
||||
+ "lib/internal/main/prof_process.js",
|
||||
+ "lib/internal/main/repl.js",
|
||||
+ "lib/internal/main/run_main_module.js",
|
||||
+ "lib/internal/main/worker_thread.js",
|
||||
+ "lib/internal/modules/run_main.js",
|
||||
+ "lib/internal/modules/package_json_reader.js",
|
||||
+ "lib/internal/modules/cjs/helpers.js",
|
||||
+ "lib/internal/modules/cjs/loader.js",
|
||||
+ "lib/internal/modules/esm/loader.js",
|
||||
+ "lib/internal/modules/esm/create_dynamic_module.js",
|
||||
+ "lib/internal/modules/esm/get_format.js",
|
||||
+ "lib/internal/modules/esm/get_source.js",
|
||||
+ "lib/internal/modules/esm/module_job.js",
|
||||
+ "lib/internal/modules/esm/module_map.js",
|
||||
+ "lib/internal/modules/esm/resolve.js",
|
||||
+ "lib/internal/modules/esm/transform_source.js",
|
||||
+ "lib/internal/modules/esm/translators.js",
|
||||
+ "lib/internal/abort_controller.js",
|
||||
+ "lib/internal/net.js",
|
||||
+ "lib/internal/options.js",
|
||||
+ "lib/internal/perf/perf.js",
|
||||
+ "lib/internal/perf/nodetiming.js",
|
||||
+ "lib/internal/perf/usertiming.js",
|
||||
+ "lib/internal/perf/observe.js",
|
||||
+ "lib/internal/perf/event_loop_delay.js",
|
||||
+ "lib/internal/perf/event_loop_utilization.js",
|
||||
+ "lib/internal/perf/timerify.js",
|
||||
+ "lib/internal/policy/manifest.js",
|
||||
+ "lib/internal/policy/sri.js",
|
||||
+ "lib/internal/priority_queue.js",
|
||||
+ "lib/internal/process/esm_loader.js",
|
||||
+ "lib/internal/process/execution.js",
|
||||
+ "lib/internal/process/per_thread.js",
|
||||
+ "lib/internal/process/policy.js",
|
||||
+ "lib/internal/process/promises.js",
|
||||
+ "lib/internal/process/warning.js",
|
||||
+ "lib/internal/process/worker_thread_only.js",
|
||||
+ "lib/internal/process/report.js",
|
||||
+ "lib/internal/process/signal.js",
|
||||
+ "lib/internal/process/task_queues.js",
|
||||
+ "lib/internal/querystring.js",
|
||||
+ "lib/internal/readline/callbacks.js",
|
||||
+ "lib/internal/readline/emitKeypressEvents.js",
|
||||
+ "lib/internal/readline/utils.js",
|
||||
+ "lib/internal/v8_prof_processor.js",
|
||||
+ "lib/internal/event_target.js",
|
||||
+ "lib/internal/inspector_async_hook.js",
|
||||
+ "lib/internal/validators.js",
|
||||
+ "lib/internal/linkedlist.js",
|
||||
+ "lib/internal/cli_table.js",
|
||||
+ "lib/internal/repl.js",
|
||||
+ "lib/internal/repl/await.js",
|
||||
+ "lib/internal/repl/history.js",
|
||||
+ "lib/internal/repl/utils.js",
|
||||
+ "lib/internal/socketaddress.js",
|
||||
+ "lib/internal/util.js",
|
||||
+ "lib/internal/histogram.js",
|
||||
+ "lib/internal/error_serdes.js",
|
||||
+ "lib/internal/dgram.js",
|
||||
+ "lib/internal/child_process.js",
|
||||
+ "lib/internal/assert.js",
|
||||
+ "lib/internal/fixed_queue.js",
|
||||
+ "lib/internal/blocklist.js",
|
||||
+ "lib/internal/v8_prof_polyfill.js",
|
||||
+ "lib/internal/options.js",
|
||||
+ "lib/internal/worker.js",
|
||||
+ "lib/internal/dtrace.js",
|
||||
+ "lib/internal/idna.js",
|
||||
+ "lib/internal/watchdog.js",
|
||||
+ "lib/internal/encoding.js",
|
||||
+ "lib/internal/tty.js",
|
||||
+ "lib/internal/freeze_intrinsics.js",
|
||||
+ "lib/internal/timers.js",
|
||||
+ "lib/internal/heap_utils.js",
|
||||
+ "lib/internal/querystring.js",
|
||||
+ "lib/internal/js_stream_socket.js",
|
||||
+ "lib/internal/errors.js",
|
||||
+ "lib/internal/priority_queue.js",
|
||||
+ "lib/internal/freelist.js",
|
||||
+ "lib/internal/blob.js",
|
||||
+ "lib/internal/socket_list.js",
|
||||
+ "lib/internal/source_map/prepare_stack_trace.js",
|
||||
+ "lib/internal/source_map/source_map.js",
|
||||
+ "lib/internal/source_map/source_map_cache.js",
|
||||
+ "lib/internal/socketaddress.js",
|
||||
+ "lib/internal/stream_base_commons.js",
|
||||
+ "lib/internal/url.js",
|
||||
+ "lib/internal/async_hooks.js",
|
||||
+ "lib/internal/http.js",
|
||||
+ "lib/internal/buffer.js",
|
||||
+ "lib/internal/trace_events_async_hooks.js",
|
||||
+ "lib/internal/crypto/sig.js",
|
||||
+ "lib/internal/crypto/rsa.js",
|
||||
+ "lib/internal/crypto/aes.js",
|
||||
+ "lib/internal/crypto/util.js",
|
||||
+ "lib/internal/crypto/scrypt.js",
|
||||
+ "lib/internal/crypto/random.js",
|
||||
+ "lib/internal/crypto/keys.js",
|
||||
+ "lib/internal/crypto/x509.js",
|
||||
+ "lib/internal/crypto/certificate.js",
|
||||
+ "lib/internal/crypto/ec.js",
|
||||
+ "lib/internal/crypto/keygen.js",
|
||||
+ "lib/internal/crypto/mac.js",
|
||||
+ "lib/internal/crypto/diffiehellman.js",
|
||||
+ "lib/internal/crypto/hkdf.js",
|
||||
+ "lib/internal/crypto/cipher.js",
|
||||
+ "lib/internal/crypto/hash.js",
|
||||
+ "lib/internal/crypto/pbkdf2.js",
|
||||
+ "lib/internal/crypto/webcrypto.js",
|
||||
+ "lib/internal/crypto/dsa.js",
|
||||
+ "lib/internal/crypto/hashnames.js",
|
||||
+ "lib/internal/cluster/shared_handle.js",
|
||||
+ "lib/internal/cluster/round_robin_handle.js",
|
||||
+ "lib/internal/cluster/worker.js",
|
||||
+ "lib/internal/cluster/primary.js",
|
||||
+ "lib/internal/cluster/utils.js",
|
||||
+ "lib/internal/cluster/child.js",
|
||||
+ "lib/internal/webstreams/util.js",
|
||||
+ "lib/internal/webstreams/writablestream.js",
|
||||
+ "lib/internal/webstreams/readablestream.js",
|
||||
+ "lib/internal/webstreams/queuingstrategies.js",
|
||||
+ "lib/internal/webstreams/transformstream.js",
|
||||
+ "lib/internal/webstreams/transfer.js",
|
||||
+ "lib/internal/bootstrap/loaders.js",
|
||||
+ "lib/internal/bootstrap/pre_execution.js",
|
||||
+ "lib/internal/bootstrap/node.js",
|
||||
+ "lib/internal/bootstrap/environment.js",
|
||||
+ "lib/internal/bootstrap/switches/does_not_own_process_state.js",
|
||||
+ "lib/internal/bootstrap/switches/is_not_main_thread.js",
|
||||
+ "lib/internal/bootstrap/switches/does_own_process_state.js",
|
||||
+ "lib/internal/bootstrap/switches/is_main_thread.js",
|
||||
+ "lib/internal/test/binding.js",
|
||||
+ "lib/internal/test/transfer.js",
|
||||
+ "lib/internal/timers.js",
|
||||
+ "lib/internal/tls.js",
|
||||
+ "lib/internal/trace_events_async_hooks.js",
|
||||
+ "lib/internal/tty.js",
|
||||
+ "lib/internal/url.js",
|
||||
+ "lib/internal/util.js",
|
||||
+ "lib/internal/util/types.js",
|
||||
+ "lib/internal/util/inspector.js",
|
||||
+ "lib/internal/util/comparisons.js",
|
||||
+ "lib/internal/util/debuglog.js",
|
||||
+ "lib/internal/util/inspect.js",
|
||||
+ "lib/internal/util/inspector.js",
|
||||
+ "lib/internal/util/iterable_weak_map.js",
|
||||
+ "lib/internal/util/types.js",
|
||||
+ "lib/internal/http2/core.js",
|
||||
+ "lib/internal/http2/compat.js",
|
||||
+ "lib/internal/http2/util.js",
|
||||
+ "lib/internal/v8_prof_polyfill.js",
|
||||
+ "lib/internal/v8_prof_processor.js",
|
||||
+ "lib/internal/validators.js",
|
||||
+ "lib/internal/stream_base_commons.js",
|
||||
+ "lib/internal/vm/module.js",
|
||||
+ "lib/internal/worker.js",
|
||||
+ "lib/internal/worker/io.js",
|
||||
+ "lib/internal/worker/js_transferable.js",
|
||||
+ "lib/internal/watchdog.js",
|
||||
+ "lib/internal/streams/lazy_transform.js",
|
||||
+ "lib/internal/streams/add-abort-signal.js",
|
||||
+ "lib/internal/streams/buffer_list.js",
|
||||
+ "lib/internal/streams/duplexpair.js",
|
||||
+ "lib/internal/streams/from.js",
|
||||
+ "lib/internal/streams/legacy.js",
|
||||
+ "lib/internal/streams/readable.js",
|
||||
+ "lib/internal/streams/writable.js",
|
||||
+ "lib/internal/streams/duplex.js",
|
||||
+ "lib/internal/streams/passthrough.js",
|
||||
+ "lib/internal/streams/transform.js",
|
||||
+ "lib/internal/streams/destroy.js",
|
||||
+ "lib/internal/streams/legacy.js",
|
||||
+ "lib/internal/streams/passthrough.js",
|
||||
+ "lib/internal/streams/readable.js",
|
||||
+ "lib/internal/streams/from.js",
|
||||
+ "lib/internal/streams/writable.js",
|
||||
+ "lib/internal/streams/state.js",
|
||||
+ "lib/internal/streams/pipeline.js",
|
||||
+ "lib/internal/streams/buffer_list.js",
|
||||
+ "lib/internal/streams/end-of-stream.js",
|
||||
+ "lib/internal/streams/utils.js",
|
||||
+ "lib/internal/streams/transform.js",
|
||||
+ "lib/internal/streams/lazy_transform.js",
|
||||
+ "lib/internal/streams/duplex.js",
|
||||
+ "lib/internal/streams/pipeline.js",
|
||||
+ "lib/internal/readline/utils.js",
|
||||
+ "lib/internal/readline/emitKeypressEvents.js",
|
||||
+ "lib/internal/readline/callbacks.js",
|
||||
+ "lib/internal/repl/history.js",
|
||||
+ "lib/internal/repl/utils.js",
|
||||
+ "lib/internal/repl/await.js",
|
||||
+ "lib/internal/legacy/processbinding.js",
|
||||
+ "lib/internal/assert/calltracker.js",
|
||||
+ "lib/internal/assert/assertion_error.js",
|
||||
+ "lib/internal/http2/util.js",
|
||||
+ "lib/internal/http2/core.js",
|
||||
+ "lib/internal/http2/compat.js",
|
||||
+ "lib/internal/per_context/messageport.js",
|
||||
+ "lib/internal/per_context/primordials.js",
|
||||
+ "lib/internal/per_context/domexception.js",
|
||||
+ "lib/internal/vm/module.js",
|
||||
+ "lib/internal/tls/secure-pair.js",
|
||||
+ "lib/internal/tls/parse-cert-string.js",
|
||||
+ "lib/internal/tls/secure-context.js",
|
||||
+ "lib/internal/child_process/serialization.js",
|
||||
+ "lib/internal/debugger/inspect_repl.js",
|
||||
+ "lib/internal/debugger/inspect_client.js",
|
||||
+ "lib/internal/debugger/_inspect.js",
|
||||
+ "lib/internal/worker/io.js",
|
||||
+ "lib/internal/worker/js_transferable.js",
|
||||
+ "lib/internal/main/repl.js",
|
||||
+ "lib/internal/main/print_help.js",
|
||||
+ "lib/internal/main/eval_string.js",
|
||||
+ "lib/internal/main/check_syntax.js",
|
||||
+ "lib/internal/main/prof_process.js",
|
||||
+ "lib/internal/main/worker_thread.js",
|
||||
+ "lib/internal/main/inspect.js",
|
||||
+ "lib/internal/main/eval_stdin.js",
|
||||
+ "lib/internal/main/run_main_module.js",
|
||||
+ "lib/internal/modules/run_main.js",
|
||||
+ "lib/internal/modules/package_json_reader.js",
|
||||
+ "lib/internal/modules/esm/module_job.js",
|
||||
+ "lib/internal/modules/esm/get_source.js",
|
||||
+ "lib/internal/modules/esm/translators.js",
|
||||
+ "lib/internal/modules/esm/resolve.js",
|
||||
+ "lib/internal/modules/esm/create_dynamic_module.js",
|
||||
+ "lib/internal/modules/esm/module_map.js",
|
||||
+ "lib/internal/modules/esm/get_format.js",
|
||||
+ "lib/internal/modules/esm/transform_source.js",
|
||||
+ "lib/internal/modules/esm/loader.js",
|
||||
+ "lib/internal/modules/cjs/helpers.js",
|
||||
+ "lib/internal/modules/cjs/loader.js",
|
||||
+ "lib/internal/source_map/source_map.js",
|
||||
+ "lib/internal/source_map/prepare_stack_trace.js",
|
||||
+ "lib/internal/source_map/source_map_cache.js",
|
||||
+ "lib/internal/dns/promises.js",
|
||||
+ "lib/internal/dns/utils.js",
|
||||
+ "lib/internal/fs/watchers.js",
|
||||
+ "lib/internal/fs/promises.js",
|
||||
+ "lib/internal/fs/read_file_context.js",
|
||||
+ "lib/internal/fs/rimraf.js",
|
||||
+ "lib/internal/fs/sync_write_stream.js",
|
||||
+ "lib/internal/fs/dir.js",
|
||||
+ "lib/internal/fs/streams.js",
|
||||
+ "lib/internal/fs/utils.js",
|
||||
+ "lib/internal/perf/nodetiming.js",
|
||||
+ "lib/internal/perf/usertiming.js",
|
||||
+ "lib/internal/perf/performance_entry.js",
|
||||
+ "lib/internal/perf/performance.js",
|
||||
+ "lib/internal/perf/timerify.js",
|
||||
+ "lib/internal/perf/utils.js",
|
||||
+ "lib/internal/perf/observe.js",
|
||||
+ "lib/internal/perf/event_loop_delay.js",
|
||||
+ "lib/internal/perf/event_loop_utilization.js",
|
||||
+ "lib/internal/policy/manifest.js",
|
||||
+ "lib/internal/policy/sri.js",
|
||||
+ "lib/internal/process/task_queues.js",
|
||||
+ "lib/internal/process/per_thread.js",
|
||||
+ "lib/internal/process/warning.js",
|
||||
+ "lib/internal/process/policy.js",
|
||||
+ "lib/internal/process/promises.js",
|
||||
+ "lib/internal/process/signal.js",
|
||||
+ "lib/internal/process/execution.js",
|
||||
+ "lib/internal/process/esm_loader.js",
|
||||
+ "lib/internal/process/report.js",
|
||||
+ "lib/internal/process/worker_thread_only.js",
|
||||
+ "lib/internal/console/constructor.js",
|
||||
+ "lib/internal/console/global.js",
|
||||
+ "lib/assert/strict.js",
|
||||
+ "lib/dns/promises.js",
|
||||
+ "lib/fs/promises.js",
|
||||
+ "//v8/tools/splaytree.mjs",
|
||||
+ "//v8/tools/codemap.mjs",
|
||||
+ "//v8/tools/consarray.mjs",
|
||||
@@ -1210,10 +1228,6 @@ index 0000000000000000000000000000000000000000..7225bdb6b281837253978430a665e9ee
|
||||
+ "//v8/tools/tickprocessor-driver.mjs",
|
||||
+ "deps/acorn/acorn/dist/acorn.js",
|
||||
+ "deps/acorn/acorn-walk/dist/walk.js",
|
||||
+ "deps/acorn-plugins/acorn-class-fields/index.js",
|
||||
+ "deps/acorn-plugins/acorn-private-class-elements/index.js",
|
||||
+ "deps/acorn-plugins/acorn-private-methods/index.js",
|
||||
+ "deps/acorn-plugins/acorn-static-class-features/index.js",
|
||||
+ "deps/cjs-module-lexer/lexer.js",
|
||||
+ "deps/cjs-module-lexer/dist/lexer.js"
|
||||
+ ],
|
||||
@@ -1628,7 +1642,7 @@ index 0000000000000000000000000000000000000000..d1d6b51e8c0c5bc6a5d09e217eb30483
|
||||
+ args = rebase_path(inputs + outputs, root_build_dir)
|
||||
+}
|
||||
diff --git a/src/node_version.h b/src/node_version.h
|
||||
index e4ee8199ca434226f548ddf9f0e4e2770b48df9e..03e59cfeaa32eaba7aeba9be17f986468c34ac6e 100644
|
||||
index 0523885212d429ee5c4142137524cb127d8adc97..116815364055a01f0c0619f0f22e9a387c2f2e2e 100644
|
||||
--- a/src/node_version.h
|
||||
+++ b/src/node_version.h
|
||||
@@ -89,7 +89,10 @@
|
||||
@@ -1661,17 +1675,17 @@ index 0000000000000000000000000000000000000000..01f62d4ae6e3b9d539444e3dff069f00
|
||||
+ main(sys.argv[1:])
|
||||
diff --git a/tools/generate_gn_filenames_json.py b/tools/generate_gn_filenames_json.py
|
||||
new file mode 100755
|
||||
index 0000000000000000000000000000000000000000..cf3b8df67d73b4096a0113b55e2a916b59701b23
|
||||
index 0000000000000000000000000000000000000000..ece315d915f0a7b2c8e823caccba7ffec8420fdf
|
||||
--- /dev/null
|
||||
+++ b/tools/generate_gn_filenames_json.py
|
||||
@@ -0,0 +1,71 @@
|
||||
@@ -0,0 +1,75 @@
|
||||
+#!/usr/bin/env python
|
||||
+import json
|
||||
+import os
|
||||
+import sys
|
||||
+
|
||||
+import install
|
||||
+
|
||||
+import subprocess
|
||||
+
|
||||
+def LoadPythonDictionary(path):
|
||||
+ file_string = open(path).read()
|
||||
@@ -1704,6 +1718,7 @@ index 0000000000000000000000000000000000000000..cf3b8df67d73b4096a0113b55e2a916b
|
||||
+ if t['target_name'] == '<(node_lib_target_name)')
|
||||
+ node_source_blocklist = {
|
||||
+ '<@(library_files)',
|
||||
+ '<@(deps_files)',
|
||||
+ 'common.gypi',
|
||||
+ '<(SHARED_INTERMEDIATE_DIR)/node_javascript.cc',
|
||||
+ }
|
||||
@@ -1713,7 +1728,10 @@ index 0000000000000000000000000000000000000000..cf3b8df67d73b4096a0113b55e2a916b
|
||||
+ files = [f.replace('deps/v8/', '//v8/', 1) for f in files]
|
||||
+ return files
|
||||
+
|
||||
+ out['library_files'] = filter_v8_files(node_gyp['variables']['library_files'])
|
||||
+ cwd = os.path.join(os.getcwd(), 'tools/search_files.py')
|
||||
+ lib_files = subprocess.check_output('{} {} --ext js lib'.format('python3', cwd), shell=True).split()
|
||||
+ out['library_files'] = filter_v8_files(lib_files)
|
||||
+ out['library_files'] += filter_v8_files(node_gyp['variables']['deps_files'])
|
||||
+
|
||||
+ blocklisted_sources = [
|
||||
+ f for f in node_lib_target['sources']
|
||||
@@ -1768,14 +1786,17 @@ index 0000000000000000000000000000000000000000..3088ae4bdf814ae255c9805ebd393b2e
|
||||
+
|
||||
+ out_file.writelines(new_contents)
|
||||
diff --git a/tools/install.py b/tools/install.py
|
||||
index 045d406d84be301722f3de62abc448db84e751f8..95aa4c985d33ef37a73eebfca8bb8651e5059d37 100755
|
||||
index 24cf51e73199e60b4c24700e1074fe9bd0a399e6..3cbf4f45fabec1a26e0edebb18fb589fbecfbe68 100755
|
||||
--- a/tools/install.py
|
||||
+++ b/tools/install.py
|
||||
@@ -159,14 +159,15 @@ def files(action):
|
||||
@@ -159,17 +159,18 @@ def files(action):
|
||||
def headers(action):
|
||||
def wanted_v8_headers(files_arg, dest):
|
||||
v8_headers = [
|
||||
- 'deps/v8/include/cppgc/common.h',
|
||||
- 'deps/v8/include/libplatform/libplatform.h',
|
||||
- 'deps/v8/include/libplatform/libplatform-export.h',
|
||||
- 'deps/v8/include/libplatform/v8-tracing.h',
|
||||
- 'deps/v8/include/v8.h',
|
||||
- 'deps/v8/include/v8-internal.h',
|
||||
- 'deps/v8/include/v8-platform.h',
|
||||
@@ -1783,6 +1804,9 @@ index 045d406d84be301722f3de62abc448db84e751f8..95aa4c985d33ef37a73eebfca8bb8651
|
||||
- 'deps/v8/include/v8-version.h',
|
||||
- 'deps/v8/include/v8config.h',
|
||||
+ '../../v8/include/cppgc/common.h',
|
||||
+ '../../v8/include/libplatform/libplatform.h',
|
||||
+ '../../v8/include/libplatform/libplatform-export.h',
|
||||
+ '../../v8/include/libplatform/v8-tracing.h',
|
||||
+ '../../v8/include/v8.h',
|
||||
+ '../../v8/include/v8-internal.h',
|
||||
+ '../../v8/include/v8-platform.h',
|
||||
@@ -1794,7 +1818,7 @@ index 045d406d84be301722f3de62abc448db84e751f8..95aa4c985d33ef37a73eebfca8bb8651
|
||||
files_arg = [name for name in files_arg if name in v8_headers]
|
||||
action(files_arg, dest)
|
||||
|
||||
@@ -187,7 +188,7 @@ def headers(action):
|
||||
@@ -190,7 +191,7 @@ def headers(action):
|
||||
if sys.platform.startswith('aix'):
|
||||
action(['out/Release/node.exp'], 'include/node/')
|
||||
|
||||
@@ -1804,10 +1828,10 @@ index 045d406d84be301722f3de62abc448db84e751f8..95aa4c985d33ef37a73eebfca8bb8651
|
||||
if 'false' == variables.get('node_shared_libuv'):
|
||||
subdir_files('deps/uv/include', 'include/node/', action)
|
||||
diff --git a/tools/js2c.py b/tools/js2c.py
|
||||
index d40f28ce2bff2b7fc28ceeafc7772831746e7f89..4af54c3fa00602f9d0ce5cc4dca253d425048706 100755
|
||||
index d93be2123e0f8c75dd6a0041ef164982db0860e4..4d9317527d46ac8c6d8066bfba707233053b8615 100755
|
||||
--- a/tools/js2c.py
|
||||
+++ b/tools/js2c.py
|
||||
@@ -130,6 +130,14 @@ def NormalizeFileName(filename):
|
||||
@@ -131,6 +131,14 @@ def NormalizeFileName(filename):
|
||||
split = split[1:]
|
||||
if len(split):
|
||||
filename = '/'.join(split)
|
||||
|
||||
@@ -35,10 +35,10 @@ index b45af42d12ff7df8a9e125e87f51af3456811c23..c84ff7feb07aebf656ada7e37d812d9d
|
||||
async function* watch(filename, options = {}) {
|
||||
const path = toNamespacedPath(getValidatedPath(filename));
|
||||
diff --git a/src/node_native_module.cc b/src/node_native_module.cc
|
||||
index f788732ae569d460a0e596397e589774ba4be4f1..186c24c0ba37781d8d9d0443d18b4f4bb0e02bef 100644
|
||||
index 2642982330e8cff166d10682af3d847105dc5c2e..f5491b416203877b89db69bde44f4ff50901ed8b 100644
|
||||
--- a/src/node_native_module.cc
|
||||
+++ b/src/node_native_module.cc
|
||||
@@ -19,6 +19,7 @@ NativeModuleLoader NativeModuleLoader::instance_;
|
||||
@@ -20,6 +20,7 @@ NativeModuleLoader NativeModuleLoader::instance_;
|
||||
|
||||
NativeModuleLoader::NativeModuleLoader() : config_(GetConfig()) {
|
||||
LoadJavaScriptSource();
|
||||
@@ -59,11 +59,11 @@ index 3be3f2364dd252bcdd668c699a0e7ae1e754e873..b2af1bce312ffca44e7005e11f92327e
|
||||
|
||||
bool Exists(const char* id);
|
||||
diff --git a/tools/js2c.py b/tools/js2c.py
|
||||
index 4af54c3fa00602f9d0ce5cc4dca253d425048706..18f584473fb87f036ce2a67c5172cccaf0caaa5f 100755
|
||||
index 4d9317527d46ac8c6d8066bfba707233053b8615..83225036208b68087a6066adf1d1948b84c5c234 100755
|
||||
--- a/tools/js2c.py
|
||||
+++ b/tools/js2c.py
|
||||
@@ -38,6 +38,8 @@ import functools
|
||||
import codecs
|
||||
@@ -39,6 +39,8 @@ import codecs
|
||||
import utils
|
||||
|
||||
def ReadFile(filename):
|
||||
+ if filename.startswith("//v8"):
|
||||
@@ -71,7 +71,7 @@ index 4af54c3fa00602f9d0ce5cc4dca253d425048706..18f584473fb87f036ce2a67c5172ccca
|
||||
if is_verbose:
|
||||
print(filename)
|
||||
with codecs.open(filename, "r", "utf-8") as f:
|
||||
@@ -56,13 +58,15 @@ namespace native_module {{
|
||||
@@ -57,13 +59,15 @@ namespace native_module {{
|
||||
|
||||
{0}
|
||||
|
||||
@@ -89,7 +89,7 @@ index 4af54c3fa00602f9d0ce5cc4dca253d425048706..18f584473fb87f036ce2a67c5172ccca
|
||||
|
||||
}} // namespace native_module
|
||||
|
||||
@@ -112,8 +116,8 @@ def GetDefinition(var, source, step=30):
|
||||
@@ -113,8 +117,8 @@ def GetDefinition(var, source, step=30):
|
||||
return definition, len(code_points)
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ index 4af54c3fa00602f9d0ce5cc4dca253d425048706..18f584473fb87f036ce2a67c5172ccca
|
||||
name = NormalizeFileName(filename)
|
||||
slug = SLUGGER_RE.sub('_', name)
|
||||
var = slug + '_raw'
|
||||
@@ -123,7 +127,9 @@ def AddModule(filename, definitions, initializers):
|
||||
@@ -124,7 +128,9 @@ def AddModule(filename, definitions, initializers):
|
||||
initializers.append(initializer)
|
||||
|
||||
def NormalizeFileName(filename):
|
||||
@@ -111,7 +111,7 @@ index 4af54c3fa00602f9d0ce5cc4dca253d425048706..18f584473fb87f036ce2a67c5172ccca
|
||||
if split[0] == 'deps':
|
||||
split = ['internal'] + split
|
||||
else: # `lib/**/*.js` so drop the 'lib' part
|
||||
@@ -141,23 +147,36 @@ def NormalizeFileName(filename):
|
||||
@@ -142,23 +148,36 @@ def NormalizeFileName(filename):
|
||||
return os.path.splitext(filename)[0]
|
||||
|
||||
|
||||
@@ -157,23 +157,24 @@ index 4af54c3fa00602f9d0ce5cc4dca253d425048706..18f584473fb87f036ce2a67c5172ccca
|
||||
write_if_chaged(out, target)
|
||||
|
||||
|
||||
@@ -213,18 +232,21 @@ def main():
|
||||
)
|
||||
parser.add_argument('--target', help='output file')
|
||||
@@ -218,6 +237,7 @@ def main():
|
||||
default=None,
|
||||
help='input file directory')
|
||||
parser.add_argument('--verbose', action='store_true', help='output file')
|
||||
+ parser.add_argument('--only-js', action='store_true', help='do not require or parse any config.gypi files')
|
||||
parser.add_argument('sources', nargs='*', help='input files')
|
||||
options = parser.parse_args()
|
||||
global is_verbose
|
||||
is_verbose = options.verbose
|
||||
source_files = functools.reduce(SourceFileByExt, options.sources, {})
|
||||
@@ -230,12 +250,15 @@ def main():
|
||||
|
||||
source_files = functools.reduce(SourceFileByExt, sources, {})
|
||||
|
||||
- # Should have exactly 3 types: `.js`, `.mjs` and `.gypi`
|
||||
- assert len(source_files) == 3
|
||||
- # Currently config.gypi is the only `.gypi` file allowed
|
||||
- assert source_files['.gypi'] == ['config.gypi']
|
||||
- source_files['config.gypi'] = source_files.pop('.gypi')[0]
|
||||
- JS2C(source_files, options.target)
|
||||
-
|
||||
+ if options.only_js:
|
||||
+ assert len(source_files) == 1
|
||||
+ else:
|
||||
@@ -184,5 +185,5 @@ index 4af54c3fa00602f9d0ce5cc4dca253d425048706..18f584473fb87f036ce2a67c5172ccca
|
||||
+ source_files['config.gypi'] = source_files.pop('.gypi')[0]
|
||||
+ JS2C(source_files, options.target, options.only_js)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -8,10 +8,10 @@ they use themselves as the entry point. We should try to upstream some form
|
||||
of this.
|
||||
|
||||
diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js
|
||||
index f7e9ffa74a4f19caa96680b3c955937f7ab31ea0..d1fb880f5f904909a1535f8253ab0b29203304bf 100644
|
||||
index c90a19d5eed72a55cb9ee89dd2471a32a30c1377..68a52e1ce1f0a2bf4fc2b19ea735948cf6586e06 100644
|
||||
--- a/lib/internal/bootstrap/pre_execution.js
|
||||
+++ b/lib/internal/bootstrap/pre_execution.js
|
||||
@@ -102,10 +102,12 @@ function patchProcessObject(expandArgv1) {
|
||||
@@ -103,10 +103,12 @@ function patchProcessObject(expandArgv1) {
|
||||
if (expandArgv1 && process.argv[1] &&
|
||||
!StringPrototypeStartsWith(process.argv[1], '-')) {
|
||||
// Expand process.argv[1] into a full path.
|
||||
|
||||
@@ -7,7 +7,7 @@ This is used so that we can modify the flag at runtime where
|
||||
config can only be set at compile time.
|
||||
|
||||
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
|
||||
index 8b70f79d2b278f2f6b15abc2eba5045b1ca503b4..bfc6edfae4b4b4f8b7ee2a97209e1801d7c2d4c9 100644
|
||||
index ef06d0563fa7452348754418867a56c9b8c6f4e1..a313402f93937cf2f1f93eb74422d9609e291d76 100644
|
||||
--- a/lib/internal/bootstrap/node.js
|
||||
+++ b/lib/internal/bootstrap/node.js
|
||||
@@ -193,7 +193,7 @@ const {
|
||||
|
||||
@@ -8,7 +8,7 @@ node modules will have different (wrong) ideas about how v8 structs are laid
|
||||
out in memory on 64-bit machines, and will summarily fail to work.
|
||||
|
||||
diff --git a/common.gypi b/common.gypi
|
||||
index 9481fdb6dd4628833e60ae7099f03eca41edd057..60789d5553352563eb41a341860df70175153e4c 100644
|
||||
index 65729132b233dc5801f142f05a00253ac951e66a..e717a0b76513401daee6910502ea580485a20612 100644
|
||||
--- a/common.gypi
|
||||
+++ b/common.gypi
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
@@ -24,7 +24,7 @@ Environment on the V8 context of blink, so no new V8 context is created.
|
||||
As a result, a renderer process may have multiple Node Environments in it.
|
||||
|
||||
diff --git a/src/node.cc b/src/node.cc
|
||||
index b60be116b6139bd4b7c10485bfdad2c1b905c995..c9d491f01651ef57fb793dda108469cb7ddccc5c 100644
|
||||
index 3ee25ebbd67a01d607456e5158c98ca2fdea396b..6302bb925339d709a54151a8fc8b58f1a2253881 100644
|
||||
--- a/src/node.cc
|
||||
+++ b/src/node.cc
|
||||
@@ -139,6 +139,8 @@ using v8::Undefined;
|
||||
|
||||
@@ -6,10 +6,10 @@ Subject: feat: initialize asar support
|
||||
This patch initializes asar support in Node.js.
|
||||
|
||||
diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js
|
||||
index 83ccfe90c110657f54e068d522c3a75c7f19c75a..f7e9ffa74a4f19caa96680b3c955937f7ab31ea0 100644
|
||||
index 3b69844dc4ea0c4e17a74ea514c4f0dc390e3a61..c90a19d5eed72a55cb9ee89dd2471a32a30c1377 100644
|
||||
--- a/lib/internal/bootstrap/pre_execution.js
|
||||
+++ b/lib/internal/bootstrap/pre_execution.js
|
||||
@@ -74,6 +74,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
|
||||
@@ -75,6 +75,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
|
||||
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
|
||||
loadPreloadModules();
|
||||
initializeFrozenIntrinsics();
|
||||
@@ -17,7 +17,7 @@ index 83ccfe90c110657f54e068d522c3a75c7f19c75a..f7e9ffa74a4f19caa96680b3c955937f
|
||||
}
|
||||
|
||||
function patchProcessObject(expandArgv1) {
|
||||
@@ -469,6 +470,10 @@ function loadPreloadModules() {
|
||||
@@ -475,6 +476,10 @@ function loadPreloadModules() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ errors. This is remedied by adding a small timeout to the test.
|
||||
|
||||
We'll either upstream this or figure out a better solution.
|
||||
|
||||
diff --git a/test/sequential/test-debugger-address.js b/test/sequential/test-debugger-address.js
|
||||
index ff31747016c2d49ac87fa272eba3231e9d4fbae5..e4f7b13aad3c60100e56df00165d1af550f1a117 100644
|
||||
--- a/test/sequential/test-debugger-address.js
|
||||
+++ b/test/sequential/test-debugger-address.js
|
||||
diff --git a/test/parallel/test-debugger-address.js b/test/parallel/test-debugger-address.js
|
||||
index 95dd1c6e3f82835d5ccaf65544d654b71efaa392..ed8dccf91247068455dd593bb3e8c02bddc89ae5 100644
|
||||
--- a/test/parallel/test-debugger-address.js
|
||||
+++ b/test/parallel/test-debugger-address.js
|
||||
@@ -59,6 +59,7 @@ function launchTarget(...args) {
|
||||
cli = startCLI([`${host || '127.0.0.1'}:${port}`]);
|
||||
return cli.waitForPrompt();
|
||||
@@ -22,14 +22,14 @@ index ff31747016c2d49ac87fa272eba3231e9d4fbae5..e4f7b13aad3c60100e56df00165d1af5
|
||||
.then(() => cli.waitFor(/break/))
|
||||
.then(() => cli.waitForPrompt())
|
||||
diff --git a/test/sequential/test-debugger-pid.js b/test/sequential/test-debugger-pid.js
|
||||
index 97de9f40369d2d1df9674c6df5bbaf78022667c6..3d51a8963ba24e5e5f6a64cd792859535670dd9a 100644
|
||||
index 402c1f86dd4ed99b413eca5fce8a2db47797b11a..74ef0a1618ccf1f6671bbe2a03548eee6cd0b88c 100644
|
||||
--- a/test/sequential/test-debugger-pid.js
|
||||
+++ b/test/sequential/test-debugger-pid.js
|
||||
@@ -38,6 +38,7 @@ function launchTarget(...args) {
|
||||
cli = startCLI(['-p', `${target.pid}`]);
|
||||
return cli.waitForPrompt();
|
||||
})
|
||||
+ .then(() => new Promise(resolve => setTimeout(resolve, 1000)))
|
||||
@@ -41,6 +41,7 @@ function launchTarget(...args) {
|
||||
.then(() => cli.command('sb("alive.js", 3)'))
|
||||
.then(() => cli.waitFor(/break/))
|
||||
.then(() => cli.waitForPrompt())
|
||||
+ .then(() => new Promise(resolve => setTimeout(resolve, 1000)))
|
||||
.then(() => {
|
||||
assert.match(
|
||||
cli.output,
|
||||
|
||||
@@ -7,7 +7,7 @@ common.gypi is a file that's included in the node header bundle, despite
|
||||
the fact that we do not build node with gyp.
|
||||
|
||||
diff --git a/common.gypi b/common.gypi
|
||||
index aa42c69f96391b72e5e3cbada27fd662cb0cc69d..9481fdb6dd4628833e60ae7099f03eca41edd057 100644
|
||||
index 71862791dae3be5f05fbe00b91df6240473debb1..65729132b233dc5801f142f05a00253ac951e66a 100644
|
||||
--- a/common.gypi
|
||||
+++ b/common.gypi
|
||||
@@ -81,6 +81,23 @@
|
||||
|
||||
@@ -6,7 +6,7 @@ Subject: fix: add v8_enable_reverse_jsargs defines in common.gypi
|
||||
This can be removed once node upgrades V8 and inevitably has to do this exact same thing. Also hi node people if you are looking at this.
|
||||
|
||||
diff --git a/common.gypi b/common.gypi
|
||||
index 60789d5553352563eb41a341860df70175153e4c..9067a6a27606099ec5decbc4cd74283fced77711 100644
|
||||
index e717a0b76513401daee6910502ea580485a20612..e7c50b5e46624b537de3ffed51fc21a15b666dc8 100644
|
||||
--- a/common.gypi
|
||||
+++ b/common.gypi
|
||||
@@ -65,6 +65,7 @@
|
||||
|
||||
@@ -51,10 +51,10 @@ index b3b1ea908253b9240cc37931f34b2a8c8c9fa3ab..dc37298aa0e13bb79030123f38070d02
|
||||
return emit_filehandle_warning_;
|
||||
}
|
||||
diff --git a/src/env.h b/src/env.h
|
||||
index 7b136f70fbad1e0a90406add90d5e538577e2a2b..eaf8a17c99aa6a4135c54616f68b15e0620e4378 100644
|
||||
index e1b89261fcb1e94220424aae2273db9fba010331..45210f074a0ca4d57f9fdc5019e8e82540b28b72 100644
|
||||
--- a/src/env.h
|
||||
+++ b/src/env.h
|
||||
@@ -1197,6 +1197,7 @@ class Environment : public MemoryRetainer {
|
||||
@@ -1199,6 +1199,7 @@ class Environment : public MemoryRetainer {
|
||||
inline bool owns_process_state() const;
|
||||
inline bool owns_inspector() const;
|
||||
inline bool tracks_unmanaged_fds() const;
|
||||
|
||||
@@ -428,19 +428,6 @@ index cae9301517c37c7e90292d71fe5a6086cf55e0be..b9bc86e4d8b897cec583dea16f64f680
|
||||
};
|
||||
}
|
||||
|
||||
diff --git a/test/parallel/test-crypto-ecdh-convert-key.js b/test/parallel/test-crypto-ecdh-convert-key.js
|
||||
index f4d5a651ed6b888d3527a462ab5fccee58ea48b6..c0046099df9ec0c7a33ed9baa2127da849871001 100644
|
||||
--- a/test/parallel/test-crypto-ecdh-convert-key.js
|
||||
+++ b/test/parallel/test-crypto-ecdh-convert-key.js
|
||||
@@ -117,7 +117,7 @@ if (getCurves().includes('secp256k1')) {
|
||||
// rather than Node's generic error message.
|
||||
const badKey = 'f'.repeat(128);
|
||||
assert.throws(
|
||||
- () => ECDH.convertKey(badKey, 'secp256k1', 'hex', 'hex', 'compressed'),
|
||||
+ () => ECDH.convertKey(badKey, 'secp521r1', 'hex', 'hex', 'compressed'),
|
||||
/Failed to convert Buffer to EC_POINT/);
|
||||
|
||||
// Next statement should not throw an exception.
|
||||
diff --git a/test/parallel/test-crypto-getcipherinfo.js b/test/parallel/test-crypto-getcipherinfo.js
|
||||
index 98d2a52eceac4bc564fd2878f77b50c336a67a66..bcb2de6e354c26816000f2400d9c1d46de01888a 100644
|
||||
--- a/test/parallel/test-crypto-getcipherinfo.js
|
||||
|
||||
@@ -186,19 +186,6 @@ index 271db427fa8539feb30c1712574976fb1f623e91..b2b6af1f9e6db54bdff0be7a567255f4
|
||||
if (EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0)
|
||||
return EVPKeyCtxPointer();
|
||||
|
||||
diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc
|
||||
index ea4c70ad5d8c844860ba3480fc7ef4205f0a3cdc..cdf8dd47d6e2a5894066cec01fbe347af079ec22 100644
|
||||
--- a/src/crypto/crypto_ec.cc
|
||||
+++ b/src/crypto/crypto_ec.cc
|
||||
@@ -314,7 +314,7 @@ void ECDH::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
|
||||
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
|
||||
"Failed to set generated public key");
|
||||
|
||||
- EC_KEY_copy(ecdh->key_.get(), new_key.get());
|
||||
+ ecdh->key_.reset(EC_KEY_dup(new_key.get()));
|
||||
ecdh->group_ = EC_KEY_get0_group(ecdh->key_.get());
|
||||
}
|
||||
|
||||
diff --git a/src/crypto/crypto_hkdf.cc b/src/crypto/crypto_hkdf.cc
|
||||
index 0aa96ada47abe4b66fb616c665101278bbe0afb6..1e9a4863c5faea5f6b275483ca16f3a6e8dac25b 100644
|
||||
--- a/src/crypto/crypto_hkdf.cc
|
||||
@@ -248,26 +235,10 @@ index 7b113a8dcb06b0b0e1329ce0daf7305598ea6545..b04e53a7f24885ffb6639430988d0ffb
|
||||
const EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(pkey.get());
|
||||
const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key);
|
||||
diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
|
||||
index 13c40dcb757661220288465c39101de0b4018e90..7d1d4400319292a8ddf3afe013b5678f84c25576 100644
|
||||
index f18304cd655842e999a39659315c4eb3ce1c0c6e..1aed0e7e88460cea63950f71dac502829d662cff 100644
|
||||
--- a/src/crypto/crypto_util.cc
|
||||
+++ b/src/crypto/crypto_util.cc
|
||||
@@ -139,7 +139,6 @@ void InitCryptoOnce() {
|
||||
OPENSSL_init_ssl(0, settings);
|
||||
OPENSSL_INIT_free(settings);
|
||||
settings = nullptr;
|
||||
-#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
if (per_process::cli_options->secure_heap != 0) {
|
||||
@@ -160,6 +159,7 @@ void InitCryptoOnce() {
|
||||
}
|
||||
#endif
|
||||
|
||||
+#endif
|
||||
// Turn off compression. Saves memory and protects against CRIME attacks.
|
||||
// No-op with OPENSSL_NO_COMP builds of OpenSSL.
|
||||
sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
|
||||
@@ -490,24 +490,14 @@ Maybe<bool> Decorate(Environment* env, Local<Object> obj,
|
||||
@@ -491,24 +491,14 @@ Maybe<bool> Decorate(Environment* env, Local<Object> obj,
|
||||
V(BIO) \
|
||||
V(PKCS7) \
|
||||
V(X509V3) \
|
||||
@@ -292,7 +263,7 @@ index 13c40dcb757661220288465c39101de0b4018e90..7d1d4400319292a8ddf3afe013b5678f
|
||||
V(USER) \
|
||||
|
||||
#define V(name) case ERR_LIB_##name: lib = #name "_"; break;
|
||||
@@ -667,7 +657,7 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -668,7 +658,7 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK(args[0]->IsUint32());
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
uint32_t len = args[0].As<Uint32>()->Value();
|
||||
@@ -301,7 +272,7 @@ index 13c40dcb757661220288465c39101de0b4018e90..7d1d4400319292a8ddf3afe013b5678f
|
||||
if (data == nullptr) {
|
||||
// There's no memory available for the allocation.
|
||||
// Return nothing.
|
||||
@@ -679,7 +669,7 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -680,7 +670,7 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
data,
|
||||
len,
|
||||
[](void* data, size_t len, void* deleter_data) {
|
||||
@@ -310,7 +281,7 @@ index 13c40dcb757661220288465c39101de0b4018e90..7d1d4400319292a8ddf3afe013b5678f
|
||||
},
|
||||
data);
|
||||
Local<ArrayBuffer> buffer = ArrayBuffer::New(env->isolate(), store);
|
||||
@@ -687,10 +677,12 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -688,10 +678,12 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
void SecureHeapUsed(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -337,19 +308,6 @@ index ac95612a0b1a856d7fe07efde59786e811f1b98d..aa62753d7c929027f5265fa4330b0429
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/ssl.h>
|
||||
diff --git a/src/node.cc b/src/node.cc
|
||||
index c9d491f01651ef57fb793dda108469cb7ddccc5c..6a55535b5c6ef72b1cdb366299840a2e78643911 100644
|
||||
--- a/src/node.cc
|
||||
+++ b/src/node.cc
|
||||
@@ -1035,7 +1035,7 @@ InitializationResult InitializeOncePerProcess(
|
||||
}
|
||||
|
||||
if (init_flags & kInitOpenSSL) {
|
||||
-#if HAVE_OPENSSL
|
||||
+#if HAVE_OPENSSL && !defined(OPENSSL_IS_BORINGSSL)
|
||||
{
|
||||
std::string extra_ca_certs;
|
||||
if (credentials::SafeGetenv("NODE_EXTRA_CA_CERTS", &extra_ca_certs))
|
||||
diff --git a/src/node_metadata.h b/src/node_metadata.h
|
||||
index 4486d5af2c1622c7c8f44401dc3ebb986d8e3c2e..db1769f1b3f1617ed8dbbea57b5e324183b42be2 100644
|
||||
--- a/src/node_metadata.h
|
||||
|
||||
@@ -7,10 +7,10 @@ This broke the build at some point. Does it still? We should probably remove
|
||||
this patch and find out!
|
||||
|
||||
diff --git a/src/node_internals.h b/src/node_internals.h
|
||||
index 31076551e70c46da2c32cafd7ac08aee91366537..d57c51ebcc3c9dda1ef41b10ee49453839781deb 100644
|
||||
index 8f7929994f3243fbd58b47374dfcadafb1feda8f..c333dc3464d2a23437fa22659d38dd17b6678112 100644
|
||||
--- a/src/node_internals.h
|
||||
+++ b/src/node_internals.h
|
||||
@@ -391,10 +391,11 @@ class TraceEventScope {
|
||||
@@ -386,10 +386,11 @@ class TraceEventScope {
|
||||
TraceEventScope(const char* category,
|
||||
const char* name,
|
||||
void* id) : category_(category), name_(name), id_(id) {
|
||||
|
||||
@@ -7,7 +7,7 @@ We use this to allow node's 'fs' module to read from ASAR files as if they were
|
||||
a real filesystem.
|
||||
|
||||
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
|
||||
index 863d4ef5608bcebc9b49c3988509be9cfb18b6cd..8b70f79d2b278f2f6b15abc2eba5045b1ca503b4 100644
|
||||
index 58f7396990dddb7dd4cf3d23fcdcc1d48f52623e..ef06d0563fa7452348754418867a56c9b8c6f4e1 100644
|
||||
--- a/lib/internal/bootstrap/node.js
|
||||
+++ b/lib/internal/bootstrap/node.js
|
||||
@@ -62,6 +62,10 @@ setupBuffer();
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
"parallel/test-child-process-stdio-overlapped",
|
||||
"parallel/test-cli-node-print-help",
|
||||
"parallel/test-code-cache",
|
||||
"parallel/test-cluster-bind-privileged-port",
|
||||
"parallel/test-cluster-shared-handle-bind-privileged-port",
|
||||
"parallel/test-crypto-aes-wrap",
|
||||
"parallel/test-crypto-authenticated-stream",
|
||||
"parallel/test-crypto-async-sign-verify",
|
||||
|
||||
@@ -25,8 +25,9 @@ const runGit = async (args) => {
|
||||
};
|
||||
|
||||
const tagIsSupported = tag => tag && !tag.includes('nightly') && !tag.includes('unsupported');
|
||||
const tagIsAlpha = tag => tag && tag.includes('alpha');
|
||||
const tagIsBeta = tag => tag && tag.includes('beta');
|
||||
const tagIsStable = tag => tagIsSupported(tag) && !tagIsBeta(tag);
|
||||
const tagIsStable = tag => tagIsSupported(tag) && !tagIsBeta(tag) && !tagIsAlpha(tag);
|
||||
|
||||
const getTagsOf = async (point) => {
|
||||
try {
|
||||
@@ -108,6 +109,9 @@ const getPreviousStabilizationBranch = async (current) => {
|
||||
if (newestMatch && semver.lte(semverify(branch), semverify(newestMatch))) {
|
||||
continue;
|
||||
}
|
||||
if ((await getTagsOnBranch(branch)).filter(tag => tagIsStable(tag)).length === 0) {
|
||||
continue;
|
||||
}
|
||||
newestMatch = branch;
|
||||
}
|
||||
return newestMatch;
|
||||
|
||||
@@ -25,7 +25,7 @@ const pass = '✓'.green;
|
||||
const fail = '✗'.red;
|
||||
|
||||
if (!bumpType && !args.notesOnly) {
|
||||
console.log('Usage: prepare-release [stable | minor | beta | nightly]' +
|
||||
console.log('Usage: prepare-release [stable | minor | beta | alpha | nightly]' +
|
||||
' (--stable) (--notesOnly) (--automaticRelease) (--branch)');
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -93,6 +93,11 @@ async function createRelease (branchToTarget, isBeta) {
|
||||
'for any bugs you find in it.\n \n This release is published to npm ' +
|
||||
'under the electron-nightly package and can be installed via `npm install electron-nightly`, ' +
|
||||
`or \`npm install electron-nightly@${newVersion.substr(1)}\`.\n \n ${releaseNotes.text}`;
|
||||
} else if (newVersion.indexOf('alpha') > 0) {
|
||||
releaseBody = 'Note: This is an alpha release. Please file new issues ' +
|
||||
'for any bugs you find in it.\n \n This release is published to npm ' +
|
||||
'under the alpha tag and can be installed via `npm install electron@alpha`, ' +
|
||||
`or \`npm install electron@${newVersion.substr(1)}\`.\n \n ${releaseNotes.text}`;
|
||||
} else {
|
||||
releaseBody = 'Note: This is a beta release. Please file new issues ' +
|
||||
'for any bugs you find in it.\n \n This release is published to npm ' +
|
||||
@@ -182,7 +187,8 @@ async function promptForVersion (version) {
|
||||
|
||||
// function to determine if there have been commits to main since the last release
|
||||
async function changesToRelease () {
|
||||
const lastCommitWasRelease = new RegExp('^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$', 'g');
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const lastCommitWasRelease = new RegExp('^Bump v[0-9]+\.[0-9]+\.[0-9]+(-beta\.[0-9]+)?(-alpha\.[0-9]+)?(-nightly\.[0-9]+)?$', 'g');
|
||||
const lastCommit = await GitProcess.exec(['log', '-n', '1', '--pretty=format:\'%s\''], ELECTRON_DIR);
|
||||
return !lastCommitWasRelease.test(lastCommit.stdout);
|
||||
}
|
||||
|
||||
@@ -135,6 +135,9 @@ new Promise((resolve, reject) => {
|
||||
} else if (!release.prerelease) {
|
||||
// Tag the release with a `2-0-x` style tag
|
||||
npmTag = currentBranch;
|
||||
} else if (release.tag_name.indexOf('alpha') > 0) {
|
||||
// Tag the release with an `alpha-3-0-x` style tag
|
||||
npmTag = `alpha-${currentBranch}`;
|
||||
} else {
|
||||
// Tag the release with a `beta-3-0-x` style tag
|
||||
npmTag = `beta-${currentBranch}`;
|
||||
@@ -175,6 +178,10 @@ new Promise((resolve, reject) => {
|
||||
semver.gt(localVersion, currentTags.beta)) {
|
||||
childProcess.execSync(`npm dist-tag add electron@${localVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`);
|
||||
}
|
||||
if (parsedLocalVersion.prerelease[0] === 'alpha' &&
|
||||
semver.gt(localVersion, currentTags.alpha)) {
|
||||
childProcess.execSync(`npm dist-tag add electron@${localVersion} alpha --otp=${process.env.ELECTRON_NPM_OTP}`);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
@@ -457,4 +457,8 @@ async function verifyShasumsForRemoteFiles (remoteFilesToHash, filesAreNodeJSArt
|
||||
}
|
||||
}
|
||||
|
||||
makeRelease(args.validateRelease);
|
||||
makeRelease(args.validateRelease)
|
||||
.catch((err) => {
|
||||
console.error('Error occurred while making release:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -71,13 +71,20 @@ async function main () {
|
||||
console.log(`Bumped to version: ${version}`);
|
||||
}
|
||||
|
||||
// get next version for release based on [nightly, beta, stable]
|
||||
// get next version for release based on [nightly, alpha, beta, stable]
|
||||
async function nextVersion (bumpType, version) {
|
||||
if (versionUtils.isNightly(version) || versionUtils.isBeta(version)) {
|
||||
if (
|
||||
versionUtils.isNightly(version) ||
|
||||
versionUtils.isAlpha(version) ||
|
||||
versionUtils.isBeta(version)
|
||||
) {
|
||||
switch (bumpType) {
|
||||
case 'nightly':
|
||||
version = await versionUtils.nextNightly(version);
|
||||
break;
|
||||
case 'alpha':
|
||||
version = await versionUtils.nextAlpha(version);
|
||||
break;
|
||||
case 'beta':
|
||||
version = await versionUtils.nextBeta(version);
|
||||
break;
|
||||
@@ -92,6 +99,8 @@ async function nextVersion (bumpType, version) {
|
||||
case 'nightly':
|
||||
version = versionUtils.nextNightly(version);
|
||||
break;
|
||||
case 'alpha':
|
||||
throw new Error('Cannot bump to alpha from stable.');
|
||||
case 'beta':
|
||||
throw new Error('Cannot bump to beta from stable.');
|
||||
case 'minor':
|
||||
@@ -165,7 +174,7 @@ async function updateWinRC (components) {
|
||||
// updates support.md file with new semver values (stable only)
|
||||
async function updateSupported (version, filePath) {
|
||||
const v = parseInt(version);
|
||||
const newVersions = [`* ${v}.x.y`, `* ${v - 1}.x.y`, `* ${v - 2}.x.y`];
|
||||
const newVersions = [`* ${v}.x.y`, `* ${v - 1}.x.y`, `* ${v - 2}.x.y`, `* ${v - 3}`];
|
||||
const contents = await readFile(filePath, 'utf8');
|
||||
const previousVersions = contents.split('\n').filter((elem) => {
|
||||
return (/[^\n]*\.x\.y[^\n]*/).test(elem);
|
||||
|
||||
@@ -23,6 +23,7 @@ const getCurrentDate = () => {
|
||||
};
|
||||
|
||||
const isNightly = v => v.includes('nightly');
|
||||
const isAlpha = v => v.includes('alpha');
|
||||
const isBeta = v => v.includes('beta');
|
||||
const isStable = v => {
|
||||
const parsed = semver.parse(v);
|
||||
@@ -39,9 +40,22 @@ const makeVersion = (components, delim, pre = preType.NONE) => {
|
||||
return version;
|
||||
};
|
||||
|
||||
async function nextAlpha (v) {
|
||||
const next = semver.coerce(semver.clean(v));
|
||||
const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-alpha.*`], ELECTRON_DIR);
|
||||
const tags = tagBlob.stdout.split('\n').filter(e => e !== '');
|
||||
tags.sort((t1, t2) => {
|
||||
const a = parseInt(t1.split('.').pop(), 10);
|
||||
const b = parseInt(t2.split('.').pop(), 10);
|
||||
return a - b;
|
||||
});
|
||||
|
||||
// increment the latest existing alpha tag or start at alpha.1 if it's a new alpha line
|
||||
return tags.length === 0 ? `${next}-alpha.1` : semver.inc(tags.pop(), 'prerelease');
|
||||
}
|
||||
|
||||
async function nextBeta (v) {
|
||||
const next = semver.coerce(semver.clean(v));
|
||||
|
||||
const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-beta.*`], ELECTRON_DIR);
|
||||
const tags = tagBlob.stdout.split('\n').filter(e => e !== '');
|
||||
tags.sort((t1, t2) => {
|
||||
@@ -94,8 +108,10 @@ function getNextReleaseBranch (branches) {
|
||||
|
||||
module.exports = {
|
||||
isStable,
|
||||
isAlpha,
|
||||
isBeta,
|
||||
isNightly,
|
||||
nextAlpha,
|
||||
nextBeta,
|
||||
makeVersion,
|
||||
getElectronVersion,
|
||||
|
||||
@@ -13,7 +13,7 @@ const fail = '✗'.red;
|
||||
|
||||
const args = require('minimist')(process.argv, {
|
||||
string: ['runners', 'target'],
|
||||
boolean: ['buildNativeTests'],
|
||||
boolean: ['buildNativeTests', 'runTestFilesSeperately'],
|
||||
unknown: arg => unknownFlags.push(arg)
|
||||
});
|
||||
|
||||
@@ -123,24 +123,55 @@ async function runElectronTests () {
|
||||
}
|
||||
}
|
||||
|
||||
async function runRemoteBasedElectronTests () {
|
||||
async function runTestUsingElectron (specDir, testName) {
|
||||
let exe = path.resolve(BASE, utils.getElectronExec());
|
||||
const runnerArgs = ['electron/spec', ...unknownArgs.slice(2)];
|
||||
const runnerArgs = [`electron/${specDir}`, ...unknownArgs.slice(2)];
|
||||
if (process.platform === 'linux') {
|
||||
runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe);
|
||||
exe = 'python3';
|
||||
}
|
||||
|
||||
const { status } = childProcess.spawnSync(exe, runnerArgs, {
|
||||
const { status, signal } = childProcess.spawnSync(exe, runnerArgs, {
|
||||
cwd: path.resolve(__dirname, '../..'),
|
||||
stdio: 'inherit'
|
||||
});
|
||||
if (status !== 0) {
|
||||
const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString();
|
||||
console.log(`${fail} Electron tests failed with code ${textStatus}.`);
|
||||
if (status) {
|
||||
const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString();
|
||||
console.log(`${fail} Electron tests failed with code ${textStatus}.`);
|
||||
} else {
|
||||
console.log(`${fail} Electron tests failed with kill signal ${signal}.`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${pass} Electron remote process tests passed.`);
|
||||
console.log(`${pass} Electron ${testName} process tests passed.`);
|
||||
}
|
||||
|
||||
const specFilter = (file) => {
|
||||
if (!/-spec\.[tj]s$/.test(file)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
async function runTests (specDir, testName) {
|
||||
if (args.runTestFilesSeperately) {
|
||||
const getFiles = require('../spec/static/get-files');
|
||||
const testFiles = await getFiles(path.resolve(__dirname, `../${specDir}`), { filter: specFilter });
|
||||
const baseElectronDir = path.resolve(__dirname, '..');
|
||||
unknownArgs.splice(unknownArgs.length, 0, '--files', '');
|
||||
testFiles.sort().forEach(async (file) => {
|
||||
unknownArgs.splice((unknownArgs.length - 1), 1, path.relative(baseElectronDir, file));
|
||||
console.log(`Running tests for ${unknownArgs[unknownArgs.length - 1]}`);
|
||||
await runTestUsingElectron(specDir, testName);
|
||||
});
|
||||
} else {
|
||||
await runTestUsingElectron(specDir, testName);
|
||||
}
|
||||
}
|
||||
|
||||
async function runRemoteBasedElectronTests () {
|
||||
await runTests('spec', 'remote');
|
||||
}
|
||||
|
||||
async function runNativeElectronTests () {
|
||||
@@ -195,27 +226,7 @@ async function runNativeElectronTests () {
|
||||
}
|
||||
|
||||
async function runMainProcessElectronTests () {
|
||||
let exe = path.resolve(BASE, utils.getElectronExec());
|
||||
const runnerArgs = ['electron/spec-main', ...unknownArgs.slice(2)];
|
||||
if (process.platform === 'linux') {
|
||||
runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe);
|
||||
exe = 'python3';
|
||||
}
|
||||
|
||||
const { status, signal } = childProcess.spawnSync(exe, runnerArgs, {
|
||||
cwd: path.resolve(__dirname, '../..'),
|
||||
stdio: 'inherit'
|
||||
});
|
||||
if (status !== 0) {
|
||||
if (status) {
|
||||
const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString();
|
||||
console.log(`${fail} Electron tests failed with code ${textStatus}.`);
|
||||
} else {
|
||||
console.log(`${fail} Electron tests failed with kill signal ${signal}.`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${pass} Electron main process tests passed.`);
|
||||
await runTests('spec-main', 'main');
|
||||
}
|
||||
|
||||
async function installSpecModules (dir) {
|
||||
|
||||
@@ -3,6 +3,7 @@ LICENSES.chromium.html
|
||||
chrome-sandbox
|
||||
chrome_100_percent.pak
|
||||
chrome_200_percent.pak
|
||||
crashpad_handler
|
||||
electron
|
||||
icudtl.dat
|
||||
libEGL.so
|
||||
|
||||
@@ -3,6 +3,7 @@ LICENSES.chromium.html
|
||||
chrome-sandbox
|
||||
chrome_100_percent.pak
|
||||
chrome_200_percent.pak
|
||||
crashpad_handler
|
||||
electron
|
||||
icudtl.dat
|
||||
libEGL.so
|
||||
|
||||
@@ -3,6 +3,7 @@ LICENSES.chromium.html
|
||||
chrome-sandbox
|
||||
chrome_100_percent.pak
|
||||
chrome_200_percent.pak
|
||||
crashpad_handler
|
||||
electron
|
||||
icudtl.dat
|
||||
libEGL.so
|
||||
|
||||
@@ -3,6 +3,7 @@ LICENSES.chromium.html
|
||||
chrome-sandbox
|
||||
chrome_100_percent.pak
|
||||
chrome_200_percent.pak
|
||||
crashpad_handler
|
||||
electron
|
||||
icudtl.dat
|
||||
libEGL.so
|
||||
|
||||
@@ -153,6 +153,9 @@ bool ElectronCrashReporterClient::GetCrashDumpLocation(
|
||||
base::FilePath* crash_dir) {
|
||||
bool result = base::PathService::Get(electron::DIR_CRASH_DUMPS, crash_dir);
|
||||
{
|
||||
// If the DIR_CRASH_DUMPS path is overridden with
|
||||
// app.setPath('crashDumps', ...) then the directory might not have been
|
||||
// created.
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
if (result && !base::PathExists(*crash_dir)) {
|
||||
return base::CreateDirectory(*crash_dir);
|
||||
@@ -162,13 +165,6 @@ bool ElectronCrashReporterClient::GetCrashDumpLocation(
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(OS_MAC) || defined(OS_LINUX)
|
||||
bool ElectronCrashReporterClient::GetCrashMetricsLocation(
|
||||
base::FilePath* metrics_dir) {
|
||||
return base::PathService::Get(chrome::DIR_USER_DATA, metrics_dir);
|
||||
}
|
||||
#endif // OS_MAC || OS_LINUX
|
||||
|
||||
bool ElectronCrashReporterClient::IsRunningUnattended() {
|
||||
return !collect_stats_consent_;
|
||||
}
|
||||
|
||||
@@ -52,10 +52,6 @@ class ElectronCrashReporterClient : public crash_reporter::CrashReporterClient {
|
||||
bool GetCrashDumpLocation(base::FilePath* crash_dir) override;
|
||||
#endif
|
||||
|
||||
#if defined(OS_MAC) || defined(OS_LINUX)
|
||||
bool GetCrashMetricsLocation(base::FilePath* metrics_dir) override;
|
||||
#endif
|
||||
|
||||
bool IsRunningUnattended() override;
|
||||
|
||||
bool GetCollectStatsConsent() override;
|
||||
|
||||
@@ -58,7 +58,8 @@
|
||||
#endif
|
||||
|
||||
#if !defined(MAS_BUILD)
|
||||
#include "components/crash/core/app/crashpad.h" // nogncheck
|
||||
#include "components/crash/core/app/crash_switches.h" // nogncheck
|
||||
#include "components/crash/core/app/crashpad.h" // nogncheck
|
||||
#include "components/crash/core/common/crash_key.h"
|
||||
#include "components/crash/core/common/crash_keys.h"
|
||||
#include "shell/app/electron_crash_reporter_client.h"
|
||||
@@ -123,7 +124,8 @@ bool ElectronPathProvider(int key, base::FilePath* result) {
|
||||
case chrome::DIR_USER_DATA:
|
||||
if (!base::PathService::Get(DIR_APP_DATA, &cur))
|
||||
return false;
|
||||
cur = cur.Append(base::FilePath::FromUTF8Unsafe(GetApplicationName()));
|
||||
cur = cur.Append(base::FilePath::FromUTF8Unsafe(
|
||||
GetPossiblyOverriddenApplicationName()));
|
||||
create_dir = true;
|
||||
break;
|
||||
case DIR_CRASH_DUMPS:
|
||||
@@ -153,7 +155,8 @@ bool ElectronPathProvider(int key, base::FilePath* result) {
|
||||
#endif
|
||||
if (!base::PathService::Get(parent_key, &cur))
|
||||
return false;
|
||||
cur = cur.Append(base::FilePath::FromUTF8Unsafe(GetApplicationName()));
|
||||
cur = cur.Append(base::FilePath::FromUTF8Unsafe(
|
||||
GetPossiblyOverriddenApplicationName()));
|
||||
create_dir = true;
|
||||
break;
|
||||
}
|
||||
@@ -178,7 +181,8 @@ bool ElectronPathProvider(int key, base::FilePath* result) {
|
||||
return false;
|
||||
cur = cur.Append(FILE_PATH_LITERAL("Library"));
|
||||
cur = cur.Append(FILE_PATH_LITERAL("Logs"));
|
||||
cur = cur.Append(base::FilePath::FromUTF8Unsafe(GetApplicationName()));
|
||||
cur = cur.Append(base::FilePath::FromUTF8Unsafe(
|
||||
GetPossiblyOverriddenApplicationName()));
|
||||
#else
|
||||
if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
|
||||
return false;
|
||||
@@ -366,9 +370,19 @@ void ElectronMainDelegate::PreSandboxStartup() {
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
// Zygote needs to call InitCrashReporter() in RunZygote().
|
||||
if (process_type != ::switches::kZygoteProcess && !process_type.empty()) {
|
||||
ElectronCrashReporterClient::Create();
|
||||
breakpad::InitCrashReporter(process_type);
|
||||
if (crash_reporter::IsCrashpadEnabled()) {
|
||||
if (command_line->HasSwitch(
|
||||
crash_reporter::switches::kCrashpadHandlerPid)) {
|
||||
crash_reporter::InitializeCrashpad(false, process_type);
|
||||
crash_reporter::SetFirstChanceExceptionHandler(
|
||||
v8::TryHandleWebAssemblyTrapPosix);
|
||||
}
|
||||
} else {
|
||||
breakpad::InitCrashReporter(process_type);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -463,7 +477,16 @@ void ElectronMainDelegate::ZygoteForked() {
|
||||
base::CommandLine::ForCurrentProcess();
|
||||
std::string process_type =
|
||||
command_line->GetSwitchValueASCII(::switches::kProcessType);
|
||||
breakpad::InitCrashReporter(process_type);
|
||||
if (crash_reporter::IsCrashpadEnabled()) {
|
||||
if (command_line->HasSwitch(
|
||||
crash_reporter::switches::kCrashpadHandlerPid)) {
|
||||
crash_reporter::InitializeCrashpad(false, process_type);
|
||||
crash_reporter::SetFirstChanceExceptionHandler(
|
||||
v8::TryHandleWebAssemblyTrapPosix);
|
||||
}
|
||||
} else {
|
||||
breakpad::InitCrashReporter(process_type);
|
||||
}
|
||||
|
||||
// Reset the command line for the newly spawned process.
|
||||
crash_keys::SetCrashKeysFromCommandLine(*command_line);
|
||||
|
||||
@@ -716,8 +716,9 @@ void App::OnDidFailToContinueUserActivity(const std::string& type,
|
||||
|
||||
void App::OnContinueUserActivity(bool* prevent_default,
|
||||
const std::string& type,
|
||||
const base::DictionaryValue& user_info) {
|
||||
if (Emit("continue-activity", type, user_info)) {
|
||||
const base::DictionaryValue& user_info,
|
||||
const base::DictionaryValue& details) {
|
||||
if (Emit("continue-activity", type, user_info, details)) {
|
||||
*prevent_default = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,8 @@ class App : public ElectronBrowserClient::Delegate,
|
||||
const std::string& error) override;
|
||||
void OnContinueUserActivity(bool* prevent_default,
|
||||
const std::string& type,
|
||||
const base::DictionaryValue& user_info) override;
|
||||
const base::DictionaryValue& user_info,
|
||||
const base::DictionaryValue& details) override;
|
||||
void OnUserActivityWasContinued(
|
||||
const std::string& type,
|
||||
const base::DictionaryValue& user_info) override;
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "base/guid.h"
|
||||
#include "components/crash/core/app/breakpad_linux.h"
|
||||
#include "components/crash/core/common/crash_keys.h"
|
||||
#include "components/upload_list/combining_upload_list.h"
|
||||
#include "v8/include/v8-wasm-trap-handler-posix.h"
|
||||
#include "v8/include/v8.h"
|
||||
#endif
|
||||
@@ -150,16 +151,29 @@ void Start(const std::string& submit_url,
|
||||
? "node"
|
||||
: command_line->GetSwitchValueASCII(::switches::kProcessType);
|
||||
#if defined(OS_LINUX)
|
||||
::crash_keys::SetMetricsClientIdFromGUID(GetClientId());
|
||||
auto& global_crash_keys = GetGlobalCrashKeysMutable();
|
||||
for (const auto& pair : global_extra) {
|
||||
global_crash_keys[pair.first] = pair.second;
|
||||
if (::crash_reporter::IsCrashpadEnabled()) {
|
||||
for (const auto& pair : extra)
|
||||
electron::crash_keys::SetCrashKey(pair.first, pair.second);
|
||||
{
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
::crash_reporter::InitializeCrashpad(process_type.empty(), process_type);
|
||||
}
|
||||
if (ignore_system_crash_handler) {
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo()
|
||||
->set_system_crash_reporter_forwarding(crashpad::TriState::kDisabled);
|
||||
}
|
||||
} else {
|
||||
::crash_keys::SetMetricsClientIdFromGUID(GetClientId());
|
||||
auto& global_crash_keys = GetGlobalCrashKeysMutable();
|
||||
for (const auto& pair : global_extra) {
|
||||
global_crash_keys[pair.first] = pair.second;
|
||||
}
|
||||
for (const auto& pair : extra)
|
||||
electron::crash_keys::SetCrashKey(pair.first, pair.second);
|
||||
for (const auto& pair : global_extra)
|
||||
electron::crash_keys::SetCrashKey(pair.first, pair.second);
|
||||
breakpad::InitCrashReporter(process_type);
|
||||
}
|
||||
for (const auto& pair : extra)
|
||||
electron::crash_keys::SetCrashKey(pair.first, pair.second);
|
||||
for (const auto& pair : global_extra)
|
||||
electron::crash_keys::SetCrashKey(pair.first, pair.second);
|
||||
breakpad::InitCrashReporter(process_type);
|
||||
#elif defined(OS_MAC)
|
||||
for (const auto& pair : extra)
|
||||
electron::crash_keys::SetCrashKey(pair.first, pair.second);
|
||||
@@ -203,7 +217,20 @@ scoped_refptr<UploadList> CreateCrashUploadList() {
|
||||
base::PathService::Get(electron::DIR_CRASH_DUMPS, &crash_dir_path);
|
||||
base::FilePath upload_log_path =
|
||||
crash_dir_path.AppendASCII(CrashUploadList::kReporterLogFilename);
|
||||
return base::MakeRefCounted<TextLogUploadList>(upload_log_path);
|
||||
scoped_refptr<UploadList> result =
|
||||
base::MakeRefCounted<TextLogUploadList>(upload_log_path);
|
||||
if (crash_reporter::IsCrashpadEnabled()) {
|
||||
// Crashpad keeps the records of C++ crashes (segfaults, etc) in its
|
||||
// internal database. The JavaScript error reporter writes JS error upload
|
||||
// records to the older text format. Combine the two to present a complete
|
||||
// list to the user.
|
||||
// TODO(nornagon): what is "The JavaScript error reporter", and do we care
|
||||
// about it?
|
||||
std::vector<scoped_refptr<UploadList>> uploaders = {
|
||||
base::MakeRefCounted<CrashUploadListCrashpad>(), std::move(result)};
|
||||
result = base::MakeRefCounted<CombiningUploadList>(std::move(uploaders));
|
||||
}
|
||||
return result;
|
||||
#endif // defined(OS_MAC) || defined(OS_WIN)
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
gin_helper::Dictionary dict(isolate, exports);
|
||||
dict.SetMethod("showMessageBoxSync", &ShowMessageBoxSync);
|
||||
dict.SetMethod("showMessageBox", &ShowMessageBox);
|
||||
dict.SetMethod("_closeMessageBox", &electron::CloseMessageBox);
|
||||
dict.SetMethod("showErrorBox", &electron::ShowErrorBox);
|
||||
dict.SetMethod("showOpenDialogSync", &ShowOpenDialogSync);
|
||||
dict.SetMethod("showOpenDialog", &ShowOpenDialog);
|
||||
|
||||
@@ -406,7 +406,7 @@ std::string SystemPreferences::GetSystemColor(gin_helper::ErrorThrower thrower,
|
||||
return "";
|
||||
}
|
||||
|
||||
return ToRGBHex(skia::NSSystemColorToSkColor(sysColor));
|
||||
return ToRGBAHex(skia::NSSystemColorToSkColor(sysColor));
|
||||
}
|
||||
|
||||
bool SystemPreferences::CanPromptTouchID() {
|
||||
|
||||
@@ -914,16 +914,6 @@ void WebContents::InitWithWebContents(content::WebContents* web_contents,
|
||||
inspectable_web_contents_ = std::make_unique<InspectableWebContents>(
|
||||
web_contents, browser_context->prefs(), is_guest);
|
||||
inspectable_web_contents_->SetDelegate(this);
|
||||
|
||||
if (web_preferences) {
|
||||
std::string color_name;
|
||||
if (web_preferences->GetPreference(options::kBackgroundColor,
|
||||
&color_name)) {
|
||||
web_contents->SetPageBaseBackgroundColor(ParseHexColor(color_name));
|
||||
} else {
|
||||
web_contents->SetPageBaseBackgroundColor(SK_ColorTRANSPARENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebContents::~WebContents() {
|
||||
@@ -1383,6 +1373,18 @@ void WebContents::HandleNewRenderFrame(
|
||||
if (!rwhv)
|
||||
return;
|
||||
|
||||
// Set the background color of RenderWidgetHostView.
|
||||
auto* web_preferences = WebContentsPreferences::From(web_contents());
|
||||
if (web_preferences) {
|
||||
std::string color_name;
|
||||
if (web_preferences->GetPreference(options::kBackgroundColor,
|
||||
&color_name)) {
|
||||
rwhv->SetBackgroundColor(ParseHexColor(color_name));
|
||||
} else {
|
||||
rwhv->SetBackgroundColor(SK_ColorTRANSPARENT);
|
||||
}
|
||||
}
|
||||
|
||||
if (!background_throttling_)
|
||||
render_frame_host->GetRenderViewHost()->SetSchedulerThrottling(false);
|
||||
|
||||
@@ -3050,14 +3052,6 @@ std::vector<base::FilePath> WebContents::GetPreloadPaths() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> WebContents::GetWebPreferences(
|
||||
v8::Isolate* isolate) const {
|
||||
auto* web_preferences = WebContentsPreferences::From(web_contents());
|
||||
if (!web_preferences)
|
||||
return v8::Null(isolate);
|
||||
return gin::ConvertToV8(isolate, *web_preferences->preference());
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> WebContents::GetLastWebPreferences(
|
||||
v8::Isolate* isolate) const {
|
||||
auto* web_preferences = WebContentsPreferences::From(web_contents());
|
||||
@@ -3612,9 +3606,7 @@ void WebContents::UpdateHtmlApiFullscreen(bool fullscreen) {
|
||||
manager->ForEachGuest(
|
||||
web_contents(), base::BindRepeating([](content::WebContents* guest) {
|
||||
WebContents* api_web_contents = WebContents::From(guest);
|
||||
// Use UpdateHtmlApiFullscreen instead of SetXXX becuase there is no
|
||||
// need to interact with the owner window.
|
||||
api_web_contents->UpdateHtmlApiFullscreen(false);
|
||||
api_web_contents->SetHtmlApiFullscreen(false);
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
@@ -3719,7 +3711,6 @@ v8::Local<v8::ObjectTemplate> WebContents::FillObjectTemplate(
|
||||
.SetMethod("getZoomFactor", &WebContents::GetZoomFactor)
|
||||
.SetMethod("getType", &WebContents::GetType)
|
||||
.SetMethod("_getPreloadPaths", &WebContents::GetPreloadPaths)
|
||||
.SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
|
||||
.SetMethod("getLastWebPreferences", &WebContents::GetLastWebPreferences)
|
||||
.SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
|
||||
.SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker)
|
||||
|
||||
@@ -136,25 +136,25 @@ void Browser::Shutdown() {
|
||||
}
|
||||
|
||||
std::string Browser::GetVersion() const {
|
||||
std::string ret = GetOverriddenApplicationVersion();
|
||||
std::string ret = OverriddenApplicationVersion();
|
||||
if (ret.empty())
|
||||
ret = GetExecutableFileVersion();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Browser::SetVersion(const std::string& version) {
|
||||
OverrideApplicationVersion(version);
|
||||
OverriddenApplicationVersion() = version;
|
||||
}
|
||||
|
||||
std::string Browser::GetName() const {
|
||||
std::string ret = GetOverriddenApplicationName();
|
||||
std::string ret = OverriddenApplicationName();
|
||||
if (ret.empty())
|
||||
ret = GetExecutableFileProductName();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Browser::SetName(const std::string& name) {
|
||||
OverrideApplicationName(name);
|
||||
OverriddenApplicationName() = name;
|
||||
}
|
||||
|
||||
int Browser::GetBadgeCount() {
|
||||
|
||||
@@ -187,7 +187,8 @@ class Browser : public WindowListObserver {
|
||||
|
||||
// Resumes an activity via hand-off.
|
||||
bool ContinueUserActivity(const std::string& type,
|
||||
base::DictionaryValue user_info);
|
||||
base::DictionaryValue user_info,
|
||||
base::DictionaryValue details);
|
||||
|
||||
// Indicates that an activity was continued on another device.
|
||||
void UserActivityWasContinued(const std::string& type,
|
||||
|
||||
@@ -277,10 +277,11 @@ void Browser::DidFailToContinueUserActivity(const std::string& type,
|
||||
}
|
||||
|
||||
bool Browser::ContinueUserActivity(const std::string& type,
|
||||
base::DictionaryValue user_info) {
|
||||
base::DictionaryValue user_info,
|
||||
base::DictionaryValue details) {
|
||||
bool prevent_default = false;
|
||||
for (BrowserObserver& observer : observers_)
|
||||
observer.OnContinueUserActivity(&prevent_default, type, user_info);
|
||||
observer.OnContinueUserActivity(&prevent_default, type, user_info, details);
|
||||
return prevent_default;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,8 @@ class BrowserObserver : public base::CheckedObserver {
|
||||
// The browser wants to resume a user activity via handoff. (macOS only)
|
||||
virtual void OnContinueUserActivity(bool* prevent_default,
|
||||
const std::string& type,
|
||||
const base::DictionaryValue& user_info) {}
|
||||
const base::DictionaryValue& user_info,
|
||||
const base::DictionaryValue& details) {}
|
||||
// The browser wants to notify that an user activity was resumed. (macOS only)
|
||||
virtual void OnUserActivityWasContinued(
|
||||
const std::string& type,
|
||||
|
||||
@@ -298,6 +298,12 @@ breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost(
|
||||
}
|
||||
|
||||
int GetCrashSignalFD(const base::CommandLine& command_line) {
|
||||
if (crash_reporter::IsCrashpadEnabled()) {
|
||||
int fd;
|
||||
pid_t pid;
|
||||
return crash_reporter::GetHandlerSocket(&fd, &pid) ? fd : -1;
|
||||
}
|
||||
|
||||
// Extensions have the same process type as renderers.
|
||||
if (command_line.HasSwitch(extensions::switches::kExtensionProcess)) {
|
||||
static breakpad::CrashHandlerHostLinux* crash_handler = nullptr;
|
||||
@@ -526,20 +532,37 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
bool enable_crash_reporter = false;
|
||||
enable_crash_reporter = breakpad::IsCrashReporterEnabled();
|
||||
if (crash_reporter::IsCrashpadEnabled()) {
|
||||
command_line->AppendSwitch(::switches::kEnableCrashpad);
|
||||
enable_crash_reporter = true;
|
||||
|
||||
int fd;
|
||||
pid_t pid;
|
||||
|
||||
if (crash_reporter::GetHandlerSocket(&fd, &pid)) {
|
||||
command_line->AppendSwitchASCII(
|
||||
crash_reporter::switches::kCrashpadHandlerPid,
|
||||
base::NumberToString(pid));
|
||||
}
|
||||
} else {
|
||||
enable_crash_reporter = breakpad::IsCrashReporterEnabled();
|
||||
}
|
||||
|
||||
if (enable_crash_reporter) {
|
||||
std::string switch_value =
|
||||
api::crash_reporter::GetClientId() + ",no_channel";
|
||||
command_line->AppendSwitchASCII(::switches::kEnableCrashReporter,
|
||||
switch_value);
|
||||
for (const auto& pair : api::crash_reporter::GetGlobalCrashKeys()) {
|
||||
if (!switch_value.empty())
|
||||
switch_value += ",";
|
||||
switch_value += pair.first;
|
||||
switch_value += "=";
|
||||
switch_value += pair.second;
|
||||
if (!crash_reporter::IsCrashpadEnabled()) {
|
||||
for (const auto& pair : api::crash_reporter::GetGlobalCrashKeys()) {
|
||||
if (!switch_value.empty())
|
||||
switch_value += ",";
|
||||
switch_value += pair.first;
|
||||
switch_value += "=";
|
||||
switch_value += pair.second;
|
||||
}
|
||||
command_line->AppendSwitchASCII(switches::kGlobalCrashKeys, switch_value);
|
||||
}
|
||||
command_line->AppendSwitchASCII(switches::kGlobalCrashKeys, switch_value);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -118,11 +118,7 @@ ElectronBrowserContext::ElectronBrowserContext(const std::string& partition,
|
||||
base::StringToInt(command_line->GetSwitchValueASCII(switches::kDiskCacheSize),
|
||||
&max_cache_size_);
|
||||
|
||||
if (!base::PathService::Get(chrome::DIR_USER_DATA, &path_)) {
|
||||
base::PathService::Get(DIR_APP_DATA, &path_);
|
||||
path_ = path_.Append(base::FilePath::FromUTF8Unsafe(GetApplicationName()));
|
||||
base::PathService::Override(chrome::DIR_USER_DATA, path_);
|
||||
}
|
||||
CHECK(base::PathService::Get(chrome::DIR_USER_DATA, &path_));
|
||||
|
||||
if (!in_memory && !partition.empty())
|
||||
path_ = path_.Append(FILE_PATH_LITERAL("Partitions"))
|
||||
|
||||
@@ -66,11 +66,15 @@ void LoginHandler::EmitEvent(
|
||||
details.Set("firstAuthAttempt", first_auth_attempt);
|
||||
details.Set("responseHeaders", response_headers.get());
|
||||
|
||||
auto weak_this = weak_factory_.GetWeakPtr();
|
||||
bool default_prevented =
|
||||
api_web_contents->Emit("login", std::move(details), auth_info,
|
||||
base::BindOnce(&LoginHandler::CallbackFromJS,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
if (!default_prevented && auth_required_callback_) {
|
||||
// ⚠️ NB, if CallbackFromJS is called during Emit(), |this| will have been
|
||||
// deleted. Check the weak ptr before accessing any member variables to
|
||||
// prevent UAF.
|
||||
if (weak_this && !default_prevented && auth_required_callback_) {
|
||||
std::move(auth_required_callback_).Run(absl::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,13 +154,16 @@ static NSDictionary* UNNotificationResponseToNSDictionary(
|
||||
#endif
|
||||
restorationHandler {
|
||||
std::string activity_type(base::SysNSStringToUTF8(userActivity.activityType));
|
||||
NSURL* url = userActivity.webpageURL;
|
||||
NSDictionary* details = url ? @{@"webpageURL" : [url absoluteString]} : @{};
|
||||
if (!userActivity.userInfo)
|
||||
return NO;
|
||||
|
||||
electron::Browser* browser = electron::Browser::Get();
|
||||
return browser->ContinueUserActivity(
|
||||
activity_type,
|
||||
electron::NSDictionaryToDictionaryValue(userActivity.userInfo))
|
||||
electron::NSDictionaryToDictionaryValue(userActivity.userInfo),
|
||||
electron::NSDictionaryToDictionaryValue(details))
|
||||
? YES
|
||||
: NO;
|
||||
}
|
||||
|
||||
@@ -1690,7 +1690,7 @@ bool NativeWindowMac::IsActive() const {
|
||||
}
|
||||
|
||||
void NativeWindowMac::ReorderButtonsView() {
|
||||
if (buttons_view_) {
|
||||
if (buttons_view_ && !IsFullscreen()) {
|
||||
[buttons_view_ removeFromSuperview];
|
||||
[[window_ contentView] addSubview:buttons_view_];
|
||||
}
|
||||
|
||||
@@ -170,6 +170,83 @@ void OnWrite(std::unique_ptr<WriteData> write_data, MojoResult result) {
|
||||
|
||||
} // namespace
|
||||
|
||||
ElectronURLLoaderFactory::RedirectedRequest::RedirectedRequest(
|
||||
const net::RedirectInfo& redirect_info,
|
||||
mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& request,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_remote)
|
||||
: redirect_info_(redirect_info),
|
||||
request_id_(request_id),
|
||||
options_(options),
|
||||
request_(request),
|
||||
client_(std::move(client)),
|
||||
traffic_annotation_(traffic_annotation) {
|
||||
loader_receiver_.Bind(std::move(loader_receiver));
|
||||
loader_receiver_.set_disconnect_handler(
|
||||
base::BindOnce(&ElectronURLLoaderFactory::RedirectedRequest::DeleteThis,
|
||||
base::Unretained(this)));
|
||||
target_factory_remote_.Bind(std::move(target_factory_remote));
|
||||
target_factory_remote_.set_disconnect_handler(base::BindOnce(
|
||||
&ElectronURLLoaderFactory::RedirectedRequest::OnTargetFactoryError,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
ElectronURLLoaderFactory::RedirectedRequest::~RedirectedRequest() = default;
|
||||
|
||||
void ElectronURLLoaderFactory::RedirectedRequest::FollowRedirect(
|
||||
const std::vector<std::string>& removed_headers,
|
||||
const net::HttpRequestHeaders& modified_headers,
|
||||
const net::HttpRequestHeaders& modified_cors_exempt_headers,
|
||||
const absl::optional<GURL>& new_url) {
|
||||
// Update |request_| with info from the redirect, so that it's accurate
|
||||
// The following references code in WorkerScriptLoader::FollowRedirect
|
||||
bool should_clear_upload = false;
|
||||
net::RedirectUtil::UpdateHttpRequest(
|
||||
request_.url, request_.method, redirect_info_, removed_headers,
|
||||
modified_headers, &request_.headers, &should_clear_upload);
|
||||
request_.cors_exempt_headers.MergeFrom(modified_cors_exempt_headers);
|
||||
for (const std::string& name : removed_headers)
|
||||
request_.cors_exempt_headers.RemoveHeader(name);
|
||||
|
||||
if (should_clear_upload)
|
||||
request_.request_body = nullptr;
|
||||
|
||||
request_.url = redirect_info_.new_url;
|
||||
request_.method = redirect_info_.new_method;
|
||||
request_.site_for_cookies = redirect_info_.new_site_for_cookies;
|
||||
request_.referrer = GURL(redirect_info_.new_referrer);
|
||||
request_.referrer_policy = redirect_info_.new_referrer_policy;
|
||||
|
||||
// Create a new loader to process the redirect and destroy this one
|
||||
target_factory_remote_->CreateLoaderAndStart(
|
||||
loader_receiver_.Unbind(), request_id_, options_, request_,
|
||||
std::move(client_), traffic_annotation_);
|
||||
|
||||
DeleteThis();
|
||||
}
|
||||
|
||||
void ElectronURLLoaderFactory::RedirectedRequest::OnTargetFactoryError() {
|
||||
// Can't create a new loader at this point, so the request can't continue
|
||||
mojo::Remote<network::mojom::URLLoaderClient> client_remote(
|
||||
std::move(client_));
|
||||
client_remote->OnComplete(
|
||||
network::URLLoaderCompletionStatus(net::ERR_FAILED));
|
||||
client_remote.reset();
|
||||
|
||||
DeleteThis();
|
||||
}
|
||||
|
||||
void ElectronURLLoaderFactory::RedirectedRequest::DeleteThis() {
|
||||
loader_receiver_.reset();
|
||||
target_factory_remote_.reset();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
// static
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory>
|
||||
ElectronURLLoaderFactory::Create(ProtocolType type,
|
||||
@@ -202,12 +279,18 @@ void ElectronURLLoaderFactory::CreateLoaderAndStart(
|
||||
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> proxy_factory;
|
||||
|
||||
// |StartLoading| is used for both intercepted and registered protocols,
|
||||
// and on redirects it needs a factory to use to create a loader for the
|
||||
// new request. So in this case, this factory is the target factory.
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory;
|
||||
this->Clone(target_factory.InitWithNewPipeAndPassReceiver());
|
||||
|
||||
handler_.Run(
|
||||
request,
|
||||
base::BindOnce(&ElectronURLLoaderFactory::StartLoading, std::move(loader),
|
||||
request_id, options, request, std::move(client),
|
||||
traffic_annotation, std::move(proxy_factory), type_));
|
||||
traffic_annotation, std::move(target_factory), type_));
|
||||
}
|
||||
|
||||
// static
|
||||
@@ -230,7 +313,7 @@ void ElectronURLLoaderFactory::StartLoading(
|
||||
const network::ResourceRequest& request,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> proxy_factory,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,
|
||||
ProtocolType type,
|
||||
gin::Arguments* args) {
|
||||
// Send network error when there is no argument passed.
|
||||
@@ -280,13 +363,6 @@ void ElectronURLLoaderFactory::StartLoading(
|
||||
request.url.Resolve(location),
|
||||
net::RedirectUtil::GetReferrerPolicyHeader(head->headers.get()), false);
|
||||
|
||||
network::ResourceRequest new_request = request;
|
||||
new_request.method = redirect_info.new_method;
|
||||
new_request.url = redirect_info.new_url;
|
||||
new_request.site_for_cookies = redirect_info.new_site_for_cookies;
|
||||
new_request.referrer = GURL(redirect_info.new_referrer);
|
||||
new_request.referrer_policy = redirect_info.new_referrer_policy;
|
||||
|
||||
DCHECK(client.is_valid());
|
||||
|
||||
mojo::Remote<network::mojom::URLLoaderClient> client_remote(
|
||||
@@ -294,33 +370,15 @@ void ElectronURLLoaderFactory::StartLoading(
|
||||
|
||||
client_remote->OnReceiveRedirect(redirect_info, std::move(head));
|
||||
|
||||
// Unbound client, so it an be passed to sub-methods
|
||||
client = client_remote.Unbind();
|
||||
// When the redirection comes from an intercepted scheme (which has
|
||||
// |proxy_factory| passed), we ask the proxy factory to create a loader
|
||||
// for new URL, otherwise we call |StartLoadingHttp|, which creates
|
||||
// loader with default factory.
|
||||
//
|
||||
// Note that when handling requests for intercepted scheme, creating loader
|
||||
// with default factory (i.e. calling StartLoadingHttp) would bypass the
|
||||
// ProxyingURLLoaderFactory, we have to explicitly use the proxy factory to
|
||||
// create loader so it is possible to have handlers of intercepted scheme
|
||||
// getting called recursively, which is a behavior expected in protocol
|
||||
// module.
|
||||
//
|
||||
// I'm not sure whether this is an intended behavior in Chromium.
|
||||
if (proxy_factory.is_valid()) {
|
||||
mojo::Remote<network::mojom::URLLoaderFactory> proxy_factory_remote(
|
||||
std::move(proxy_factory));
|
||||
// Bind the URLLoader receiver and wait for a FollowRedirect request, or for
|
||||
// the remote to disconnect, which will happen if the request is aborted.
|
||||
// That may happen when the redirect is to a different scheme, which will
|
||||
// cause the URL loader to be destroyed and a new one created using the
|
||||
// factory for that scheme.
|
||||
new RedirectedRequest(redirect_info, std::move(loader), request_id, options,
|
||||
request, client_remote.Unbind(), traffic_annotation,
|
||||
std::move(target_factory));
|
||||
|
||||
proxy_factory_remote->CreateLoaderAndStart(
|
||||
std::move(loader), request_id, options, new_request,
|
||||
std::move(client), traffic_annotation);
|
||||
} else {
|
||||
StartLoadingHttp(std::move(loader), new_request, std::move(client),
|
||||
traffic_annotation,
|
||||
gin::Dictionary::CreateEmpty(args->isolate()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -360,7 +418,7 @@ void ElectronURLLoaderFactory::StartLoading(
|
||||
}
|
||||
StartLoading(std::move(loader), request_id, options, request,
|
||||
std::move(client), traffic_annotation,
|
||||
std::move(proxy_factory), type, args);
|
||||
std::move(target_factory), type, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
@@ -15,9 +16,11 @@
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
#include "services/network/public/cpp/self_deleting_url_loader_factory.h"
|
||||
#include "services/network/public/mojom/url_loader.mojom.h"
|
||||
#include "services/network/public/mojom/url_loader_factory.mojom.h"
|
||||
#include "services/network/public/mojom/url_response_head.mojom.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -43,6 +46,52 @@ using HandlersMap =
|
||||
// Implementation of URLLoaderFactory.
|
||||
class ElectronURLLoaderFactory : public network::SelfDeletingURLLoaderFactory {
|
||||
public:
|
||||
// This class binds a URLLoader receiver in the case of a redirect, waiting
|
||||
// for |FollowRedirect| to be called at which point the new request will be
|
||||
// started, and the receiver will be unbound letting a new URLLoader bind it
|
||||
class RedirectedRequest : public network::mojom::URLLoader {
|
||||
public:
|
||||
RedirectedRequest(
|
||||
const net::RedirectInfo& redirect_info,
|
||||
mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
|
||||
int32_t request_id,
|
||||
uint32_t options,
|
||||
const network::ResourceRequest& request,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory>
|
||||
target_factory_remote);
|
||||
~RedirectedRequest() override;
|
||||
|
||||
// network::mojom::URLLoader:
|
||||
void FollowRedirect(
|
||||
const std::vector<std::string>& removed_headers,
|
||||
const net::HttpRequestHeaders& modified_headers,
|
||||
const net::HttpRequestHeaders& modified_cors_exempt_headers,
|
||||
const absl::optional<GURL>& new_url) override;
|
||||
void SetPriority(net::RequestPriority priority,
|
||||
int32_t intra_priority_value) override {}
|
||||
void PauseReadingBodyFromNet() override {}
|
||||
void ResumeReadingBodyFromNet() override {}
|
||||
|
||||
void OnTargetFactoryError();
|
||||
void DeleteThis();
|
||||
|
||||
private:
|
||||
net::RedirectInfo redirect_info_;
|
||||
|
||||
mojo::Receiver<network::mojom::URLLoader> loader_receiver_{this};
|
||||
int32_t request_id_;
|
||||
uint32_t options_;
|
||||
network::ResourceRequest request_;
|
||||
mojo::PendingRemote<network::mojom::URLLoaderClient> client_;
|
||||
net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
|
||||
|
||||
mojo::Remote<network::mojom::URLLoaderFactory> target_factory_remote_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RedirectedRequest);
|
||||
};
|
||||
|
||||
static mojo::PendingRemote<network::mojom::URLLoaderFactory> Create(
|
||||
ProtocolType type,
|
||||
const ProtocolHandler& handler);
|
||||
@@ -64,7 +113,7 @@ class ElectronURLLoaderFactory : public network::SelfDeletingURLLoaderFactory {
|
||||
const network::ResourceRequest& request,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
|
||||
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> proxy_factory,
|
||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory,
|
||||
ProtocolType type,
|
||||
gin::Arguments* args);
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 15,0,0,20210712
|
||||
PRODUCTVERSION 15,0,0,20210712
|
||||
FILEVERSION 15,0,0,1
|
||||
PRODUCTVERSION 15,0,0,1
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
#define SHELL_BROWSER_UI_MESSAGE_BOX_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
#include "ui/gfx/image/image_skia.h"
|
||||
|
||||
namespace electron {
|
||||
@@ -24,12 +24,11 @@ enum class MessageBoxType {
|
||||
kQuestion,
|
||||
};
|
||||
|
||||
using DialogResult = std::pair<int, bool>;
|
||||
|
||||
struct MessageBoxSettings {
|
||||
electron::NativeWindow* parent_window = nullptr;
|
||||
MessageBoxType type = electron::MessageBoxType::kNone;
|
||||
std::vector<std::string> buttons;
|
||||
absl::optional<int> id;
|
||||
int default_id;
|
||||
int cancel_id;
|
||||
bool no_link = false;
|
||||
@@ -53,6 +52,8 @@ typedef base::OnceCallback<void(int code, bool checkbox_checked)>
|
||||
void ShowMessageBox(const MessageBoxSettings& settings,
|
||||
MessageBoxCallback callback);
|
||||
|
||||
void CloseMessageBox(int id);
|
||||
|
||||
// Like ShowMessageBox with simplest settings, but safe to call at very early
|
||||
// stage of application.
|
||||
void ShowErrorBox(const std::u16string& title, const std::u16string& content);
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/ui/gtk_util.h"
|
||||
#include "shell/browser/ui/message_box.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/native_window_observer.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/gtk_util.h"
|
||||
#include "shell/browser/unresponsive_suppressor.h"
|
||||
#include "ui/base/glib/glib_signal.h"
|
||||
#include "ui/gfx/image/image_skia.h"
|
||||
@@ -38,10 +42,17 @@ MessageBoxSettings::~MessageBoxSettings() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
// <ID, messageBox> map
|
||||
std::map<int, GtkWidget*>& GetDialogsMap() {
|
||||
static base::NoDestructor<std::map<int, GtkWidget*>> dialogs;
|
||||
return *dialogs;
|
||||
}
|
||||
|
||||
class GtkMessageBox : public NativeWindowObserver {
|
||||
public:
|
||||
explicit GtkMessageBox(const MessageBoxSettings& settings)
|
||||
: cancel_id_(settings.cancel_id),
|
||||
: id_(settings.id),
|
||||
cancel_id_(settings.cancel_id),
|
||||
parent_(static_cast<NativeWindow*>(settings.parent_window)) {
|
||||
// Create dialog.
|
||||
dialog_ =
|
||||
@@ -50,6 +61,8 @@ class GtkMessageBox : public NativeWindowObserver {
|
||||
GetMessageType(settings.type), // type
|
||||
GTK_BUTTONS_NONE, // no buttons
|
||||
"%s", settings.message.c_str());
|
||||
if (id_)
|
||||
GetDialogsMap()[*id_] = dialog_;
|
||||
if (!settings.detail.empty())
|
||||
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog_),
|
||||
"%s", settings.detail.c_str());
|
||||
@@ -183,6 +196,9 @@ class GtkMessageBox : public NativeWindowObserver {
|
||||
private:
|
||||
electron::UnresponsiveSuppressor unresponsive_suppressor_;
|
||||
|
||||
// The id of the dialog.
|
||||
absl::optional<int> id_;
|
||||
|
||||
// The id to return when the dialog is closed without pressing buttons.
|
||||
int cancel_id_ = 0;
|
||||
|
||||
@@ -196,6 +212,8 @@ class GtkMessageBox : public NativeWindowObserver {
|
||||
};
|
||||
|
||||
void GtkMessageBox::OnResponseDialog(GtkWidget* widget, int response) {
|
||||
if (id_)
|
||||
GetDialogsMap().erase(*id_);
|
||||
gtk_widget_hide(dialog_);
|
||||
|
||||
if (response < 0)
|
||||
@@ -217,9 +235,20 @@ int ShowMessageBoxSync(const MessageBoxSettings& settings) {
|
||||
|
||||
void ShowMessageBox(const MessageBoxSettings& settings,
|
||||
MessageBoxCallback callback) {
|
||||
if (settings.id && base::Contains(GetDialogsMap(), *settings.id))
|
||||
CloseMessageBox(*settings.id);
|
||||
(new GtkMessageBox(settings))->RunAsynchronous(std::move(callback));
|
||||
}
|
||||
|
||||
void CloseMessageBox(int id) {
|
||||
auto it = GetDialogsMap().find(id);
|
||||
if (it == GetDialogsMap().end()) {
|
||||
LOG(ERROR) << "CloseMessageBox called with nonexistent ID";
|
||||
return;
|
||||
}
|
||||
gtk_window_close(GTK_WINDOW(it->second));
|
||||
}
|
||||
|
||||
void ShowErrorBox(const std::u16string& title, const std::u16string& content) {
|
||||
if (Browser::Get()->is_ready()) {
|
||||
electron::MessageBoxSettings settings;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "shell/browser/ui/message_box.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -11,8 +12,10 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "skia/ext/skia_utils_mac.h"
|
||||
@@ -26,6 +29,12 @@ MessageBoxSettings::~MessageBoxSettings() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
// <ID, messageBox> map
|
||||
std::map<int, NSAlert*>& GetDialogsMap() {
|
||||
static base::NoDestructor<std::map<int, NSAlert*>> dialogs;
|
||||
return *dialogs;
|
||||
}
|
||||
|
||||
NSAlert* CreateNSAlert(const MessageBoxSettings& settings) {
|
||||
// Ignore the title; it's the window title on other platforms and ignorable.
|
||||
NSAlert* alert = [[NSAlert alloc] init];
|
||||
@@ -128,6 +137,12 @@ void ShowMessageBox(const MessageBoxSettings& settings,
|
||||
int ret = [[alert autorelease] runModal];
|
||||
std::move(callback).Run(ret, alert.suppressionButton.state == NSOnState);
|
||||
} else {
|
||||
if (settings.id) {
|
||||
if (base::Contains(GetDialogsMap(), *settings.id))
|
||||
CloseMessageBox(*settings.id);
|
||||
GetDialogsMap()[*settings.id] = alert;
|
||||
}
|
||||
|
||||
NSWindow* window =
|
||||
settings.parent_window
|
||||
? settings.parent_window->GetNativeWindow().GetNativeNSWindow()
|
||||
@@ -136,9 +151,19 @@ void ShowMessageBox(const MessageBoxSettings& settings,
|
||||
// Duplicate the callback object here since c is a reference and gcd would
|
||||
// only store the pointer, by duplication we can force gcd to store a copy.
|
||||
__block MessageBoxCallback callback_ = std::move(callback);
|
||||
__block absl::optional<int> id = std::move(settings.id);
|
||||
__block int cancel_id = settings.cancel_id;
|
||||
|
||||
[alert beginSheetModalForWindow:window
|
||||
completionHandler:^(NSModalResponse response) {
|
||||
if (id)
|
||||
GetDialogsMap().erase(*id);
|
||||
// When the alert is cancelled programmatically, the
|
||||
// response would be something like -1000. This currently
|
||||
// only happens when users call CloseMessageBox API, and we
|
||||
// should return cancelId as result.
|
||||
if (response < 0)
|
||||
response = cancel_id;
|
||||
std::move(callback_).Run(
|
||||
response, alert.suppressionButton.state == NSOnState);
|
||||
[alert release];
|
||||
@@ -146,6 +171,15 @@ void ShowMessageBox(const MessageBoxSettings& settings,
|
||||
}
|
||||
}
|
||||
|
||||
void CloseMessageBox(int id) {
|
||||
auto it = GetDialogsMap().find(id);
|
||||
if (it == GetDialogsMap().end()) {
|
||||
LOG(ERROR) << "CloseMessageBox called with nonexistent ID";
|
||||
return;
|
||||
}
|
||||
[NSApp endSheet:it->second.window];
|
||||
}
|
||||
|
||||
void ShowErrorBox(const std::u16string& title, const std::u16string& content) {
|
||||
NSAlert* alert = [[NSAlert alloc] init];
|
||||
[alert setMessageText:base::SysUTF16ToNSString(title)];
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/task/post_task.h"
|
||||
#include "base/win/scoped_gdi_object.h"
|
||||
#include "shell/browser/browser.h"
|
||||
@@ -30,6 +33,41 @@ MessageBoxSettings::~MessageBoxSettings() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
struct DialogResult {
|
||||
int button_id;
|
||||
bool verification_flag_checked;
|
||||
};
|
||||
|
||||
// <ID, messageBox> map.
|
||||
//
|
||||
// Note that the HWND is stored in a unique_ptr, because the pointer of HWND
|
||||
// will be passed between threads and we need to ensure the memory of HWND is
|
||||
// not changed while dialogs map is modified.
|
||||
std::map<int, std::unique_ptr<HWND>>& GetDialogsMap() {
|
||||
static base::NoDestructor<std::map<int, std::unique_ptr<HWND>>> dialogs;
|
||||
return *dialogs;
|
||||
}
|
||||
|
||||
// Speical HWND used by the dialogs map.
|
||||
//
|
||||
// - ID is used but window has not been created yet.
|
||||
const HWND kHwndReserve = reinterpret_cast<HWND>(-1);
|
||||
// - Notification to cancel message box.
|
||||
const HWND kHwndCancel = reinterpret_cast<HWND>(-2);
|
||||
|
||||
// Lock used for modifying HWND between threads.
|
||||
//
|
||||
// Note that there might be multiple dialogs being opened at the same time, but
|
||||
// we only use one lock for them all, because each dialog is independent from
|
||||
// each other and there is no need to use different lock for each one.
|
||||
// Also note that the |GetDialogsMap| is only used in the main thread, what is
|
||||
// shared between threads is the memory of HWND, so there is no need to use lock
|
||||
// when accessing dialogs map.
|
||||
base::Lock& GetHWNDLock() {
|
||||
static base::NoDestructor<base::Lock> lock;
|
||||
return *lock;
|
||||
}
|
||||
|
||||
// Small command ID values are already taken by Windows, we have to start from
|
||||
// a large number to avoid conflicts with Windows.
|
||||
const int kIDStart = 100;
|
||||
@@ -76,6 +114,31 @@ void MapToCommonID(const std::vector<std::wstring>& buttons,
|
||||
}
|
||||
}
|
||||
|
||||
// Callback of the task dialog. The TaskDialogIndirect API does not provide the
|
||||
// HWND of the dialog, and we have to listen to the TDN_CREATED message to get
|
||||
// it.
|
||||
// Note that this callback runs in dialog thread instead of main thread, so it
|
||||
// is possible for CloseMessageBox to be called before or all after the dialog
|
||||
// window is created.
|
||||
HRESULT CALLBACK
|
||||
TaskDialogCallback(HWND hwnd, UINT msg, WPARAM, LPARAM, LONG_PTR data) {
|
||||
if (msg == TDN_CREATED) {
|
||||
HWND* target = reinterpret_cast<HWND*>(data);
|
||||
// Lock since CloseMessageBox might be called.
|
||||
base::AutoLock lock(GetHWNDLock());
|
||||
if (*target == kHwndCancel) {
|
||||
// The dialog is cancelled before it is created, close it directly.
|
||||
::PostMessage(hwnd, WM_CLOSE, 0, 0);
|
||||
} else if (*target == kHwndReserve) {
|
||||
// Otherwise save the hwnd.
|
||||
*target = hwnd;
|
||||
} else {
|
||||
NOTREACHED();
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
DialogResult ShowTaskDialogWstr(NativeWindow* parent,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::wstring>& buttons,
|
||||
@@ -87,7 +150,8 @@ DialogResult ShowTaskDialogWstr(NativeWindow* parent,
|
||||
const std::u16string& detail,
|
||||
const std::u16string& checkbox_label,
|
||||
bool checkbox_checked,
|
||||
const gfx::ImageSkia& icon) {
|
||||
const gfx::ImageSkia& icon,
|
||||
HWND* hwnd) {
|
||||
TASKDIALOG_FLAGS flags =
|
||||
TDF_SIZE_TO_CONTENT | // Show all content.
|
||||
TDF_ALLOW_DIALOG_CANCELLATION; // Allow canceling the dialog.
|
||||
@@ -169,12 +233,17 @@ DialogResult ShowTaskDialogWstr(NativeWindow* parent,
|
||||
config.dwFlags |= TDF_USE_COMMAND_LINKS; // custom buttons as links.
|
||||
}
|
||||
|
||||
int button_id;
|
||||
// Pass a callback to receive the HWND of the message box.
|
||||
if (hwnd) {
|
||||
config.pfCallback = &TaskDialogCallback;
|
||||
config.lpCallbackData = reinterpret_cast<LONG_PTR>(hwnd);
|
||||
}
|
||||
|
||||
int id = 0;
|
||||
BOOL verificationFlagChecked = FALSE;
|
||||
TaskDialogIndirect(&config, &id, nullptr, &verificationFlagChecked);
|
||||
BOOL verification_flag_checked = FALSE;
|
||||
TaskDialogIndirect(&config, &id, nullptr, &verification_flag_checked);
|
||||
|
||||
int button_id;
|
||||
if (id_map.find(id) != id_map.end()) // common button.
|
||||
button_id = id_map[id];
|
||||
else if (id >= kIDStart) // custom button.
|
||||
@@ -182,10 +251,11 @@ DialogResult ShowTaskDialogWstr(NativeWindow* parent,
|
||||
else
|
||||
button_id = cancel_id;
|
||||
|
||||
return std::make_pair(button_id, verificationFlagChecked);
|
||||
return {button_id, static_cast<bool>(verification_flag_checked)};
|
||||
}
|
||||
|
||||
DialogResult ShowTaskDialogUTF8(const MessageBoxSettings& settings) {
|
||||
DialogResult ShowTaskDialogUTF8(const MessageBoxSettings& settings,
|
||||
HWND* hwnd) {
|
||||
std::vector<std::wstring> buttons;
|
||||
for (const auto& button : settings.buttons)
|
||||
buttons.push_back(base::UTF8ToWide(button));
|
||||
@@ -199,31 +269,67 @@ DialogResult ShowTaskDialogUTF8(const MessageBoxSettings& settings) {
|
||||
return ShowTaskDialogWstr(
|
||||
settings.parent_window, settings.type, buttons, settings.default_id,
|
||||
settings.cancel_id, settings.no_link, title, message, detail,
|
||||
checkbox_label, settings.checkbox_checked, settings.icon);
|
||||
checkbox_label, settings.checkbox_checked, settings.icon, hwnd);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int ShowMessageBoxSync(const MessageBoxSettings& settings) {
|
||||
electron::UnresponsiveSuppressor suppressor;
|
||||
DialogResult result = ShowTaskDialogUTF8(settings);
|
||||
return result.first;
|
||||
DialogResult result = ShowTaskDialogUTF8(settings, nullptr);
|
||||
return result.button_id;
|
||||
}
|
||||
|
||||
void ShowMessageBox(const MessageBoxSettings& settings,
|
||||
MessageBoxCallback callback) {
|
||||
dialog_thread::Run(base::BindOnce(&ShowTaskDialogUTF8, settings),
|
||||
base::BindOnce(
|
||||
[](MessageBoxCallback callback, DialogResult result) {
|
||||
std::move(callback).Run(result.first, result.second);
|
||||
},
|
||||
std::move(callback)));
|
||||
// The dialog is created in a new thread so we don't know its HWND yet, put
|
||||
// kHwndReserve in the dialogs map for now.
|
||||
HWND* hwnd = nullptr;
|
||||
if (settings.id) {
|
||||
if (base::Contains(GetDialogsMap(), *settings.id))
|
||||
CloseMessageBox(*settings.id);
|
||||
auto it = GetDialogsMap().emplace(*settings.id,
|
||||
std::make_unique<HWND>(kHwndReserve));
|
||||
hwnd = it.first->second.get();
|
||||
}
|
||||
|
||||
dialog_thread::Run(
|
||||
base::BindOnce(&ShowTaskDialogUTF8, settings, base::Unretained(hwnd)),
|
||||
base::BindOnce(
|
||||
[](MessageBoxCallback callback, absl::optional<int> id,
|
||||
DialogResult result) {
|
||||
if (id)
|
||||
GetDialogsMap().erase(*id);
|
||||
std::move(callback).Run(result.button_id,
|
||||
result.verification_flag_checked);
|
||||
},
|
||||
std::move(callback), settings.id));
|
||||
}
|
||||
|
||||
void CloseMessageBox(int id) {
|
||||
auto it = GetDialogsMap().find(id);
|
||||
if (it == GetDialogsMap().end()) {
|
||||
LOG(ERROR) << "CloseMessageBox called with nonexistent ID";
|
||||
return;
|
||||
}
|
||||
HWND* hwnd = it->second.get();
|
||||
// Lock since the TaskDialogCallback might be saving the dialog's HWND.
|
||||
base::AutoLock lock(GetHWNDLock());
|
||||
DCHECK(*hwnd != kHwndCancel);
|
||||
if (*hwnd == kHwndReserve) {
|
||||
// If the dialog window has not been created yet, tell it to cancel.
|
||||
*hwnd = kHwndCancel;
|
||||
} else {
|
||||
// Otherwise send a message to close it.
|
||||
::PostMessage(*hwnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowErrorBox(const std::u16string& title, const std::u16string& content) {
|
||||
electron::UnresponsiveSuppressor suppressor;
|
||||
ShowTaskDialogWstr(nullptr, MessageBoxType::kError, {}, -1, 0, false,
|
||||
u"Error", title, content, u"", false, gfx::ImageSkia());
|
||||
u"Error", title, content, u"", false, gfx::ImageSkia(),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
weight:NSFontWeightRegular]
|
||||
};
|
||||
[attributed_title
|
||||
setAttributes:attributes
|
||||
addAttributes:attributes
|
||||
range:NSMakeRange(0, [attributed_title length])];
|
||||
}
|
||||
} else if ([font_type isEqualToString:@"monospacedDigit"]) {
|
||||
@@ -132,7 +132,7 @@
|
||||
weight:NSFontWeightRegular]
|
||||
};
|
||||
[attributed_title
|
||||
setAttributes:attributes
|
||||
addAttributes:attributes
|
||||
range:NSMakeRange(0, [attributed_title length])];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,27 +13,21 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
base::NoDestructor<std::string> g_overridden_application_name;
|
||||
base::NoDestructor<std::string> g_overridden_application_version;
|
||||
|
||||
} // namespace
|
||||
|
||||
// name
|
||||
void OverrideApplicationName(const std::string& name) {
|
||||
*g_overridden_application_name = name;
|
||||
}
|
||||
std::string GetOverriddenApplicationName() {
|
||||
return *g_overridden_application_name;
|
||||
std::string& OverriddenApplicationName() {
|
||||
static base::NoDestructor<std::string> overridden_application_name;
|
||||
return *overridden_application_name;
|
||||
}
|
||||
|
||||
// version
|
||||
void OverrideApplicationVersion(const std::string& version) {
|
||||
*g_overridden_application_version = version;
|
||||
std::string& OverriddenApplicationVersion() {
|
||||
static base::NoDestructor<std::string> overridden_application_version;
|
||||
return *overridden_application_version;
|
||||
}
|
||||
std::string GetOverriddenApplicationVersion() {
|
||||
return *g_overridden_application_version;
|
||||
|
||||
std::string GetPossiblyOverriddenApplicationName() {
|
||||
std::string ret = OverriddenApplicationName();
|
||||
if (!ret.empty())
|
||||
return ret;
|
||||
return GetApplicationName();
|
||||
}
|
||||
|
||||
std::string GetApplicationUserAgent() {
|
||||
|
||||
@@ -13,11 +13,10 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
void OverrideApplicationName(const std::string& name);
|
||||
std::string GetOverriddenApplicationName();
|
||||
std::string& OverriddenApplicationName();
|
||||
std::string& OverriddenApplicationVersion();
|
||||
|
||||
void OverrideApplicationVersion(const std::string& version);
|
||||
std::string GetOverriddenApplicationVersion();
|
||||
std::string GetPossiblyOverriddenApplicationName();
|
||||
|
||||
std::string GetApplicationName();
|
||||
std::string GetApplicationVersion();
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace electron {
|
||||
|
||||
std::string GetApplicationName() {
|
||||
// attempt #1: the string set in app.setName()
|
||||
std::string ret = GetOverriddenApplicationName();
|
||||
std::string ret = OverriddenApplicationName();
|
||||
|
||||
// attempt #2: the 'Name' entry from .desktop file's [Desktop] section
|
||||
if (ret.empty()) {
|
||||
@@ -64,7 +64,7 @@ std::string GetApplicationVersion() {
|
||||
|
||||
// try to use the string set in app.setVersion()
|
||||
if (ret.empty())
|
||||
ret = GetOverriddenApplicationVersion();
|
||||
ret = OverriddenApplicationVersion();
|
||||
|
||||
// no known version number; return some safe fallback
|
||||
if (ret.empty()) {
|
||||
|
||||
@@ -19,6 +19,21 @@
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "third_party/crashpad/crashpad/client/annotation.h"
|
||||
|
||||
#include "gin/wrappable.h"
|
||||
#include "shell/browser/api/electron_api_browser_view.h"
|
||||
#include "shell/browser/api/electron_api_cookies.h"
|
||||
#include "shell/browser/api/electron_api_desktop_capturer.h"
|
||||
#include "shell/browser/api/electron_api_menu.h"
|
||||
#include "shell/browser/api/electron_api_net_log.h"
|
||||
#include "shell/browser/api/electron_api_notification.h"
|
||||
#include "shell/browser/api/electron_api_power_monitor.h"
|
||||
#include "shell/browser/api/electron_api_protocol.h"
|
||||
#include "shell/browser/api/electron_api_service_worker_context.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/api/electron_api_web_frame_main.h"
|
||||
#include "shell/browser/api/electron_api_web_request.h"
|
||||
#include "shell/common/api/electron_api_native_image.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace crash_keys {
|
||||
@@ -155,6 +170,48 @@ void SetPlatformCrashKey() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetCrashKeyForGinWrappable(gin::WrapperInfo* info) {
|
||||
std::string crash_location;
|
||||
|
||||
// Adds a breadcrumb for crashes within gin::WrappableBase::SecondWeakCallback
|
||||
// (see patch: add_gin_wrappable_crash_key.patch)
|
||||
// Compares the pointers for the kWrapperInfo within SecondWeakCallback
|
||||
// with the wrapper info from classes that use gin::Wrappable and
|
||||
// could potentially retain a reference after deletion.
|
||||
if (info == &electron::api::WebContents::kWrapperInfo)
|
||||
crash_location = "WebContents";
|
||||
else if (info == &electron::api::BrowserView::kWrapperInfo)
|
||||
crash_location = "BrowserView";
|
||||
else if (info == &electron::api::Notification::kWrapperInfo)
|
||||
crash_location = "Notification";
|
||||
else if (info == &electron::api::Cookies::kWrapperInfo)
|
||||
crash_location = "Cookies";
|
||||
else if (info == &electron::api::DesktopCapturer::kWrapperInfo)
|
||||
crash_location = "DesktopCapturer";
|
||||
else if (info == &electron::api::NetLog::kWrapperInfo)
|
||||
crash_location = "NetLog";
|
||||
else if (info == &electron::api::NativeImage::kWrapperInfo)
|
||||
crash_location = "NativeImage";
|
||||
else if (info == &electron::api::Menu::kWrapperInfo)
|
||||
crash_location = "Menu";
|
||||
else if (info == &electron::api::PowerMonitor::kWrapperInfo)
|
||||
crash_location = "PowerMonitor";
|
||||
else if (info == &electron::api::Protocol::kWrapperInfo)
|
||||
crash_location = "Protocol";
|
||||
else if (info == &electron::api::ServiceWorkerContext::kWrapperInfo)
|
||||
crash_location = "ServiceWorkerContext";
|
||||
else if (info == &electron::api::WebFrameMain::kWrapperInfo)
|
||||
crash_location = "WebFrameMain";
|
||||
else if (info == &electron::api::WebRequest::kWrapperInfo)
|
||||
crash_location = "WebRequest";
|
||||
else
|
||||
crash_location =
|
||||
"Deleted kWrapperInfo does not match listed component. Please review "
|
||||
"listed crash keys.";
|
||||
|
||||
SetCrashKey("gin-wrappable-fatal.location", crash_location);
|
||||
}
|
||||
|
||||
} // namespace crash_keys
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "gin/wrappable.h"
|
||||
|
||||
namespace base {
|
||||
class CommandLine;
|
||||
}
|
||||
@@ -22,6 +24,7 @@ void GetCrashKeys(std::map<std::string, std::string>* keys);
|
||||
|
||||
void SetCrashKeysFromCommandLine(const base::CommandLine& command_line);
|
||||
void SetPlatformCrashKey();
|
||||
void SetCrashKeyForGinWrappable(gin::WrapperInfo* info);
|
||||
|
||||
} // namespace crash_keys
|
||||
|
||||
|
||||
@@ -144,10 +144,44 @@ struct Converter<blink::WebInputEvent::Modifiers> {
|
||||
*out = blink::WebInputEvent::Modifiers::kIsLeft;
|
||||
else if (modifier == "right")
|
||||
*out = blink::WebInputEvent::Modifiers::kIsRight;
|
||||
// TODO(nornagon): the rest of the modifiers
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::string> ModifiersToArray(int modifiers) {
|
||||
using Modifiers = blink::WebInputEvent::Modifiers;
|
||||
std::vector<std::string> modifier_strings;
|
||||
if (modifiers & Modifiers::kShiftKey)
|
||||
modifier_strings.push_back("shift");
|
||||
if (modifiers & Modifiers::kControlKey)
|
||||
modifier_strings.push_back("control");
|
||||
if (modifiers & Modifiers::kAltKey)
|
||||
modifier_strings.push_back("alt");
|
||||
if (modifiers & Modifiers::kMetaKey)
|
||||
modifier_strings.push_back("meta");
|
||||
if (modifiers & Modifiers::kIsKeyPad)
|
||||
modifier_strings.push_back("iskeypad");
|
||||
if (modifiers & Modifiers::kIsAutoRepeat)
|
||||
modifier_strings.push_back("isautorepeat");
|
||||
if (modifiers & Modifiers::kLeftButtonDown)
|
||||
modifier_strings.push_back("leftbuttondown");
|
||||
if (modifiers & Modifiers::kMiddleButtonDown)
|
||||
modifier_strings.push_back("middlebuttondown");
|
||||
if (modifiers & Modifiers::kRightButtonDown)
|
||||
modifier_strings.push_back("rightbuttondown");
|
||||
if (modifiers & Modifiers::kCapsLockOn)
|
||||
modifier_strings.push_back("capslock");
|
||||
if (modifiers & Modifiers::kNumLockOn)
|
||||
modifier_strings.push_back("numlock");
|
||||
if (modifiers & Modifiers::kIsLeft)
|
||||
modifier_strings.push_back("left");
|
||||
if (modifiers & Modifiers::kIsRight)
|
||||
modifier_strings.push_back("right");
|
||||
// TODO(nornagon): the rest of the modifiers
|
||||
return modifier_strings;
|
||||
}
|
||||
|
||||
blink::WebInputEvent::Type GetWebInputEventType(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val) {
|
||||
blink::WebInputEvent::Type type = blink::WebInputEvent::Type::kUndefined;
|
||||
@@ -219,6 +253,51 @@ bool Converter<blink::WebKeyboardEvent>::FromV8(v8::Isolate* isolate,
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetKeyLocationCode(const blink::WebInputEvent& key) {
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/events/keyboard_event.h;l=46;drc=1ff6437e65b183e673b7b4f25060b74dc2ba5c37
|
||||
enum KeyLocationCode {
|
||||
kDomKeyLocationStandard = 0x00,
|
||||
kDomKeyLocationLeft = 0x01,
|
||||
kDomKeyLocationRight = 0x02,
|
||||
kDomKeyLocationNumpad = 0x03
|
||||
};
|
||||
using Modifiers = blink::WebInputEvent::Modifiers;
|
||||
if (key.GetModifiers() & Modifiers::kIsKeyPad)
|
||||
return kDomKeyLocationNumpad;
|
||||
if (key.GetModifiers() & Modifiers::kIsLeft)
|
||||
return kDomKeyLocationLeft;
|
||||
if (key.GetModifiers() & Modifiers::kIsRight)
|
||||
return kDomKeyLocationRight;
|
||||
return kDomKeyLocationStandard;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Converter<blink::WebKeyboardEvent>::ToV8(
|
||||
v8::Isolate* isolate,
|
||||
const blink::WebKeyboardEvent& in) {
|
||||
gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty(isolate);
|
||||
|
||||
if (in.GetType() == blink::WebInputEvent::Type::kRawKeyDown)
|
||||
dict.Set("type", "keyDown");
|
||||
else if (in.GetType() == blink::WebInputEvent::Type::kKeyUp)
|
||||
dict.Set("type", "keyUp");
|
||||
dict.Set("key", ui::KeycodeConverter::DomKeyToKeyString(in.dom_key));
|
||||
dict.Set("code", ui::KeycodeConverter::DomCodeToCodeString(
|
||||
static_cast<ui::DomCode>(in.dom_code)));
|
||||
|
||||
using Modifiers = blink::WebInputEvent::Modifiers;
|
||||
dict.Set("isAutoRepeat", (in.GetModifiers() & Modifiers::kIsAutoRepeat) != 0);
|
||||
dict.Set("isComposing", (in.GetModifiers() & Modifiers::kIsComposing) != 0);
|
||||
dict.Set("shift", (in.GetModifiers() & Modifiers::kShiftKey) != 0);
|
||||
dict.Set("control", (in.GetModifiers() & Modifiers::kControlKey) != 0);
|
||||
dict.Set("alt", (in.GetModifiers() & Modifiers::kAltKey) != 0);
|
||||
dict.Set("meta", (in.GetModifiers() & Modifiers::kMetaKey) != 0);
|
||||
dict.Set("location", GetKeyLocationCode(in));
|
||||
dict.Set("_modifiers", in.GetModifiers());
|
||||
dict.Set("modifiers", ModifiersToArray(in.GetModifiers()));
|
||||
|
||||
return dict.GetHandle();
|
||||
}
|
||||
|
||||
bool Converter<blink::WebMouseEvent>::FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
blink::WebMouseEvent* out) {
|
||||
|
||||
@@ -36,6 +36,8 @@ struct Converter<blink::WebKeyboardEvent> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
blink::WebKeyboardEvent* out);
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const blink::WebKeyboardEvent& in);
|
||||
};
|
||||
|
||||
template <>
|
||||
|
||||
@@ -303,25 +303,7 @@ bool Converter<content::NativeWebKeyboardEvent>::FromV8(
|
||||
v8::Local<v8::Value> Converter<content::NativeWebKeyboardEvent>::ToV8(
|
||||
v8::Isolate* isolate,
|
||||
const content::NativeWebKeyboardEvent& in) {
|
||||
gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty(isolate);
|
||||
|
||||
if (in.GetType() == blink::WebInputEvent::Type::kRawKeyDown)
|
||||
dict.Set("type", "keyDown");
|
||||
else if (in.GetType() == blink::WebInputEvent::Type::kKeyUp)
|
||||
dict.Set("type", "keyUp");
|
||||
dict.Set("key", ui::KeycodeConverter::DomKeyToKeyString(in.dom_key));
|
||||
dict.Set("code", ui::KeycodeConverter::DomCodeToCodeString(
|
||||
static_cast<ui::DomCode>(in.dom_code)));
|
||||
|
||||
using Modifiers = blink::WebInputEvent::Modifiers;
|
||||
dict.Set("isAutoRepeat", (in.GetModifiers() & Modifiers::kIsAutoRepeat) != 0);
|
||||
dict.Set("isComposing", (in.GetModifiers() & Modifiers::kIsComposing) != 0);
|
||||
dict.Set("shift", (in.GetModifiers() & Modifiers::kShiftKey) != 0);
|
||||
dict.Set("control", (in.GetModifiers() & Modifiers::kControlKey) != 0);
|
||||
dict.Set("alt", (in.GetModifiers() & Modifiers::kAltKey) != 0);
|
||||
dict.Set("meta", (in.GetModifiers() & Modifiers::kMetaKey) != 0);
|
||||
|
||||
return dict.GetHandle();
|
||||
return ConvertToV8(isolate, static_cast<blink::WebKeyboardEvent>(in));
|
||||
}
|
||||
|
||||
} // namespace gin
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include "shell/common/gin_converters/message_box_converter.h"
|
||||
|
||||
#include "gin/dictionary.h"
|
||||
#include "shell/common/gin_converters/image_converter.h"
|
||||
#include "shell/common/gin_converters/native_window_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
|
||||
namespace gin {
|
||||
|
||||
@@ -14,7 +14,7 @@ bool Converter<electron::MessageBoxSettings>::FromV8(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
electron::MessageBoxSettings* out) {
|
||||
gin::Dictionary dict(nullptr);
|
||||
gin_helper::Dictionary dict;
|
||||
int type = 0;
|
||||
if (!ConvertFromV8(isolate, val, &dict))
|
||||
return false;
|
||||
@@ -22,6 +22,7 @@ bool Converter<electron::MessageBoxSettings>::FromV8(
|
||||
dict.Get("messageBoxType", &type);
|
||||
out->type = static_cast<electron::MessageBoxType>(type);
|
||||
dict.Get("buttons", &out->buttons);
|
||||
dict.GetOptional("id", &out->id);
|
||||
dict.Get("defaultId", &out->default_id);
|
||||
dict.Get("cancelId", &out->cancel_id);
|
||||
dict.Get("title", &out->title);
|
||||
|
||||
@@ -924,6 +924,10 @@ describe('app module', () => {
|
||||
expect(app.getPath('recent')).to.equal('C:\\fake-path');
|
||||
});
|
||||
}
|
||||
|
||||
it('uses the app name in getPath(userData)', () => {
|
||||
expect(app.getPath('userData')).to.include(app.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setPath(name, path)', () => {
|
||||
|
||||
@@ -4204,7 +4204,8 @@ describe('BrowserWindow module', () => {
|
||||
expect(w.isFullScreen()).to.be.false('isFullScreen');
|
||||
});
|
||||
|
||||
it('multiple windows inherit correct fullscreen state', async () => {
|
||||
// FIXME: https://github.com/electron/electron/issues/30140
|
||||
xit('multiple windows inherit correct fullscreen state', async () => {
|
||||
const w = new BrowserWindow();
|
||||
const enterFullScreen = emittedOnce(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
|
||||
@@ -138,224 +138,455 @@ function waitForNewFileInDir (dir: string): Promise<string[]> {
|
||||
|
||||
// TODO(nornagon): Fix tests on linux/arm.
|
||||
ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_TESTS)('crashReporter module', function () {
|
||||
describe('should send minidump', () => {
|
||||
it('when renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
it('when sandboxed renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
// TODO(nornagon): Minidump generation in main/node process on Linux/Arm is
|
||||
// broken (//components/crash prints "Failed to generate minidump"). Figure
|
||||
// out why.
|
||||
ifit(!isLinuxOnArm)('when main process crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('browser', crash);
|
||||
expect(crash.mainProcessSpecific).to.equal('mps');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('when a node process crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('node', port);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('node', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
describe('with guid', () => {
|
||||
for (const processType of ['main', 'renderer', 'sandboxed-renderer']) {
|
||||
it(`when ${processType} crashes`, async () => {
|
||||
for (const withLinuxCrashpad of (process.platform === 'linux' ? [false, true] : [false])) {
|
||||
const crashpadExtraArgs = withLinuxCrashpad ? ['--enable-crashpad'] : [];
|
||||
describe(withLinuxCrashpad ? '(with crashpad)' : '', () => {
|
||||
describe('should send minidump', () => {
|
||||
it('when renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp(processType, port);
|
||||
runCrashApp('renderer', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.guid).to.be.a('string');
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
});
|
||||
}
|
||||
|
||||
it('is a consistent id', async () => {
|
||||
let crash1Guid;
|
||||
let crash2Guid;
|
||||
{
|
||||
it('when sandboxed renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port);
|
||||
runCrashApp('sandboxed-renderer', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
crash1Guid = crash.guid;
|
||||
}
|
||||
{
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
// TODO(nornagon): Minidump generation in main/node process on Linux/Arm is
|
||||
// broken (//components/crash prints "Failed to generate minidump"). Figure
|
||||
// out why.
|
||||
ifit(!isLinuxOnArm)('when main process crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port);
|
||||
runCrashApp('main', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
crash2Guid = crash.guid;
|
||||
}
|
||||
expect(crash2Guid).to.equal(crash1Guid);
|
||||
});
|
||||
});
|
||||
checkCrash('browser', crash);
|
||||
expect(crash.mainProcessSpecific).to.equal('mps');
|
||||
});
|
||||
|
||||
describe('with extra parameters', () => {
|
||||
it('when renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port, ['--set-extra-parameters-in-renderer']);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.equal('rs');
|
||||
expect(crash.addedThenRemoved).to.be.undefined();
|
||||
});
|
||||
ifit(!isLinuxOnArm)('when a node process crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('node', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('node', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.be.undefined();
|
||||
});
|
||||
|
||||
it('when sandboxed renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port, ['--set-extra-parameters-in-renderer']);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.equal('rs');
|
||||
expect(crash.addedThenRemoved).to.be.undefined();
|
||||
});
|
||||
describe('with guid', () => {
|
||||
for (const processType of ['main', 'renderer', 'sandboxed-renderer']) {
|
||||
it(`when ${processType} crashes`, async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp(processType, port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.guid).to.be.a('string');
|
||||
});
|
||||
}
|
||||
|
||||
it('contains v8 crash keys when a v8 crash occurs', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const { port, waitForCrash } = await startServer();
|
||||
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
it('is a consistent id', async () => {
|
||||
let crash1Guid;
|
||||
let crash2Guid;
|
||||
{
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
crash1Guid = crash.guid;
|
||||
}
|
||||
{
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, crashpadExtraArgs);
|
||||
const crash = await waitForCrash();
|
||||
crash2Guid = crash.guid;
|
||||
}
|
||||
expect(crash2Guid).to.equal(crash1Guid);
|
||||
});
|
||||
}, [port]);
|
||||
|
||||
remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process._linkedBinding(\'electron_common_v8_util\').triggerFatalErrorForTesting()');
|
||||
});
|
||||
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.prod).to.equal('Electron');
|
||||
expect(crash._productName).to.equal('electron-test-remote-control');
|
||||
expect(crash.process_type).to.equal('renderer');
|
||||
expect(crash['electron.v8-fatal.location']).to.equal('v8::Context::New()');
|
||||
expect(crash['electron.v8-fatal.message']).to.equal('Circular extension dependency');
|
||||
describe('with extra parameters', () => {
|
||||
it('when renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port, ['--set-extra-parameters-in-renderer', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.equal('rs');
|
||||
expect(crash.addedThenRemoved).to.be.undefined();
|
||||
});
|
||||
|
||||
it('when sandboxed renderer crashes', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port, ['--set-extra-parameters-in-renderer', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
checkCrash('renderer', crash);
|
||||
expect(crash.mainProcessSpecific).to.be.undefined();
|
||||
expect(crash.rendererSpecific).to.equal('rs');
|
||||
expect(crash.addedThenRemoved).to.be.undefined();
|
||||
});
|
||||
|
||||
it('contains v8 crash keys when a v8 crash occurs', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const { port, waitForCrash } = await startServer();
|
||||
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
});
|
||||
}, [port]);
|
||||
|
||||
remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process._linkedBinding(\'electron_common_v8_util\').triggerFatalErrorForTesting()');
|
||||
});
|
||||
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.prod).to.equal('Electron');
|
||||
expect(crash._productName).to.equal('electron-test-remote-control');
|
||||
expect(crash.process_type).to.equal('renderer');
|
||||
expect(crash['electron.v8-fatal.location']).to.equal('v8::Context::New()');
|
||||
expect(crash['electron.v8-fatal.message']).to.equal('Circular extension dependency');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(!isLinuxOnArm)('extra parameter limits', () => {
|
||||
function stitchLongCrashParam (crash: any, paramKey: string) {
|
||||
if (crash[paramKey]) return crash[paramKey];
|
||||
let chunk = 1;
|
||||
let stitched = '';
|
||||
while (crash[`${paramKey}__${chunk}`]) {
|
||||
stitched += crash[`${paramKey}__${chunk}`];
|
||||
chunk++;
|
||||
}
|
||||
return stitched;
|
||||
}
|
||||
ifdescribe(!isLinuxOnArm)('extra parameter limits', () => {
|
||||
function stitchLongCrashParam (crash: any, paramKey: string) {
|
||||
if (crash[paramKey]) return crash[paramKey];
|
||||
let chunk = 1;
|
||||
let stitched = '';
|
||||
while (crash[`${paramKey}__${chunk}`]) {
|
||||
stitched += crash[`${paramKey}__${chunk}`];
|
||||
chunk++;
|
||||
}
|
||||
return stitched;
|
||||
}
|
||||
|
||||
it('should truncate extra values longer than 5 * 4096 characters', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: { longParam: 'a'.repeat(100000) }
|
||||
it('should truncate extra values longer than 5 * 4096 characters', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: { longParam: 'a'.repeat(100000) }
|
||||
});
|
||||
setTimeout(() => process.crash());
|
||||
}, port);
|
||||
const crash = await waitForCrash();
|
||||
expect(stitchLongCrashParam(crash, 'longParam')).to.have.lengthOf(160 * 127 + (withLinuxCrashpad ? 159 : 0), 'crash should have truncated longParam');
|
||||
});
|
||||
setTimeout(() => process.crash());
|
||||
}, port);
|
||||
const crash = await waitForCrash();
|
||||
expect(stitchLongCrashParam(crash, 'longParam')).to.have.lengthOf(160 * 127, 'crash should have truncated longParam');
|
||||
});
|
||||
|
||||
it('should omit extra keys with names longer than the maximum', async () => {
|
||||
const kKeyLengthMax = 39;
|
||||
const { port, waitForCrash } = await startServer();
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
remotely((port: number, kKeyLengthMax: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: {
|
||||
['a'.repeat(kKeyLengthMax + 10)]: 'value',
|
||||
['b'.repeat(kKeyLengthMax)]: 'value',
|
||||
'not-long': 'not-long-value'
|
||||
it('should omit extra keys with names longer than the maximum', async () => {
|
||||
const kKeyLengthMax = 39;
|
||||
const { port, waitForCrash } = await startServer();
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
remotely((port: number, kKeyLengthMax: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true,
|
||||
extra: {
|
||||
['a'.repeat(kKeyLengthMax + 10)]: 'value',
|
||||
['b'.repeat(kKeyLengthMax)]: 'value',
|
||||
'not-long': 'not-long-value'
|
||||
}
|
||||
});
|
||||
require('electron').crashReporter.addExtraParameter('c'.repeat(kKeyLengthMax + 10), 'value');
|
||||
setTimeout(() => process.crash());
|
||||
}, port, kKeyLengthMax);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax + 10));
|
||||
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax));
|
||||
expect(crash).to.have.property('b'.repeat(kKeyLengthMax), 'value');
|
||||
expect(crash).to.have.property('not-long', 'not-long-value');
|
||||
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax + 10));
|
||||
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax));
|
||||
});
|
||||
});
|
||||
|
||||
describe('globalExtra', () => {
|
||||
ifit(!isLinuxOnArm)('should be sent with main process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=globalParam:globalValue', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
|
||||
it('should be sent with renderer process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port, ['--add-global-param=globalParam:globalValue', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
|
||||
it('should be sent with sandboxed renderer process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port, ['--add-global-param=globalParam:globalValue', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('should not be overridden by extra in main process', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=mainProcessSpecific:global', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.mainProcessSpecific).to.equal('global');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('should not be overridden by extra in renderer process', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=rendererSpecific:global', ...crashpadExtraArgs]);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.rendererSpecific).to.equal('global');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(nornagon): also test crashing main / sandboxed renderers.
|
||||
ifit(!isWindowsOnArm)('should not send a minidump when uploadToServer is false', async () => {
|
||||
const { port, waitForCrash, getCrashes } = await startServer();
|
||||
waitForCrash().then(() => expect.fail('expected not to receive a dump'));
|
||||
await runCrashApp('renderer', port, ['--no-upload', ...crashpadExtraArgs]);
|
||||
// wait a sec in case the crash reporter is about to upload a crash
|
||||
await delay(1000);
|
||||
expect(getCrashes()).to.have.length(0);
|
||||
});
|
||||
|
||||
describe('getUploadedReports', () => {
|
||||
it('returns an array of reports', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
|
||||
});
|
||||
const reports = await remotely(() => require('electron').crashReporter.getUploadedReports());
|
||||
expect(reports).to.be.an('array');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(nornagon): re-enable on woa
|
||||
ifdescribe(!isWindowsOnArm)('getLastCrashReport', () => {
|
||||
it('returns the last uploaded report', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const { port, waitForCrash } = await startServer();
|
||||
|
||||
// 0. clear the crash reports directory.
|
||||
const dir = await remotely(() => require('electron').app.getPath('crashDumps'));
|
||||
try {
|
||||
fs.rmdirSync(dir, { recursive: true });
|
||||
fs.mkdirSync(dir);
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
// 1. start the crash reporter.
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
});
|
||||
}, [port]);
|
||||
// 2. generate a crash in the renderer.
|
||||
remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process.crash()');
|
||||
});
|
||||
await waitForCrash();
|
||||
// 3. get the crash from getLastCrashReport.
|
||||
const firstReport = await remotely(() => require('electron').crashReporter.getLastCrashReport());
|
||||
expect(firstReport).to.not.be.null();
|
||||
expect(firstReport.date).to.be.an.instanceOf(Date);
|
||||
expect((+new Date()) - (+firstReport.date)).to.be.lessThan(30000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getParameters', () => {
|
||||
it('returns all of the current parameters', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: 'http://127.0.0.1',
|
||||
extra: { extra1: 'hi' }
|
||||
});
|
||||
});
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).to.have.property('extra1', 'hi');
|
||||
});
|
||||
|
||||
it('reflects added and removed parameters', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
|
||||
require('electron').crashReporter.addExtraParameter('hello', 'world');
|
||||
});
|
||||
{
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).to.have.property('hello', 'world');
|
||||
}
|
||||
|
||||
await remotely(() => { require('electron').crashReporter.removeExtraParameter('hello'); });
|
||||
|
||||
{
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).not.to.have.property('hello');
|
||||
}
|
||||
});
|
||||
require('electron').crashReporter.addExtraParameter('c'.repeat(kKeyLengthMax + 10), 'value');
|
||||
setTimeout(() => process.crash());
|
||||
}, port, kKeyLengthMax);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax + 10));
|
||||
expect(crash).not.to.have.property('a'.repeat(kKeyLengthMax));
|
||||
expect(crash).to.have.property('b'.repeat(kKeyLengthMax), 'value');
|
||||
expect(crash).to.have.property('not-long', 'not-long-value');
|
||||
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax + 10));
|
||||
expect(crash).not.to.have.property('c'.repeat(kKeyLengthMax));
|
||||
});
|
||||
});
|
||||
|
||||
describe('globalExtra', () => {
|
||||
ifit(!isLinuxOnArm)('should be sent with main process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=globalParam:globalValue']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
it('can be called in the renderer', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const rendererParameters = await remotely(async () => {
|
||||
const { crashReporter, BrowserWindow } = require('electron');
|
||||
crashReporter.start({ submitURL: 'http://' });
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
await bw.webContents.executeJavaScript('require(\'electron\').crashReporter.addExtraParameter(\'hello\', \'world\')');
|
||||
return bw.webContents.executeJavaScript('require(\'electron\').crashReporter.getParameters()');
|
||||
});
|
||||
if (process.platform === 'linux') {
|
||||
// On Linux, 'getParameters' will also include the global parameters,
|
||||
// because breakpad doesn't support global parameters.
|
||||
expect(rendererParameters).to.have.property('hello', 'world');
|
||||
} else {
|
||||
expect(rendererParameters).to.deep.equal({ hello: 'world' });
|
||||
}
|
||||
});
|
||||
|
||||
it('should be sent with renderer process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('renderer', port, ['--add-global-param=globalParam:globalValue']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
it('can be called in a node child process', async () => {
|
||||
function slurp (stream: NodeJS.ReadableStream): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks: Buffer[] = [];
|
||||
stream.on('data', chunk => { chunks.push(chunk); });
|
||||
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
||||
stream.on('error', e => reject(e));
|
||||
});
|
||||
}
|
||||
// TODO(nornagon): how to enable crashpad in a node child process...?
|
||||
const child = childProcess.fork(path.join(__dirname, 'fixtures', 'module', 'print-crash-parameters.js'), [], { silent: true });
|
||||
const output = await slurp(child.stdout!);
|
||||
expect(JSON.parse(output)).to.deep.equal({ hello: 'world' });
|
||||
});
|
||||
});
|
||||
|
||||
it('should be sent with sandboxed renderer process dumps', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('sandboxed-renderer', port, ['--add-global-param=globalParam:globalValue']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.globalParam).to.equal('globalValue');
|
||||
});
|
||||
describe('crash dumps directory', () => {
|
||||
it('is set by default', () => {
|
||||
expect(app.getPath('crashDumps')).to.be.a('string');
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('should not be overridden by extra in main process', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=mainProcessSpecific:global']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.mainProcessSpecific).to.equal('global');
|
||||
});
|
||||
it('is inside the user data dir', () => {
|
||||
expect(app.getPath('crashDumps')).to.include(app.getPath('userData'));
|
||||
});
|
||||
|
||||
ifit(!isLinuxOnArm)('should not be overridden by extra in renderer process', async () => {
|
||||
const { port, waitForCrash } = await startServer();
|
||||
runCrashApp('main', port, ['--add-global-param=rendererSpecific:global']);
|
||||
const crash = await waitForCrash();
|
||||
expect(crash.rendererSpecific).to.equal('global');
|
||||
});
|
||||
});
|
||||
function crash (processType: string, remotely: Function) {
|
||||
if (processType === 'main') {
|
||||
return remotely(() => {
|
||||
setTimeout(() => { process.crash(); });
|
||||
});
|
||||
} else if (processType === 'renderer') {
|
||||
return remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process.crash()');
|
||||
});
|
||||
} else if (processType === 'sandboxed-renderer') {
|
||||
const preloadPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'sandbox-preload.js');
|
||||
return remotely((preload: string) => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { sandbox: true, preload, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
}, preloadPath);
|
||||
} else if (processType === 'node') {
|
||||
const crashScriptPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'node-crash.js');
|
||||
return remotely((crashScriptPath: string) => {
|
||||
const { app } = require('electron');
|
||||
const childProcess = require('child_process');
|
||||
const version = app.getVersion();
|
||||
const url = 'http://127.0.0.1';
|
||||
childProcess.fork(crashScriptPath, [url, version], { silent: true });
|
||||
}, crashScriptPath);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(nornagon): also test crashing main / sandboxed renderers.
|
||||
ifit(!isWindowsOnArm)('should not send a minidump when uploadToServer is false', async () => {
|
||||
const { port, waitForCrash, getCrashes } = await startServer();
|
||||
waitForCrash().then(() => expect.fail('expected not to receive a dump'));
|
||||
await runCrashApp('renderer', port, ['--no-upload']);
|
||||
// wait a sec in case the crash reporter is about to upload a crash
|
||||
await delay(1000);
|
||||
expect(getCrashes()).to.have.length(0);
|
||||
});
|
||||
const processList = process.platform === 'linux' ? ['main', 'renderer', 'sandboxed-renderer']
|
||||
: ['main', 'renderer', 'sandboxed-renderer', 'node'];
|
||||
for (const crashingProcess of processList) {
|
||||
describe(`when ${crashingProcess} crashes`, () => {
|
||||
it('stores crashes in the crash dump directory when uploadToServer: false', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const crashesDir = await remotely(() => {
|
||||
const { crashReporter, app } = require('electron');
|
||||
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
|
||||
return app.getPath('crashDumps');
|
||||
});
|
||||
let reportsDir = crashesDir;
|
||||
if (process.platform === 'darwin' || (process.platform === 'linux' && withLinuxCrashpad)) {
|
||||
reportsDir = path.join(crashesDir, 'completed');
|
||||
} else if (process.platform === 'win32') {
|
||||
reportsDir = path.join(crashesDir, 'reports');
|
||||
}
|
||||
const newFileAppeared = waitForNewFileInDir(reportsDir);
|
||||
crash(crashingProcess, remotely);
|
||||
const newFiles = await newFileAppeared;
|
||||
expect(newFiles.length).to.be.greaterThan(0);
|
||||
if (process.platform === 'linux' && !withLinuxCrashpad) {
|
||||
if (crashingProcess === 'main') {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8}-[0-9a-f]{8}\.dmp$/);
|
||||
} else {
|
||||
const process = crashingProcess === 'sandboxed-renderer' ? 'renderer' : crashingProcess;
|
||||
const regex = RegExp(`chromium-${process}-minidump-[0-9a-f]{16}.dmp`);
|
||||
expect(newFiles[0]).to.match(regex);
|
||||
}
|
||||
} else {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
|
||||
}
|
||||
});
|
||||
|
||||
it('respects an overridden crash dump directory', async () => {
|
||||
const { remotely } = await startRemoteControlApp(crashpadExtraArgs);
|
||||
const crashesDir = path.join(app.getPath('temp'), uuid.v4());
|
||||
const remoteCrashesDir = await remotely((crashesDir: string) => {
|
||||
const { crashReporter, app } = require('electron');
|
||||
app.setPath('crashDumps', crashesDir);
|
||||
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
|
||||
return app.getPath('crashDumps');
|
||||
}, crashesDir);
|
||||
expect(remoteCrashesDir).to.equal(crashesDir);
|
||||
|
||||
let reportsDir = crashesDir;
|
||||
if (process.platform === 'darwin' || (process.platform === 'linux' && withLinuxCrashpad)) {
|
||||
reportsDir = path.join(crashesDir, 'completed');
|
||||
} else if (process.platform === 'win32') {
|
||||
reportsDir = path.join(crashesDir, 'reports');
|
||||
}
|
||||
const newFileAppeared = waitForNewFileInDir(reportsDir);
|
||||
crash(crashingProcess, remotely);
|
||||
const newFiles = await newFileAppeared;
|
||||
expect(newFiles.length).to.be.greaterThan(0);
|
||||
if (process.platform === 'linux' && !withLinuxCrashpad) {
|
||||
if (crashingProcess === 'main') {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8}-[0-9a-f]{8}\.dmp$/);
|
||||
} else {
|
||||
const process = crashingProcess !== 'sandboxed-renderer' ? crashingProcess : 'renderer';
|
||||
const regex = RegExp(`chromium-${process}-minidump-[0-9a-f]{16}.dmp`);
|
||||
expect(newFiles[0]).to.match(regex);
|
||||
}
|
||||
} else {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('start() option validation', () => {
|
||||
it('requires that the submitURL option be specified', () => {
|
||||
@@ -380,54 +611,6 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUploadedReports', () => {
|
||||
it('returns an array of reports', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
|
||||
});
|
||||
const reports = await remotely(() => require('electron').crashReporter.getUploadedReports());
|
||||
expect(reports).to.be.an('array');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(nornagon): re-enable on woa
|
||||
ifdescribe(!isWindowsOnArm)('getLastCrashReport', () => {
|
||||
it('returns the last uploaded report', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const { port, waitForCrash } = await startServer();
|
||||
|
||||
// 0. clear the crash reports directory.
|
||||
const dir = await remotely(() => require('electron').app.getPath('crashDumps'));
|
||||
try {
|
||||
fs.rmdirSync(dir, { recursive: true });
|
||||
fs.mkdirSync(dir);
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
// 1. start the crash reporter.
|
||||
await remotely((port: number) => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: `http://127.0.0.1:${port}`,
|
||||
compress: false,
|
||||
ignoreSystemCrashHandler: true
|
||||
});
|
||||
}, [port]);
|
||||
// 2. generate a crash in the renderer.
|
||||
remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process.crash()');
|
||||
});
|
||||
await waitForCrash();
|
||||
// 3. get the crash from getLastCrashReport.
|
||||
const firstReport = await remotely(() => require('electron').crashReporter.getLastCrashReport());
|
||||
expect(firstReport).to.not.be.null();
|
||||
expect(firstReport.date).to.be.an.instanceOf(Date);
|
||||
expect((+new Date()) - (+firstReport.date)).to.be.lessThan(30000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUploadToServer()', () => {
|
||||
it('returns true when uploadToServer is set to true (by default)', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
@@ -454,183 +637,6 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||
});
|
||||
});
|
||||
|
||||
describe('getParameters', () => {
|
||||
it('returns all of the current parameters', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({
|
||||
submitURL: 'http://127.0.0.1',
|
||||
extra: { extra1: 'hi' }
|
||||
});
|
||||
});
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).to.have.property('extra1', 'hi');
|
||||
});
|
||||
|
||||
it('reflects added and removed parameters', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
await remotely(() => {
|
||||
require('electron').crashReporter.start({ submitURL: 'http://127.0.0.1' });
|
||||
require('electron').crashReporter.addExtraParameter('hello', 'world');
|
||||
});
|
||||
{
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).to.have.property('hello', 'world');
|
||||
}
|
||||
|
||||
await remotely(() => { require('electron').crashReporter.removeExtraParameter('hello'); });
|
||||
|
||||
{
|
||||
const parameters = await remotely(() => require('electron').crashReporter.getParameters());
|
||||
expect(parameters).not.to.have.property('hello');
|
||||
}
|
||||
});
|
||||
|
||||
it('can be called in the renderer', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const rendererParameters = await remotely(async () => {
|
||||
const { crashReporter, BrowserWindow } = require('electron');
|
||||
crashReporter.start({ submitURL: 'http://' });
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
await bw.webContents.executeJavaScript('require(\'electron\').crashReporter.addExtraParameter(\'hello\', \'world\')');
|
||||
return bw.webContents.executeJavaScript('require(\'electron\').crashReporter.getParameters()');
|
||||
});
|
||||
if (process.platform === 'linux') {
|
||||
// On Linux, 'getParameters' will also include the global parameters,
|
||||
// because breakpad doesn't support global parameters.
|
||||
expect(rendererParameters).to.have.property('hello', 'world');
|
||||
} else {
|
||||
expect(rendererParameters).to.deep.equal({ hello: 'world' });
|
||||
}
|
||||
});
|
||||
|
||||
it('can be called in a node child process', async () => {
|
||||
function slurp (stream: NodeJS.ReadableStream): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks: Buffer[] = [];
|
||||
stream.on('data', chunk => { chunks.push(chunk); });
|
||||
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
||||
stream.on('error', e => reject(e));
|
||||
});
|
||||
}
|
||||
const child = childProcess.fork(path.join(__dirname, 'fixtures', 'module', 'print-crash-parameters.js'), [], { silent: true });
|
||||
const output = await slurp(child.stdout!);
|
||||
expect(JSON.parse(output)).to.deep.equal({ hello: 'world' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('crash dumps directory', () => {
|
||||
it('is set by default', () => {
|
||||
expect(app.getPath('crashDumps')).to.be.a('string');
|
||||
});
|
||||
|
||||
it('is inside the user data dir', () => {
|
||||
expect(app.getPath('crashDumps')).to.include(app.getPath('userData'));
|
||||
});
|
||||
|
||||
function crash (processType: string, remotely: Function) {
|
||||
if (processType === 'main') {
|
||||
return remotely(() => {
|
||||
setTimeout(() => { process.crash(); });
|
||||
});
|
||||
} else if (processType === 'renderer') {
|
||||
return remotely(() => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
bw.webContents.executeJavaScript('process.crash()');
|
||||
});
|
||||
} else if (processType === 'sandboxed-renderer') {
|
||||
const preloadPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'sandbox-preload.js');
|
||||
return remotely((preload: string) => {
|
||||
const { BrowserWindow } = require('electron');
|
||||
const bw = new BrowserWindow({ show: false, webPreferences: { sandbox: true, preload, contextIsolation: false } });
|
||||
bw.loadURL('about:blank');
|
||||
}, preloadPath);
|
||||
} else if (processType === 'node') {
|
||||
const crashScriptPath = path.join(__dirname, 'fixtures', 'apps', 'crash', 'node-crash.js');
|
||||
return remotely((crashScriptPath: string) => {
|
||||
const { app } = require('electron');
|
||||
const childProcess = require('child_process');
|
||||
const version = app.getVersion();
|
||||
const url = 'http://127.0.0.1';
|
||||
childProcess.fork(crashScriptPath, [url, version], { silent: true });
|
||||
}, crashScriptPath);
|
||||
}
|
||||
}
|
||||
|
||||
const processList = process.platform === 'linux' ? ['main', 'renderer', 'sandboxed-renderer']
|
||||
: ['main', 'renderer', 'sandboxed-renderer', 'node'];
|
||||
for (const crashingProcess of processList) {
|
||||
describe(`when ${crashingProcess} crashes`, () => {
|
||||
it('stores crashes in the crash dump directory when uploadToServer: false', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const crashesDir = await remotely(() => {
|
||||
const { crashReporter, app } = require('electron');
|
||||
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
|
||||
return app.getPath('crashDumps');
|
||||
});
|
||||
let reportsDir = crashesDir;
|
||||
if (process.platform === 'darwin') {
|
||||
reportsDir = path.join(crashesDir, 'completed');
|
||||
} else if (process.platform === 'win32') {
|
||||
reportsDir = path.join(crashesDir, 'reports');
|
||||
}
|
||||
const newFileAppeared = waitForNewFileInDir(reportsDir);
|
||||
crash(crashingProcess, remotely);
|
||||
const newFiles = await newFileAppeared;
|
||||
expect(newFiles.length).to.be.greaterThan(0);
|
||||
if (process.platform === 'linux') {
|
||||
if (crashingProcess === 'main') {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8}-[0-9a-f]{8}\.dmp$/);
|
||||
} else {
|
||||
const process = crashingProcess === 'sandboxed-renderer' ? 'renderer' : crashingProcess;
|
||||
const regex = RegExp(`chromium-${process}-minidump-[0-9a-f]{16}.dmp`);
|
||||
expect(newFiles[0]).to.match(regex);
|
||||
}
|
||||
} else {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
|
||||
}
|
||||
});
|
||||
|
||||
it('respects an overridden crash dump directory', async () => {
|
||||
const { remotely } = await startRemoteControlApp();
|
||||
const crashesDir = path.join(app.getPath('temp'), uuid.v4());
|
||||
const remoteCrashesDir = await remotely((crashesDir: string) => {
|
||||
const { crashReporter, app } = require('electron');
|
||||
app.setPath('crashDumps', crashesDir);
|
||||
crashReporter.start({ submitURL: 'http://127.0.0.1', uploadToServer: false, ignoreSystemCrashHandler: true });
|
||||
return app.getPath('crashDumps');
|
||||
}, crashesDir);
|
||||
expect(remoteCrashesDir).to.equal(crashesDir);
|
||||
|
||||
let reportsDir = crashesDir;
|
||||
if (process.platform === 'darwin') {
|
||||
reportsDir = path.join(crashesDir, 'completed');
|
||||
} else if (process.platform === 'win32') {
|
||||
reportsDir = path.join(crashesDir, 'reports');
|
||||
}
|
||||
const newFileAppeared = waitForNewFileInDir(reportsDir);
|
||||
crash(crashingProcess, remotely);
|
||||
const newFiles = await newFileAppeared;
|
||||
expect(newFiles.length).to.be.greaterThan(0);
|
||||
if (process.platform === 'linux') {
|
||||
if (crashingProcess === 'main') {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8}-[0-9a-f]{8}\.dmp$/);
|
||||
} else {
|
||||
const process = crashingProcess !== 'sandboxed-renderer' ? crashingProcess : 'renderer';
|
||||
const regex = RegExp(`chromium-${process}-minidump-[0-9a-f]{16}.dmp`);
|
||||
expect(newFiles[0]).to.match(regex);
|
||||
}
|
||||
} else {
|
||||
expect(newFiles[0]).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.dmp$/);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('when not started', () => {
|
||||
it('does not prevent process from crashing', async () => {
|
||||
const appPath = path.join(__dirname, '..', 'spec', 'fixtures', 'api', 'cookie-app');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai';
|
||||
import { dialog, BrowserWindow } from 'electron/main';
|
||||
import { closeAllWindows } from './window-helpers';
|
||||
import { ifit } from './spec-helpers';
|
||||
import { ifit, delay } from './spec-helpers';
|
||||
|
||||
describe('dialog module', () => {
|
||||
describe('showOpenDialog', () => {
|
||||
@@ -121,6 +121,62 @@ describe('dialog module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('showMessageBox with signal', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
it('closes message box immediately', async () => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const w = new BrowserWindow();
|
||||
const p = dialog.showMessageBox(w, { signal, message: 'i am message' });
|
||||
controller.abort();
|
||||
const result = await p;
|
||||
expect(result.response).to.equal(0);
|
||||
});
|
||||
|
||||
it('closes message box after a while', async () => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const w = new BrowserWindow();
|
||||
const p = dialog.showMessageBox(w, { signal, message: 'i am message' });
|
||||
await delay(500);
|
||||
controller.abort();
|
||||
const result = await p;
|
||||
expect(result.response).to.equal(0);
|
||||
});
|
||||
|
||||
it('cancels message box', async () => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const w = new BrowserWindow();
|
||||
const p = dialog.showMessageBox(w, {
|
||||
signal,
|
||||
message: 'i am message',
|
||||
buttons: ['OK', 'Cancel'],
|
||||
cancelId: 1
|
||||
});
|
||||
controller.abort();
|
||||
const result = await p;
|
||||
expect(result.response).to.equal(1);
|
||||
});
|
||||
|
||||
it('cancels message box after a while', async () => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const w = new BrowserWindow();
|
||||
const p = dialog.showMessageBox(w, {
|
||||
signal,
|
||||
message: 'i am message',
|
||||
buttons: ['OK', 'Cancel'],
|
||||
cancelId: 1
|
||||
});
|
||||
await delay(500);
|
||||
controller.abort();
|
||||
const result = await p;
|
||||
expect(result.response).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('showErrorBox', () => {
|
||||
it('throws errors when the options are invalid', () => {
|
||||
expect(() => {
|
||||
|
||||
@@ -120,6 +120,24 @@ describe('protocol module', () => {
|
||||
const r = await ajax(protocolName + '://fake-host');
|
||||
expect(r.data).to.equal(text);
|
||||
});
|
||||
|
||||
it('can redirect to the same scheme', async () => {
|
||||
registerStringProtocol(protocolName, (request, callback) => {
|
||||
if (request.url === `${protocolName}://fake-host/redirect`) {
|
||||
callback({
|
||||
statusCode: 302,
|
||||
headers: {
|
||||
Location: `${protocolName}://fake-host`
|
||||
}
|
||||
});
|
||||
} else {
|
||||
expect(request.url).to.equal(`${protocolName}://fake-host`);
|
||||
callback('redirected');
|
||||
}
|
||||
});
|
||||
const r = await ajax(`${protocolName}://fake-host/redirect`);
|
||||
expect(r.data).to.equal('redirected');
|
||||
});
|
||||
});
|
||||
|
||||
describe('protocol.unregisterProtocol', () => {
|
||||
|
||||
@@ -503,16 +503,6 @@ describe('webContents module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getWebPreferences() API', () => {
|
||||
afterEach(closeAllWindows);
|
||||
it('should not crash when called for devTools webContents', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.webContents.openDevTools();
|
||||
await emittedOnce(w.webContents, 'devtools-opened');
|
||||
expect(w.webContents.devToolsWebContents!.getWebPreferences()).to.be.null();
|
||||
});
|
||||
});
|
||||
|
||||
describe('openDevTools() API', () => {
|
||||
afterEach(closeAllWindows);
|
||||
it('can show window with activation', async () => {
|
||||
|
||||
@@ -181,6 +181,42 @@ describe('webRequest module', () => {
|
||||
expect(data).to.equal('/header/received');
|
||||
});
|
||||
|
||||
it('can change the request headers on a custom protocol redirect', async () => {
|
||||
protocol.registerStringProtocol('custom-scheme', (req, callback) => {
|
||||
if (req.url === 'custom-scheme://fake-host/redirect') {
|
||||
callback({
|
||||
statusCode: 302,
|
||||
headers: {
|
||||
Location: 'custom-scheme://fake-host'
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let content = '';
|
||||
if (req.headers.Accept === '*/*;test/header') {
|
||||
content = 'header-received';
|
||||
}
|
||||
callback(content);
|
||||
}
|
||||
});
|
||||
|
||||
// Note that we need to do navigation every time after a protocol is
|
||||
// registered or unregistered, otherwise the new protocol won't be
|
||||
// recognized by current page when NetworkService is used.
|
||||
await contents.loadFile(path.join(__dirname, 'fixtures', 'pages', 'jquery.html'));
|
||||
|
||||
try {
|
||||
ses.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||
const requestHeaders = details.requestHeaders;
|
||||
requestHeaders.Accept = '*/*;test/header';
|
||||
callback({ requestHeaders: requestHeaders });
|
||||
});
|
||||
const { data } = await ajax('custom-scheme://fake-host/redirect');
|
||||
expect(data).to.equal('header-received');
|
||||
} finally {
|
||||
protocol.unregisterProtocol('custom-scheme');
|
||||
}
|
||||
});
|
||||
|
||||
it('can change request origin', async () => {
|
||||
ses.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||
const requestHeaders = details.requestHeaders;
|
||||
|
||||
@@ -64,6 +64,7 @@ until the maintainers feel the maintenance burden is too high to continue doing
|
||||
|
||||
### Currently supported versions
|
||||
|
||||
* 4.x.y
|
||||
* 3.x.y
|
||||
* 2.x.y
|
||||
* 1.x.y
|
||||
|
||||
@@ -7,8 +7,17 @@ import * as fs from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
function isTestingBindingAvailable () {
|
||||
try {
|
||||
process._linkedBinding('electron_common_testing');
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// This test depends on functions that are only available when DCHECK_IS_ON.
|
||||
ifdescribe(process._linkedBinding('electron_common_testing'))('logging', () => {
|
||||
ifdescribe(isTestingBindingAvailable())('logging', () => {
|
||||
it('does not log by default', async () => {
|
||||
// ELECTRON_ENABLE_LOGGING is turned on in the appveyor config.
|
||||
const { ELECTRON_ENABLE_LOGGING: _, ...envWithoutEnableLogging } = process.env;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { expect } from 'chai';
|
||||
import { GitProcess, IGitExecutionOptions, IGitResult } from 'dugite';
|
||||
import { nextVersion, shouldUpdateSupported, updateSupported } from '../script/release/version-bumper';
|
||||
import * as utils from '../script/release/version-utils';
|
||||
import * as sinon from 'sinon';
|
||||
import { ifdescribe } from './spec-helpers';
|
||||
const { promises: fs } = require('fs');
|
||||
const path = require('path');
|
||||
@@ -9,6 +11,53 @@ const fixtureDir = path.resolve(__dirname, 'fixtures', 'version-bumper', 'fixtur
|
||||
const readFile = fs.readFile;
|
||||
const writeFile = fs.writeFile;
|
||||
|
||||
class GitFake {
|
||||
branches: {
|
||||
[key: string]: string[],
|
||||
};
|
||||
|
||||
constructor () {
|
||||
this.branches = {};
|
||||
}
|
||||
|
||||
setBranch (channel: string): void {
|
||||
this.branches[channel] = [];
|
||||
}
|
||||
|
||||
setVersion (channel: string, latestTag: string): void {
|
||||
const tags = [latestTag];
|
||||
if (channel === 'alpha') {
|
||||
const versionStrs = latestTag.split(`${channel}.`);
|
||||
const latest = parseInt(versionStrs[1]);
|
||||
|
||||
for (let i = latest; i >= 1; i--) {
|
||||
tags.push(`${versionStrs[0]}${channel}.${latest - i}`);
|
||||
}
|
||||
}
|
||||
|
||||
this.branches[channel] = tags;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
exec (args: string[], path: string, options?: IGitExecutionOptions | undefined): Promise<IGitResult> {
|
||||
let stdout = '';
|
||||
const stderr = '';
|
||||
const exitCode = 0;
|
||||
|
||||
// handle for promoting from current master HEAD
|
||||
let branch = 'stable';
|
||||
const v = (args[2] === 'HEAD') ? 'stable' : args[3];
|
||||
if (v.includes('nightly')) branch = 'nightly';
|
||||
if (v.includes('alpha')) branch = 'alpha';
|
||||
if (v.includes('beta')) branch = 'beta';
|
||||
|
||||
if (!this.branches[branch]) this.setBranch(branch);
|
||||
|
||||
stdout = this.branches[branch].join('\n');
|
||||
return Promise.resolve({ exitCode, stdout, stderr });
|
||||
}
|
||||
}
|
||||
|
||||
describe('version-bumper', () => {
|
||||
describe('makeVersion', () => {
|
||||
it('makes a version with a period delimeter', () => {
|
||||
@@ -138,101 +187,160 @@ describe('version-bumper', () => {
|
||||
// On macOS Circle CI we don't have a real git environment due to running
|
||||
// gclient sync on a linux machine. These tests therefore don't run as expected.
|
||||
ifdescribe(!(process.platform === 'linux' && process.arch.indexOf('arm') === 0) && process.platform !== 'darwin')('nextVersion', () => {
|
||||
const nightlyPattern = /[0-9.]*(-nightly.(\d{4})(\d{2})(\d{2}))$/g;
|
||||
describe('bump versions', () => {
|
||||
const nightlyPattern = /[0-9.]*(-nightly.(\d{4})(\d{2})(\d{2}))$/g;
|
||||
const betaPattern = /[0-9.]*(-beta[0-9.]*)/g;
|
||||
|
||||
it('bumps to nightly from stable', async () => {
|
||||
const version = 'v2.0.0';
|
||||
const next = await nextVersion('nightly', version);
|
||||
const matches = next.match(nightlyPattern);
|
||||
expect(matches).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it('bumps to nightly from beta', async () => {
|
||||
const version = 'v2.0.0-beta.1';
|
||||
const next = await nextVersion('nightly', version);
|
||||
const matches = next.match(nightlyPattern);
|
||||
expect(matches).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it('bumps to nightly from nightly', async () => {
|
||||
const version = 'v2.0.0-nightly.19950901';
|
||||
const next = await nextVersion('nightly', version);
|
||||
const matches = next.match(nightlyPattern);
|
||||
expect(matches).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it('bumps to a nightly version above our switch from N-0-x to N-x-y branch names', async () => {
|
||||
const version = 'v2.0.0-nightly.19950901';
|
||||
const next = await nextVersion('nightly', version);
|
||||
// If it starts with v8 then we didn't bump above the 8-x-y branch
|
||||
expect(next.startsWith('v8')).to.equal(false);
|
||||
});
|
||||
|
||||
it('throws error when bumping to beta from stable', () => {
|
||||
const version = 'v2.0.0';
|
||||
return expect(
|
||||
nextVersion('beta', version)
|
||||
).to.be.rejectedWith('Cannot bump to beta from stable.');
|
||||
});
|
||||
|
||||
// TODO ELECTRON 15: Re-enable after Electron 15 alpha has released
|
||||
it.skip('bumps to beta from nightly', async () => {
|
||||
const version = 'v2.0.0-nightly.19950901';
|
||||
const next = await nextVersion('beta', version);
|
||||
const matches = next.match(betaPattern);
|
||||
expect(matches).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it('bumps to beta from beta', async () => {
|
||||
const version = 'v2.0.0-beta.8';
|
||||
const next = await nextVersion('beta', version);
|
||||
expect(next).to.equal('2.0.0-beta.9');
|
||||
});
|
||||
|
||||
it('bumps to beta from beta if the previous beta is at least beta.10', async () => {
|
||||
const version = 'v6.0.0-beta.15';
|
||||
const next = await nextVersion('beta', version);
|
||||
expect(next).to.equal('6.0.0-beta.16');
|
||||
});
|
||||
|
||||
it('bumps to stable from beta', async () => {
|
||||
const version = 'v2.0.0-beta.1';
|
||||
const next = await nextVersion('stable', version);
|
||||
expect(next).to.equal('2.0.0');
|
||||
});
|
||||
|
||||
it('bumps to stable from stable', async () => {
|
||||
const version = 'v2.0.0';
|
||||
const next = await nextVersion('stable', version);
|
||||
expect(next).to.equal('2.0.1');
|
||||
});
|
||||
|
||||
it('bumps to minor from stable', async () => {
|
||||
const version = 'v2.0.0';
|
||||
const next = await nextVersion('minor', version);
|
||||
expect(next).to.equal('2.1.0');
|
||||
});
|
||||
|
||||
it('bumps to stable from nightly', async () => {
|
||||
const version = 'v2.0.0-nightly.19950901';
|
||||
const next = await nextVersion('stable', version);
|
||||
expect(next).to.equal('2.0.0');
|
||||
});
|
||||
|
||||
it('throws on an invalid version', () => {
|
||||
const version = 'vI.AM.INVALID';
|
||||
return expect(
|
||||
nextVersion('beta', version)
|
||||
).to.be.rejectedWith(`Invalid current version: ${version}`);
|
||||
});
|
||||
|
||||
it('throws on an invalid bump type', () => {
|
||||
const version = 'v2.0.0';
|
||||
return expect(
|
||||
nextVersion('WRONG', version)
|
||||
).to.be.rejectedWith('Invalid bump type.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// If we don't plan on continuing to support an alpha channel past Electron 15,
|
||||
// these tests will be removed. Otherwise, integrate into the bump versions tests
|
||||
describe('bump versions - alpha channel', () => {
|
||||
const alphaPattern = /[0-9.]*(-alpha[0-9.]*)/g;
|
||||
const betaPattern = /[0-9.]*(-beta[0-9.]*)/g;
|
||||
|
||||
it('bumps to nightly from stable', async () => {
|
||||
const version = 'v2.0.0';
|
||||
const next = await nextVersion('nightly', version);
|
||||
const matches = next.match(nightlyPattern);
|
||||
expect(matches).to.have.lengthOf(1);
|
||||
const sandbox = sinon.createSandbox();
|
||||
const gitFake = new GitFake();
|
||||
|
||||
beforeEach(() => {
|
||||
const wrapper = (args: string[], path: string, options?: IGitExecutionOptions | undefined) => gitFake.exec(args, path, options);
|
||||
sandbox.replace(GitProcess, 'exec', wrapper);
|
||||
});
|
||||
|
||||
it('bumps to nightly from beta', async () => {
|
||||
const version = 'v2.0.0-beta.1';
|
||||
const next = await nextVersion('nightly', version);
|
||||
const matches = next.match(nightlyPattern);
|
||||
expect(matches).to.have.lengthOf(1);
|
||||
afterEach(() => {
|
||||
gitFake.branches = {};
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('bumps to nightly from nightly', async () => {
|
||||
it('bumps to alpha from nightly', async () => {
|
||||
const version = 'v2.0.0-nightly.19950901';
|
||||
const next = await nextVersion('nightly', version);
|
||||
const matches = next.match(nightlyPattern);
|
||||
gitFake.setVersion('nightly', version);
|
||||
const next = await nextVersion('alpha', version);
|
||||
const matches = next.match(alphaPattern);
|
||||
expect(matches).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it('bumps to a nightly version above our switch from N-0-x to N-x-y branch names', async () => {
|
||||
const version = 'v2.0.0-nightly.19950901';
|
||||
const next = await nextVersion('nightly', version);
|
||||
// If it starts with v8 then we didn't bump above the 8-x-y branch
|
||||
expect(next.startsWith('v8')).to.equal(false);
|
||||
});
|
||||
|
||||
it('throws error when bumping to beta from stable', () => {
|
||||
it('throws error when bumping to alpha from stable', () => {
|
||||
const version = 'v2.0.0';
|
||||
return expect(
|
||||
nextVersion('beta', version)
|
||||
).to.be.rejectedWith('Cannot bump to beta from stable.');
|
||||
nextVersion('alpha', version)
|
||||
).to.be.rejectedWith('Cannot bump to alpha from stable.');
|
||||
});
|
||||
|
||||
it('bumps to beta from nightly', async () => {
|
||||
const version = 'v2.0.0-nightly.19950901';
|
||||
it('bumps to alpha from alpha', async () => {
|
||||
const version = 'v2.0.0-alpha.8';
|
||||
gitFake.setVersion('alpha', version);
|
||||
const next = await nextVersion('alpha', version);
|
||||
expect(next).to.equal('2.0.0-alpha.9');
|
||||
});
|
||||
|
||||
it('bumps to alpha from alpha if the previous alpha is at least alpha.10', async () => {
|
||||
const version = 'v6.0.0-alpha.15';
|
||||
gitFake.setVersion('alpha', version);
|
||||
const next = await nextVersion('alpha', version);
|
||||
expect(next).to.equal('6.0.0-alpha.16');
|
||||
});
|
||||
|
||||
it('bumps to beta from alpha', async () => {
|
||||
const version = 'v2.0.0-alpha.8';
|
||||
gitFake.setVersion('alpha', version);
|
||||
const next = await nextVersion('beta', version);
|
||||
const matches = next.match(betaPattern);
|
||||
expect(matches).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
it('bumps to beta from beta', async () => {
|
||||
const version = 'v2.0.0-beta.8';
|
||||
const next = await nextVersion('beta', version);
|
||||
expect(next).to.equal('2.0.0-beta.9');
|
||||
});
|
||||
|
||||
it('bumps to beta from beta if the previous beta is at least beta.10', async () => {
|
||||
const version = 'v6.0.0-beta.10';
|
||||
const next = await nextVersion('beta', version);
|
||||
// Last 6.0.0 beta we did was beta.15
|
||||
// So we expect a beta.16 here
|
||||
expect(next).to.equal('6.0.0-beta.16');
|
||||
});
|
||||
|
||||
it('bumps to stable from beta', async () => {
|
||||
const version = 'v2.0.0-beta.1';
|
||||
const next = await nextVersion('stable', version);
|
||||
expect(next).to.equal('2.0.0');
|
||||
});
|
||||
|
||||
it('bumps to stable from stable', async () => {
|
||||
const version = 'v2.0.0';
|
||||
const next = await nextVersion('stable', version);
|
||||
expect(next).to.equal('2.0.1');
|
||||
});
|
||||
|
||||
it('bumps to minor from stable', async () => {
|
||||
const version = 'v2.0.0';
|
||||
const next = await nextVersion('minor', version);
|
||||
expect(next).to.equal('2.1.0');
|
||||
});
|
||||
|
||||
it('bumps to stable from nightly', async () => {
|
||||
const version = 'v2.0.0-nightly.19950901';
|
||||
const next = await nextVersion('stable', version);
|
||||
expect(next).to.equal('2.0.0');
|
||||
});
|
||||
|
||||
it('throws on an invalid version', () => {
|
||||
const version = 'vI.AM.INVALID';
|
||||
return expect(
|
||||
nextVersion('beta', version)
|
||||
).to.be.rejectedWith(`Invalid current version: ${version}`);
|
||||
});
|
||||
|
||||
it('throws on an invalid bump type', () => {
|
||||
const version = 'v2.0.0';
|
||||
return expect(
|
||||
nextVersion('WRONG', version)
|
||||
).to.be.rejectedWith('Invalid bump type.');
|
||||
expect(next).to.equal('2.0.0-beta.1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as url from 'url';
|
||||
import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main';
|
||||
import { closeAllWindows } from './window-helpers';
|
||||
import { emittedOnce, emittedUntil } from './events-helpers';
|
||||
import { ifit, delay } from './spec-helpers';
|
||||
import { expect } from 'chai';
|
||||
|
||||
async function loadWebView (w: WebContents, attributes: Record<string, string>, openDevTools: boolean = false): Promise<void> {
|
||||
@@ -181,6 +182,26 @@ describe('<webview> tag', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('did-attach event', () => {
|
||||
it('is emitted when a webview has been attached', async () => {
|
||||
const w = new BrowserWindow({
|
||||
webPreferences: {
|
||||
webviewTag: true
|
||||
}
|
||||
});
|
||||
await w.loadURL('about:blank');
|
||||
const message = await w.webContents.executeJavaScript(`new Promise((resolve, reject) => {
|
||||
const webview = new WebView()
|
||||
webview.setAttribute('src', 'about:blank')
|
||||
webview.addEventListener('did-attach', (e) => {
|
||||
resolve('ok')
|
||||
})
|
||||
document.body.appendChild(webview)
|
||||
})`);
|
||||
expect(message).to.equal('ok');
|
||||
});
|
||||
});
|
||||
|
||||
describe('did-change-theme-color event', () => {
|
||||
it('emits when theme color changes', async () => {
|
||||
const w = new BrowserWindow({
|
||||
@@ -406,6 +427,35 @@ describe('<webview> tag', function () {
|
||||
await parentFullscreen;
|
||||
expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.true();
|
||||
});
|
||||
|
||||
// FIXME(zcbenz): Fullscreen events do not work on Linux.
|
||||
// This test is flaky on arm64 macOS.
|
||||
ifit(process.platform !== 'linux' && process.arch !== 'arm64')('exiting fullscreen should unfullscreen window', async () => {
|
||||
const [w, webview] = await loadWebViewWindow();
|
||||
const enterFullScreen = emittedOnce(w, 'enter-full-screen');
|
||||
await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
|
||||
await enterFullScreen;
|
||||
|
||||
const leaveFullScreen = emittedOnce(w, 'leave-full-screen');
|
||||
await webview.executeJavaScript('document.exitFullscreen()', true);
|
||||
await leaveFullScreen;
|
||||
await delay(0);
|
||||
expect(w.isFullScreen()).to.be.false();
|
||||
});
|
||||
|
||||
// Sending ESC via sendInputEvent only works on Windows.
|
||||
ifit(process.platform === 'win32')('pressing ESC should unfullscreen window', async () => {
|
||||
const [w, webview] = await loadWebViewWindow();
|
||||
const enterFullScreen = emittedOnce(w, 'enter-full-screen');
|
||||
await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true);
|
||||
await enterFullScreen;
|
||||
|
||||
const leaveFullScreen = emittedOnce(w, 'leave-full-screen');
|
||||
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Escape' });
|
||||
await leaveFullScreen;
|
||||
await delay(0);
|
||||
expect(w.isFullScreen()).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('nativeWindowOpen option', () => {
|
||||
|
||||
1
typings/internal-electron.d.ts
vendored
1
typings/internal-electron.d.ts
vendored
@@ -57,7 +57,6 @@ declare namespace Electron {
|
||||
interface WebContents {
|
||||
_loadURL(url: string, options: ElectronInternal.LoadURLOptions): void;
|
||||
getOwnerBrowserWindow(): Electron.BrowserWindow;
|
||||
getWebPreferences(): Electron.WebPreferences;
|
||||
getLastWebPreferences(): Electron.WebPreferences;
|
||||
_getPreloadPaths(): string[];
|
||||
equal(other: WebContents): boolean;
|
||||
|
||||
Reference in New Issue
Block a user