mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
30 Commits
robo/rm_li
...
v18.0.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe8c831a0c | ||
|
|
cc9ba35c51 | ||
|
|
56ccfad7a5 | ||
|
|
47bda9c0e5 | ||
|
|
4f27c367b1 | ||
|
|
339ae4c014 | ||
|
|
c1f4b6a4cb | ||
|
|
0a908224c2 | ||
|
|
34c4889bbb | ||
|
|
8c066c2a35 | ||
|
|
7c9b609389 | ||
|
|
7ab85558f5 | ||
|
|
ca06034fdd | ||
|
|
d07fe4480f | ||
|
|
c2195915d8 | ||
|
|
57408e4c2a | ||
|
|
0012e01c86 | ||
|
|
2b97ff3fba | ||
|
|
87590da2fb | ||
|
|
493751b321 | ||
|
|
36008e0dea | ||
|
|
2871698148 | ||
|
|
9d8dde5c76 | ||
|
|
f72efecf95 | ||
|
|
5b45cb3f77 | ||
|
|
ad2b136425 | ||
|
|
94f4c18d7c | ||
|
|
f7f41fee99 | ||
|
|
907e9c8c0e | ||
|
|
4d2968bfc1 |
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -4,7 +4,7 @@
|
||||
# https://git-scm.com/docs/gitignore
|
||||
|
||||
# Upgrades WG
|
||||
/patches/ @electron/wg-upgrades
|
||||
/patches/ @electron/wg-upgrades @electron/wg-security
|
||||
DEPS @electron/wg-upgrades
|
||||
|
||||
# Releases WG
|
||||
|
||||
7
BUILD.gn
7
BUILD.gn
@@ -1401,11 +1401,13 @@ dist_zip("electron_dist_zip") {
|
||||
if (is_linux) {
|
||||
data_deps += [ "//sandbox/linux:chrome_sandbox" ]
|
||||
}
|
||||
deps = data_deps
|
||||
outputs = [ "$root_build_dir/dist.zip" ]
|
||||
}
|
||||
|
||||
dist_zip("electron_ffmpeg_zip") {
|
||||
data_deps = [ "//third_party/ffmpeg" ]
|
||||
deps = data_deps
|
||||
outputs = [ "$root_build_dir/ffmpeg.zip" ]
|
||||
}
|
||||
|
||||
@@ -1423,6 +1425,7 @@ group("electron_chromedriver") {
|
||||
dist_zip("electron_chromedriver_zip") {
|
||||
testonly = true
|
||||
data_deps = electron_chromedriver_deps
|
||||
deps = data_deps
|
||||
outputs = [ "$root_build_dir/chromedriver.zip" ]
|
||||
}
|
||||
|
||||
@@ -1441,6 +1444,7 @@ group("electron_mksnapshot") {
|
||||
|
||||
dist_zip("electron_mksnapshot_zip") {
|
||||
data_deps = mksnapshot_deps
|
||||
deps = data_deps
|
||||
outputs = [ "$root_build_dir/mksnapshot.zip" ]
|
||||
}
|
||||
|
||||
@@ -1451,6 +1455,7 @@ copy("hunspell_dictionaries") {
|
||||
|
||||
dist_zip("hunspell_dictionaries_zip") {
|
||||
data_deps = [ ":hunspell_dictionaries" ]
|
||||
deps = data_deps
|
||||
flatten = true
|
||||
|
||||
outputs = [ "$root_build_dir/hunspell_dictionaries.zip" ]
|
||||
@@ -1464,6 +1469,7 @@ copy("libcxx_headers") {
|
||||
|
||||
dist_zip("libcxx_headers_zip") {
|
||||
data_deps = [ ":libcxx_headers" ]
|
||||
deps = data_deps
|
||||
flatten = true
|
||||
flatten_relative_to = rebase_path(
|
||||
"$target_gen_dir/electron_libcxx_include/buildtools/third_party/libc++/trunk",
|
||||
@@ -1479,6 +1485,7 @@ copy("libcxxabi_headers") {
|
||||
|
||||
dist_zip("libcxxabi_headers_zip") {
|
||||
data_deps = [ ":libcxxabi_headers" ]
|
||||
deps = data_deps
|
||||
flatten = true
|
||||
flatten_relative_to = rebase_path(
|
||||
"$target_gen_dir/electron_libcxxabi_include/buildtools/third_party/libc++abi/trunk",
|
||||
|
||||
@@ -1 +1 @@
|
||||
18.0.0-nightly.20220201
|
||||
18.0.0-alpha.5
|
||||
@@ -516,6 +516,15 @@ Emitted when the `WebContents` loses focus.
|
||||
|
||||
Emitted when the `WebContents` gains focus.
|
||||
|
||||
Note that on macOS, having focus means the `WebContents` is the first responder
|
||||
of window, so switching focus between windows would not trigger the `focus` and
|
||||
`blur` events of `WebContents`, as the first responder of each window is not
|
||||
changed.
|
||||
|
||||
The `focus` and `blur` events of `WebContents` should only be used to detect
|
||||
focus change between different `WebContents` and `BrowserView` in the same
|
||||
window.
|
||||
|
||||
#### Event: 'devtools-opened'
|
||||
|
||||
Emitted when DevTools is opened.
|
||||
@@ -1092,7 +1101,7 @@ Returns `string` - The user agent for this web page.
|
||||
|
||||
* `css` string
|
||||
* `options` Object (optional)
|
||||
* `cssOrigin` string (optional) - Can be either 'user' or 'author'; Specifying 'user' enables you to prevent websites from overriding the CSS you insert. Default is 'author'.
|
||||
* `cssOrigin` string (optional) - Can be either 'user' or 'author'. Sets the [cascade origin](https://www.w3.org/TR/css3-cascade/#cascade-origin) of the inserted stylesheet. Default is 'author'.
|
||||
|
||||
Returns `Promise<string>` - A promise that resolves with a key for the inserted CSS that can later be used to remove the CSS via `contents.removeInsertedCSS(key)`.
|
||||
|
||||
@@ -1847,7 +1856,7 @@ the cursor when dragging.
|
||||
|
||||
#### `contents.savePage(fullPath, saveType)`
|
||||
|
||||
* `fullPath` string - The full file path.
|
||||
* `fullPath` string - The absolute file path.
|
||||
* `saveType` string - Specify the save type.
|
||||
* `HTMLOnly` - Save only the HTML of the page.
|
||||
* `HTMLComplete` - Save complete-html page.
|
||||
|
||||
@@ -180,7 +180,7 @@ $ git push origin my-branch
|
||||
### Step 9: Opening the Pull Request
|
||||
|
||||
From within GitHub, opening a new pull request will present you with a template
|
||||
that should be filled out. It can be found [here](../../.github/PULL_REQUEST_TEMPLATE.md).
|
||||
that should be filled out. It can be found [here](https://github.com/electron/electron/blob/main/.github/PULL_REQUEST_TEMPLATE.md).
|
||||
|
||||
If you do not adequately complete this template, your PR may be delayed in being merged as maintainers
|
||||
seek more information or clarify ambiguities.
|
||||
|
||||
@@ -26,4 +26,5 @@ Special notes:
|
||||
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | M93 | v14.17 |
|
||||
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | M94 | v16.5 |
|
||||
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | M96 | v16.9 |
|
||||
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | M98 | TBD |
|
||||
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | M98 | v16.13 |
|
||||
| 18.0.0 | 2022-Feb-03 | 2022-Mar-03 | 2022-Mar-29 | M100 | TBD |
|
||||
|
||||
@@ -146,7 +146,7 @@ desktop environment that follows [Desktop Notifications
|
||||
Specification][notification-spec], including Cinnamon, Enlightenment, Unity,
|
||||
GNOME, KDE.
|
||||
|
||||
[notification-spec]: https://developer.gnome.org/notification-spec/
|
||||
[notification-spec]: https://developer-old.gnome.org/notification-spec/
|
||||
[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx
|
||||
[set-app-user-model-id]: ../api/app.md#appsetappusermodelidid-windows
|
||||
[squirrel-events]: https://github.com/electron/windows-installer/blob/master/README.md#handling-squirrel-events
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
title: Performance
|
||||
description: A set of guidelines for building performant Electron apps
|
||||
slug: performance
|
||||
hide_title: true
|
||||
toc_max_heading_level: 3
|
||||
---
|
||||
|
||||
# Performance
|
||||
|
||||
Developers frequently ask about strategies to optimize the performance of
|
||||
@@ -49,7 +57,7 @@ at once, consider the [Chrome Tracing](https://www.chromium.org/developers/how-t
|
||||
* [Get Started With Analyzing Runtime Performance][chrome-devtools-tutorial]
|
||||
* [Talk: "Visual Studio Code - The First Second"][vscode-first-second]
|
||||
|
||||
## Checklist
|
||||
## Checklist: Performance recommendations
|
||||
|
||||
Chances are that your app could be a little leaner, faster, and generally less
|
||||
resource-hungry if you attempt these steps.
|
||||
@@ -62,7 +70,7 @@ resource-hungry if you attempt these steps.
|
||||
6. [Unnecessary or blocking network requests](#6-unnecessary-or-blocking-network-requests)
|
||||
7. [Bundle your code](#7-bundle-your-code)
|
||||
|
||||
## 1) Carelessly including modules
|
||||
### 1. Carelessly including modules
|
||||
|
||||
Before adding a Node.js module to your application, examine said module. How
|
||||
many dependencies does that module include? What kind of resources does
|
||||
@@ -70,7 +78,7 @@ it need to simply be called in a `require()` statement? You might find
|
||||
that the module with the most downloads on the NPM package registry or the most stars on GitHub
|
||||
is not in fact the leanest or smallest one available.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
The reasoning behind this recommendation is best illustrated with a real-world
|
||||
example. During the early days of Electron, reliable detection of network
|
||||
@@ -99,7 +107,7 @@ running Linux might be bad news for your app's performance. In this particular
|
||||
example, the correct solution was to use no module at all, and to instead use
|
||||
connectivity checks included in later versions of Chromium.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
When considering a module, we recommend that you check:
|
||||
|
||||
@@ -128,7 +136,7 @@ In this example, on the author's machine, we saw that loading `request` took
|
||||
almost half a second, whereas `node-fetch` took dramatically less memory
|
||||
and less than 50ms.
|
||||
|
||||
## 2) Loading and running code too soon
|
||||
### 2. Loading and running code too soon
|
||||
|
||||
If you have expensive setup operations, consider deferring those. Inspect all
|
||||
the work being executed right after the application starts. Instead of firing
|
||||
@@ -141,7 +149,7 @@ using the same strategy _and_ are using sizable modules that you do not
|
||||
immediately need, apply the same strategy and defer loading to a more
|
||||
opportune time.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Loading modules is a surprisingly expensive operation, especially on Windows.
|
||||
When your app starts, it should not make users wait for operations that are
|
||||
@@ -157,14 +165,14 @@ immediately display the file to you without any code highlighting, prioritizing
|
||||
your ability to interact with the text. Once it has done that work, it will
|
||||
move on to code highlighting.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
Let's consider an example and assume that your application is parsing files
|
||||
in the fictitious `.foo` format. In order to do that, it relies on the
|
||||
equally fictitious `foo-parser` module. In traditional Node.js development,
|
||||
you might write code that eagerly loads dependencies:
|
||||
|
||||
```js
|
||||
```js title='parser.js'
|
||||
const fs = require('fs')
|
||||
const fooParser = require('foo-parser')
|
||||
|
||||
@@ -187,7 +195,7 @@ In the above example, we're doing a lot of work that's being executed as soon
|
||||
as the file is loaded. Do we need to get parsed files right away? Could we
|
||||
do this work a little later, when `getParsedFiles()` is actually called?
|
||||
|
||||
```js
|
||||
```js title='parser.js'
|
||||
// "fs" is likely already being loaded, so the `require()` call is cheap
|
||||
const fs = require('fs')
|
||||
|
||||
@@ -223,7 +231,7 @@ module.exports = { parser }
|
||||
In short, allocate resources "just in time" rather than allocating them all
|
||||
when your app starts.
|
||||
|
||||
## 3) Blocking the main process
|
||||
### 3. Blocking the main process
|
||||
|
||||
Electron's main process (sometimes called "browser process") is special: It is
|
||||
the parent process to all your app's other processes and the primary process
|
||||
@@ -235,7 +243,7 @@ Under no circumstances should you block this process and the UI thread with
|
||||
long-running operations. Blocking the UI thread means that your entire app
|
||||
will freeze until the main process is ready to continue processing.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
The main process and its UI thread are essentially the control tower for major
|
||||
operations inside your app. When the operating system tells your app about a
|
||||
@@ -246,31 +254,31 @@ the GPU process about that – once again going through the main process.
|
||||
Electron and Chromium are careful to put heavy disk I/O and CPU-bound operations
|
||||
onto new threads to avoid blocking the UI thread. You should do the same.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
Electron's powerful multi-process architecture stands ready to assist you with
|
||||
your long-running tasks, but also includes a small number of performance traps.
|
||||
|
||||
1) For long running CPU-heavy tasks, make use of
|
||||
1. For long running CPU-heavy tasks, make use of
|
||||
[worker threads][worker-threads], consider moving them to the BrowserWindow, or
|
||||
(as a last resort) spawn a dedicated process.
|
||||
|
||||
2) Avoid using the synchronous IPC and the `remote` module as much as possible.
|
||||
While there are legitimate use cases, it is far too easy to unknowingly block
|
||||
the UI thread using the `remote` module.
|
||||
2. Avoid using the synchronous IPC and the `@electron/remote` module as much
|
||||
as possible. While there are legitimate use cases, it is far too easy to
|
||||
unknowingly block the UI thread.
|
||||
|
||||
3) Avoid using blocking I/O operations in the main process. In short, whenever
|
||||
3. Avoid using blocking I/O operations in the main process. In short, whenever
|
||||
core Node.js modules (like `fs` or `child_process`) offer a synchronous or an
|
||||
asynchronous version, you should prefer the asynchronous and non-blocking
|
||||
variant.
|
||||
|
||||
## 4) Blocking the renderer process
|
||||
### 4. Blocking the renderer process
|
||||
|
||||
Since Electron ships with a current version of Chrome, you can make use of the
|
||||
latest and greatest features the Web Platform offers to defer or offload heavy
|
||||
operations in a way that keeps your app smooth and responsive.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Your app probably has a lot of JavaScript to run in the renderer process. The
|
||||
trick is to execute operations as quickly as possible without taking away
|
||||
@@ -280,7 +288,7 @@ at 60fps.
|
||||
Orchestrating the flow of operations in your renderer's code is
|
||||
particularly useful if users complain about your app sometimes "stuttering".
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
Generally speaking, all advice for building performant web apps for modern
|
||||
browsers apply to Electron's renderers, too. The two primary tools at your
|
||||
@@ -300,14 +308,14 @@ some caveats to consider – consult Electron's
|
||||
for any operation that requires a lot of CPU power for an extended period of
|
||||
time.
|
||||
|
||||
## 5) Unnecessary polyfills
|
||||
### 5. Unnecessary polyfills
|
||||
|
||||
One of Electron's great benefits is that you know exactly which engine will
|
||||
parse your JavaScript, HTML, and CSS. If you're re-purposing code that was
|
||||
written for the web at large, make sure to not polyfill features included in
|
||||
Electron.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
When building a web application for today's Internet, the oldest environments
|
||||
dictate what features you can and cannot use. Even though Electron supports
|
||||
@@ -323,7 +331,7 @@ It is rare for a JavaScript-based polyfill to be faster than the equivalent
|
||||
native feature in Electron. Do not slow down your Electron app by shipping your
|
||||
own version of standard web platform features.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
Operate under the assumption that polyfills in current versions of Electron
|
||||
are unnecessary. If you have doubts, check [caniuse.com](https://caniuse.com/)
|
||||
@@ -338,12 +346,12 @@ If you're using a transpiler/compiler like TypeScript, examine its configuration
|
||||
and ensure that you're targeting the latest ECMAScript version supported by
|
||||
Electron.
|
||||
|
||||
## 6) Unnecessary or blocking network requests
|
||||
### 6. Unnecessary or blocking network requests
|
||||
|
||||
Avoid fetching rarely changing resources from the internet if they could easily
|
||||
be bundled with your application.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Many users of Electron start with an entirely web-based app that they're
|
||||
turning into a desktop application. As web developers, we are used to loading
|
||||
@@ -360,7 +368,7 @@ will take care of the rest.
|
||||
When building an Electron app, your users are better served if you download
|
||||
the fonts and include them in your app's bundle.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
In an ideal world, your application wouldn't need the network to operate at
|
||||
all. To get there, you must understand what resources your app is downloading
|
||||
@@ -387,21 +395,21 @@ without shipping an application update is a powerful strategy. For advanced
|
||||
control over how resources are being loaded, consider investing in
|
||||
[Service Workers][service-workers].
|
||||
|
||||
## 7) Bundle your code
|
||||
### 7. Bundle your code
|
||||
|
||||
As already pointed out in
|
||||
"[Loading and running code too soon](#2-loading-and-running-code-too-soon)",
|
||||
calling `require()` is an expensive operation. If you are able to do so,
|
||||
bundle your application's code into a single file.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Modern JavaScript development usually involves many files and modules. While
|
||||
that's perfectly fine for developing with Electron, we heavily recommend that
|
||||
you bundle all your code into one single file to ensure that the overhead
|
||||
included in calling `require()` is only paid once when your application loads.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
There are numerous JavaScript bundlers out there and we know better than to
|
||||
anger the community by recommending one tool over another. We do however
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
# Security, Native Capabilities, and Your Responsibility
|
||||
---
|
||||
title: Security
|
||||
description: A set of guidelines for building secure Electron apps
|
||||
slug: security
|
||||
hide_title: true
|
||||
toc_max_heading_level: 3
|
||||
---
|
||||
# Security
|
||||
|
||||
As web developers, we usually enjoy the strong security net of the browser -
|
||||
:::info Reporting security issues
|
||||
For information on how to properly disclose an Electron vulnerability,
|
||||
see [SECURITY.md](https://github.com/electron/electron/tree/main/SECURITY.md).
|
||||
|
||||
For upstream Chromium vulnerabilities: Electron keeps up to date with alternating
|
||||
Chromium releases. For more information, see the
|
||||
[Electron Release Timelines](../tutorial/electron-timelines.md) document.
|
||||
:::
|
||||
|
||||
## Preface
|
||||
|
||||
As web developers, we usually enjoy the strong security net of the browser —
|
||||
the risks associated with the code we write are relatively small. Our websites
|
||||
are granted limited powers in a sandbox, and we trust that our users enjoy a
|
||||
browser built by a large team of engineers that is able to quickly respond to
|
||||
@@ -17,20 +35,12 @@ With that in mind, be aware that displaying arbitrary content from untrusted
|
||||
sources poses a severe security risk that Electron is not intended to handle.
|
||||
In fact, the most popular Electron apps (Atom, Slack, Visual Studio Code, etc)
|
||||
display primarily local content (or trusted, secure remote content without Node
|
||||
integration) – if your application executes code from an online source, it is
|
||||
integration) — if your application executes code from an online source, it is
|
||||
your responsibility to ensure that the code is not malicious.
|
||||
|
||||
## Reporting Security Issues
|
||||
## General guidelines
|
||||
|
||||
For information on how to properly disclose an Electron vulnerability,
|
||||
see [SECURITY.md](https://github.com/electron/electron/tree/main/SECURITY.md)
|
||||
|
||||
## Chromium Security Issues and Upgrades
|
||||
|
||||
Electron keeps up to date with alternating Chromium releases. For more information,
|
||||
see the [Electron Release Cadence blog post](https://electronjs.org/blog/12-week-cadence).
|
||||
|
||||
## Security Is Everyone's Responsibility
|
||||
### Security is everyone's responsibility
|
||||
|
||||
It is important to remember that the security of your Electron application is
|
||||
the result of the overall security of the framework foundation
|
||||
@@ -56,7 +66,7 @@ is your own code. Common web vulnerabilities, such as Cross-Site Scripting (XSS)
|
||||
have a higher security impact on Electron applications hence it is highly recommended
|
||||
to adopt secure software development best practices and perform security testing.
|
||||
|
||||
## Isolation For Untrusted Content
|
||||
### Isolation for untrusted content
|
||||
|
||||
A security issue exists whenever you receive code from an untrusted source (e.g.
|
||||
a remote server) and execute it locally. As an example, consider a remote
|
||||
@@ -65,72 +75,74 @@ an attacker somehow manages to change said content (either by attacking the
|
||||
source directly, or by sitting between your app and the actual destination), they
|
||||
will be able to execute native code on the user's machine.
|
||||
|
||||
> :warning: Under no circumstances should you load and execute remote code with
|
||||
:::warning
|
||||
Under no circumstances should you load and execute remote code with
|
||||
Node.js integration enabled. Instead, use only local files (packaged together
|
||||
with your application) to execute Node.js code. To display remote content, use
|
||||
the [`<webview>`][webview-tag] tag or [`BrowserView`][browser-view], make sure
|
||||
to disable the `nodeIntegration` and enable `contextIsolation`.
|
||||
:::
|
||||
|
||||
## Electron Security Warnings
|
||||
|
||||
From Electron 2.0 on, developers will see warnings and recommendations printed
|
||||
to the developer console. They only show up when the binary's name is Electron,
|
||||
indicating that a developer is currently looking at the console.
|
||||
:::info Electron security warnings
|
||||
Security warnings and recommendations are printed to the developer console.
|
||||
They only show up when the binary's name is Electron, indicating that a developer
|
||||
is currently looking at the console.
|
||||
|
||||
You can force-enable or force-disable these warnings by setting
|
||||
`ELECTRON_ENABLE_SECURITY_WARNINGS` or `ELECTRON_DISABLE_SECURITY_WARNINGS` on
|
||||
either `process.env` or the `window` object.
|
||||
:::
|
||||
|
||||
## Checklist: Security Recommendations
|
||||
## Checklist: Security recommendations
|
||||
|
||||
You should at least follow these steps to improve the security of your application:
|
||||
|
||||
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. [Enable sandboxing](#4-enable-sandboxing)
|
||||
4. [Enable process sandboxing](#4-enable-process-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)
|
||||
8. [Do not enable `allowRunningInsecureContent`](#8-do-not-enable-allowrunninginsecurecontent)
|
||||
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)
|
||||
11. [`<webview>`: Do not use `allowpopups`](#11-do-not-use-allowpopups-for-webviews)
|
||||
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)
|
||||
15. [Do not use `shell.openExternal` with untrusted content](#15-do-not-use-shellopenexternal-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
|
||||
[electronegativity](https://github.com/doyensec/electronegativity). For
|
||||
[Electronegativity](https://github.com/doyensec/electronegativity). For
|
||||
additional details on potential weaknesses and implementation bugs when
|
||||
developing applications using Electron, please refer to this [guide for
|
||||
developers and auditors](https://doyensec.com/resources/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security-wp.pdf)
|
||||
developers and auditors](https://doyensec.com/resources/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security-wp.pdf).
|
||||
|
||||
## 1) Only Load Secure Content
|
||||
### 1. Only load secure content
|
||||
|
||||
Any resources not included with your application should be loaded using a
|
||||
secure protocol like `HTTPS`. In other words, do not use insecure protocols
|
||||
like `HTTP`. Similarly, we recommend the use of `WSS` over `WS`, `FTPS` over
|
||||
`FTP`, and so on.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
`HTTPS` has three main benefits:
|
||||
|
||||
1) It authenticates the remote server, ensuring your app connects to the correct
|
||||
1. It authenticates the remote server, ensuring your app connects to the correct
|
||||
host instead of an impersonator.
|
||||
2) It ensures data integrity, asserting that the data was not modified while in
|
||||
1. It ensures data integrity, asserting that the data was not modified while in
|
||||
transit between your application and the host.
|
||||
3) It encrypts the traffic between your user and the destination host, making it
|
||||
1. It encrypts the traffic between your user and the destination host, making it
|
||||
more difficult to eavesdrop on the information sent between your app and
|
||||
the host.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Bad
|
||||
browserWindow.loadURL('http://example.com')
|
||||
|
||||
@@ -138,7 +150,7 @@ browserWindow.loadURL('http://example.com')
|
||||
browserWindow.loadURL('https://example.com')
|
||||
```
|
||||
|
||||
```html
|
||||
```html title='index.html (Renderer Process)'
|
||||
<!-- Bad -->
|
||||
<script crossorigin src="http://example.com/react.js"></script>
|
||||
<link rel="stylesheet" href="http://example.com/style.css">
|
||||
@@ -148,9 +160,11 @@ browserWindow.loadURL('https://example.com')
|
||||
<link rel="stylesheet" href="https://example.com/style.css">
|
||||
```
|
||||
|
||||
## 2) Do not enable Node.js Integration for Remote Content
|
||||
### 2. Do not enable Node.js integration for remote content
|
||||
|
||||
_This recommendation is the default behavior in Electron since 5.0.0._
|
||||
:::info
|
||||
This recommendation is the default behavior in Electron since 5.0.0.
|
||||
:::
|
||||
|
||||
It is paramount that you do not enable Node.js integration in any renderer
|
||||
([`BrowserWindow`][browser-window], [`BrowserView`][browser-view], or
|
||||
@@ -163,7 +177,7 @@ After this, you can grant additional permissions for specific hosts. For example
|
||||
if you are opening a BrowserWindow pointed at `https://example.com/`, you can
|
||||
give that website exactly the abilities it needs, but no more.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
A cross-site-scripting (XSS) attack is more dangerous if an attacker can jump
|
||||
out of the renderer process and execute code on the user's computer.
|
||||
@@ -172,12 +186,13 @@ power is usually limited to messing with the website that they are executed on.
|
||||
Disabling Node.js integration helps prevent an XSS from being escalated into a
|
||||
so-called "Remote Code Execution" (RCE) attack.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Bad
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
contextIsolation: false,
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true
|
||||
}
|
||||
@@ -186,7 +201,7 @@ const mainWindow = new BrowserWindow({
|
||||
mainWindow.loadURL('https://example.com')
|
||||
```
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Good
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
@@ -197,7 +212,7 @@ const mainWindow = new BrowserWindow({
|
||||
mainWindow.loadURL('https://example.com')
|
||||
```
|
||||
|
||||
```html
|
||||
```html title='index.html (Renderer Process)'
|
||||
<!-- Bad -->
|
||||
<webview nodeIntegration src="page.html"></webview>
|
||||
|
||||
@@ -208,21 +223,13 @@ mainWindow.loadURL('https://example.com')
|
||||
When disabling Node.js integration, you can still expose APIs to your website that
|
||||
do consume Node.js modules or features. Preload scripts continue to have access
|
||||
to `require` and other Node.js features, allowing developers to expose a custom
|
||||
API to remotely loaded content.
|
||||
API to remotely loaded content via the [contextBridge API](../api/context-bridge.md).
|
||||
|
||||
In the following example preload script, the later loaded website will have
|
||||
access to a `window.readConfig()` method, but no Node.js features.
|
||||
### 3. Enable Context Isolation for remote content
|
||||
|
||||
```js
|
||||
const { readFileSync } = require('fs')
|
||||
|
||||
window.readConfig = () => {
|
||||
const data = readFileSync('./config.json')
|
||||
return data
|
||||
}
|
||||
```
|
||||
|
||||
## 3) Enable Context Isolation for Remote Content
|
||||
:::info
|
||||
This recommendation is the default behavior in Electron since 12.0.0.
|
||||
:::
|
||||
|
||||
Context isolation is an Electron feature that allows developers to run code
|
||||
in preload scripts and in Electron APIs in a dedicated JavaScript context. In
|
||||
@@ -235,48 +242,42 @@ to enable this behavior.
|
||||
Even when `nodeIntegration: false` is used, to truly enforce strong isolation
|
||||
and prevent the use of Node primitives `contextIsolation` **must** also be used.
|
||||
|
||||
### Why & How?
|
||||
|
||||
:::info
|
||||
For more information on what `contextIsolation` is and how to enable it please
|
||||
see our dedicated [Context Isolation](context-isolation.md) document.
|
||||
:::info
|
||||
|
||||
## 4) Enable Sandboxing
|
||||
### 4. Enable process sandboxing
|
||||
|
||||
[Sandboxing](sandbox.md) is a Chromium feature that uses the operating system to
|
||||
[Sandboxing](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/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?
|
||||
:::info
|
||||
For more information on what `contextIsolation` is and how to enable it please
|
||||
see our dedicated [Process Sandboxing](sandbox.md) document.
|
||||
:::info
|
||||
|
||||
When creating a window, pass the `sandbox: true` option in `webPreferences`:
|
||||
### 5. Handle session permission requests from remote content
|
||||
|
||||
```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
|
||||
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 (
|
||||
like notifications).
|
||||
|
||||
The API is based on the [Chromium permissions API](https://developer.chrome.com/extensions/permissions)
|
||||
and implements the same types of permissions.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
By default, Electron will automatically approve all permission requests unless
|
||||
the developer has manually configured a custom handler. While a solid default,
|
||||
security-conscious developers might want to assume the very opposite.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
const { session } = require('electron')
|
||||
|
||||
session
|
||||
@@ -297,9 +298,11 @@ session
|
||||
})
|
||||
```
|
||||
|
||||
## 6) Do Not Disable WebSecurity
|
||||
### 6. Do not disable `webSecurity`
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
:::info
|
||||
This recommendation is Electron's default.
|
||||
:::
|
||||
|
||||
You may have already guessed that disabling the `webSecurity` property on a
|
||||
renderer process ([`BrowserWindow`][browser-window],
|
||||
@@ -308,15 +311,15 @@ security features.
|
||||
|
||||
Do not disable `webSecurity` in production applications.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Disabling `webSecurity` will disable the same-origin policy and set
|
||||
`allowRunningInsecureContent` property to `true`. In other words, it allows
|
||||
the execution of insecure code from different domains.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Bad
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
@@ -325,12 +328,12 @@ const mainWindow = new BrowserWindow({
|
||||
})
|
||||
```
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Good
|
||||
const mainWindow = new BrowserWindow()
|
||||
```
|
||||
|
||||
```html
|
||||
```html title='index.html (Renderer Process)'
|
||||
<!-- Bad -->
|
||||
<webview disablewebsecurity src="page.html"></webview>
|
||||
|
||||
@@ -338,13 +341,13 @@ const mainWindow = new BrowserWindow()
|
||||
<webview src="page.html"></webview>
|
||||
```
|
||||
|
||||
## 7) 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
|
||||
be enabled by any website you load inside Electron.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
CSP allows the server serving content to restrict and control the resources
|
||||
Electron can load for that given web page. `https://example.com` should
|
||||
@@ -352,6 +355,8 @@ be allowed to load scripts from the origins you defined while scripts from
|
||||
`https://evil.attacker.com` should not be allowed to run. Defining a CSP is an
|
||||
easy way to improve your application's security.
|
||||
|
||||
#### How?
|
||||
|
||||
The following CSP will allow Electron to execute scripts from the current
|
||||
website and from `apis.example.com`.
|
||||
|
||||
@@ -363,14 +368,14 @@ Content-Security-Policy: '*'
|
||||
Content-Security-Policy: script-src 'self' https://apis.example.com
|
||||
```
|
||||
|
||||
### CSP HTTP Header
|
||||
#### CSP HTTP headers
|
||||
|
||||
Electron respects the [`Content-Security-Policy` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
|
||||
which can be set using Electron's
|
||||
[`webRequest.onHeadersReceived`](../api/web-request.md#webrequestonheadersreceivedfilter-listener)
|
||||
handler:
|
||||
|
||||
```javascript
|
||||
```javascript title='main.js (Main Process)'
|
||||
const { session } = require('electron')
|
||||
|
||||
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
||||
@@ -383,20 +388,22 @@ session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
||||
})
|
||||
```
|
||||
|
||||
### CSP Meta Tag
|
||||
#### CSP meta tag
|
||||
|
||||
CSP's preferred delivery mechanism is an HTTP header, however it is not possible
|
||||
CSP's preferred delivery mechanism is an HTTP header. However, it is not possible
|
||||
to use this method when loading a resource using the `file://` protocol. It can
|
||||
be useful in some cases, such as using the `file://` protocol, to set a policy
|
||||
on a page directly in the markup using a `<meta>` tag:
|
||||
be useful in some cases to set a policy on a page directly in the markup using a
|
||||
`<meta>` tag:
|
||||
|
||||
```html
|
||||
```html title='index.html (Renderer Process)'
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'">
|
||||
```
|
||||
|
||||
## 8) Do Not Set `allowRunningInsecureContent` to `true`
|
||||
### 8. Do not enable `allowRunningInsecureContent`
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
:::info
|
||||
This recommendation is Electron's default.
|
||||
:::
|
||||
|
||||
By default, Electron will not allow websites loaded over `HTTPS` to load and
|
||||
execute scripts, CSS, or plugins from insecure sources (`HTTP`). Setting the
|
||||
@@ -405,15 +412,15 @@ property `allowRunningInsecureContent` to `true` disables that protection.
|
||||
Loading the initial HTML of a website over `HTTPS` and attempting to load
|
||||
subsequent resources via `HTTP` is also known as "mixed content".
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Loading content over `HTTPS` assures the authenticity and integrity
|
||||
of the loaded resources while encrypting the traffic itself. See the section on
|
||||
[only displaying secure content](#1-only-load-secure-content) for more details.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Bad
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
@@ -422,19 +429,21 @@ const mainWindow = new BrowserWindow({
|
||||
})
|
||||
```
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Good
|
||||
const mainWindow = new BrowserWindow({})
|
||||
```
|
||||
|
||||
## 9) Do Not Enable Experimental Features
|
||||
### 9. Do not enable experimental features
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
:::info
|
||||
This recommendation is Electron's default.
|
||||
:::
|
||||
|
||||
Advanced users of Electron can enable experimental Chromium features using the
|
||||
`experimentalFeatures` property.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Experimental features are, as the name suggests, experimental and have not been
|
||||
enabled for all Chromium users. Furthermore, their impact on Electron as a whole
|
||||
@@ -443,9 +452,9 @@ has likely not been tested.
|
||||
Legitimate use cases exist, but unless you know what you are doing, you should
|
||||
not enable this property.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Bad
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
@@ -454,20 +463,22 @@ const mainWindow = new BrowserWindow({
|
||||
})
|
||||
```
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Good
|
||||
const mainWindow = new BrowserWindow({})
|
||||
```
|
||||
|
||||
## 10) Do Not Use `enableBlinkFeatures`
|
||||
### 10. Do not use `enableBlinkFeatures`
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
:::info
|
||||
This recommendation is Electron's default.
|
||||
:::
|
||||
|
||||
Blink is the name of the rendering engine behind Chromium. As with
|
||||
`experimentalFeatures`, the `enableBlinkFeatures` property allows developers to
|
||||
enable features that have been disabled by default.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Generally speaking, there are likely good reasons if a feature was not enabled
|
||||
by default. Legitimate use cases for enabling specific features exist. As a
|
||||
@@ -475,9 +486,9 @@ developer, you should know exactly why you need to enable a feature, what the
|
||||
ramifications are, and how it impacts the security of your application. Under
|
||||
no circumstances should you enable features speculatively.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Bad
|
||||
const mainWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
@@ -486,14 +497,16 @@ const mainWindow = new BrowserWindow({
|
||||
})
|
||||
```
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Good
|
||||
const mainWindow = new BrowserWindow()
|
||||
```
|
||||
|
||||
## 11) Do Not Use `allowpopups`
|
||||
### 11. Do not use `allowpopups` for WebViews
|
||||
|
||||
_Recommendation is Electron's default_
|
||||
:::info
|
||||
This recommendation is Electron's default.
|
||||
:::
|
||||
|
||||
If you are using [`<webview>`][webview-tag], you might need the pages and scripts
|
||||
loaded in your `<webview>` tag to open new windows. The `allowpopups` attribute
|
||||
@@ -501,16 +514,16 @@ enables them to create new [`BrowserWindows`][browser-window] using the
|
||||
`window.open()` method. `<webview>` tags are otherwise not allowed to create new
|
||||
windows.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
If you do not need popups, you are better off not allowing the creation of
|
||||
new [`BrowserWindows`][browser-window] by default. This follows the principle
|
||||
of minimally required access: Don't let a website create new popups unless
|
||||
you know it needs that feature.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
```html
|
||||
```html title='index.html (Renderer Process)'
|
||||
<!-- Bad -->
|
||||
<webview allowpopups src="page.html"></webview>
|
||||
|
||||
@@ -518,7 +531,7 @@ you know it needs that feature.
|
||||
<webview src="page.html"></webview>
|
||||
```
|
||||
|
||||
## 12) 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
|
||||
@@ -528,7 +541,7 @@ It is a good idea to control the creation of new [`<webview>`][webview-tag] tags
|
||||
from the main process and to verify that their webPreferences do not disable
|
||||
security features.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Since `<webview>` live in the DOM, they can be created by a script running on your
|
||||
website even if Node.js integration is otherwise disabled.
|
||||
@@ -538,13 +551,13 @@ a renderer process. In most cases, developers do not need to disable any of
|
||||
those features - and you should therefore not allow different configurations
|
||||
for newly created [`<webview>`][webview-tag] tags.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
Before a [`<webview>`][webview-tag] tag is attached, Electron will fire the
|
||||
`will-attach-webview` event on the hosting `webContents`. Use the event to
|
||||
prevent the creation of `webViews` with possibly insecure options.
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
app.on('web-contents-created', (event, contents) => {
|
||||
contents.on('will-attach-webview', (event, webPreferences, params) => {
|
||||
// Strip away preload scripts if unused or verify their location is legitimate
|
||||
@@ -562,16 +575,16 @@ app.on('web-contents-created', (event, contents) => {
|
||||
})
|
||||
```
|
||||
|
||||
Again, this list merely minimizes the risk, it does not remove it. If your goal
|
||||
Again, this list merely minimizes the risk, but does not remove it. If your goal
|
||||
is to display a website, a browser will be a more secure option.
|
||||
|
||||
## 13) 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
|
||||
any other kinds of navigation.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Navigation is a common attack vector. If an attacker can convince your app to
|
||||
navigate away from its current page, they can possibly force your app to open
|
||||
@@ -584,7 +597,7 @@ A common attack pattern is that the attacker convinces your app's users to
|
||||
interact with the app in such a way that it navigates to one of the attacker's
|
||||
pages. This is usually done via links, plugins, or other user-generated content.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
If your app has no need for navigation, you can call `event.preventDefault()`
|
||||
in a [`will-navigate`][will-navigate] handler. If you know which pages your app
|
||||
@@ -595,7 +608,7 @@ We recommend that you use Node's parser for URLs. Simple string comparisons can
|
||||
sometimes be fooled - a `startsWith('https://example.com')` test would let
|
||||
`https://example.com.attacker.com` through.
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
const URL = require('url').URL
|
||||
|
||||
app.on('web-contents-created', (event, contents) => {
|
||||
@@ -609,12 +622,12 @@ app.on('web-contents-created', (event, contents) => {
|
||||
})
|
||||
```
|
||||
|
||||
## 14) 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.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Much like navigation, the creation of new `webContents` is a common attack
|
||||
vector. Attackers attempt to convince your app to create new windows, frames,
|
||||
@@ -627,7 +640,7 @@ security at no cost. This is commonly the case for apps that open one
|
||||
`BrowserWindow` and do not need to open an arbitrary number of additional
|
||||
windows at runtime.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
[`webContents`][web-contents] will delegate to its [window open
|
||||
handler][window-open-handler] before creating new windows. The handler will
|
||||
@@ -635,7 +648,7 @@ receive, amongst other parameters, the `url` the window was requested to open
|
||||
and the options used to create it. We recommend that you register a handler to
|
||||
monitor the creation of windows, and deny any unexpected window creation.
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
const { shell } = require('electron')
|
||||
|
||||
app.on('web-contents-created', (event, contents) => {
|
||||
@@ -656,40 +669,40 @@ app.on('web-contents-created', (event, contents) => {
|
||||
})
|
||||
```
|
||||
|
||||
## 15) Do not use `openExternal` with untrusted content
|
||||
### 15. Do not use `shell.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
|
||||
to the `open` terminal command utility and will open the specific application
|
||||
based on the URI and filetype association.
|
||||
The shell module's [`openExternal`][open-external] API allows opening a given
|
||||
protocol URI with the desktop's native utilities. On macOS, for instance, this
|
||||
function is similar to the `open` terminal command utility and will open the
|
||||
specific application based on the URI and filetype association.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
Improper use of [`openExternal`][open-external] can be leveraged to compromise
|
||||
the user's host. When openExternal is used with untrusted content, it can be
|
||||
leveraged to execute arbitrary commands.
|
||||
|
||||
### How?
|
||||
#### How?
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Bad
|
||||
const { shell } = require('electron')
|
||||
shell.openExternal(USER_CONTROLLED_DATA_HERE)
|
||||
```
|
||||
|
||||
```js
|
||||
```js title='main.js (Main Process)'
|
||||
// Good
|
||||
const { shell } = require('electron')
|
||||
shell.openExternal('https://example.com/index.html')
|
||||
```
|
||||
|
||||
## 16) 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
|
||||
app as quickly as possible.
|
||||
|
||||
### Why?
|
||||
#### Why?
|
||||
|
||||
An application built with an older version of Electron, Chromium, and Node.js
|
||||
is an easier target than an application that is using more recent versions of
|
||||
@@ -705,6 +718,13 @@ to fix issues before publishing them. Your application will be more secure if
|
||||
it is running a recent version of Electron (and thus, Chromium and Node.js) for
|
||||
which potential security issues are not as widely known.
|
||||
|
||||
#### How?
|
||||
|
||||
Migrate your app one major version at a time, while referring to Electron's
|
||||
[Breaking Changes][breaking-changes] document to see if any code needs to
|
||||
be updated.
|
||||
|
||||
[breaking-changes]: ../breaking-changes.md
|
||||
[browser-window]: ../api/browser-window.md
|
||||
[browser-view]: ../api/browser-view.md
|
||||
[webview-tag]: ../api/webview-tag.md
|
||||
@@ -712,5 +732,4 @@ which potential security issues are not as widely known.
|
||||
[window-open-handler]: ../api/web-contents.md#contentssetwindowopenhandlerhandler
|
||||
[will-navigate]: ../api/web-contents.md#event-will-navigate
|
||||
[open-external]: ../api/shell.md#shellopenexternalurl-options
|
||||
[sandbox]: ../tutorial/sandbox.md
|
||||
[responsible-disclosure]: https://en.wikipedia.org/wiki/Responsible_disclosure
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "18.0.0-nightly.20220201",
|
||||
"version": "18.0.0-alpha.5",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -113,3 +113,5 @@ revert_stop_using_nsrunloop_in_renderer_process.patch
|
||||
fix_dont_delete_SerialPortManager_on_main_thread.patch
|
||||
feat_add_data_transfer_to_requestsingleinstancelock.patch
|
||||
fix_crash_when_saving_edited_pdf_files.patch
|
||||
drop_extra_printingcontext_calls_to_newpage_pagedone.patch
|
||||
fix_don_t_restore_maximized_windows_when_calling_showinactive.patch
|
||||
|
||||
@@ -0,0 +1,511 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Alan Screen <awscreen@chromium.org>
|
||||
Date: Wed, 5 Jan 2022 23:15:29 +0000
|
||||
Subject: Drop extra PrintingContext calls to NewPage/PageDone
|
||||
|
||||
When system calls were refactored out of PrintedDocument in
|
||||
https://crrev.com/948253, there were extra calls to PrintingContext
|
||||
NewPage() and PageDone() left in PrintedDocument::RenderPrintedDocument.
|
||||
These had no effect on Windows or Linux, but caused an issue for macOS.
|
||||
|
||||
This was undetected by tests which use TestPrintingContext, and would
|
||||
only be detectable if a test was capable of using system drivers (which
|
||||
generally does not occur on the bots). Adding a test to detect this is
|
||||
difficult at this time, but should be easier to do once later portions
|
||||
of the out-of-process print drivers commit chains fill in more testing
|
||||
capabilities. At that point it should be easier to fire off a request
|
||||
to have the macOS Preview be the output. https://crbug.com/1284745
|
||||
has been filed to capture this needed effort.
|
||||
|
||||
Remove NewPage()/PageDone() as common methods to override from
|
||||
PrintingContext and make these specific to macOS only. Update the
|
||||
various implementations of PrintingContext::PrintDocument() to include
|
||||
the aborted and in-print-job checks that were previously in all
|
||||
NewPage() implementations. The same corresponding checks from
|
||||
PageDone() can be handled by PrintedDocument::RenderPrintedDocument()
|
||||
and PrintedDocumentWin::RenderPrintedPage(). There is no concern about
|
||||
`in_print_job_` having changed during the lifetime of
|
||||
RenderPrintedDocument()/RenderPrintedPage(), so just add extra checks
|
||||
against printing having been asynchronouly aborted before allowing a
|
||||
final successful return.
|
||||
|
||||
Bug: 1283651
|
||||
Change-Id: I52992bc7550dd25d4ad9a1dc633c7d9452a27bb1
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3367333
|
||||
Reviewed-by: Lei Zhang <thestig@chromium.org>
|
||||
Commit-Queue: Alan Screen <awscreen@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#955936}
|
||||
|
||||
diff --git a/chrome/browser/printing/print_job_worker.cc b/chrome/browser/printing/print_job_worker.cc
|
||||
index 4283a5743c695a7376722f80925722d9e7a6496e..992a59c32ea082e3593c0183819d1b174fc8db7a 100644
|
||||
--- a/chrome/browser/printing/print_job_worker.cc
|
||||
+++ b/chrome/browser/printing/print_job_worker.cc
|
||||
@@ -524,12 +524,6 @@ void PrintJobWorker::SpoolPage(PrintedPage* page) {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK_NE(page_number_, PageNumber::npos());
|
||||
|
||||
- // Preprocess.
|
||||
- if (printing_context_->NewPage() != mojom::ResultCode::kSuccess) {
|
||||
- OnFailure();
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
// Actual printing.
|
||||
if (document_->RenderPrintedPage(*page, printing_context_.get()) !=
|
||||
mojom::ResultCode::kSuccess) {
|
||||
@@ -537,12 +531,6 @@ void PrintJobWorker::SpoolPage(PrintedPage* page) {
|
||||
return;
|
||||
}
|
||||
|
||||
- // Postprocess.
|
||||
- if (printing_context_->PageDone() != mojom::ResultCode::kSuccess) {
|
||||
- OnFailure();
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
// Signal everyone that the page is printed.
|
||||
DCHECK(print_job_);
|
||||
print_job_->PostTask(FROM_HERE,
|
||||
diff --git a/printing/emf_win_unittest.cc b/printing/emf_win_unittest.cc
|
||||
index e830a1017f2262d2d1c226aa16d41a6ffe53aadf..7daa9f9bedd664123d7590b9831169e99688b9c9 100644
|
||||
--- a/printing/emf_win_unittest.cc
|
||||
+++ b/printing/emf_win_unittest.cc
|
||||
@@ -115,7 +115,6 @@ TEST_F(EmfPrintingTest, Enumerate) {
|
||||
// current directory.
|
||||
// TODO(maruel): Clean the .PRN file generated in current directory.
|
||||
context.NewDocument(u"EmfTest.Enumerate");
|
||||
- context.NewPage();
|
||||
// Process one at a time.
|
||||
RECT page_bounds = emf.GetPageBounds(1).ToRECT();
|
||||
Emf::Enumerator emf_enum(emf, context.context(), &page_bounds);
|
||||
@@ -129,7 +128,6 @@ TEST_F(EmfPrintingTest, Enumerate) {
|
||||
EXPECT_TRUE(itr->SafePlayback(&emf_enum.context_))
|
||||
<< " index: " << index << " type: " << itr->record()->iType;
|
||||
}
|
||||
- context.PageDone();
|
||||
context.DocumentDone();
|
||||
}
|
||||
|
||||
diff --git a/printing/printed_document.cc b/printing/printed_document.cc
|
||||
index 54dd798f4c76c34fd6414f68d3ad11a15c1673af..81ebe973fc6a99d66f5d8792aa19e01764f33d3f 100644
|
||||
--- a/printing/printed_document.cc
|
||||
+++ b/printing/printed_document.cc
|
||||
@@ -187,17 +187,18 @@ const MetafilePlayer* PrintedDocument::GetMetafile() {
|
||||
|
||||
mojom::ResultCode PrintedDocument::RenderPrintedDocument(
|
||||
PrintingContext* context) {
|
||||
- mojom::ResultCode result = context->NewPage();
|
||||
+ base::AutoLock lock(lock_);
|
||||
+ mojom::ResultCode result = context->PrintDocument(
|
||||
+ *GetMetafile(), *immutable_.settings_, mutable_.expected_page_count_);
|
||||
if (result != mojom::ResultCode::kSuccess)
|
||||
return result;
|
||||
- {
|
||||
- base::AutoLock lock(lock_);
|
||||
- result = context->PrintDocument(*GetMetafile(), *immutable_.settings_,
|
||||
- mutable_.expected_page_count_);
|
||||
- if (result != mojom::ResultCode::kSuccess)
|
||||
- return result;
|
||||
- }
|
||||
- return context->PageDone();
|
||||
+
|
||||
+ // Beware of any asynchronous aborts of the print job that happened during
|
||||
+ // printing.
|
||||
+ if (context->PrintingAborted())
|
||||
+ return mojom::ResultCode::kCanceled;
|
||||
+
|
||||
+ return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
||||
bool PrintedDocument::IsComplete() const {
|
||||
diff --git a/printing/printed_document_win.cc b/printing/printed_document_win.cc
|
||||
index 4024150677fb64f8f8c9d53dfa73cf709c819a7c..8e34b288ec518c4e2d0c5d8113f38440ad2c648b 100644
|
||||
--- a/printing/printed_document_win.cc
|
||||
+++ b/printing/printed_document_win.cc
|
||||
@@ -24,8 +24,17 @@ mojom::ResultCode PrintedDocument::RenderPrintedPage(
|
||||
#endif
|
||||
|
||||
DCHECK(context);
|
||||
- return context->RenderPage(page,
|
||||
- immutable_.settings_->page_setup_device_units());
|
||||
+ mojom::ResultCode result = context->RenderPage(
|
||||
+ page, immutable_.settings_->page_setup_device_units());
|
||||
+ if (result != mojom::ResultCode::kSuccess)
|
||||
+ return result;
|
||||
+
|
||||
+ // Beware of any asynchronous aborts of the print job that happened during
|
||||
+ // printing.
|
||||
+ if (context->PrintingAborted())
|
||||
+ return mojom::ResultCode::kCanceled;
|
||||
+
|
||||
+ return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
||||
} // namespace printing
|
||||
diff --git a/printing/printing_context.h b/printing/printing_context.h
|
||||
index e87170e6957733f06bcc296bcca3fc331557ed46..e196a7d448ca41ea02d523a4de410dedf4298b5e 100644
|
||||
--- a/printing/printing_context.h
|
||||
+++ b/printing/printing_context.h
|
||||
@@ -122,18 +122,12 @@ class COMPONENT_EXPORT(PRINTING) PrintingContext {
|
||||
virtual mojom::ResultCode NewDocument(
|
||||
const std::u16string& document_name) = 0;
|
||||
|
||||
- // Starts a new page.
|
||||
- virtual mojom::ResultCode NewPage() = 0;
|
||||
-
|
||||
#if defined(OS_WIN)
|
||||
// Renders a page.
|
||||
virtual mojom::ResultCode RenderPage(const PrintedPage& page,
|
||||
const PageSetup& page_setup) = 0;
|
||||
#endif
|
||||
|
||||
- // Closes the printed page.
|
||||
- virtual mojom::ResultCode PageDone() = 0;
|
||||
-
|
||||
// Prints the document contained in `metafile`.
|
||||
virtual mojom::ResultCode PrintDocument(const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
@@ -178,6 +172,8 @@ class COMPONENT_EXPORT(PRINTING) PrintingContext {
|
||||
// Reinitializes the settings for object reuse.
|
||||
void ResetSettings();
|
||||
|
||||
+ bool PrintingAborted() const { return abort_printing_; }
|
||||
+
|
||||
int job_id() const { return job_id_; }
|
||||
|
||||
protected:
|
||||
diff --git a/printing/printing_context_android.cc b/printing/printing_context_android.cc
|
||||
index c28a40eb0a9ce0058d9f85948eda5f83d0c64791..5b2f096cb2a705e7c4c7ebbeddbf93cac1b9eafb 100644
|
||||
--- a/printing/printing_context_android.cc
|
||||
+++ b/printing/printing_context_android.cc
|
||||
@@ -213,30 +213,13 @@ mojom::ResultCode PrintingContextAndroid::NewDocument(
|
||||
return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
||||
-mojom::ResultCode PrintingContextAndroid::NewPage() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op.
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
-mojom::ResultCode PrintingContextAndroid::PageDone() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op.
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
mojom::ResultCode PrintingContextAndroid::PrintDocument(
|
||||
const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) {
|
||||
+ if (abort_printing_)
|
||||
+ return mojom::ResultCode::kCanceled;
|
||||
+ DCHECK(in_print_job_);
|
||||
DCHECK(is_file_descriptor_valid());
|
||||
|
||||
return metafile.SaveToFileDescriptor(fd_) ? mojom::ResultCode::kSuccess
|
||||
diff --git a/printing/printing_context_android.h b/printing/printing_context_android.h
|
||||
index 676d98949da675e3283969dd3c9c61028dc1c7b5..b4f2f25edec45f91f241b0b1a7bc73b3f51af252 100644
|
||||
--- a/printing/printing_context_android.h
|
||||
+++ b/printing/printing_context_android.h
|
||||
@@ -60,8 +60,6 @@ class COMPONENT_EXPORT(PRINTING) PrintingContextAndroid
|
||||
mojom::ResultCode UpdatePrinterSettings(
|
||||
const PrinterSettings& printer_settings) override;
|
||||
mojom::ResultCode NewDocument(const std::u16string& document_name) override;
|
||||
- mojom::ResultCode NewPage() override;
|
||||
- mojom::ResultCode PageDone() override;
|
||||
mojom::ResultCode PrintDocument(const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) override;
|
||||
diff --git a/printing/printing_context_chromeos.cc b/printing/printing_context_chromeos.cc
|
||||
index d996c3e1f7b2d9f96f5c50aadff67f8273c7e760..670df66ae406fa883f801682de699aab16779591 100644
|
||||
--- a/printing/printing_context_chromeos.cc
|
||||
+++ b/printing/printing_context_chromeos.cc
|
||||
@@ -402,32 +402,14 @@ mojom::ResultCode PrintingContextChromeos::NewDocument(
|
||||
return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
||||
-mojom::ResultCode PrintingContextChromeos::NewPage() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
-
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op.
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
-mojom::ResultCode PrintingContextChromeos::PageDone() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
-
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op.
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
mojom::ResultCode PrintingContextChromeos::PrintDocument(
|
||||
const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) {
|
||||
+ if (abort_printing_)
|
||||
+ return mojom::ResultCode::kCanceled;
|
||||
+ DCHECK(in_print_job_);
|
||||
+
|
||||
#if defined(USE_CUPS)
|
||||
std::vector<char> buffer;
|
||||
if (!metafile.GetDataAsVector(&buffer))
|
||||
diff --git a/printing/printing_context_chromeos.h b/printing/printing_context_chromeos.h
|
||||
index 77353372b2c6e003428d37ab634167e8dc824fa4..06cb16c8a569a4d90ccdccc5fefcaaf527e49f5f 100644
|
||||
--- a/printing/printing_context_chromeos.h
|
||||
+++ b/printing/printing_context_chromeos.h
|
||||
@@ -39,8 +39,6 @@ class COMPONENT_EXPORT(PRINTING) PrintingContextChromeos
|
||||
mojom::ResultCode UpdatePrinterSettings(
|
||||
const PrinterSettings& printer_settings) override;
|
||||
mojom::ResultCode NewDocument(const std::u16string& document_name) override;
|
||||
- mojom::ResultCode NewPage() override;
|
||||
- mojom::ResultCode PageDone() override;
|
||||
mojom::ResultCode PrintDocument(const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) override;
|
||||
diff --git a/printing/printing_context_linux.cc b/printing/printing_context_linux.cc
|
||||
index c5adfa317747cda3d370a2ace52d843662f4ce91..204cec8311bec69674f1da2223e8d6c4b5a13ba3 100644
|
||||
--- a/printing/printing_context_linux.cc
|
||||
+++ b/printing/printing_context_linux.cc
|
||||
@@ -151,30 +151,13 @@ mojom::ResultCode PrintingContextLinux::NewDocument(
|
||||
return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
||||
-mojom::ResultCode PrintingContextLinux::NewPage() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op.
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
-mojom::ResultCode PrintingContextLinux::PageDone() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op.
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
mojom::ResultCode PrintingContextLinux::PrintDocument(
|
||||
const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) {
|
||||
+ if (abort_printing_)
|
||||
+ return mojom::ResultCode::kCanceled;
|
||||
+ DCHECK(in_print_job_);
|
||||
DCHECK(print_dialog_);
|
||||
// TODO(crbug.com/1252685) Plumb error code back from
|
||||
// `PrintDialogGtkInterface`.
|
||||
diff --git a/printing/printing_context_linux.h b/printing/printing_context_linux.h
|
||||
index 17d768a24141954df80bed3c5dd6735c34115195..653170ba60e8341c972cb0126109d48394e0de7b 100644
|
||||
--- a/printing/printing_context_linux.h
|
||||
+++ b/printing/printing_context_linux.h
|
||||
@@ -45,8 +45,6 @@ class COMPONENT_EXPORT(PRINTING) PrintingContextLinux : public PrintingContext {
|
||||
mojom::ResultCode UpdatePrinterSettings(
|
||||
const PrinterSettings& printer_settings) override;
|
||||
mojom::ResultCode NewDocument(const std::u16string& document_name) override;
|
||||
- mojom::ResultCode NewPage() override;
|
||||
- mojom::ResultCode PageDone() override;
|
||||
mojom::ResultCode PrintDocument(const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) override;
|
||||
diff --git a/printing/printing_context_mac.h b/printing/printing_context_mac.h
|
||||
index 85363bd922bf0ab2630e3d5f350de0c58792963a..221019f5df71e1d66accbf2ea2d161bd1125666f 100644
|
||||
--- a/printing/printing_context_mac.h
|
||||
+++ b/printing/printing_context_mac.h
|
||||
@@ -35,8 +35,6 @@ class COMPONENT_EXPORT(PRINTING) PrintingContextMac : public PrintingContext {
|
||||
mojom::ResultCode UpdatePrinterSettings(
|
||||
const PrinterSettings& printer_settings) override;
|
||||
mojom::ResultCode NewDocument(const std::u16string& document_name) override;
|
||||
- mojom::ResultCode NewPage() override;
|
||||
- mojom::ResultCode PageDone() override;
|
||||
mojom::ResultCode PrintDocument(const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) override;
|
||||
@@ -105,6 +103,12 @@ class COMPONENT_EXPORT(PRINTING) PrintingContextMac : public PrintingContext {
|
||||
// Returns true is the pair is set.
|
||||
bool SetKeyValue(base::StringPiece key, base::StringPiece value);
|
||||
|
||||
+ // Starts a new page.
|
||||
+ mojom::ResultCode NewPage();
|
||||
+
|
||||
+ // Closes the printed page.
|
||||
+ mojom::ResultCode PageDone();
|
||||
+
|
||||
// The native print info object.
|
||||
base::scoped_nsobject<NSPrintInfo> print_info_;
|
||||
|
||||
diff --git a/printing/printing_context_no_system_dialog.cc b/printing/printing_context_no_system_dialog.cc
|
||||
index c10123f35b11e1600c813080585a2282d775dba1..253507fcee5476f497421316bc42a0794f97b12b 100644
|
||||
--- a/printing/printing_context_no_system_dialog.cc
|
||||
+++ b/printing/printing_context_no_system_dialog.cc
|
||||
@@ -98,26 +98,6 @@ mojom::ResultCode PrintingContextNoSystemDialog::NewDocument(
|
||||
return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
||||
-mojom::ResultCode PrintingContextNoSystemDialog::NewPage() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op.
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
-mojom::ResultCode PrintingContextNoSystemDialog::PageDone() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op.
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
mojom::ResultCode PrintingContextNoSystemDialog::PrintDocument(
|
||||
const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
diff --git a/printing/printing_context_no_system_dialog.h b/printing/printing_context_no_system_dialog.h
|
||||
index 2753d7ba7a09e1c5e88c1eb21a9b146034ea53f5..cb0d0b8c12535c83dbfc07ecb2817f0504b4a8ba 100644
|
||||
--- a/printing/printing_context_no_system_dialog.h
|
||||
+++ b/printing/printing_context_no_system_dialog.h
|
||||
@@ -32,8 +32,6 @@ class COMPONENT_EXPORT(PRINTING) PrintingContextNoSystemDialog
|
||||
mojom::ResultCode UpdatePrinterSettings(
|
||||
const PrinterSettings& printer_settings) override;
|
||||
mojom::ResultCode NewDocument(const std::u16string& document_name) override;
|
||||
- mojom::ResultCode NewPage() override;
|
||||
- mojom::ResultCode PageDone() override;
|
||||
mojom::ResultCode PrintDocument(const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) override;
|
||||
diff --git a/printing/printing_context_win.cc b/printing/printing_context_win.cc
|
||||
index 0addb52cd2f65b6f05b7cfb7cf8602baaf592541..4f1fb12619fc9bee0cce104ba60621008284d675 100644
|
||||
--- a/printing/printing_context_win.cc
|
||||
+++ b/printing/printing_context_win.cc
|
||||
@@ -366,18 +366,6 @@ mojom::ResultCode PrintingContextWin::NewDocument(
|
||||
return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
||||
-mojom::ResultCode PrintingContextWin::NewPage() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(context_);
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op. MetafileSkia::SafePlayback takes care of calling
|
||||
- // ::StartPage().
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
mojom::ResultCode PrintingContextWin::RenderPage(const PrintedPage& page,
|
||||
const PageSetup& page_setup) {
|
||||
if (abort_printing_)
|
||||
@@ -416,17 +404,6 @@ mojom::ResultCode PrintingContextWin::RenderPage(const PrintedPage& page,
|
||||
return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
||||
-mojom::ResultCode PrintingContextWin::PageDone() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // Intentional No-op. MetafileSkia::SafePlayback takes care of calling
|
||||
- // ::EndPage().
|
||||
-
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
mojom::ResultCode PrintingContextWin::PrintDocument(
|
||||
const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
diff --git a/printing/printing_context_win.h b/printing/printing_context_win.h
|
||||
index 700bfe2c744cc18b1be25b9db02dd690b5e306f9..babd443a1586c92a70ff6362a824e29a6395fae2 100644
|
||||
--- a/printing/printing_context_win.h
|
||||
+++ b/printing/printing_context_win.h
|
||||
@@ -35,10 +35,8 @@ class COMPONENT_EXPORT(PRINTING) PrintingContextWin : public PrintingContext {
|
||||
mojom::ResultCode UpdatePrinterSettings(
|
||||
const PrinterSettings& printer_settings) override;
|
||||
mojom::ResultCode NewDocument(const std::u16string& document_name) override;
|
||||
- mojom::ResultCode NewPage() override;
|
||||
mojom::ResultCode RenderPage(const PrintedPage& page,
|
||||
const PageSetup& page_setup) override;
|
||||
- mojom::ResultCode PageDone() override;
|
||||
mojom::ResultCode PrintDocument(const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) override;
|
||||
diff --git a/printing/test_printing_context.cc b/printing/test_printing_context.cc
|
||||
index 4f0bf0ae891164120d36c79a7df98845f38e55de..208e8a16b5648383ff26d8dec4a15f6485ef4454 100644
|
||||
--- a/printing/test_printing_context.cc
|
||||
+++ b/printing/test_printing_context.cc
|
||||
@@ -116,15 +116,6 @@ mojom::ResultCode TestPrintingContext::NewDocument(
|
||||
return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
||||
-mojom::ResultCode TestPrintingContext::NewPage() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // No-op.
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
#if defined(OS_WIN)
|
||||
mojom::ResultCode TestPrintingContext::RenderPage(const PrintedPage& page,
|
||||
const PageSetup& page_setup) {
|
||||
@@ -138,15 +129,6 @@ mojom::ResultCode TestPrintingContext::RenderPage(const PrintedPage& page,
|
||||
}
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
-mojom::ResultCode TestPrintingContext::PageDone() {
|
||||
- if (abort_printing_)
|
||||
- return mojom::ResultCode::kCanceled;
|
||||
- DCHECK(in_print_job_);
|
||||
-
|
||||
- // No-op.
|
||||
- return mojom::ResultCode::kSuccess;
|
||||
-}
|
||||
-
|
||||
mojom::ResultCode TestPrintingContext::PrintDocument(
|
||||
const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
diff --git a/printing/test_printing_context.h b/printing/test_printing_context.h
|
||||
index e2a0f906216f5bae2d05dd6efd6246257a3deea4..579a9269459470e8eac6e506d87503d828bed4cb 100644
|
||||
--- a/printing/test_printing_context.h
|
||||
+++ b/printing/test_printing_context.h
|
||||
@@ -58,12 +58,10 @@ class TestPrintingContext : public PrintingContext {
|
||||
mojom::ResultCode UpdatePrinterSettings(
|
||||
const PrinterSettings& printer_settings) override;
|
||||
mojom::ResultCode NewDocument(const std::u16string& document_name) override;
|
||||
- mojom::ResultCode NewPage() override;
|
||||
#if defined(OS_WIN)
|
||||
mojom::ResultCode RenderPage(const PrintedPage& page,
|
||||
const PageSetup& page_setup) override;
|
||||
#endif
|
||||
- mojom::ResultCode PageDone() override;
|
||||
mojom::ResultCode PrintDocument(const MetafilePlayer& metafile,
|
||||
const PrintSettings& settings,
|
||||
uint32_t num_pages) override;
|
||||
@@ -0,0 +1,42 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: David Sanders <dsanders11@ucsbalum.com>
|
||||
Date: Fri, 11 Feb 2022 22:37:39 +0000
|
||||
Subject: fix: don't restore maximized windows when calling ShowInactive
|
||||
|
||||
This is a backport from Chromium of
|
||||
https://chromium-review.googlesource.com/c/chromium/src/+/3371573.
|
||||
|
||||
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
|
||||
index 067861bb743ee2f3c1916794d45efb7dd591b230..6507557bf5a47492343602607e0dbb7d8f88246d 100644
|
||||
--- a/ui/views/win/hwnd_message_handler.cc
|
||||
+++ b/ui/views/win/hwnd_message_handler.cc
|
||||
@@ -664,9 +664,16 @@ void HWNDMessageHandler::Show(ui::WindowShowState show_state,
|
||||
SetWindowPlacement(hwnd(), &placement);
|
||||
native_show_state = SW_SHOWMAXIMIZED;
|
||||
} else {
|
||||
+ const bool is_maximized = IsMaximized();
|
||||
+
|
||||
+ // Use SW_SHOW/SW_SHOWNA instead of SW_SHOWNORMAL/SW_SHOWNOACTIVATE so that
|
||||
+ // the window is not restored to its original position if it is maximized.
|
||||
+ // This could be used unconditionally for ui::SHOW_STATE_INACTIVE, but
|
||||
+ // cross-platform behavior when showing a minimized window is inconsistent,
|
||||
+ // some platforms restore the position, some do not. See crbug.com/1296710
|
||||
switch (show_state) {
|
||||
case ui::SHOW_STATE_INACTIVE:
|
||||
- native_show_state = SW_SHOWNOACTIVATE;
|
||||
+ native_show_state = is_maximized ? SW_SHOWNA : SW_SHOWNOACTIVATE;
|
||||
break;
|
||||
case ui::SHOW_STATE_MAXIMIZED:
|
||||
native_show_state = SW_SHOWMAXIMIZED;
|
||||
@@ -677,9 +684,9 @@ void HWNDMessageHandler::Show(ui::WindowShowState show_state,
|
||||
case ui::SHOW_STATE_NORMAL:
|
||||
if ((GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) ||
|
||||
(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) {
|
||||
- native_show_state = SW_SHOWNOACTIVATE;
|
||||
+ native_show_state = is_maximized ? SW_SHOWNA : SW_SHOWNOACTIVATE;
|
||||
} else {
|
||||
- native_show_state = SW_SHOWNORMAL;
|
||||
+ native_show_state = is_maximized ? SW_SHOW : SW_SHOWNORMAL;
|
||||
}
|
||||
break;
|
||||
case ui::SHOW_STATE_FULLSCREEN:
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
"src/electron/patches/boringssl": "src/third_party/boringssl/src",
|
||||
|
||||
"src/electron/patches/devtools_frontend": "src/third_party/devtools-frontend/src",
|
||||
|
||||
"src/electron/patches/webrtc": "src/third_party/webrtc",
|
||||
|
||||
"src/electron/patches/v8": "src/v8",
|
||||
|
||||
1
patches/devtools_frontend/.patches
Normal file
1
patches/devtools_frontend/.patches
Normal file
@@ -0,0 +1 @@
|
||||
fix_expose_globals_to_allow_patching_devtools_dock.patch
|
||||
@@ -0,0 +1,48 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Wed, 9 Feb 2022 10:55:54 +0100
|
||||
Subject: fix: expose globals to allow patching Devtools dock
|
||||
|
||||
Electron calls into UI.DockController.instance().setDockSide(side) in
|
||||
order to allow users to set the devtools dock position via
|
||||
webContents.openDevTools({ mode }). In https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3310870
|
||||
the globals which we used to enable this were removed, and so we need to
|
||||
re-expose them to fix this broken functionality. We should look to
|
||||
upstream a more durable approach to allowing us to do this, at which
|
||||
point this patch can be removed.
|
||||
|
||||
diff --git a/front_end/entrypoints/shell/BUILD.gn b/front_end/entrypoints/shell/BUILD.gn
|
||||
index bf96adcaa42a2406cf1dd7cd2468802886aa4fd7..cd84442cab4a0da772dd37cd4e921041b3b6173a 100644
|
||||
--- a/front_end/entrypoints/shell/BUILD.gn
|
||||
+++ b/front_end/entrypoints/shell/BUILD.gn
|
||||
@@ -31,6 +31,7 @@ devtools_entrypoint("shell") {
|
||||
"../../ui/legacy/components/perf_ui:meta",
|
||||
"../../ui/legacy/components/quick_open:meta",
|
||||
"../../ui/legacy/components/source_frame:meta",
|
||||
+ "../../ui/legacy:legacy",
|
||||
]
|
||||
|
||||
visibility = [
|
||||
diff --git a/front_end/entrypoints/shell/shell.ts b/front_end/entrypoints/shell/shell.ts
|
||||
index 89255a0927a647ca32d1a9508853425a3207b441..f0e1e32f80d79e400ad139818edce60aff6aeb89 100644
|
||||
--- a/front_end/entrypoints/shell/shell.ts
|
||||
+++ b/front_end/entrypoints/shell/shell.ts
|
||||
@@ -20,6 +20,7 @@ import '../../models/logs/logs-meta.js';
|
||||
import '../main/main-meta.js';
|
||||
import '../../ui/legacy/components/perf_ui/perf_ui-meta.js';
|
||||
import '../../ui/legacy/components/quick_open/quick_open-meta.js';
|
||||
+import '../../ui/legacy/legacy-legacy.js';
|
||||
import '../../core/sdk/sdk-meta.js';
|
||||
import '../../ui/legacy/components/source_frame/source_frame-meta.js';
|
||||
import '../../panels/console_counters/console_counters-meta.js';
|
||||
diff --git a/front_end/ui/legacy/BUILD.gn b/front_end/ui/legacy/BUILD.gn
|
||||
index fe7c150cf6ce24b10821fa5a91d55f82cf865222..7177d495ec9e2b31a641c7a531b63584c597cf97 100644
|
||||
--- a/front_end/ui/legacy/BUILD.gn
|
||||
+++ b/front_end/ui/legacy/BUILD.gn
|
||||
@@ -182,5 +182,6 @@ devtools_entrypoint("legacy") {
|
||||
visibility = [
|
||||
"../..:legacy_entrypoints",
|
||||
"../../legacy_test_runner/*",
|
||||
+ "../../entrypoints/shell/*",
|
||||
]
|
||||
}
|
||||
@@ -27,3 +27,6 @@ test_add_fixture_trim_option.patch
|
||||
fix_crash_caused_by_gethostnamew_on_windows_7.patch
|
||||
fix_suppress_clang_-wdeprecated-declarations_in_libuv.patch
|
||||
fix_don_t_create_console_window_when_creating_process.patch
|
||||
darwin_remove_eprototype_error_workaround_3405.patch
|
||||
darwin_translate_eprototype_to_econnreset_3413.patch
|
||||
darwin_bump_minimum_supported_version_to_10_15_3406.patch
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ben Noordhuis <info@bnoordhuis.nl>
|
||||
Date: Tue, 8 Feb 2022 14:18:29 +0100
|
||||
Subject: darwin: bump minimum supported version to 10.15 (#3406)
|
||||
|
||||
We can't realistically claim to support 10.7 or any version that Apple
|
||||
no longer supports so let's bump the baseline to something more
|
||||
realistic.
|
||||
|
||||
Refs: https://github.com/libuv/libuv/pull/482
|
||||
Refs: https://github.com/libuv/libuv/pull/3405
|
||||
|
||||
diff --git a/deps/uv/SUPPORTED_PLATFORMS.md b/deps/uv/SUPPORTED_PLATFORMS.md
|
||||
index 30e0ea617a6fcaa5b4b7c7c5b117652e61f367d3..dc57dfb12dc7ddf8d29308ac44f46084a933d5ca 100644
|
||||
--- a/deps/uv/SUPPORTED_PLATFORMS.md
|
||||
+++ b/deps/uv/SUPPORTED_PLATFORMS.md
|
||||
@@ -3,7 +3,7 @@
|
||||
| System | Support type | Supported versions | Notes |
|
||||
|---|---|---|---|
|
||||
| GNU/Linux | Tier 1 | Linux >= 2.6.32 with glibc >= 2.12 | |
|
||||
-| macOS | Tier 1 | macOS >= 10.7 | |
|
||||
+| macOS | Tier 1 | macOS >= 10.15 | Current and previous macOS release |
|
||||
| Windows | Tier 1 | >= Windows 8 | VS 2015 and later are supported |
|
||||
| FreeBSD | Tier 1 | >= 10 | |
|
||||
| AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix |
|
||||
@@ -0,0 +1,65 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ben Noordhuis <info@bnoordhuis.nl>
|
||||
Date: Sun, 9 Jan 2022 12:20:15 +0100
|
||||
Subject: darwin: remove EPROTOTYPE error workaround (#3405)
|
||||
|
||||
It's been reported in the past that OS X 10.10, because of a race
|
||||
condition in the XNU kernel, sometimes returns a transient EPROTOTYPE
|
||||
error when trying to write to a socket. Libuv handles that by retrying
|
||||
the operation until it succeeds or fails with a different error.
|
||||
|
||||
Recently it's been reported that current versions of the operating
|
||||
system formerly known as OS X fail permanently with EPROTOTYPE under
|
||||
certain conditions, resulting in an infinite loop.
|
||||
|
||||
Because Apple isn't exactly forthcoming with bug fixes or even details,
|
||||
I'm opting to simply remove the workaround and have the error bubble up.
|
||||
|
||||
Refs: https://github.com/libuv/libuv/pull/482
|
||||
|
||||
diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c
|
||||
index bc64fe8f44b26d9f4c0d4d0d282b65cdf11a531b..1af448e7691392c3f7794eed1905d9132394e207 100644
|
||||
--- a/deps/uv/src/unix/stream.c
|
||||
+++ b/deps/uv/src/unix/stream.c
|
||||
@@ -58,20 +58,6 @@ struct uv__stream_select_s {
|
||||
fd_set* swrite;
|
||||
size_t swrite_sz;
|
||||
};
|
||||
-
|
||||
-/* Due to a possible kernel bug at least in OS X 10.10 "Yosemite",
|
||||
- * EPROTOTYPE can be returned while trying to write to a socket that is
|
||||
- * shutting down. If we retry the write, we should get the expected EPIPE
|
||||
- * instead.
|
||||
- */
|
||||
-# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR || errno == EPROTOTYPE)
|
||||
-# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \
|
||||
- (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS || \
|
||||
- (errno == EMSGSIZE && send_handle != NULL))
|
||||
-#else
|
||||
-# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR)
|
||||
-# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \
|
||||
- (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
static void uv__stream_connect(uv_stream_t*);
|
||||
@@ -866,17 +852,17 @@ static int uv__try_write(uv_stream_t* stream,
|
||||
|
||||
do
|
||||
n = sendmsg(uv__stream_fd(stream), &msg, 0);
|
||||
- while (n == -1 && RETRY_ON_WRITE_ERROR(errno));
|
||||
+ while (n == -1 && errno == EINTR);
|
||||
} else {
|
||||
do
|
||||
n = uv__writev(uv__stream_fd(stream), iov, iovcnt);
|
||||
- while (n == -1 && RETRY_ON_WRITE_ERROR(errno));
|
||||
+ while (n == -1 && errno == EINTR);
|
||||
}
|
||||
|
||||
if (n >= 0)
|
||||
return n;
|
||||
|
||||
- if (IS_TRANSIENT_WRITE_ERROR(errno, send_handle))
|
||||
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
return UV_EAGAIN;
|
||||
|
||||
return UV__ERR(errno);
|
||||
@@ -0,0 +1,44 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ben Noordhuis <info@bnoordhuis.nl>
|
||||
Date: Wed, 12 Jan 2022 16:11:43 +0100
|
||||
Subject: darwin: translate EPROTOTYPE to ECONNRESET (#3413)
|
||||
|
||||
macOS versions 10.10 and 10.15 - and presumbaly 10.11 to 10.14, too -
|
||||
have a bug where a race condition causes the kernel to return EPROTOTYPE
|
||||
because the socket isn't fully constructed.
|
||||
|
||||
It's probably the result of the peer closing the connection and that is
|
||||
why libuv translates it to ECONNRESET.
|
||||
|
||||
Previously, libuv retried until the EPROTOTYPE error went away but some
|
||||
VPN software causes the same behavior except the error is permanent, not
|
||||
transient, turning the retry mechanism into an infinite loop.
|
||||
|
||||
Refs: https://github.com/libuv/libuv/pull/482
|
||||
Refs: https://github.com/libuv/libuv/pull/3405
|
||||
|
||||
diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c
|
||||
index 1af448e7691392c3f7794eed1905d9132394e207..9d22debf2bf5bd5912ade152e55a85ad652e3819 100644
|
||||
--- a/deps/uv/src/unix/stream.c
|
||||
+++ b/deps/uv/src/unix/stream.c
|
||||
@@ -865,6 +865,20 @@ static int uv__try_write(uv_stream_t* stream,
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
return UV_EAGAIN;
|
||||
|
||||
+#ifdef __APPLE__
|
||||
+ /* macOS versions 10.10 and 10.15 - and presumbaly 10.11 to 10.14, too -
|
||||
+ * have a bug where a race condition causes the kernel to return EPROTOTYPE
|
||||
+ * because the socket isn't fully constructed. It's probably the result of
|
||||
+ * the peer closing the connection and that is why libuv translates it to
|
||||
+ * ECONNRESET. Previously, libuv retried until the EPROTOTYPE error went
|
||||
+ * away but some VPN software causes the same behavior except the error is
|
||||
+ * permanent, not transient, turning the retry mechanism into an infinite
|
||||
+ * loop. See https://github.com/libuv/libuv/pull/482.
|
||||
+ */
|
||||
+ if (errno == EPROTOTYPE)
|
||||
+ return UV_ECONNRESET;
|
||||
+#endif /* __APPLE__ */
|
||||
+
|
||||
return UV__ERR(errno);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,10 @@ async function getDraftRelease (version, skipValidation) {
|
||||
const releaseInfo = await octokit.repos.listReleases({
|
||||
owner: 'electron',
|
||||
repo: targetRepo
|
||||
}).catch(err => {
|
||||
console.error(`Failed to fetch releases: ${err}`);
|
||||
});
|
||||
console.log('releaseInfo: ', releaseInfo);
|
||||
|
||||
const versionToCheck = version || pkgVersion;
|
||||
const drafts = releaseInfo.data.filter(release => {
|
||||
@@ -46,6 +49,7 @@ async function getDraftRelease (version, skipValidation) {
|
||||
});
|
||||
|
||||
const draft = drafts[0];
|
||||
console.log('drafts: ', drafts);
|
||||
if (!skipValidation) {
|
||||
failureCount = 0;
|
||||
check(drafts.length === 1, 'one draft exists', true);
|
||||
|
||||
@@ -89,8 +89,10 @@ gin::Handle<Tray> Tray::New(gin_helper::ErrorThrower thrower,
|
||||
}
|
||||
#endif
|
||||
|
||||
return gin::CreateHandle(thrower.isolate(),
|
||||
new Tray(args->isolate(), image, guid));
|
||||
auto handle = gin::CreateHandle(args->isolate(),
|
||||
new Tray(args->isolate(), image, guid));
|
||||
handle->Pin(args->isolate());
|
||||
return handle;
|
||||
}
|
||||
|
||||
void Tray::OnClicked(const gfx::Rect& bounds,
|
||||
@@ -180,6 +182,7 @@ void Tray::OnDragEnded() {
|
||||
}
|
||||
|
||||
void Tray::Destroy() {
|
||||
Unpin();
|
||||
menu_.Reset();
|
||||
tray_icon_.reset();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "shell/common/gin_helper/cleaned_up_at_exit.h"
|
||||
#include "shell/common/gin_helper/constructible.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
#include "shell/common/gin_helper/pinnable.h"
|
||||
|
||||
namespace gfx {
|
||||
class Image;
|
||||
@@ -38,6 +39,7 @@ class Tray : public gin::Wrappable<Tray>,
|
||||
public gin_helper::EventEmitterMixin<Tray>,
|
||||
public gin_helper::Constructible<Tray>,
|
||||
public gin_helper::CleanedUpAtExit,
|
||||
public gin_helper::Pinnable<Tray>,
|
||||
public TrayIconObserver {
|
||||
public:
|
||||
// gin_helper::Constructible
|
||||
|
||||
@@ -766,8 +766,12 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||
#if BUILDFLAG(ENABLE_OSR)
|
||||
}
|
||||
} else if (IsOffScreen()) {
|
||||
bool transparent = false;
|
||||
options.Get(options::kTransparent, &transparent);
|
||||
// webPreferences does not have a transparent option, so if the window needs
|
||||
// to be transparent, that will be set at electron_api_browser_window.cc#L57
|
||||
// and we then need to pull it back out and check it here.
|
||||
std::string background_color;
|
||||
options.GetHidden(options::kBackgroundColor, &background_color);
|
||||
bool transparent = ParseHexColor(background_color) == SK_ColorTRANSPARENT;
|
||||
|
||||
content::WebContents::CreateParams params(session->browser_context());
|
||||
auto* view = new OffScreenWebContentsView(
|
||||
@@ -2353,6 +2357,11 @@ v8::Local<v8::Promise> WebContents::SavePage(
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
if (!full_file_path.IsAbsolute()) {
|
||||
promise.RejectWithErrorMessage("Path must be absolute");
|
||||
return handle;
|
||||
}
|
||||
|
||||
auto* handler = new SavePageHandler(web_contents(), std::move(promise));
|
||||
handler->Handle(full_file_path, save_type);
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ bool GetProtocolLaunchPath(gin::Arguments* args, std::wstring* exe) {
|
||||
std::vector<std::wstring> launch_args;
|
||||
if (args->GetNext(&launch_args) && !launch_args.empty())
|
||||
*exe = base::StringPrintf(L"\"%ls\" \"%ls\" \"%%1\"", exe->c_str(),
|
||||
base::JoinString(launch_args, L" ").c_str());
|
||||
base::JoinString(launch_args, L"\" \"").c_str());
|
||||
else
|
||||
*exe = base::StringPrintf(L"\"%ls\" \"%%1\"", exe->c_str());
|
||||
return true;
|
||||
|
||||
@@ -1450,6 +1450,12 @@ bool ElectronBrowserClient::PreSpawnChild(sandbox::TargetPolicy* policy,
|
||||
}
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
void BindElectronBrowser(
|
||||
mojo::PendingAssociatedReceiver<electron::mojom::ElectronBrowser> receiver,
|
||||
content::RenderFrameHost* frame_host) {
|
||||
ElectronBrowserHandlerImpl::Create(frame_host, std::move(receiver));
|
||||
}
|
||||
|
||||
bool ElectronBrowserClient::BindAssociatedReceiverFromFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
const std::string& interface_name,
|
||||
@@ -1461,6 +1467,13 @@ bool ElectronBrowserClient::BindAssociatedReceiverFromFrame(
|
||||
render_frame_host);
|
||||
return true;
|
||||
}
|
||||
if (interface_name == electron::mojom::ElectronBrowser::Name_) {
|
||||
BindElectronBrowser(
|
||||
mojo::PendingAssociatedReceiver<electron::mojom::ElectronBrowser>(
|
||||
std::move(*handle)),
|
||||
render_frame_host);
|
||||
return true;
|
||||
}
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
if (interface_name == printing::mojom::PrintManagerHost::Name_) {
|
||||
mojo::PendingAssociatedReceiver<printing::mojom::PrintManagerHost> receiver(
|
||||
@@ -1522,12 +1535,6 @@ void ElectronBrowserClient::BindHostReceiverForRenderer(
|
||||
#endif
|
||||
}
|
||||
|
||||
void BindElectronBrowser(
|
||||
content::RenderFrameHost* frame_host,
|
||||
mojo::PendingReceiver<electron::mojom::ElectronBrowser> receiver) {
|
||||
ElectronBrowserHandlerImpl::Create(frame_host, std::move(receiver));
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
void BindMimeHandlerService(
|
||||
content::RenderFrameHost* frame_host,
|
||||
@@ -1576,8 +1583,6 @@ void ElectronBrowserClient::RegisterBrowserInterfaceBindersForFrame(
|
||||
base::BindRepeating(&BindNetworkHintsHandler));
|
||||
map->Add<blink::mojom::BadgeService>(
|
||||
base::BindRepeating(&badging::BadgeManager::BindFrameReceiver));
|
||||
map->Add<electron::mojom::ElectronBrowser>(
|
||||
base::BindRepeating(&BindElectronBrowser));
|
||||
map->Add<blink::mojom::KeyboardLockService>(base::BindRepeating(
|
||||
&content::KeyboardLockServiceImpl::CreateMojoService));
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
namespace electron {
|
||||
ElectronBrowserHandlerImpl::ElectronBrowserHandlerImpl(
|
||||
content::RenderFrameHost* frame_host,
|
||||
mojo::PendingReceiver<mojom::ElectronBrowser> receiver)
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronBrowser> receiver)
|
||||
: render_process_id_(frame_host->GetProcess()->GetID()),
|
||||
render_frame_id_(frame_host->GetRoutingID()) {
|
||||
content::WebContents* web_contents =
|
||||
@@ -135,7 +135,7 @@ content::RenderFrameHost* ElectronBrowserHandlerImpl::GetRenderFrameHost() {
|
||||
// static
|
||||
void ElectronBrowserHandlerImpl::Create(
|
||||
content::RenderFrameHost* frame_host,
|
||||
mojo::PendingReceiver<mojom::ElectronBrowser> receiver) {
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronBrowser> receiver) {
|
||||
new ElectronBrowserHandlerImpl(frame_host, std::move(receiver));
|
||||
}
|
||||
} // namespace electron
|
||||
|
||||
@@ -23,10 +23,11 @@ class ElectronBrowserHandlerImpl : public mojom::ElectronBrowser,
|
||||
public:
|
||||
explicit ElectronBrowserHandlerImpl(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
mojo::PendingReceiver<mojom::ElectronBrowser> receiver);
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronBrowser> receiver);
|
||||
|
||||
static void Create(content::RenderFrameHost* frame_host,
|
||||
mojo::PendingReceiver<mojom::ElectronBrowser> receiver);
|
||||
static void Create(
|
||||
content::RenderFrameHost* frame_host,
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronBrowser> receiver);
|
||||
|
||||
// disable copy
|
||||
ElectronBrowserHandlerImpl(const ElectronBrowserHandlerImpl&) = delete;
|
||||
@@ -75,7 +76,7 @@ class ElectronBrowserHandlerImpl : public mojom::ElectronBrowser,
|
||||
const int render_process_id_;
|
||||
const int render_frame_id_;
|
||||
|
||||
mojo::Receiver<mojom::ElectronBrowser> receiver_{this};
|
||||
mojo::AssociatedReceiver<mojom::ElectronBrowser> receiver_{this};
|
||||
|
||||
base::WeakPtrFactory<ElectronBrowserHandlerImpl> weak_factory_{this};
|
||||
};
|
||||
|
||||
@@ -455,7 +455,8 @@ void ElectronBrowserMainParts::WillRunMainMessageLoop(
|
||||
std::unique_ptr<base::RunLoop>& run_loop) {
|
||||
js_env_->OnMessageLoopCreated();
|
||||
exit_code_ = content::RESULT_CODE_NORMAL_EXIT;
|
||||
Browser::Get()->SetMainMessageLoopQuitClosure(run_loop->QuitClosure());
|
||||
Browser::Get()->SetMainMessageLoopQuitClosure(
|
||||
run_loop->QuitWhenIdleClosure());
|
||||
}
|
||||
|
||||
void ElectronBrowserMainParts::PostCreateMainMessageLoop() {
|
||||
|
||||
@@ -329,15 +329,14 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
||||
// Set Window style so that we get a minimize and maximize animation when
|
||||
// frameless.
|
||||
DWORD frame_style = WS_CAPTION | WS_OVERLAPPED;
|
||||
if (resizable_)
|
||||
if (resizable_ && thick_frame_)
|
||||
frame_style |= WS_THICKFRAME;
|
||||
if (minimizable_)
|
||||
frame_style |= WS_MINIMIZEBOX;
|
||||
if (maximizable_)
|
||||
frame_style |= WS_MAXIMIZEBOX;
|
||||
// We should not show a frame for transparent window.
|
||||
if (!thick_frame_)
|
||||
frame_style &= ~(WS_THICKFRAME | WS_CAPTION);
|
||||
if (!thick_frame_ || !has_frame())
|
||||
frame_style &= ~WS_CAPTION;
|
||||
::SetWindowLong(GetAcceleratedWidget(), GWL_STYLE, frame_style);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 18,0,0,20220201
|
||||
PRODUCTVERSION 18,0,0,20220201
|
||||
FILEVERSION 18,0,0,5
|
||||
PRODUCTVERSION 18,0,0,5
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
||||
@@ -99,4 +99,21 @@ bool ElectronDesktopWindowTreeHostWin::GetClientAreaInsets(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ElectronDesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) {
|
||||
// Call the default implementation of this method to get the event to its
|
||||
// proper handler.
|
||||
bool handled = views::DesktopWindowTreeHostWin::HandleMouseEvent(event);
|
||||
|
||||
// On WCO-enabled windows, we need to mark non-client mouse moved events as
|
||||
// handled so they don't incorrectly propogate back to the OS.
|
||||
if (native_window_view_->IsWindowControlsOverlayEnabled() &&
|
||||
event->type() == ui::ET_MOUSE_MOVED &&
|
||||
(event->flags() & ui::EF_IS_NON_CLIENT) != 0) {
|
||||
event->SetHandled();
|
||||
handled = true;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -36,6 +36,7 @@ class ElectronDesktopWindowTreeHostWin
|
||||
bool GetDwmFrameInsetsInPixels(gfx::Insets* insets) const override;
|
||||
bool GetClientAreaInsets(gfx::Insets* insets,
|
||||
HMONITOR monitor) const override;
|
||||
bool HandleMouseEvent(ui::MouseEvent* event) override;
|
||||
|
||||
private:
|
||||
NativeWindowViews* native_window_view_; // weak ref
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "shell/common/node_bindings.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/v8_value_serializer.h"
|
||||
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
#include "third_party/blink/public/web/web_local_frame.h"
|
||||
#include "third_party/blink/public/web/web_message_port_converter.h"
|
||||
|
||||
@@ -59,8 +59,8 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer>,
|
||||
v8::Global<v8::Context>(isolate, isolate->GetCurrentContext());
|
||||
weak_context_.SetWeak();
|
||||
|
||||
render_frame->GetBrowserInterfaceBroker()->GetInterface(
|
||||
electron_browser_remote_.BindNewPipeAndPassReceiver());
|
||||
render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
&electron_browser_remote_);
|
||||
}
|
||||
|
||||
void OnDestruct() override { electron_browser_remote_.reset(); }
|
||||
@@ -223,7 +223,8 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer>,
|
||||
}
|
||||
|
||||
v8::Global<v8::Context> weak_context_;
|
||||
mojo::Remote<electron::mojom::ElectronBrowser> electron_browser_remote_;
|
||||
mojo::AssociatedRemote<electron::mojom::ElectronBrowser>
|
||||
electron_browser_remote_;
|
||||
};
|
||||
|
||||
gin::WrapperInfo IPCRenderer::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "shell/renderer/api/electron_api_context_bridge.h"
|
||||
#include "shell/renderer/api/electron_api_spell_check_client.h"
|
||||
#include "shell/renderer/renderer_client_base.h"
|
||||
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
#include "third_party/blink/public/common/page/page_zoom.h"
|
||||
#include "third_party/blink/public/common/web_cache/web_cache_resource_type_stats.h"
|
||||
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
|
||||
@@ -453,9 +453,9 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||
if (!MaybeGetRenderFrame(isolate, "setZoomLevel", &render_frame))
|
||||
return;
|
||||
|
||||
mojo::Remote<mojom::ElectronBrowser> browser_remote;
|
||||
render_frame->GetBrowserInterfaceBroker()->GetInterface(
|
||||
browser_remote.BindNewPipeAndPassReceiver());
|
||||
mojo::AssociatedRemote<mojom::ElectronBrowser> browser_remote;
|
||||
render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
&browser_remote);
|
||||
browser_remote->SetTemporaryZoomLevel(level);
|
||||
}
|
||||
|
||||
@@ -465,9 +465,9 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||
if (!MaybeGetRenderFrame(isolate, "getZoomLevel", &render_frame))
|
||||
return result;
|
||||
|
||||
mojo::Remote<mojom::ElectronBrowser> browser_remote;
|
||||
render_frame->GetBrowserInterfaceBroker()->GetInterface(
|
||||
browser_remote.BindNewPipeAndPassReceiver());
|
||||
mojo::AssociatedRemote<mojom::ElectronBrowser> browser_remote;
|
||||
render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
&browser_remote);
|
||||
browser_remote->DoGetZoomLevel(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "shell/common/world_ids.h"
|
||||
#include "shell/renderer/renderer_client_base.h"
|
||||
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
|
||||
#include "third_party/blink/public/platform/web_isolated_world_info.h"
|
||||
#include "third_party/blink/public/web/blink.h"
|
||||
@@ -149,9 +149,8 @@ void ElectronRenderFrameObserver::DraggableRegionsChanged() {
|
||||
regions.push_back(std::move(region));
|
||||
}
|
||||
|
||||
mojo::Remote<mojom::ElectronBrowser> browser_remote;
|
||||
render_frame_->GetBrowserInterfaceBroker()->GetInterface(
|
||||
browser_remote.BindNewPipeAndPassReceiver());
|
||||
mojo::AssociatedRemote<mojom::ElectronBrowser> browser_remote;
|
||||
render_frame_->GetRemoteAssociatedInterfaces()->GetInterface(&browser_remote);
|
||||
browser_remote->UpdateDraggableRegions(std::move(regions));
|
||||
}
|
||||
|
||||
@@ -169,9 +168,9 @@ void ElectronRenderFrameObserver::OnDestruct() {
|
||||
void ElectronRenderFrameObserver::DidMeaningfulLayout(
|
||||
blink::WebMeaningfulLayout layout_type) {
|
||||
if (layout_type == blink::WebMeaningfulLayout::kVisuallyNonEmpty) {
|
||||
mojo::Remote<mojom::ElectronBrowser> browser_remote;
|
||||
render_frame_->GetBrowserInterfaceBroker()->GetInterface(
|
||||
browser_remote.BindNewPipeAndPassReceiver());
|
||||
mojo::AssociatedRemote<mojom::ElectronBrowser> browser_remote;
|
||||
render_frame_->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
&browser_remote);
|
||||
browser_remote->OnFirstNonEmptyLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,6 +735,22 @@ describe('BrowserWindow module', () => {
|
||||
w.showInactive();
|
||||
expect(w.isFocused()).to.equal(false);
|
||||
});
|
||||
|
||||
// TODO(dsanders11): Enable for Linux once CI plays nice with these kinds of tests
|
||||
ifit(process.platform !== 'linux')('should not restore maximized windows', async () => {
|
||||
const maximize = emittedOnce(w, 'maximize');
|
||||
const shown = emittedOnce(w, 'show');
|
||||
w.maximize();
|
||||
// TODO(dsanders11): The maximize event isn't firing on macOS for a window initially hidden
|
||||
if (process.platform !== 'darwin') {
|
||||
await maximize;
|
||||
} else {
|
||||
await delay(1000);
|
||||
}
|
||||
w.showInactive();
|
||||
await shown;
|
||||
expect(w.isMaximized()).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserWindow.focus()', () => {
|
||||
@@ -3419,20 +3435,56 @@ describe('BrowserWindow module', () => {
|
||||
const savePageJsPath = path.join(savePageDir, 'save_page_files', 'test.js');
|
||||
const savePageCssPath = path.join(savePageDir, 'save_page_files', 'test.css');
|
||||
|
||||
after(() => {
|
||||
afterEach(() => {
|
||||
closeAllWindows();
|
||||
|
||||
try {
|
||||
fs.unlinkSync(savePageCssPath);
|
||||
fs.unlinkSync(savePageJsPath);
|
||||
fs.unlinkSync(savePageHtmlPath);
|
||||
fs.rmdirSync(path.join(savePageDir, 'save_page_files'));
|
||||
fs.rmdirSync(savePageDir);
|
||||
} catch (e) {
|
||||
// Ignore error
|
||||
}
|
||||
} catch {}
|
||||
});
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
it('should save page to disk', async () => {
|
||||
it('should throw when passing relative paths', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixtures, 'pages', 'save_page', 'index.html'));
|
||||
|
||||
await expect(
|
||||
w.webContents.savePage('save_page.html', 'HTMLComplete')
|
||||
).to.eventually.be.rejectedWith('Path must be absolute');
|
||||
|
||||
await expect(
|
||||
w.webContents.savePage('save_page.html', 'HTMLOnly')
|
||||
).to.eventually.be.rejectedWith('Path must be absolute');
|
||||
|
||||
await expect(
|
||||
w.webContents.savePage('save_page.html', 'MHTML')
|
||||
).to.eventually.be.rejectedWith('Path must be absolute');
|
||||
});
|
||||
|
||||
it('should save page to disk with HTMLOnly', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixtures, 'pages', 'save_page', 'index.html'));
|
||||
await w.webContents.savePage(savePageHtmlPath, 'HTMLOnly');
|
||||
|
||||
expect(fs.existsSync(savePageHtmlPath)).to.be.true('html path');
|
||||
expect(fs.existsSync(savePageJsPath)).to.be.false('js path');
|
||||
expect(fs.existsSync(savePageCssPath)).to.be.false('css path');
|
||||
});
|
||||
|
||||
it('should save page to disk with MHTML', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixtures, 'pages', 'save_page', 'index.html'));
|
||||
await w.webContents.savePage(savePageHtmlPath, 'MHTML');
|
||||
|
||||
expect(fs.existsSync(savePageHtmlPath)).to.be.true('html path');
|
||||
expect(fs.existsSync(savePageJsPath)).to.be.false('js path');
|
||||
expect(fs.existsSync(savePageCssPath)).to.be.false('css path');
|
||||
});
|
||||
|
||||
it('should save page to disk with HTMLComplete', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadFile(path.join(fixtures, 'pages', 'save_page', 'index.html'));
|
||||
await w.webContents.savePage(savePageHtmlPath, 'HTMLComplete');
|
||||
|
||||
@@ -826,15 +826,20 @@ describe('webContents module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
const moveFocusToDevTools = async (win: BrowserWindow) => {
|
||||
const devToolsOpened = emittedOnce(win.webContents, 'devtools-opened');
|
||||
win.webContents.openDevTools({ mode: 'right' });
|
||||
await devToolsOpened;
|
||||
win.webContents.devToolsWebContents!.focus();
|
||||
};
|
||||
|
||||
describe('focus event', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
it('is triggered when web contents is focused', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL('about:blank');
|
||||
const devToolsOpened = emittedOnce(w.webContents, 'devtools-opened');
|
||||
w.webContents.openDevTools();
|
||||
await devToolsOpened;
|
||||
w.webContents.devToolsWebContents!.focus();
|
||||
await moveFocusToDevTools(w);
|
||||
const focusPromise = emittedOnce(w.webContents, 'focus');
|
||||
w.webContents.focus();
|
||||
await expect(focusPromise).to.eventually.be.fulfilled();
|
||||
@@ -849,16 +854,17 @@ describe('webContents module', () => {
|
||||
window2.loadURL('about:blank')
|
||||
]);
|
||||
|
||||
const focusPromise1 = emittedOnce(window1.webContents, 'focus');
|
||||
const focusPromise2 = emittedOnce(window2.webContents, 'focus');
|
||||
|
||||
window1.showInactive();
|
||||
window2.showInactive();
|
||||
|
||||
let focusPromise = emittedOnce(window1.webContents, 'focus');
|
||||
window1.focus();
|
||||
await expect(focusPromise).to.eventually.be.fulfilled();
|
||||
await expect(focusPromise1).to.eventually.be.fulfilled();
|
||||
|
||||
focusPromise = emittedOnce(window2.webContents, 'focus');
|
||||
window2.focus();
|
||||
await expect(focusPromise).to.eventually.be.fulfilled();
|
||||
await expect(focusPromise2).to.eventually.be.fulfilled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -867,11 +873,9 @@ describe('webContents module', () => {
|
||||
it('is triggered when web contents is blurred', async () => {
|
||||
const w = new BrowserWindow({ show: true });
|
||||
await w.loadURL('about:blank');
|
||||
w.webContents.focus();
|
||||
const blurPromise = emittedOnce(w.webContents, 'blur');
|
||||
const devToolsOpened = emittedOnce(w.webContents, 'devtools-opened');
|
||||
w.webContents.openDevTools({ mode: 'detach' });
|
||||
await devToolsOpened;
|
||||
w.webContents.devToolsWebContents!.focus();
|
||||
await moveFocusToDevTools(w);
|
||||
await expect(blurPromise).to.eventually.be.fulfilled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ const ChildProcess = require('child_process');
|
||||
const { ipcRenderer } = require('electron');
|
||||
const { emittedOnce, waitForEvent } = require('./events-helpers');
|
||||
const { resolveGetters } = require('./expect-helpers');
|
||||
const { ifdescribe, delay } = require('./spec-helpers');
|
||||
const { ifit, ifdescribe, delay } = require('./spec-helpers');
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
|
||||
/* Most of the APIs here don't use standard callbacks */
|
||||
@@ -81,7 +81,8 @@ describe('chromium feature', () => {
|
||||
expect(event.data).to.equal(`size: ${width} ${height}`);
|
||||
});
|
||||
|
||||
it('disables node integration when it is disabled on the parent window', async () => {
|
||||
// FIXME(zcbenz): This test is making the spec runner hang on exit on Windows.
|
||||
ifit(process.platform !== 'win32')('disables node integration when it is disabled on the parent window', async () => {
|
||||
const windowUrl = require('url').format({
|
||||
pathname: `${fixtures}/pages/window-opener-no-node-integration.html`,
|
||||
protocol: 'file',
|
||||
|
||||
Reference in New Issue
Block a user