Compare commits

...

30 Commits

Author SHA1 Message Date
Sudowoodo Release Bot
fe8c831a0c Bump v18.0.0-alpha.5 2022-02-28 05:30:50 -08:00
trop[bot]
cc9ba35c51 fix: tray garbage collection (#33074)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-02-25 14:17:18 +01:00
Sudowoodo Release Bot
56ccfad7a5 Bump v18.0.0-alpha.4 2022-02-24 11:06:40 -08:00
Sudowoodo Release Bot
47bda9c0e5 Revert "Bump v18.0.0-alpha.4"
This reverts commit 339ae4c014.
2022-02-24 11:04:23 -08:00
Keeley Hammond
4f27c367b1 build: debug getDraftRelease call (#33073) 2022-02-24 14:03:56 -05:00
Sudowoodo Release Bot
339ae4c014 Bump v18.0.0-alpha.4 2022-02-24 05:31:27 -08:00
trop[bot]
c1f4b6a4cb fix: broken OSR transparent option (#33053)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-02-24 12:09:46 +01:00
trop[bot]
0a908224c2 fix: savePage throw on relative paths (#33019)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-02-22 16:29:32 +09:00
trop[bot]
34c4889bbb fix: command string for windows protocol handler (#33013)
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2022-02-22 16:26:39 +09:00
trop[bot]
8c066c2a35 fix: don't restore maximized BrowserWindow when calling showInactive (#33022)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2022-02-22 16:25:09 +09:00
trop[bot]
7c9b609389 chore: backport EPROTOTYPE fixes from libuv (#32944)
* chore: backport EPROTOTYPE fixes from libuv

This commit backports three commits from libuv's 1.x branch to fix
issues with CPU going to 100% on macOS when EPROTOTYPE is returned.

See: abb109f30f
See: 3a7b95593a
See: de24da8c11

* Update .patches

Co-authored-by: Fedor Indutny <fedor@indutny.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-02-22 16:18:18 +09:00
trop[bot]
7ab85558f5 chore: add @electron/wg-security to patches/ CODEOWNERS (#33007)
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-02-21 15:04:51 +01:00
trop[bot]
ca06034fdd fix: stale renderer process on quit (#32971)
Co-authored-by: Micha Hanselmann <micha.hanselmann@gmail.com>
2022-02-21 10:53:39 +01:00
trop[bot]
d07fe4480f fix: webContents.openDevTools({mode}) not working (#32946)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-02-21 10:25:02 +01:00
trop[bot]
c2195915d8 fix: failure to print on macOS (#32813)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-02-21 16:40:14 +09:00
trop[bot]
57408e4c2a Fix broken link to GNOME notifications spec (#33002)
Co-authored-by: Kev <kevslashnull@gmail.com>
2022-02-21 16:37:23 +09:00
trop[bot]
0012e01c86 docs: update checklists (#32931)
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2022-02-21 10:21:31 +09:00
trop[bot]
2b97ff3fba docs: fix relative link in developer documentation (#32922)
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2022-02-15 14:36:39 -08:00
Sudowoodo Release Bot
87590da2fb Bump v18.0.0-alpha.3 2022-02-14 07:44:33 -08:00
trop[bot]
493751b321 Make ElectronBrowser mojo interface frame associated. (#32851)
Co-authored-by: Marek Haranczyk <marek@openfin.co>
2022-02-14 20:34:11 +09:00
trop[bot]
36008e0dea test: disable the test that makes spec runner hang on exit (#32838)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2022-02-09 12:00:38 -06:00
trop[bot]
2871698148 build: rebuild the dist_zips when the deps get modified (#32820)
* build: rebuild the dist_zips when the deps get modified

The dist.zip generated by the electron_dist_zip action was not getting
updated when changes were being made to the dependencies, like the
source files. It turns out, we were using data_deps for the dependencies
instead of deps. Here is the difference:

data_deps: things needed to ultimately run the thing built by a target
deps: things needed to build the target

So the difference in treatment of both sets of dependencies is actually
intentional.

Signed-off-by: Darshan Sen <raisinten@gmail.com>

* fixup! build: rebuild the dist_zips when the deps get modified

Signed-off-by: Darshan Sen <raisinten@gmail.com>

Co-authored-by: Darshan Sen <raisinten@gmail.com>
2022-02-09 11:12:29 -05:00
trop[bot]
9d8dde5c76 docs: clarify meaning of cssOrigin (#32810)
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-02-08 12:59:17 -06:00
trop[bot]
f72efecf95 test: improve webContents.savePage() specs (#32745)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-02-08 12:59:45 +01:00
Sudowoodo Release Bot
5b45cb3f77 Bump v18.0.0-alpha.2 2022-02-07 06:13:30 -08:00
trop[bot]
ad2b136425 fix: WCO window hover on window controls on Windows (#32723)
* fix: WCO window hover on window controls

* Update shell/browser/ui/win/electron_desktop_window_tree_host_win.cc

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

* Trigger Build

Co-authored-by: clavin <clavin@electronjs.org>
Co-authored-by: Calvin <clavin@users.noreply.github.com>
Co-authored-by: Robo <hop2deep@gmail.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-02-03 09:46:32 -05:00
trop[bot]
94f4c18d7c fix: some frameless windows showing a frame on Windows (#32714)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-02-03 21:51:16 +09:00
trop[bot]
f7f41fee99 test: fix failing tests of focus/blur events of WebContents (#32724)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2022-02-02 18:32:37 -05:00
trop[bot]
907e9c8c0e docs: Update E18 release date (#32722)
Co-authored-by: Sofia Nguy <sofianguy@gmail.com>
2022-02-02 09:27:25 -08:00
Sudowoodo Release Bot
4d2968bfc1 Bump v18.0.0-alpha.1 2022-02-02 08:20:06 -08:00
39 changed files with 1126 additions and 240 deletions

2
.github/CODEOWNERS vendored
View File

@@ -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

View File

@@ -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",

View File

@@ -1 +1 @@
18.0.0-nightly.20220201
18.0.0-alpha.5

View File

@@ -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.

View File

@@ -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.

View File

@@ -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 |

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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": {

View File

@@ -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

View File

@@ -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;

View File

@@ -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:

View File

@@ -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",

View File

@@ -0,0 +1 @@
fix_expose_globals_to_allow_patching_devtools_dock.patch

View File

@@ -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/*",
]
}

View File

@@ -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

View File

@@ -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 |

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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};
};

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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};

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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');

View File

@@ -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();
});
});

View File

@@ -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',