Compare commits

...

39 Commits

Author SHA1 Message Date
Electron Bot
bdbf78dca8 Bump v14.0.0-beta.4 2021-06-07 06:31:35 -07:00
trop[bot]
40dbeb6836 docs: Update notifications (renderer) docs (#29566)
* remove version information from html

* change format for readability

* clarify which console the message should appear in

* minor changes to renderer.md

* update UI on click instead of developer console

* remove node-integration and fix md

* update content

* chore: remove ****

Co-authored-by: Jeremy Foster <jeremy.foster@live.com>
Co-authored-by: Ethan Arrowood <ethan.arrowood@gmail.com>
Co-authored-by: Cheng Zhao <github@zcbenz.com>
2021-06-07 14:44:26 +09:00
trop[bot]
3ff100521d docs: Updated "recent documents" fiddle tutorial (#29562)
* Port recent-documents fiddle to 12-x-y.

* Update recent-documents tutorial.

* update for review comments

Co-authored-by: Kevin Hartman <kevin@hart.mn>
Co-authored-by: Ethan Arrowood <ethan.arrowood@gmail.com>
2021-06-07 14:43:24 +09:00
trop[bot]
f1752a0b6f chore: return early on promise rejection (#29539)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-06-07 10:17:00 +09:00
trop[bot]
e67b244571 fix: change ASAR archive cache to per-process to fix leak (#29536)
* fix: change ASAR archive cache to per-process to fix leak (#29292)

* chore: address code review comments

* chore: tighten up thread-safety

* chore: better address code review comments

* chore: more code review changes

Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-06-04 13:18:58 +09:00
Electron Bot
fef79701e0 Bump v14.0.0-beta.3 2021-06-03 06:31:50 -07:00
trop[bot]
c2877a342c chore: fix typos in comments (#29519)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-06-03 01:00:01 -07:00
trop[bot]
4e610b1948 build: Improve squirrel.mac BUILD.gn xcrun_action error (#29514)
Right now, if executing `xcrun` fails, then the error message prints the
second argument to the `xcrun.py` script, which is the first argument to
the tool that `xcrun` is executing, making the whole error message quite
confusing.

Consider the following error:

```
python ../../third_party/squirrel.mac/build/xcrun.py dtrace -h -s /private/tmp/20210531211008-def376dc/src/third_party/squirrel.mac/vendor/ReactiveObjC/ReactiveObjC/RACSignalProvider.d -o /private/tmp/20210531211008-def376dc/src/out/release/gen/third_party/squirrel.mac/dtrace/RACSignalProvider.h
xcrun script '-h' failed with code '71':
xcrun: error: can't exec '/tmp/20210531211008-def376dc/dtrace' (errno=Permission denied)
```

The command that `xcrun` is executing is `dtrace`, but the error just
mentions the `-h` flag.

Notes: none
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>

Co-authored-by: Juan Cruz Viotti <jv@jviotti.com>
2021-06-03 00:53:46 -07:00
trop[bot]
4d30e7618a fix: keep shifted character in menu accelerator (#29481)
* fix: correctly handle shifted char in accelerator

* test: use actual accelerator of NSMenuItem

* chore: simplify KeyboardCodeFromStr

* chore: GetAcceleratorTextAt is testing only

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2021-06-03 00:53:09 -07:00
trop[bot]
7381738d85 docs: link to IncomingMessage (#29517)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-06-03 16:20:21 +09:00
trop[bot]
0f2ad3e384 chore: remove unused methods (#29475)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2021-06-03 14:47:59 +09:00
trop[bot]
b8812c8942 build: update support.md on stable version bumps (#29500)
* build: update support.md on stable version bumps

* build: update supported on major stable & nightly bumps

* test: updateSupported tests

* chore: fix syntax

* chore: use fspromise in version-bumper script/spec

Co-authored-by: VerteDinde <khammond@slack-corp.com>
2021-06-03 14:47:30 +09:00
Milan Burda
7a11390b8a test: add spec for --require filtering in NODE_OPTIONS (#29508) 2021-06-03 14:43:28 +09:00
trop[bot]
6ab2684234 feat: support loading debug urls with loadURL() (#29466)
* feat: support loading debug urls with loadURL()

* use FROM_ADDRESS_BAR

Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
2021-06-02 17:04:08 -07:00
Samuel Attard
80f051d859 feat: add experimental cookie encryption support (#27524) (#29492)
* feat: add experimental cookie encryption support on macOS

* chore: fix TODO

* update patches

* feat: make cookie encryption work on windows

* chore: update cookie encryption support comments

* fix: only call OSCrypt::Init on windows

* chore: make cookie encryption work on linux

* Update shell/browser/net/system_network_context_manager.cc

Co-authored-by: Jeremy Rose <jeremya@chromium.org>

* chore: fix lint

* chore: update patches

* chore: update patches to upstreamed variants

* chore: use chrome ::switches constants

* chore: remove bad patch

* build: disable cookie encryption by default

* chore: update patches

* fix: provide std::string to NoDestructor

* chore: fix macos, nodestructor syntax

* build: fix macOS build due to mismatch in DEFINE

Co-authored-by: Electron Bot <electron@github.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>

Co-authored-by: Electron Bot <electron@github.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2021-06-02 16:54:35 -07:00
trop[bot]
95e7c6d53a refactor: point prepare-release at main (#29498)
Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
2021-06-02 14:05:30 -07:00
trop[bot]
1b4c3428a9 chore: don't use after move (#29480)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-06-02 22:25:31 +09:00
trop[bot]
8edb7b456f build: Support building Electron on msys2 (#29476)
Electron already seems to support `cygwin`, so `msys` is a natural
addition. This is the only required change as far as I can see on my
local development environment, as otherwise the build scripts don't
realize that msys = windows.

Notes: none
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>

Co-authored-by: Juan Cruz Viotti <jv@jviotti.com>
2021-06-02 22:24:37 +09:00
trop[bot]
6362736703 fix: inspector context menu throwing an error (#29472)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2021-06-02 16:17:57 +09:00
trop[bot]
670ae438b9 docs: Updated "progress bar" fiddle feature in docs (#29471)
* improve progress bar fiddle

* add comments to code snippet

* edits to progress-bar tutorial

* remove versions and nodeIntegration

* limit line length to 100

* implement standard linter suggestions

* add indeterminate and clear timers

* update to have reader replace all of main.js

* remove extra button

* loop the progress bar

* add logic to show reset state briefly

* Update docs/tutorial/progress-bar.md

Co-authored-by: Erick Zhao <erick@hotmail.ca>

* chore: fix lint

Co-authored-by: Jeremy Foster <jeremy.foster@live.com>
Co-authored-by: Cheng Zhao <github@zcbenz.com>
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2021-06-02 16:17:35 +09:00
trop[bot]
10c6959c7f refactor: use main in release-notes (#29410)
* refactor: use main in release-notes

* fix: use default_branch in release-notes (#29415)

Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2021-06-01 14:06:56 -07:00
trop[bot]
1066dce975 chore: remove duplicate option get for CustomScheme (#29455)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-06-01 19:12:59 +09:00
trop[bot]
d584afdf5b fix: Alt+Click should not toggle menu bar (#29452)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2021-06-01 19:11:54 +09:00
trop[bot]
0d69ba8ca2 fix: add service worker schemes from command line in renderer (#29440)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-06-01 17:51:42 +09:00
trop[bot]
c2ba3ab114 chore: use consistent parameter names (#29441)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-06-01 15:25:46 +09:00
trop[bot]
72a33e79d0 fix: correctly handle Alt+Key shortcuts (#29444)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2021-06-01 15:25:04 +09:00
trop[bot]
3ee0536b1d Fixes issue with reference links (#29429)
Co-authored-by: Vishal <34849822+vthukral94@users.noreply.github.com>
2021-06-01 10:38:12 +09:00
Electron Bot
846a12056d Bump v14.0.0-beta.2 2021-05-31 06:33:28 -07:00
trop[bot]
e0f6313739 refactor: publish-to-npm respects main (#29408) 2021-05-31 10:33:01 +02:00
trop[bot]
8866b312ad docs: update link to Chromium's coding style guide (#29398)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-05-31 10:19:48 +02:00
trop[bot]
cb8fada7a0 fix: close autoHide menu bar when focus is lost (#29349)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2021-05-28 19:00:55 +09:00
trop[bot]
b983bda721 Update quick-start.md (#29371)
In version 13.0.1 preload process should added to webPreferences.

Co-authored-by: aydon <41415004+congjiye@users.noreply.github.com>
2021-05-27 18:52:56 -07:00
trop[bot]
3125ec093d docs: fix typos in clang-tidy examples (#29355)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2021-05-27 18:52:44 -07:00
trop[bot]
a27329d9ad refactor: version-utils respects main (#29392)
Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
2021-05-27 18:44:59 -07:00
trop[bot]
5362882cf6 refactor: getCurrentBranch respects main (#29386)
* refactor: getCurrentBranch respects main

* add note about migration

Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
2021-05-27 18:43:44 -07:00
trop[bot]
c58446d9d7 docs: remove freenode channel from support list (#29383)
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2021-05-27 17:24:25 -07:00
trop[bot]
1c2ed2ba95 docs: Update notifications (main) docs (#29357)
* remove version info from index.html page

* remove nodeIntegration

* format code and update readme

* add note to user in index.html

Co-authored-by: Jeremy Foster <jeremy.foster@live.com>
2021-05-27 12:41:33 -07:00
trop[bot]
1c0e496ee2 docs: fix link to docs/fiddle/quick-start (#29353)
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2021-05-27 12:38:48 -07:00
Electron Bot
0f32b0f1ce Bump v14.0.0-beta.1 2021-05-26 10:13:55 -07:00
98 changed files with 1266 additions and 359 deletions

View File

@@ -1,6 +1,7 @@
import("//build/config/locales.gni")
import("//build/config/ui.gni")
import("//build/config/win/manifest.gni")
import("//components/os_crypt/features.gni")
import("//components/spellcheck/spellcheck_build_features.gni")
import("//content/public/app/mac_helpers.gni")
import("//extensions/buildflags/buildflags.gni")
@@ -296,7 +297,7 @@ templated_file("electron_version_header") {
action("electron_fuses") {
script = "build/fuses/build.py"
inputs = [ "build/fuses/fuses.json" ]
inputs = [ "build/fuses/fuses.json5" ]
outputs = [
"$target_gen_dir/fuses.h",
@@ -335,6 +336,7 @@ source_set("electron_lib") {
"//components/network_hints/common:mojo_bindings",
"//components/network_hints/renderer",
"//components/network_session_configurator/common",
"//components/os_crypt",
"//components/pref_registry",
"//components/prefs",
"//components/security_state/content",
@@ -684,6 +686,10 @@ source_set("electron_lib") {
]
libs += [ "uxtheme.lib" ]
}
if (allow_runtime_configurable_key_storage) {
defines += [ "ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE" ]
}
}
electron_paks("packed_resources") {

View File

@@ -1 +1 @@
14.0.0-nightly.20210524
14.0.0-beta.4

View File

@@ -30,3 +30,6 @@ enable_pseudolocales = false
is_cfi = false
enable_pak_file_integrity_checks = false
# Make application name configurable at runtime for cookie crypto
allow_runtime_configurable_key_storage = true

View File

@@ -49,8 +49,8 @@ const volatile char kFuseWire[] = { /* sentinel */ {sentinel}, /* fuse_version *
}
"""
with open(os.path.join(dir_path, "fuses.json"), 'r') as f:
fuse_defaults = json.load(f)
with open(os.path.join(dir_path, "fuses.json5"), 'r') as f:
fuse_defaults = json.loads(''.join(line for line in f.readlines() if not line.strip()[0] == "/"))
fuse_version = fuse_defaults['_version']
del fuse_defaults['_version']

View File

@@ -2,5 +2,6 @@
"_comment": "Modifying the fuse schema in any breaking way should result in the _version prop being incremented. NEVER remove a fuse or change its meaning, instead mark it as removed with 'r'",
"_schema": "0 == off, 1 == on, r == removed fuse",
"_version": 1,
"run_as_node": "1"
"run_as_node": "1",
"cookie_encryption": "0"
}

View File

@@ -71,7 +71,7 @@ const request = net.request({
Returns:
* `response` IncomingMessage - An object representing the HTTP response message.
* `response` [IncomingMessage](incoming-message.md) - An object representing the HTTP response message.
#### Event: 'login'

View File

@@ -10,12 +10,12 @@ files, you need to have built Electron so that it knows which compiler flags
were used. There is one required option for the script `--output-dir`, which
tells the script which build directory to pull the compilation information
from. A typical usage would be:
`npm run lint:clang-tiy --out-dir ../out/Testing`
`npm run lint:clang-tidy --out-dir ../out/Testing`
With no filenames provided, all C/C++/Objective-C files will be checked.
You can provide a list of files to be checked by passing the filenames after
the options:
`npm run lint:clang-tiy --out-dir ../out/Testing shell/browser/api/electron_api_app.cc`
`npm run lint:clang-tidy --out-dir ../out/Testing shell/browser/api/electron_api_app.cc`
While `clang-tidy` has a
[long list](https://clang.llvm.org/extra/clang-tidy/checks/list.html)

View File

@@ -25,7 +25,7 @@ You can run `npm run lint` to show any style issues detected by `cpplint` and
## C++ and Python
For C++ and Python, we follow Chromium's [Coding
Style](https://www.chromium.org/developers/coding-style). You can use
Style](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/styleguide/styleguide.md). You can use
[clang-format](clang-format.md) to format the C++ code automatically. There is
also a script `script/cpplint.py` to check whether all files conform.

View File

@@ -7,10 +7,6 @@
</head>
<body>
<h1>Hello World!</h1>
<p>
We are using node <script>document.write(process.versions.node)</script>,
Chrome <script>document.write(process.versions.chrome)</script>,
and Electron <script>document.write(process.versions.electron)</script>.
</p>
<p>After launching this application, you should see the system notification.</p>
</body>
</html>

View File

@@ -3,21 +3,17 @@ const { app, BrowserWindow, Notification } = require('electron')
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
height: 600
})
win.loadFile('index.html')
}
const NOTIFICATION_TITLE = 'Basic Notification'
const NOTIFICATION_BODY = 'Notification from the Main process'
function showNotification () {
const notification = {
title: 'Basic Notification',
body: 'Notification from the Main process'
}
new Notification(notification).show()
new Notification({ title: NOTIFICATION_TITLE, body: NOTIFICATION_BODY }).show()
}
app.whenReady().then(createWindow).then(showNotification)

View File

@@ -7,11 +7,9 @@
</head>
<body>
<h1>Hello World!</h1>
<p>
We are using node <script>document.write(process.versions.node)</script>,
Chrome <script>document.write(process.versions.chrome)</script>,
and Electron <script>document.write(process.versions.electron)</script>.
</p>
<p>After launching this application, you should see the system notification.</p>
<p id="output">Click it to see the effect in this interface.</p>
<script src="renderer.js"></script>
</body>
</html>

View File

@@ -3,10 +3,7 @@ const { app, BrowserWindow } = require('electron')
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
height: 600
})
win.loadFile('index.html')

View File

@@ -1,7 +1,6 @@
const myNotification = new Notification('Title', {
body: 'Notification from the Renderer process'
})
const NOTIFICATION_TITLE = 'Title'
const NOTIFICATION_BODY = 'Notification from the Renderer process. Click to log to console.'
const CLICK_MESSAGE = 'Notification clicked!'
myNotification.onclick = () => {
console.log('Notification clicked')
}
new Notification(NOTIFICATION_TITLE, { body: NOTIFICATION_BODY })
.onclick = () => document.getElementById("output").innerText = CLICK_MESSAGE

View File

@@ -7,10 +7,9 @@
</head>
<body>
<h1>Hello World!</h1>
<p>
We are using node <script>document.write(process.versions.node)</script>,
Chrome <script>document.write(process.versions.chrome)</script>,
and Electron <script>document.write(process.versions.electron)</script>.
</p>
<p>Keep an eye on the dock (Mac) or taskbar (Windows, Unity) for this application!</p>
<p>It should indicate a progress that advances from 0 to 100%.</p>
<p>It should then show indeterminate (Windows) or pin at 100% (other operating systems)
briefly and then loop.</p>
</body>
</html>

View File

@@ -1,21 +1,39 @@
const { app, BrowserWindow } = require('electron')
let progressInterval
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
height: 600
})
win.loadFile('index.html')
win.setProgressBar(0.5)
}
const INCREMENT = 0.03
const INTERVAL_DELAY = 100 // ms
let c = 0
progressInterval = setInterval(() => {
// update progress bar to next value
// values between 0 and 1 will show progress, >1 will show indeterminate or stick at 100%
win.setProgressBar(c)
// increment or reset progress bar
if (c < 2) {
c += INCREMENT
} else {
c = (-INCREMENT * 5) // reset to a bit less than 0 to show reset state
}
}, INTERVAL_DELAY)
}
app.whenReady().then(createWindow)
// before the app is terminated, clear both timers
app.on('before-quit', () => {
clearInterval(progressInterval)
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {

View File

@@ -2,15 +2,14 @@
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<title>Recent Documents</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
</head>
<body>
<h1>Hello World!</h1>
<h1>Recent Documents</h1>
<p>
We are using node <script>document.write(process.versions.node)</script>,
Chrome <script>document.write(process.versions.chrome)</script>,
and Electron <script>document.write(process.versions.electron)</script>.
Right click on the app icon to see recent documents.
You should see `recently-used.md` added to the list of recent files
</p>
</body>
</html>

View File

@@ -5,17 +5,15 @@ const path = require('path')
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
height: 600
})
win.loadFile('index.html')
}
const fileName = 'recently-used.md'
fs.writeFile(fileName, 'Lorem Ipsum', () => {
app.addRecentDocument(path.join(process.cwd(), `${fileName}`))
app.addRecentDocument(path.join(__dirname, fileName))
})
app.whenReady().then(createWindow)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -51,4 +51,4 @@ Somewhere in the Electron binary there will be a sequence of bytes that look lik
To flip a fuse you find its position in the fuse wire and change it to "0" or "1" depending on the state you'd like.
You can view the current schema [here](https://github.com/electron/electron/blob/master/build/fuses/fuses.json).
You can view the current schema [here](https://github.com/electron/electron/blob/master/build/fuses/fuses.json5).

View File

@@ -49,11 +49,11 @@ is a great place to get advice from other Electron app developers.
the [GitHub issue tracker][issue-tracker] to see if any existing issues match your
problem. If not, feel free to fill out our bug report template and submit a new issue.
[chromium](https://www.chromium.org/)
[node](https://nodejs.org/)
[mdn-guide](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web)
[node-guide](https://nodejs.dev/learn)
[comic](https://www.google.com/googlebooks/chrome/)
[fiddle](https://electronjs.org/fiddle)
[issue-tracker](https://github.com/electron/electron/issues)
[discord](https://discord.gg/electron)
[chromium]: https://www.chromium.org/
[node]: https://nodejs.org/
[mdn-guide]: https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web
[node-guide]: https://nodejs.dev/learn
[comic]: https://www.google.com/googlebooks/chrome/
[fiddle]: https://electronjs.org/fiddle
[issue-tracker]: https://github.com/electron/electron/issues
[discord]: https://discord.gg/electron

View File

@@ -18,7 +18,7 @@ To show notifications in the Main process, you need to use the
### Show notifications in the Renderer process
Assuming you have a working Electron application from the
Starting with a working application from the
[Quick Start Guide](quick-start.md), add the following line to the
`index.html` file before the closing `</body>` tag:
@@ -26,26 +26,22 @@ Assuming you have a working Electron application from the
<script src="renderer.js"></script>
```
and add the `renderer.js` file:
...and add the `renderer.js` file:
```javascript fiddle='docs/fiddles/features/notifications/renderer'
const myNotification = new Notification('Title', {
body: 'Notification from the Renderer process'
})
const NOTIFICATION_TITLE = 'Title'
const NOTIFICATION_BODY = 'Notification from the Renderer process. Click to log to console.'
const CLICK_MESSAGE = 'Notification clicked'
myNotification.onclick = () => {
console.log('Notification clicked')
}
new Notification(NOTIFICATION_TITLE, { body: NOTIFICATION_BODY })
.onclick = () => console.log(CLICK_MESSAGE)
```
After launching the Electron application, you should see the notification:
![Notification in the Renderer process](../images/notification-renderer.png)
If you open the Console and then click the notification, you will see the
message that was generated after triggering the `onclick` event:
![Onclick message for the notification](../images/message-notification-renderer.png)
Additionally, if you click on the notification, the DOM will update to show "Notification clicked!".
### Show notifications in the Main process
@@ -55,18 +51,17 @@ Starting with a working application from the
```javascript fiddle='docs/fiddles/features/notifications/main'
const { Notification } = require('electron')
const NOTIFICATION_TITLE = 'Basic Notification'
const NOTIFICATION_BODY = 'Notification from the Main process'
function showNotification () {
const notification = {
title: 'Basic Notification',
body: 'Notification from the Main process'
}
new Notification(notification).show()
new Notification({ title: NOTIFICATION_TITLE, body: NOTIFICATION_BODY }).show()
}
app.whenReady().then(createWindow).then(showNotification)
```
After launching the Electron application, you should see the notification:
After launching the Electron application, you should see the system notification:
![Notification in the Main process](../images/notification-main.png)

View File

@@ -31,30 +31,70 @@ currently at 63% towards completion, you would call it as
`setProgressBar(0.63)`.
Setting the parameter to negative values (e.g. `-1`) will remove the progress
bar, whereas setting it to values greater than `1` (e.g. `2`) will switch the
progress bar to indeterminate mode (Windows-only -- it will clamp to 100%
otherwise). In this mode, a progress bar remains active but does not show an
actual percentage. Use this mode for situations when you do not know how long
an operation will take to complete.
bar. Setting it to a value greater than `1` will indicate an indeterminate progress bar
in Windows or clamp to 100% in other operating systems. An indeterminate progress bar
remains active but does not show an actual percentage, and is used for situations when
you do not know how long an operation will take to complete.
See the [API documentation for more options and modes][setprogressbar].
## Example
Starting with a working application from the
[Quick Start Guide](quick-start.md), add the following lines to the
`main.js` file:
In this example, we add a progress bar to the main window that increments over time
using Node.js timers.
```javascript fiddle='docs/fiddles/features/progress-bar'
const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
const { app, BrowserWindow } = require('electron')
win.setProgressBar(0.5)
let progressInterval
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
const INCREMENT = 0.03
const INTERVAL_DELAY = 100 // ms
let c = 0
progressInterval = setInterval(() => {
// update progress bar to next value
// values between 0 and 1 will show progress, >1 will show indeterminate or stick at 100%
win.setProgressBar(c)
// increment or reset progress bar
if (c < 2) c += INCREMENT
else c = 0
}, INTERVAL_DELAY)
}
app.whenReady().then(createWindow)
// before the app is terminated, clear both timers
app.on('before-quit', () => {
clearInterval(progressInterval)
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
```
After launching the Electron application, you should see the bar in
the dock (macOS) or taskbar (Windows, Unity), indicating the progress
percentage you just defined.
After launching the Electron application, the dock (macOS) or taskbar (Windows, Unity)
should show a progress bar that starts at zero and progresses through 100% to completion.
It should then show indeterminate (Windows) or pin to 100% (other operating systems)
briefly and then loop.
![macOS dock progress bar](../images/dock-progress-bar.png)

View File

@@ -299,7 +299,9 @@ function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
preload: path.join(__dirname, 'preload.js')
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('index.html')
@@ -440,7 +442,7 @@ window.addEventListener('DOMContentLoaded', () => {
</html>
```
```fiddle docs/fiddles/quickstart
```fiddle docs/fiddles/quick-start
```
To summarize all the steps we've done:

View File

@@ -13,39 +13,62 @@ __Application dock menu:__
![macOS Dock Menu][dock-menu-image]
To add a file to recent documents, you need to use the
[app.addRecentDocument][addrecentdocument] API.
## Example
### Add an item to recent documents
Starting with a working application from the
[Quick Start Guide](quick-start.md), add the following lines to the
`main.js` file:
### Managing recent documents
```javascript fiddle='docs/fiddles/features/recent-documents'
const { app } = require('electron')
const { app, BrowserWindow } = require('electron')
const fs = require('fs')
const path = require('path')
app.addRecentDocument('/Users/USERNAME/Desktop/work.type')
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
const fileName = 'recently-used.md'
fs.writeFile(fileName, 'Lorem Ipsum', () => {
app.addRecentDocument(path.join(__dirname, fileName))
})
app.whenReady().then(createWindow)
app.on('window-all-closed', () => {
app.clearRecentDocuments()
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
```
#### Adding a recent document
To add a file to recent documents, use the
[app.addRecentDocument][addrecentdocument] API.
After launching the Electron application, right click the application icon.
You should see the item you just added. In this guide, the item is a Markdown
file located in the root of the project:
In this guide, the item is a Markdown file located in the root of the project.
You should see `recently-used.md` added to the list of recent files:
![Recent document](../images/recent-documents.png)
### Clear the list of recent documents
#### Clearing the list of recent documents
To clear the list of recent documents, you need to use
[app.clearRecentDocuments][clearrecentdocuments] API in the `main.js` file:
```javascript
const { app } = require('electron')
app.clearRecentDocuments()
```
To clear the list of recent documents, use the
[app.clearRecentDocuments][clearrecentdocuments] API.
In this guide, the list of documents is cleared once all windows have been
closed.
## Additional information

View File

@@ -16,7 +16,6 @@ you can interact with the community in these locations:
* Sharing ideas with other Electron app developers
* And more!
* [`electron`](https://discuss.atom.io/c/electron) category on the Atom forums
* `#atom-shell` channel on Freenode
* `#electron` channel on [Atom's Slack](https://discuss.atom.io/t/join-us-on-slack/16638?source_topic_id=25406)
* [`electron-ru`](https://telegram.me/electron_ru) *(Russian)*
* [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)*

View File

@@ -54,7 +54,7 @@ const isChromeDevTools = function (pageURL: string) {
};
const assertChromeDevTools = function (contents: Electron.WebContents, api: string) {
const pageURL = contents._getURL();
const pageURL = contents.getURL();
if (!isChromeDevTools(pageURL)) {
console.error(`Blocked ${pageURL} from calling ${api}`);
throw new Error(`Blocked ${api}`);

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "14.0.0-nightly.20210524",
"version": "14.0.0-beta.4",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

View File

@@ -102,3 +102,4 @@ add_setter_for_browsermainloop_result_code.patch
chore_allow_overriding_of_enable_pak_file_integrity_checks.patch
make_include_of_stack_trace_h_unconditional.patch
build_libc_as_static_library.patch
support_runtime_configurable_key_storage_on_linux_os_crypto.patch

View File

@@ -1,16 +1,16 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
Date: Thu, 4 Oct 2018 14:57:02 -0700
Subject: accelerator.patch
Subject: fix: improve shortcut text of Accelerator
This patch makes three changes to Accelerator::GetShortcutText to improve shortcut display text in menus:
1. Ctrl-Alt-<Key> accelerators show as Ctrl-Alt-<Key> instead of as Ctrl-<Key>
2. F2-F24 accelerators show up as such
3. Ctrl-Shift-= should show as Ctrl-+
3. Ctrl-Shift-= and Ctrl-Plus show up as such
diff --git a/ui/base/accelerators/accelerator.cc b/ui/base/accelerators/accelerator.cc
index d6913b15149f773cad28b5e2278b0f80df3d2896..15f944c4bb2fde7241b643f6a979a81ebce844b1 100644
index d6913b15149f773cad28b5e2278b0f80df3d2896..25342f62acdc28806a0e6ae0bd129c59083ccf06 100644
--- a/ui/base/accelerators/accelerator.cc
+++ b/ui/base/accelerators/accelerator.cc
@@ -11,6 +11,7 @@
@@ -21,61 +21,39 @@ index d6913b15149f773cad28b5e2278b0f80df3d2896..15f944c4bb2fde7241b643f6a979a81e
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
@@ -27,9 +28,7 @@
#include <windows.h>
@@ -234,6 +235,11 @@ std::u16string Accelerator::GetShortcutText() const {
#endif
-#if !defined(OS_WIN) && (defined(USE_AURA) || defined(OS_MAC))
#include "ui/events/keycodes/keyboard_code_conversion.h"
-#endif
#if defined(OS_CHROMEOS)
#include "ui/base/ui_base_features.h"
@@ -233,7 +232,15 @@ std::u16string Accelerator::GetShortcutText() const {
shortcut = KeyCodeToName();
#endif
+ unsigned int flags = 0;
if (shortcut.empty()) {
+ const uint16_t c = DomCodeToUsLayoutCharacter(
+ UsLayoutKeyboardCodeToDomCode(key_code_), flags);
+ if (c != 0) {
+ shortcut =
+ static_cast<std::u16string::value_type>(
+ base::ToUpperASCII(static_cast<char16_t>(c)));
+ }
+ // When a shifted char is explicitly specified, for example Ctrl+Plus,
+ // use the shifted char directly.
+ if (shifted_char) {
+ shortcut += *shifted_char;
+ } else {
#if defined(OS_WIN)
// Our fallback is to try translate the key code to a regular character
// unless it is one of digits (VK_0 to VK_9). Some keyboard
@@ -242,21 +249,14 @@ std::u16string Accelerator::GetShortcutText() const {
// accent' for '0'). For display in the menu (e.g. Ctrl-0 for the
// default zoom level), we leave VK_[0-9] alone without translation.
wchar_t key;
- if (base::IsAsciiDigit(key_code_))
+ if (base::IsAsciiDigit(key_code_)) {
key = static_cast<wchar_t>(key_code_);
- else
- key = LOWORD(::MapVirtualKeyW(key_code_, MAPVK_VK_TO_CHAR));
- // If there is no translation for the given |key_code_| (e.g.
- // VKEY_UNKNOWN), |::MapVirtualKeyW| returns 0.
- if (key != 0)
- shortcut += key;
-#elif defined(USE_AURA) || defined(OS_MAC) || defined(OS_ANDROID)
- const uint16_t c = DomCodeToUsLayoutCharacter(
- UsLayoutKeyboardCodeToDomCode(key_code_), false);
- if (c != 0)
- shortcut +=
- static_cast<std::u16string::value_type>(base::ToUpperASCII(c));
+ shortcut = key;
+ }
@@ -257,6 +263,10 @@ std::u16string Accelerator::GetShortcutText() const {
shortcut +=
static_cast<std::u16string::value_type>(base::ToUpperASCII(c));
#endif
+ }
+ if (key_code_ > VKEY_F1 && key_code_ <= VKEY_F24)
+ shortcut = base::UTF8ToUTF16(
+ base::StringPrintf("F%d", key_code_ - VKEY_F1 + 1));
}
#if defined(OS_MAC)
@@ -452,7 +452,7 @@ std::u16string Accelerator::ApplyLongFormModifiers(
@@ -444,7 +454,7 @@ std::u16string Accelerator::ApplyLongFormModifiers(
const std::u16string& shortcut) const {
std::u16string result = shortcut;
- if (IsShiftDown())
+ if (!shifted_char && IsShiftDown())
result = ApplyModifierToAcceleratorString(result, IDS_APP_SHIFT_KEY);
// Note that we use 'else-if' in order to avoid using Ctrl+Alt as a shortcut.
@@ -452,7 +462,7 @@ std::u16string Accelerator::ApplyLongFormModifiers(
// more information.
if (IsCtrlDown())
result = ApplyModifierToAcceleratorString(result, IDS_APP_CTRL_KEY);
@@ -84,3 +62,24 @@ index d6913b15149f773cad28b5e2278b0f80df3d2896..15f944c4bb2fde7241b643f6a979a81e
result = ApplyModifierToAcceleratorString(result, IDS_APP_ALT_KEY);
if (IsCmdDown()) {
diff --git a/ui/base/accelerators/accelerator.h b/ui/base/accelerators/accelerator.h
index 780a45f9ca2dd60e0deac27cc6e8f69e72cd8435..b740fbbfb14b5737b18b84c07c8e9f79cfc645c0 100644
--- a/ui/base/accelerators/accelerator.h
+++ b/ui/base/accelerators/accelerator.h
@@ -16,6 +16,7 @@
#include <utility>
#include "base/component_export.h"
+#include "base/optional.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "ui/events/event_constants.h"
@@ -129,6 +130,8 @@ class COMPONENT_EXPORT(UI_BASE) Accelerator {
return interrupted_by_mouse_event_;
}
+ base::Optional<char16_t> shifted_char;
+
private:
std::u16string ApplyLongFormModifiers(const std::u16string& shortcut) const;
std::u16string ApplyShortFormModifiers(const std::u16string& shortcut) const;

View File

@@ -0,0 +1,288 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <samuel.r.attard@gmail.com>
Date: Mon, 10 May 2021 17:10:25 -0700
Subject: support runtime configurable key storage on linux (os_crypto)
This modifies the OsCrypt::Config struct used on linux to support
runtime configurable application names which are used in the Keyring and
LibSecret implementations of os_crypt on linux.
Change-Id: Ifc287b589f118da8fcd5afaf39e5ba7ffe46f5fd
diff --git a/components/os_crypt/key_storage_config_linux.h b/components/os_crypt/key_storage_config_linux.h
index a856604756aa65c52171a9eff84ba2b316d8609c..72c16682e5df615ab84f67af66cc36c2b76c30e3 100644
--- a/components/os_crypt/key_storage_config_linux.h
+++ b/components/os_crypt/key_storage_config_linux.h
@@ -26,6 +26,12 @@ struct COMPONENT_EXPORT(OS_CRYPT) Config {
std::string store;
// The product name to use for permission prompts.
std::string product_name;
+ // The application name to store the key under. For Chromium/Chrome builds
+ // leave this unset and it will default correctly. This config option is
+ // for embedders to provide their application name in place of "Chromium".
+ // Only used when the allow_runtime_configurable_key_storage feature is
+ // enabled.
+ std::string application_name;
// A runner on the main thread for gnome-keyring to be called from.
// TODO(crbug/466975): Libsecret and KWallet don't need this. We can remove
// this when we stop supporting keyring.
diff --git a/components/os_crypt/key_storage_keyring.cc b/components/os_crypt/key_storage_keyring.cc
index 29720b8e8ff81791970e054050b37bbc5f338eb3..3ad654cf4a893f497ac4567aea77f8d1f2e1525f 100644
--- a/components/os_crypt/key_storage_keyring.cc
+++ b/components/os_crypt/key_storage_keyring.cc
@@ -15,12 +15,6 @@
namespace {
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-const char kApplicationName[] = "chrome";
-#else
-const char kApplicationName[] = "chromium";
-#endif
-
const GnomeKeyringPasswordSchema kSchema = {
GNOME_KEYRING_ITEM_GENERIC_SECRET,
{{"application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING}, {nullptr}}};
@@ -28,8 +22,10 @@ const GnomeKeyringPasswordSchema kSchema = {
} // namespace
KeyStorageKeyring::KeyStorageKeyring(
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner)
- : main_thread_runner_(main_thread_runner) {}
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
+ std::string application_name)
+ : main_thread_runner_(main_thread_runner),
+ application_name_(std::move(application_name)) {}
KeyStorageKeyring::~KeyStorageKeyring() {}
@@ -49,7 +45,8 @@ absl::optional<std::string> KeyStorageKeyring::GetKeyImpl() {
gchar* password_c = nullptr;
GnomeKeyringResult result =
GnomeKeyringLoader::gnome_keyring_find_password_sync_ptr(
- &kSchema, &password_c, "application", kApplicationName, nullptr);
+ &kSchema, &password_c, "application", application_name_.c_str(),
+ nullptr);
if (result == GNOME_KEYRING_RESULT_OK) {
password = password_c;
GnomeKeyringLoader::gnome_keyring_free_password_ptr(password_c);
@@ -71,7 +68,7 @@ absl::optional<std::string> KeyStorageKeyring::AddRandomPasswordInKeyring() {
GnomeKeyringResult result =
GnomeKeyringLoader::gnome_keyring_store_password_sync_ptr(
&kSchema, nullptr /* default keyring */, KeyStorageLinux::kKey,
- password.c_str(), "application", kApplicationName, nullptr);
+ password.c_str(), "application", application_name_.c_str(), nullptr);
if (result != GNOME_KEYRING_RESULT_OK) {
VLOG(1) << "OSCrypt failed to store generated password to gnome-keyring";
return absl::nullopt;
diff --git a/components/os_crypt/key_storage_keyring.h b/components/os_crypt/key_storage_keyring.h
index 26a3f587b49cdea3e4913ce0e08eeade6d1853a0..598c8b8ad35efa002a29dd98c9cba98453bef5ac 100644
--- a/components/os_crypt/key_storage_keyring.h
+++ b/components/os_crypt/key_storage_keyring.h
@@ -20,8 +20,9 @@ class SingleThreadTaskRunner;
// Specialisation of KeyStorageLinux that uses Libsecret.
class COMPONENT_EXPORT(OS_CRYPT) KeyStorageKeyring : public KeyStorageLinux {
public:
- explicit KeyStorageKeyring(
- scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner);
+ KeyStorageKeyring(
+ scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
+ std::string application_name);
~KeyStorageKeyring() override;
protected:
@@ -37,6 +38,8 @@ class COMPONENT_EXPORT(OS_CRYPT) KeyStorageKeyring : public KeyStorageLinux {
// Keyring calls need to originate from the main thread.
scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner_;
+ const std::string application_name_;
+
DISALLOW_COPY_AND_ASSIGN(KeyStorageKeyring);
};
diff --git a/components/os_crypt/key_storage_keyring_unittest.cc b/components/os_crypt/key_storage_keyring_unittest.cc
index aba31971dc33f4b60c2847fd037c419baf084eaa..93fc216d54168eb3fd7e9f1b23f71a77f6e6735d 100644
--- a/components/os_crypt/key_storage_keyring_unittest.cc
+++ b/components/os_crypt/key_storage_keyring_unittest.cc
@@ -130,7 +130,7 @@ class GnomeKeyringTest : public testing::Test {
};
GnomeKeyringTest::GnomeKeyringTest()
- : task_runner_(new base::TestSimpleTaskRunner()), keyring_(task_runner_) {
+ : task_runner_(new base::TestSimpleTaskRunner()), keyring_(task_runner_, "chromium") {
MockGnomeKeyringLoader::ResetForOSCrypt();
}
diff --git a/components/os_crypt/key_storage_libsecret.cc b/components/os_crypt/key_storage_libsecret.cc
index 0857cc1874f4153541c7f21dd8cc6bdc1d59b39d..ccc60522791508f02dc759e55e781dbb9627967b 100644
--- a/components/os_crypt/key_storage_libsecret.cc
+++ b/components/os_crypt/key_storage_libsecret.cc
@@ -14,12 +14,6 @@
namespace {
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-const char kApplicationName[] = "chrome";
-#else
-const char kApplicationName[] = "chromium";
-#endif
-
const SecretSchema kKeystoreSchemaV2 = {
"chrome_libsecret_os_crypt_password_v2",
SECRET_SCHEMA_DONT_MATCH_NAME,
@@ -64,6 +58,9 @@ void AnalyseKeyHistory(GList* secret_items) {
} // namespace
+KeyStorageLibsecret::KeyStorageLibsecret(std::string application_name)
+ : application_name_(std::move(application_name)) {}
+
absl::optional<std::string>
KeyStorageLibsecret::AddRandomPasswordInLibsecret() {
std::string password;
@@ -71,7 +68,7 @@ KeyStorageLibsecret::AddRandomPasswordInLibsecret() {
GError* error = nullptr;
bool success = LibsecretLoader::secret_password_store_sync(
&kKeystoreSchemaV2, nullptr, KeyStorageLinux::kKey, password.c_str(),
- nullptr, &error, "application", kApplicationName, nullptr);
+ nullptr, &error, "application", application_name_.c_str(), nullptr);
if (error) {
VLOG(1) << "Libsecret lookup failed: " << error->message;
g_error_free(error);
@@ -88,7 +85,7 @@ KeyStorageLibsecret::AddRandomPasswordInLibsecret() {
absl::optional<std::string> KeyStorageLibsecret::GetKeyImpl() {
LibsecretAttributesBuilder attrs;
- attrs.Append("application", kApplicationName);
+ attrs.Append("application", application_name_);
LibsecretLoader::SearchHelper helper;
helper.Search(&kKeystoreSchemaV2, attrs.Get(),
diff --git a/components/os_crypt/key_storage_libsecret.h b/components/os_crypt/key_storage_libsecret.h
index 4759d076ea0017a41b398491dd24dde76a61e463..5292e21b8e7e1a873591e474571adb5b4ed8fe16 100644
--- a/components/os_crypt/key_storage_libsecret.h
+++ b/components/os_crypt/key_storage_libsecret.h
@@ -15,7 +15,7 @@
// Specialisation of KeyStorageLinux that uses Libsecret.
class COMPONENT_EXPORT(OS_CRYPT) KeyStorageLibsecret : public KeyStorageLinux {
public:
- KeyStorageLibsecret() = default;
+ explicit KeyStorageLibsecret(std::string application_name);
~KeyStorageLibsecret() override = default;
protected:
@@ -26,6 +26,8 @@ class COMPONENT_EXPORT(OS_CRYPT) KeyStorageLibsecret : public KeyStorageLinux {
private:
absl::optional<std::string> AddRandomPasswordInLibsecret();
+ const std::string application_name_;
+
DISALLOW_COPY_AND_ASSIGN(KeyStorageLibsecret);
};
diff --git a/components/os_crypt/key_storage_libsecret_unittest.cc b/components/os_crypt/key_storage_libsecret_unittest.cc
index ebe9a6b4bcbf748cd7f7e5fcf941b78ab1835749..a17bbc1f8061b33d55444919f546256d63bc809b 100644
--- a/components/os_crypt/key_storage_libsecret_unittest.cc
+++ b/components/os_crypt/key_storage_libsecret_unittest.cc
@@ -236,7 +236,7 @@ class LibsecretTest : public testing::Test {
};
TEST_F(LibsecretTest, LibsecretRepeats) {
- KeyStorageLibsecret libsecret;
+ KeyStorageLibsecret libsecret("chromium");
MockLibsecretLoader::ResetForOSCrypt();
g_password_store.Pointer()->SetPassword("initial password");
absl::optional<std::string> password = libsecret.GetKey();
@@ -248,7 +248,7 @@ TEST_F(LibsecretTest, LibsecretRepeats) {
}
TEST_F(LibsecretTest, LibsecretCreatesRandomised) {
- KeyStorageLibsecret libsecret;
+ KeyStorageLibsecret libsecret("chromium");
MockLibsecretLoader::ResetForOSCrypt();
absl::optional<std::string> password = libsecret.GetKey();
MockLibsecretLoader::ResetForOSCrypt();
diff --git a/components/os_crypt/key_storage_linux.cc b/components/os_crypt/key_storage_linux.cc
index 8feb147c96baa2f853a647eb648297b4b747f515..53b1903ff04043e2d058deda086e116a0e00fdca 100644
--- a/components/os_crypt/key_storage_linux.cc
+++ b/components/os_crypt/key_storage_linux.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/nix/xdg_util.h"
+#include "base/no_destructor.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/task_runner_util.h"
@@ -147,12 +148,29 @@ std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateService(
std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateServiceInternal(
os_crypt::SelectedLinuxBackend selected_backend,
const os_crypt::Config& config) {
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+ static const base::NoDestructor<std::string> kDefaultApplicationName("chrome");
+#else
+ static const base::NoDestructor<std::string> kDefaultApplicationName("chromium");
+#endif
+
std::unique_ptr<KeyStorageLinux> key_storage;
+#if defined(USE_LIBSECRET) || defined(USE_KEYRING)
+#if defined(ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE)
+ std::string application_name = config.application_name;
+ if (application_name.empty()) {
+ application_name = *kDefaultApplicationName;
+ }
+#else
+ std::string application_name = *kDefaultApplicationName;
+#endif
+#endif
+
#if defined(USE_LIBSECRET)
if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
selected_backend == os_crypt::SelectedLinuxBackend::GNOME_LIBSECRET) {
- key_storage = std::make_unique<KeyStorageLibsecret>();
+ key_storage = std::make_unique<KeyStorageLibsecret>(std::move(application_name));
if (key_storage->WaitForInitOnTaskRunner()) {
VLOG(1) << "OSCrypt using Libsecret as backend.";
return key_storage;
@@ -164,8 +182,8 @@ std::unique_ptr<KeyStorageLinux> KeyStorageLinux::CreateServiceInternal(
#if defined(USE_KEYRING)
if (selected_backend == os_crypt::SelectedLinuxBackend::GNOME_ANY ||
selected_backend == os_crypt::SelectedLinuxBackend::GNOME_KEYRING) {
- key_storage =
- std::make_unique<KeyStorageKeyring>(config.main_thread_runner);
+ key_storage = std::make_unique<KeyStorageKeyring>(config.main_thread_runner,
+ std::move(application_name));
if (key_storage->WaitForInitOnTaskRunner()) {
VLOG(1) << "OSCrypt using Keyring as backend.";
return key_storage;
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 02c8af43258f6a31f3cc667e73f55f8f67330b51..bea06f626cc2b302ca8a25c753c37cea0d4c9e8c 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -622,6 +622,7 @@ void NetworkService::SetCryptConfig(mojom::CryptConfigPtr crypt_config) {
auto config = std::make_unique<os_crypt::Config>();
config->store = crypt_config->store;
config->product_name = crypt_config->product_name;
+ config->application_name = crypt_config->application_name;
config->main_thread_runner = base::ThreadTaskRunnerHandle::Get();
config->should_use_preference = crypt_config->should_use_preference;
config->user_data_path = crypt_config->user_data_path;
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index 3a1abc4d1b64ad9480f5218cd1de8fd61a064f34..0edbe59d3a70b5bca759cd8a726baded6b81ea71 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -99,6 +99,13 @@ struct CryptConfig {
// The product name to use for permission prompts.
string product_name;
+ // The application name to store the crypto key against. For Chromium/Chrome
+ // builds leave this unset and it will default correctly. This config option
+ // is for embedders to provide their application name in place of "Chromium".
+ // Only used when the allow_runtime_configurable_key_storage feature is
+ // enabled
+ string application_name;
+
// Controls whether preference on using or ignoring backends is used.
bool should_use_preference;

View File

@@ -508,7 +508,7 @@ index 0000000000000000000000000000000000000000..bdfaf95f3eca65b3e0831db1b66f651d
+}
diff --git a/build/xcrun.py b/build/xcrun.py
new file mode 100644
index 0000000000000000000000000000000000000000..20d0cdb51cc933f56b7a7193c195457e82500870
index 0000000000000000000000000000000000000000..18ac587f80441106405d00fafd9ee1f25b147772
--- /dev/null
+++ b/build/xcrun.py
@@ -0,0 +1,14 @@
@@ -524,7 +524,7 @@ index 0000000000000000000000000000000000000000..20d0cdb51cc933f56b7a7193c195457e
+try:
+ subprocess.check_output(args, stderr=subprocess.STDOUT)
+except subprocess.CalledProcessError as e:
+ print("xcrun script '" + sys.argv[2] + "' failed with code '" + str(e.returncode) + "':\n" + e.output)
+ print("xcrun script '" + ' '.join(sys.argv[1:]) + "' failed with code '" + str(e.returncode) + "':\n" + e.output)
+ sys.exit(e.returncode)
diff --git a/filenames.gni b/filenames.gni
new file mode 100644

View File

@@ -6,6 +6,7 @@ import sys
PLATFORM = {
'cygwin': 'win32',
'msys': 'win32',
'darwin': 'darwin',
'linux': 'linux',
'linux2': 'linux',

View File

@@ -6,6 +6,9 @@ const ELECTRON_DIR = path.resolve(__dirname, '..', '..');
const SRC_DIR = path.resolve(ELECTRON_DIR, '..');
const RELEASE_BRANCH_PATTERN = /(\d)+-(?:(?:[0-9]+-x$)|(?:x+-y$))/;
// TODO(main-migration): Simplify once main branch is renamed
const MAIN_BRANCH_PATTERN = /^(main|master)$/;
const ORIGIN_MAIN_BRANCH_PATTERN = /^origin\/(main|master)$/;
require('colors');
const pass = '✓'.green;
@@ -73,7 +76,7 @@ async function handleGitCall (args, gitDir) {
async function getCurrentBranch (gitDir) {
let branch = await handleGitCall(['rev-parse', '--abbrev-ref', 'HEAD'], gitDir);
if (branch !== 'master' && !RELEASE_BRANCH_PATTERN.test(branch)) {
if (!MAIN_BRANCH_PATTERN.test(branch) && !RELEASE_BRANCH_PATTERN.test(branch)) {
const lastCommit = await handleGitCall(['rev-parse', 'HEAD'], gitDir);
const branches = (await handleGitCall([
'branch',
@@ -82,7 +85,7 @@ async function getCurrentBranch (gitDir) {
'--remote'
], gitDir)).split('\n');
branch = branches.filter(b => b.trim() === 'master' || b.trim() === 'origin/master' || RELEASE_BRANCH_PATTERN.test(b.trim()))[0];
branch = branches.find(b => MAIN_BRANCH_PATTERN.test(b.trim()) || ORIGIN_MAIN_BRANCH_PATTERN.test(b.trim()) || RELEASE_BRANCH_PATTERN.test(b.trim()));
if (!branch) {
console.log(`${fail} no release branch exists for this ref`);
process.exit(1);

View File

@@ -8,6 +8,11 @@ const semver = require('semver');
const { ELECTRON_DIR } = require('../../lib/utils');
const notesGenerator = require('./notes.js');
const { Octokit } = require('@octokit/rest');
const octokit = new Octokit({
auth: process.env.ELECTRON_GITHUB_TOKEN
});
const semverify = version => version.replace(/^origin\//, '').replace(/[xy]/g, '0').replace(/-/g, '.');
const runGit = async (args) => {
@@ -37,13 +42,17 @@ const getTagsOf = async (point) => {
};
const getTagsOnBranch = async (point) => {
const masterTags = await getTagsOf('master');
if (point === 'master') {
return masterTags;
const { data: { default_branch: defaultBranch } } = await octokit.repos.get({
owner: 'electron',
repo: 'electron'
});
const mainTags = await getTagsOf(defaultBranch);
if (point === defaultBranch) {
return mainTags;
}
const masterTagsSet = new Set(masterTags);
return (await getTagsOf(point)).filter(tag => !masterTagsSet.has(tag));
const mainTagsSet = new Set(mainTags);
return (await getTagsOf(point)).filter(tag => !mainTagsSet.has(tag));
};
const getBranchOf = async (point) => {
@@ -66,7 +75,8 @@ const getAllBranches = async () => {
return branches.split('\n')
.map(branch => branch.trim())
.filter(branch => !!branch)
.filter(branch => branch !== 'origin/HEAD -> origin/master')
// TODO(main-migration): Simplify once branch rename is complete.
.filter(branch => branch !== 'origin/HEAD -> origin/master' && branch !== 'origin/HEAD -> origin/main')
.sort();
} catch (err) {
console.error('Failed to fetch all branches');

View File

@@ -112,7 +112,7 @@ async function createRelease (branchToTarget, isBeta) {
name: `electron ${newVersion}`,
body: releaseBody,
prerelease: releaseIsPrelease,
target_commitish: newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget
target_commitish: newVersion.indexOf('nightly') !== -1 ? 'main' : branchToTarget
}).catch(err => {
console.log(`${fail} Error creating new release: `, err);
process.exit(1);
@@ -180,7 +180,7 @@ async function promptForVersion (version) {
});
}
// function to determine if there have been commits to master since the last release
// function to determine if there have been commits to main since the last release
async function changesToRelease () {
const lastCommitWasRelease = new RegExp('^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$', 'g');
const lastCommit = await GitProcess.exec(['log', '-n', '1', '--pretty=format:\'%s\''], ELECTRON_DIR);

View File

@@ -111,8 +111,9 @@ new Promise((resolve, reject) => {
const currentBranch = await getCurrentBranch();
if (release.tag_name.indexOf('nightly') > 0) {
if (currentBranch === 'master') {
// Nightlies get published to their own module, so master nightlies should be tagged as latest
// TODO(main-migration): Simplify once main branch is renamed.
if (currentBranch === 'master' || currentBranch === 'main') {
// Nightlies get published to their own module, so they should be tagged as latest
npmTag = 'latest';
} else {
npmTag = `nightly-${currentBranch}`;
@@ -127,10 +128,10 @@ new Promise((resolve, reject) => {
JSON.stringify(currentJson, null, 2)
);
} else {
if (currentBranch === 'master') {
// This should never happen, master releases should be nightly releases
if (currentBranch === 'master' || currentBranch === 'main') {
// This should never happen, main releases should be nightly releases
// this is here just-in-case
npmTag = 'master';
throw new Error('Unreachable release phase, can\'t tag a non-nightly release on the main branch');
} else if (!release.prerelease) {
// Tag the release with a `2-0-x` style tag
npmTag = currentBranch;

View File

@@ -1,17 +1,17 @@
#!/usr/bin/env node
const { GitProcess } = require('dugite');
const fs = require('fs');
const { promises: fs } = require('fs');
const semver = require('semver');
const path = require('path');
const { promisify } = require('util');
const minimist = require('minimist');
const { ELECTRON_DIR } = require('../lib/utils');
const versionUtils = require('./version-utils');
const supported = path.resolve(ELECTRON_DIR, 'docs', 'tutorial', 'support.md');
const writeFile = promisify(fs.writeFile);
const readFile = promisify(fs.readFile);
const writeFile = fs.writeFile;
const readFile = fs.readFile;
function parseCommandLine () {
let help;
@@ -54,6 +54,10 @@ async function main () {
return 0;
}
if (shouldUpdateSupported(opts.bump, currentVersion, version)) {
await updateSupported(version, supported);
}
// update all version-related files
await Promise.all([
updateVersion(version),
@@ -105,6 +109,22 @@ async function nextVersion (bumpType, version) {
return version;
}
function shouldUpdateSupported (bump, current, version) {
return isMajorStable(bump, current) || isMajorNightly(version, current);
}
function isMajorStable (bump, currentVersion) {
if (versionUtils.isBeta(currentVersion) && (bump === 'stable')) return true;
return false;
}
function isMajorNightly (version, currentVersion) {
const parsed = semver.parse(version);
const current = semver.parse(currentVersion);
if (versionUtils.isNightly(currentVersion) && (parsed.major > current.major)) return true;
return false;
}
// update VERSION file with latest release info
async function updateVersion (version) {
const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION');
@@ -142,6 +162,22 @@ async function updateWinRC (components) {
await writeFile(filePath, arr.join('\n'));
}
// updates support.md file with new semver values (stable only)
async function updateSupported (version, filePath) {
const v = parseInt(version);
const newVersions = [`* ${v}.x.y`, `* ${v - 1}.x.y`, `* ${v - 2}.x.y`];
const contents = await readFile(filePath, 'utf8');
const previousVersions = contents.split('\n').filter((elem) => {
return (/[^\n]*\.x\.y[^\n]*/).test(elem);
}, []);
const newContents = previousVersions.reduce((contents, current, i) => {
return contents.replace(current, newVersions[i]);
}, contents);
await writeFile(filePath, newContents, 'utf8');
}
if (process.mainModule === module) {
main().catch((error) => {
console.error(error);
@@ -149,4 +185,4 @@ if (process.mainModule === module) {
});
}
module.exports = { nextVersion };
module.exports = { nextVersion, shouldUpdateSupported, updateSupported };

View File

@@ -65,8 +65,9 @@ async function nextNightly (v) {
const pre = `nightly.${getCurrentDate()}`;
const branch = (await GitProcess.exec(['rev-parse', '--abbrev-ref', 'HEAD'], ELECTRON_DIR)).stdout.trim();
if (branch === 'master') {
next = semver.inc(await getLastMajorForMaster(), 'major');
// TODO(main-migration): Simplify once main branch is renamed
if (branch === 'master' || branch === 'main') {
next = semver.inc(await getLastMajorForMain(), 'major');
} else if (isStable(v)) {
next = semver.inc(next, 'patch');
}
@@ -74,7 +75,7 @@ async function nextNightly (v) {
return `${next}-${pre}`;
}
async function getLastMajorForMaster () {
async function getLastMajorForMain () {
let branchNames;
const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], ELECTRON_DIR);
if (result.exitCode === 0) {

View File

@@ -126,7 +126,7 @@ class App : public ElectronBrowserClient::Delegate,
base::OnceClosure SelectClientCertificate(
content::WebContents* web_contents,
net::SSLCertRequestInfo* cert_request_info,
net::ClientCertIdentityList client_certs,
net::ClientCertIdentityList identities,
std::unique_ptr<content::ClientCertificateDelegate> delegate) override;
bool CanCreateWindow(content::RenderFrameHost* opener,
const GURL& opener_url,

View File

@@ -35,7 +35,7 @@ class AutoUpdater : public gin::Wrappable<AutoUpdater>,
~AutoUpdater() override;
// Delegate implementations.
void OnError(const std::string& error) override;
void OnError(const std::string& message) override;
void OnError(const std::string& message,
const int code,
const std::string& domain) override;

View File

@@ -291,6 +291,7 @@ v8::Local<v8::Promise> Cookies::Set(v8::Isolate* isolate,
const std::string* url_string = details.FindStringKey("url");
if (!url_string) {
promise.RejectWithErrorMessage("Missing required option 'url'");
return handle;
}
const std::string* name = details.FindStringKey("name");
const std::string* value = details.FindStringKey("value");

View File

@@ -74,17 +74,15 @@ const void* kElectronApiDownloadItemKey = &kElectronApiDownloadItemKey;
gin::WrapperInfo DownloadItem::kWrapperInfo = {gin::kEmbedderNativeGin};
// static
DownloadItem* DownloadItem::FromDownloadItem(
download::DownloadItem* download_item) {
DownloadItem* DownloadItem::FromDownloadItem(download::DownloadItem* item) {
// ^- say that 7 times fast in a row
auto* data = static_cast<UserDataLink*>(
download_item->GetUserData(kElectronApiDownloadItemKey));
item->GetUserData(kElectronApiDownloadItemKey));
return data ? data->download_item.get() : nullptr;
}
DownloadItem::DownloadItem(v8::Isolate* isolate,
download::DownloadItem* download_item)
: download_item_(download_item), isolate_(isolate) {
DownloadItem::DownloadItem(v8::Isolate* isolate, download::DownloadItem* item)
: download_item_(item), isolate_(isolate) {
download_item_->AddObserver(this);
download_item_->SetUserData(
kElectronApiDownloadItemKey,
@@ -119,7 +117,7 @@ void DownloadItem::OnDownloadUpdated(download::DownloadItem* item) {
}
}
void DownloadItem::OnDownloadDestroyed(download::DownloadItem* download_item) {
void DownloadItem::OnDownloadDestroyed(download::DownloadItem* /*item*/) {
download_item_ = nullptr;
Unpin();
}

View File

@@ -30,7 +30,7 @@ class DownloadItem : public gin::Wrappable<DownloadItem>,
static gin::Handle<DownloadItem> FromOrCreate(v8::Isolate* isolate,
download::DownloadItem* item);
static DownloadItem* FromDownloadItem(download::DownloadItem*);
static DownloadItem* FromDownloadItem(download::DownloadItem* item);
// gin::Wrappable
static gin::WrapperInfo kWrapperInfo;
@@ -44,14 +44,14 @@ class DownloadItem : public gin::Wrappable<DownloadItem>,
file_dialog::DialogSettings GetSaveDialogOptions() const;
private:
DownloadItem(v8::Isolate* isolate, download::DownloadItem* download_item);
DownloadItem(v8::Isolate* isolate, download::DownloadItem* item);
~DownloadItem() override;
bool CheckAlive() const;
// download::DownloadItem::Observer
void OnDownloadUpdated(download::DownloadItem* download) override;
void OnDownloadDestroyed(download::DownloadItem* download) override;
void OnDownloadUpdated(download::DownloadItem* item) override;
void OnDownloadDestroyed(download::DownloadItem* item) override;
// JS API
void Pause();

View File

@@ -236,11 +236,13 @@ std::u16string Menu::GetToolTipAt(int index) const {
return model_->GetToolTipAt(index);
}
std::u16string Menu::GetAcceleratorTextAt(int index) const {
#ifdef DCHECK_IS_ON
std::u16string Menu::GetAcceleratorTextAtForTesting(int index) const {
ui::Accelerator accelerator;
model_->GetAcceleratorAtWithParams(index, true, &accelerator);
return accelerator.GetShortcutText();
}
#endif
bool Menu::IsItemCheckedAt(int index) const {
return model_->IsItemCheckedAt(index);
@@ -289,13 +291,15 @@ v8::Local<v8::ObjectTemplate> Menu::FillObjectTemplate(
.SetMethod("getLabelAt", &Menu::GetLabelAt)
.SetMethod("getSublabelAt", &Menu::GetSublabelAt)
.SetMethod("getToolTipAt", &Menu::GetToolTipAt)
.SetMethod("getAcceleratorTextAt", &Menu::GetAcceleratorTextAt)
.SetMethod("isItemCheckedAt", &Menu::IsItemCheckedAt)
.SetMethod("isEnabledAt", &Menu::IsEnabledAt)
.SetMethod("worksWhenHiddenAt", &Menu::WorksWhenHiddenAt)
.SetMethod("isVisibleAt", &Menu::IsVisibleAt)
.SetMethod("popupAt", &Menu::PopupAt)
.SetMethod("closePopupAt", &Menu::ClosePopupAt)
#ifdef DCHECK_IS_ON
.SetMethod("getAcceleratorTextAt", &Menu::GetAcceleratorTextAtForTesting)
#endif
.Build();
}

View File

@@ -78,6 +78,9 @@ class Menu : public gin::Wrappable<Menu>,
int positioning_item,
base::OnceClosure callback) = 0;
virtual void ClosePopupAt(int32_t window_id) = 0;
#ifdef DCHECK_IS_ON
virtual std::u16string GetAcceleratorTextAtForTesting(int index) const;
#endif
std::unique_ptr<ElectronMenuModel> model_;
Menu* parent_ = nullptr;
@@ -111,7 +114,6 @@ class Menu : public gin::Wrappable<Menu>,
std::u16string GetLabelAt(int index) const;
std::u16string GetSublabelAt(int index) const;
std::u16string GetToolTipAt(int index) const;
std::u16string GetAcceleratorTextAt(int index) const;
bool IsItemCheckedAt(int index) const;
bool IsEnabledAt(int index) const;
bool IsVisibleAt(int index) const;

View File

@@ -35,11 +35,14 @@ class MenuMac : public Menu {
int positioning_item,
base::OnceClosure callback);
void ClosePopupAt(int32_t window_id) override;
void ClosePopupOnUI(int32_t window_id);
#ifdef DCHECK_IS_ON
std::u16string GetAcceleratorTextAtForTesting(int index) const override;
#endif
private:
friend class Menu;
void ClosePopupOnUI(int32_t window_id);
void OnClosed(int32_t window_id, base::OnceClosure callback);
scoped_nsobject<ElectronMenuController> menu_controller_;

View File

@@ -127,6 +127,44 @@ void MenuMac::ClosePopupAt(int32_t window_id) {
std::move(close_popup));
}
#ifdef DCHECK_IS_ON
std::u16string MenuMac::GetAcceleratorTextAtForTesting(int index) const {
// A least effort to get the real shortcut text of NSMenuItem, the code does
// not need to be perfect since it is test only.
base::scoped_nsobject<ElectronMenuController> controller(
[[ElectronMenuController alloc] initWithModel:model()
useDefaultAccelerator:NO]);
NSMenuItem* item = [[controller menu] itemAtIndex:index];
std::u16string text;
NSEventModifierFlags modifiers = [item keyEquivalentModifierMask];
if (modifiers & NSEventModifierFlagControl)
text += u"Ctrl";
if (modifiers & NSEventModifierFlagShift) {
if (!text.empty())
text += u"+";
text += u"Shift";
}
if (modifiers & NSEventModifierFlagOption) {
if (!text.empty())
text += u"+";
text += u"Alt";
}
if (modifiers & NSEventModifierFlagCommand) {
if (!text.empty())
text += u"+";
text += u"Command";
}
if (!text.empty())
text += u"+";
auto key = base::ToUpperASCII(base::SysNSStringToUTF16([item keyEquivalent]));
if (key == u"\t")
text += u"Tab";
else
text += key;
return text;
}
#endif
void MenuMac::ClosePopupOnUI(int32_t window_id) {
auto controller = popup_controllers_.find(window_id);
if (controller != popup_controllers_.end()) {

View File

@@ -86,7 +86,7 @@ class Notification : public gin::Wrappable<Notification>,
void SetHasReply(bool new_has_reply);
void SetUrgency(const std::u16string& new_urgency);
void SetTimeoutType(const std::u16string& new_timeout_type);
void SetReplyPlaceholder(const std::u16string& new_reply_placeholder);
void SetReplyPlaceholder(const std::u16string& new_placeholder);
void SetSound(const std::u16string& sound);
void SetActions(const std::vector<electron::NotificationAction>& actions);
void SetCloseButtonText(const std::u16string& text);

View File

@@ -66,7 +66,6 @@ struct Converter<CustomScheme> {
// options are optional. Default values specified in SchemeOptions are used
if (dict.Get("privileges", &opt)) {
opt.Get("standard", &(out->options.standard));
opt.Get("supportFetchAPI", &(out->options.supportFetchAPI));
opt.Get("secure", &(out->options.secure));
opt.Get("bypassCSP", &(out->options.bypassCSP));
opt.Get("allowServiceWorkers", &(out->options.allowServiceWorkers));
@@ -89,6 +88,16 @@ std::vector<std::string> GetStandardSchemes() {
return g_standard_schemes;
}
void AddServiceWorkerScheme(const std::string& scheme) {
// There is no API to add service worker scheme, but there is an API to
// return const reference to the schemes vector.
// If in future the API is changed to return a copy instead of reference,
// the compilation will fail, and we should add a patch at that time.
auto& mutable_schemes =
const_cast<std::vector<std::string>&>(content::GetServiceWorkerSchemes());
mutable_schemes.push_back(scheme);
}
void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> val) {
std::vector<CustomScheme> custom_schemes;
@@ -125,13 +134,7 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
}
if (custom_scheme.options.allowServiceWorkers) {
service_worker_schemes.push_back(custom_scheme.scheme);
// There is no API to add service worker scheme, but there is an API to
// return const reference to the schemes vector.
// If in future the API is changed to return a copy instead of reference,
// the compilation will fail, and we should add a patch at that time.
auto& mutable_schemes = const_cast<std::vector<std::string>&>(
content::GetServiceWorkerSchemes());
mutable_schemes.push_back(custom_scheme.scheme);
AddServiceWorkerScheme(custom_scheme.scheme);
}
if (custom_scheme.options.stream) {
g_streaming_schemes.push_back(custom_scheme.scheme);

View File

@@ -22,6 +22,8 @@ namespace api {
std::vector<std::string> GetStandardSchemes();
void AddServiceWorkerScheme(const std::string& scheme);
void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
v8::Local<v8::Value> val);

View File

@@ -97,7 +97,7 @@ class SimpleURLLoaderWrapper
int64_t sent_bytes) override {}
void Clone(
mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver>
listener) override;
observer) override;
// SimpleURLLoader callbacks
void OnResponseStarted(const GURL& final_url,

View File

@@ -1978,7 +1978,8 @@ void WebContents::LoadURL(const GURL& url,
// Calling LoadURLWithParams() can trigger JS which destroys |this|.
auto weak_this = GetWeakPtr();
params.transition_type = ui::PAGE_TRANSITION_TYPED;
params.transition_type = ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
// Discord non-committed entries to ensure that we don't re-use a pending
// entry

View File

@@ -387,7 +387,7 @@ void WebRequest::SetListener(Event event,
std::set<std::string> filter_patterns;
gin::Dictionary dict(args->isolate());
if (args->GetNext(&arg) && !arg->IsFunction()) {
// Note that gin treats Function as Dictionary when doing convertions, so we
// Note that gin treats Function as Dictionary when doing conversions, so we
// have to explicitly check if the argument is Function before trying to
// convert it to Dictionary.
if (gin::ConvertFromV8(args->isolate(), arg, &dict)) {

View File

@@ -24,9 +24,9 @@ namespace auto_updater {
class Delegate {
public:
// An error happened.
virtual void OnError(const std::string& error) {}
virtual void OnError(const std::string& message) {}
virtual void OnError(const std::string& error,
virtual void OnError(const std::string& message,
const int code,
const std::string& domain) {}

View File

@@ -9,8 +9,12 @@
#include <utility>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "chrome/common/chrome_switches.h"
#include "components/os_crypt/os_crypt.h"
#include "components/prefs/in_memory_pref_store.h"
#include "components/prefs/json_pref_store.h"
#include "components/prefs/overlay_user_pref_store.h"
#include "components/prefs/pref_registry.h"
#include "components/prefs/pref_registry_simple.h"
@@ -20,11 +24,13 @@
#include "components/proxy_config/proxy_config_pref_names.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/common/content_switches.h"
#include "electron/fuses.h"
#include "extensions/common/constants.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/proxy_resolution/proxy_config_service.h"
#include "net/proxy_resolution/proxy_config_with_annotation.h"
#include "services/network/public/cpp/network_switches.h"
#include "shell/common/electron_paths.h"
#if BUILDFLAG(ENABLE_PRINTING)
#include "chrome/browser/printing/print_job_manager.h"
@@ -83,15 +89,32 @@ BuildState* BrowserProcessImpl::GetBuildState() {
}
void BrowserProcessImpl::PostEarlyInitialization() {
// Mock user prefs, as we only need to track changes for a
// in memory pref store. There are no persistent preferences
PrefServiceFactory prefs_factory;
auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
PrefProxyConfigTrackerImpl::RegisterPrefs(pref_registry.get());
#if defined(OS_WIN)
OSCrypt::RegisterLocalPrefs(pref_registry.get());
#endif
auto pref_store = base::MakeRefCounted<ValueMapPrefStore>();
ApplyProxyModeFromCommandLine(pref_store.get());
prefs_factory.set_command_line_prefs(std::move(pref_store));
prefs_factory.set_user_prefs(new OverlayUserPrefStore(new InMemoryPrefStore));
// Only use a persistent prefs store when cookie encryption is enabled as that
// is the only key that needs it
if (electron::fuses::IsCookieEncryptionEnabled()) {
base::FilePath prefs_path;
CHECK(base::PathService::Get(electron::DIR_USER_DATA, &prefs_path));
prefs_path = prefs_path.Append(FILE_PATH_LITERAL("Local State"));
base::ThreadRestrictions::ScopedAllowIO allow_io;
scoped_refptr<JsonPrefStore> user_pref_store =
base::MakeRefCounted<JsonPrefStore>(prefs_path);
user_pref_store->ReadPrefs();
prefs_factory.set_user_prefs(user_pref_store);
} else {
prefs_factory.set_user_prefs(
new OverlayUserPrefStore(new InMemoryPrefStore));
}
local_state_ = prefs_factory.Create(std::move(pref_registry));
}

View File

@@ -17,6 +17,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/icon_manager.h"
#include "components/os_crypt/os_crypt.h"
#include "content/browser/browser_main_loop.h" // nogncheck
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_security_policy.h"
@@ -26,6 +27,7 @@
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "electron/buildflags/buildflags.h"
#include "electron/fuses.h"
#include "media/base/localized_strings.h"
#include "services/network/public/cpp/features.h"
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
@@ -42,7 +44,6 @@
#include "shell/browser/ui/devtools_manager_delegate.h"
#include "shell/common/api/electron_bindings.h"
#include "shell/common/application_info.h"
#include "shell/common/asar/asar_util.h"
#include "shell/common/electron_paths.h"
#include "shell/common/gin_helper/trackable_object.h"
#include "shell/common/node_bindings.h"
@@ -207,9 +208,7 @@ ElectronBrowserMainParts::ElectronBrowserMainParts(
self_ = this;
}
ElectronBrowserMainParts::~ElectronBrowserMainParts() {
asar::ClearArchives();
}
ElectronBrowserMainParts::~ElectronBrowserMainParts() = default;
// static
ElectronBrowserMainParts* ElectronBrowserMainParts::Get() {
@@ -538,6 +537,16 @@ void ElectronBrowserMainParts::PreCreateMainMessageLoopCommon() {
RegisterURLHandler();
#endif
media::SetLocalizedStringProvider(MediaStringProvider);
#if defined(OS_WIN)
if (electron::fuses::IsCookieEncryptionEnabled()) {
auto* local_state = g_browser_process->local_state();
DCHECK(local_state);
bool os_crypt_init = OSCrypt::Init(local_state);
DCHECK(os_crypt_init);
}
#endif
}
device::mojom::GeolocationControl*

View File

@@ -45,7 +45,7 @@ class ElectronMessagingDelegate : public MessagingDelegate {
void QueryIncognitoConnectability(
content::BrowserContext* context,
const Extension* extension,
content::WebContents* web_contents,
content::WebContents* source_contents,
const GURL& url,
base::OnceCallback<void(bool)> callback) override;

View File

@@ -74,11 +74,11 @@ class FileSelectHelper : public content::WebContentsObserver,
// of the package.
void ProcessSelectedFilesMac(const std::vector<ui::SelectedFileInfo>& files);
// Saves the paths of |zipped_files| for later deletion. Passes |files| to the
// render view host.
// Saves the paths of |temporary_files| for later deletion. Passes |files| to
// the render view host.
void ProcessSelectedFilesMacOnUIThread(
const std::vector<ui::SelectedFileInfo>& files,
const std::vector<base::FilePath>& zipped_files);
const std::vector<base::FilePath>& temporary_files);
// Zips the package at |path| into a temporary destination. Returns the
// temporary destination, if the zip was successful. Otherwise returns an

View File

@@ -99,7 +99,7 @@ class NativeWindow : public base::SupportsUserData,
virtual bool IsNormal();
virtual gfx::Rect GetNormalBounds() = 0;
virtual void SetSizeConstraints(
const extensions::SizeConstraints& size_constraints);
const extensions::SizeConstraints& window_constraints);
virtual extensions::SizeConstraints GetSizeConstraints() const;
virtual void SetContentSizeConstraints(
const extensions::SizeConstraints& size_constraints);

View File

@@ -78,7 +78,7 @@ class NativeWindowMac : public NativeWindow,
bool IsClosable() override;
void SetAlwaysOnTop(ui::ZOrderLevel z_order,
const std::string& level,
int relativeLevel) override;
int relative_level) override;
ui::ZOrderLevel GetZOrderLevel() override;
void Center() override;
void Invalidate() override;

View File

@@ -1465,7 +1465,7 @@ void NativeWindowMac::SetTrafficLightPosition(
base::Optional<gfx::Point> position) {
traffic_light_position_ = std::move(position);
if (buttons_view_) {
[buttons_view_ setMargin:position];
[buttons_view_ setMargin:traffic_light_position_];
[buttons_view_ viewDidMoveToWindow];
}
}

View File

@@ -332,12 +332,12 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
last_window_state_ = ui::SHOW_STATE_NORMAL;
#endif
#if defined(OS_LINUX)
// Listen to move events.
// Listen to mouse events.
aura::Window* window = GetNativeWindow();
if (window)
window->AddPreTargetHandler(this);
#if defined(OS_LINUX)
// On linux after the widget is initialized we might have to force set the
// bounds if the bounds are smaller than the current display
SetBounds(gfx::Rect(GetPosition(), bounds.size()), false);
@@ -352,11 +352,9 @@ NativeWindowViews::~NativeWindowViews() {
SetForwardMouseMessages(false);
#endif
#if defined(OS_LINUX)
aura::Window* window = GetNativeWindow();
if (window)
window->RemovePreTargetHandler(this);
#endif
}
void NativeWindowViews::SetGTKDarkThemeEnabled(bool use_dark_theme) {
@@ -1461,11 +1459,9 @@ void NativeWindowViews::OnWidgetBoundsChanged(views::Widget* changed_widget,
}
void NativeWindowViews::OnWidgetDestroying(views::Widget* widget) {
#if defined(OS_LINUX)
aura::Window* window = GetNativeWindow();
if (window)
window->RemovePreTargetHandler(this);
#endif
}
void NativeWindowViews::OnWidgetDestroyed(views::Widget* changed_widget) {
@@ -1583,17 +1579,20 @@ void NativeWindowViews::HandleKeyboardEvent(
root_view_->HandleKeyEvent(event);
}
#if defined(OS_LINUX)
void NativeWindowViews::OnMouseEvent(ui::MouseEvent* event) {
if (event->type() != ui::ET_MOUSE_PRESSED)
return;
// Alt+Click should not toggle menu bar.
root_view_->ResetAltState();
#if defined(OS_LINUX)
if (event->changed_button_flags() == ui::EF_BACK_MOUSE_BUTTON)
NotifyWindowExecuteAppCommand(kBrowserBackward);
else if (event->changed_button_flags() == ui::EF_FORWARD_MOUSE_BUTTON)
NotifyWindowExecuteAppCommand(kBrowserForward);
}
#endif
}
ui::WindowShowState NativeWindowViews::GetRestoredState() {
if (IsMaximized())

View File

@@ -225,10 +225,8 @@ class NativeWindowViews : public NativeWindow,
content::WebContents*,
const content::NativeWebKeyboardEvent& event) override;
#if defined(OS_LINUX)
// ui::EventHandler:
void OnMouseEvent(ui::MouseEvent* event) override;
#endif
// Returns the restore state for the window.
ui::WindowShowState GetRestoredState();

View File

@@ -9,6 +9,7 @@
#include "chrome/common/chrome_constants.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "electron/fuses.h"
#include "net/net_buildflags.h"
#include "services/network/network_service.h"
#include "services/network/public/cpp/cors/origin_access_list.h"
@@ -77,9 +78,8 @@ void NetworkContextService::ConfigureNetworkContextParams(
network_context_params->restore_old_session_cookies = false;
network_context_params->persist_session_cookies = false;
// TODO(deepak1556): Matches the existing behavior https://git.io/fxHMl,
// enable encryption as a followup.
network_context_params->enable_encrypted_cookies = false;
network_context_params->enable_encrypted_cookies =
electron::fuses::IsCookieEncryptionEnabled();
network_context_params->transport_security_persister_path = path;
}

View File

@@ -8,11 +8,16 @@
#include <utility>
#include "base/command_line.h"
#include "base/path_service.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
#include "chrome/common/chrome_switches.h"
#include "components/os_crypt/os_crypt.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/common/content_features.h"
#include "content/public/common/network_service_util.h"
#include "electron/fuses.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "net/net_buildflags.h"
#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
@@ -21,11 +26,17 @@
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "shell/browser/browser.h"
#include "shell/browser/electron_browser_client.h"
#include "shell/common/application_info.h"
#include "shell/common/electron_paths.h"
#include "shell/common/options_switches.h"
#include "url/gurl.h"
#if defined(OS_MAC)
#include "components/os_crypt/keychain_password_mac.h"
#endif
namespace {
// The global instance of the SystemNetworkContextmanager.
@@ -218,6 +229,39 @@ void SystemNetworkContextManager::OnNetworkServiceCreated(
network_service->CreateNetworkContext(
network_context_.BindNewPipeAndPassReceiver(),
CreateNetworkContextParams());
if (electron::fuses::IsCookieEncryptionEnabled()) {
std::string app_name = electron::Browser::Get()->GetName();
#if defined(OS_MAC)
*KeychainPassword::service_name = app_name + " Safe Storage";
*KeychainPassword::account_name = app_name;
#endif
// The OSCrypt keys are process bound, so if network service is out of
// process, send it the required key.
if (content::IsOutOfProcessNetworkService()) {
#if defined(OS_LINUX)
// c.f.
// https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/net/system_network_context_manager.cc;l=515;drc=9d82515060b9b75fa941986f5db7390299669ef1;bpv=1;bpt=1
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
network::mojom::CryptConfigPtr config =
network::mojom::CryptConfig::New();
config->application_name = app_name;
config->product_name = app_name;
// c.f.
// https://source.chromium.org/chromium/chromium/src/+/master:chrome/common/chrome_switches.cc;l=689;drc=9d82515060b9b75fa941986f5db7390299669ef1
config->store =
command_line.GetSwitchValueASCII(::switches::kPasswordStore);
config->should_use_preference =
command_line.HasSwitch(::switches::kEnableEncryptionSelection);
base::PathService::Get(electron::DIR_USER_DATA, &config->user_data_path);
network_service->SetCryptConfig(std::move(config));
#else
network_service->SetEncryptionKey(OSCrypt::GetRawEncryptionKey());
#endif
}
}
}
network::mojom::NetworkContextParamsPtr

View File

@@ -16,7 +16,7 @@ class CocoaNotification;
class NotificationPresenterMac : public NotificationPresenter {
public:
CocoaNotification* GetNotification(NSUserNotification* notif);
CocoaNotification* GetNotification(NSUserNotification* ns_notification);
NotificationPresenterMac();
~NotificationPresenterMac() override;

View File

@@ -113,7 +113,7 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
// content::RenderWidgetHostViewBase:
void ResetFallbackToFirstNavigationSurface() override;
void InitAsPopup(content::RenderWidgetHostView* rwhv,
void InitAsPopup(content::RenderWidgetHostView* parent_host_view,
const gfx::Rect& rect) override;
void UpdateCursor(const content::WebCursor&) override;
void SetIsLoading(bool is_loading) override;
@@ -127,7 +127,7 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
const gfx::Rect& src_rect,
const gfx::Size& output_size,
base::OnceCallback<void(const SkBitmap&)> callback) override;
void GetScreenInfo(blink::ScreenInfo* results) override;
void GetScreenInfo(blink::ScreenInfo* screen_info) override;
void TransformPointToRootSurface(gfx::PointF* point) override;
gfx::Rect GetBoundsInRootWindow(void) override;
base::Optional<content::DisplayFeature> GetDisplayFeature() override;

View File

@@ -50,10 +50,10 @@ using StringVector = base::CommandLine::StringVector;
// Relaunches the application using the helper application associated with the
// currently running instance of Chrome in the parent browser process as the
// executable for the relauncher process. |args| is an argv-style vector of
// command line arguments of the form normally passed to execv. args[0] is
// executable for the relauncher process. |argv| is an argv-style vector of
// command line arguments of the form normally passed to execv. argv[0] is
// also the path to the relaunched process. Because the relauncher process
// will ultimately launch the relaunched process via Launch Services, args[0]
// will ultimately launch the relaunched process via Launch Services, argv[0]
// may be either a pathname to an executable file or a pathname to an .app
// bundle directory. The caller should exit soon after RelaunchApp returns
// successfully. Returns true on success, although some failures can occur
@@ -63,7 +63,7 @@ bool RelaunchApp(const StringVector& argv);
// Identical to RelaunchApp, but uses |helper| as the path to the relauncher
// process, and allows additional arguments to be supplied to the relauncher
// process in relauncher_args. Unlike args[0], |helper| must be a pathname to
// process in relauncher_args. Unlike argv[0], |helper| must be a pathname to
// an executable file. The helper path given must be from the same version of
// Chrome as the running parent browser process, as there are no guarantees
// that the parent and relauncher processes from different versions will be
@@ -72,7 +72,7 @@ bool RelaunchApp(const StringVector& argv);
// location's helper.
bool RelaunchAppWithHelper(const base::FilePath& helper,
const StringVector& relauncher_args,
const StringVector& args);
const StringVector& argv);
// The entry point from ChromeMain into the relauncher process.
int RelauncherMain(const content::MainFunctionParams& main_parameters);

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 14,0,0,20210524
PRODUCTVERSION 14,0,0,20210524
FILEVERSION 14,0,0,4
PRODUCTVERSION 14,0,0,4
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L

View File

@@ -31,10 +31,10 @@ bool StringToAccelerator(const std::string& shortcut,
// Now, parse it into an accelerator.
int modifiers = ui::EF_NONE;
ui::KeyboardCode key = ui::VKEY_UNKNOWN;
base::Optional<char16_t> shifted_char;
for (const auto& token : tokens) {
bool shifted = false;
ui::KeyboardCode code = electron::KeyboardCodeFromStr(token, &shifted);
if (shifted)
ui::KeyboardCode code = electron::KeyboardCodeFromStr(token, &shifted_char);
if (shifted_char)
modifiers |= ui::EF_SHIFT_DOWN;
switch (code) {
// The token can be a modifier.
@@ -65,6 +65,7 @@ bool StringToAccelerator(const std::string& shortcut,
}
*accelerator = ui::Accelerator(key, modifiers);
accelerator->shifted_char = shifted_char;
return true;
}

View File

@@ -20,10 +20,10 @@ typedef struct {
typedef std::map<ui::Accelerator, MenuItem> AcceleratorTable;
// Parse a string as an accelerator.
bool StringToAccelerator(const std::string& description,
bool StringToAccelerator(const std::string& shortcut,
ui::Accelerator* accelerator);
// Generate a table that contains memu model's accelerators and command ids.
// Generate a table that contains menu model's accelerators and command ids.
void GenerateAcceleratorTable(AcceleratorTable* table,
electron::ElectronMenuModel* model);

View File

@@ -21,9 +21,9 @@
#include "shell/browser/ui/electron_menu_model.h"
#include "shell/browser/window_list.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/platform_accelerator_cocoa.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/events/keycodes/keyboard_code_conversion_mac.h"
#include "ui/gfx/image/image.h"
#include "ui/strings/grit/ui_strings.h"
@@ -392,11 +392,31 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
ui::Accelerator accelerator;
if (model->GetAcceleratorAtWithParams(index, useDefaultAccelerator_,
&accelerator)) {
NSString* key_equivalent;
NSUInteger modifier_mask;
GetKeyEquivalentAndModifierMaskFromAccelerator(
accelerator, &key_equivalent, &modifier_mask);
[item setKeyEquivalent:key_equivalent];
// Note that we are not using Chromium's
// GetKeyEquivalentAndModifierMaskFromAccelerator API,
// because it will convert Shift+Character to ShiftedCharacter, for
// example Shift+/ would be converted to ?, which is against macOS HIG.
// See also https://github.com/electron/electron/issues/21790.
NSUInteger modifier_mask = 0;
if (accelerator.IsCtrlDown())
modifier_mask |= NSEventModifierFlagControl;
if (accelerator.IsAltDown())
modifier_mask |= NSEventModifierFlagOption;
if (accelerator.IsCmdDown())
modifier_mask |= NSEventModifierFlagCommand;
unichar character;
if (accelerator.shifted_char) {
// When a shifted char is explicitly specified, for example Ctrl+Plus,
// use the shifted char directly.
character = static_cast<unichar>(*accelerator.shifted_char);
} else {
// Otherwise use the unshifted combinations, for example Ctrl+Shift+=.
if (accelerator.IsShiftDown())
modifier_mask |= NSEventModifierFlagShift;
ui::MacKeyCodeForWindowsKeyCode(accelerator.key_code(), modifier_mask,
nullptr, &character);
}
[item setKeyEquivalent:[NSString stringWithFormat:@"%C", character]];
[item setKeyEquivalentModifierMask:modifier_mask];
}

View File

@@ -56,6 +56,8 @@ void MenuBar::SetAcceleratorVisibility(bool visible) {
}
MenuBar::View* MenuBar::FindAccelChild(char16_t key) {
if (key == 0)
return nullptr;
for (auto* child : GetChildrenInZOrder()) {
if (static_cast<SubmenuButton*>(child)->accelerator() == key)
return child;
@@ -124,7 +126,10 @@ bool MenuBar::AcceleratorPressed(const ui::Accelerator& accelerator) {
? ui::Accelerator(ui::VKEY_ESCAPE, accelerator.modifiers(),
accelerator.key_state(), accelerator.time_stamp())
: accelerator;
return views::AccessiblePaneView::AcceleratorPressed(translated);
bool result = views::AccessiblePaneView::AcceleratorPressed(translated);
if (result && !pane_has_focus())
root_view_->RestoreFocus();
return result;
}
bool MenuBar::SetPaneFocusAndFocusDefault() {
@@ -149,6 +154,8 @@ void MenuBar::OnThemeChanged() {
void MenuBar::OnDidChangeFocus(View* focused_before, View* focused_now) {
views::AccessiblePaneView::OnDidChangeFocus(focused_before, focused_now);
SetAcceleratorVisibility(pane_has_focus());
if (!pane_has_focus())
root_view_->RestoreFocus();
}
const char* MenuBar::GetClassName() const {

View File

@@ -119,18 +119,18 @@ void RootView::HandleKeyEvent(const content::NativeWebKeyboardEvent& event) {
// Show the submenu when "Alt+Key" is pressed.
if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown &&
!IsAltKey(event) && IsAltModifier(event)) {
if (menu_bar_->HasAccelerator(event.windows_key_code)) {
if (!menu_bar_visible_) {
SetMenuBarVisibility(true);
event.windows_key_code >= ui::VKEY_A &&
event.windows_key_code <= ui::VKEY_Z && IsAltModifier(event) &&
menu_bar_->HasAccelerator(event.windows_key_code)) {
if (!menu_bar_visible_) {
SetMenuBarVisibility(true);
View* focused_view = GetFocusManager()->GetFocusedView();
last_focused_view_tracker_->SetView(focused_view);
menu_bar_->RequestFocus();
}
menu_bar_->ActivateAccelerator(event.windows_key_code);
View* focused_view = GetFocusManager()->GetFocusedView();
last_focused_view_tracker_->SetView(focused_view);
menu_bar_->RequestFocus();
}
menu_bar_->ActivateAccelerator(event.windows_key_code);
return;
}

View File

@@ -35,7 +35,7 @@ class WebDialogHelper {
const blink::mojom::FileChooserParams& params);
void EnumerateDirectory(content::WebContents* web_contents,
scoped_refptr<content::FileSelectListener> listener,
const base::FilePath& path);
const base::FilePath& dir);
private:
NativeWindow* window_;

View File

@@ -8,6 +8,7 @@
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
@@ -118,7 +119,7 @@ bool FillFileInfoWithNode(Archive::FileInfo* info,
} // namespace
Archive::Archive(const base::FilePath& path)
: path_(path), file_(base::File::FILE_OK) {
: initialized_(false), path_(path), file_(base::File::FILE_OK) {
base::ThreadRestrictions::ScopedAllowIO allow_io;
file_.Initialize(path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
#if defined(OS_WIN)
@@ -141,6 +142,10 @@ Archive::~Archive() {
}
bool Archive::Init() {
// Should only be initialized once
CHECK(!initialized_);
initialized_ = true;
if (!file_.IsValid()) {
if (file_.error_details() != base::File::FILE_ERROR_NOT_FOUND) {
LOG(WARNING) << "Opening " << path_.value() << ": "
@@ -198,7 +203,7 @@ bool Archive::Init() {
return true;
}
bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) {
bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) const {
if (!header_)
return false;
@@ -213,7 +218,7 @@ bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) {
return FillFileInfoWithNode(info, header_size_, node);
}
bool Archive::Stat(const base::FilePath& path, Stats* stats) {
bool Archive::Stat(const base::FilePath& path, Stats* stats) const {
if (!header_)
return false;
@@ -237,7 +242,7 @@ bool Archive::Stat(const base::FilePath& path, Stats* stats) {
}
bool Archive::Readdir(const base::FilePath& path,
std::vector<base::FilePath>* list) {
std::vector<base::FilePath>* files) const {
if (!header_)
return false;
@@ -245,19 +250,20 @@ bool Archive::Readdir(const base::FilePath& path,
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
return false;
const base::DictionaryValue* files;
if (!GetFilesNode(header_.get(), node, &files))
const base::DictionaryValue* files_node;
if (!GetFilesNode(header_.get(), node, &files_node))
return false;
base::DictionaryValue::Iterator iter(*files);
base::DictionaryValue::Iterator iter(*files_node);
while (!iter.IsAtEnd()) {
list->push_back(base::FilePath::FromUTF8Unsafe(iter.key()));
files->push_back(base::FilePath::FromUTF8Unsafe(iter.key()));
iter.Advance();
}
return true;
}
bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) {
bool Archive::Realpath(const base::FilePath& path,
base::FilePath* realpath) const {
if (!header_)
return false;
@@ -276,6 +282,11 @@ bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) {
}
bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
if (!header_)
return false;
base::AutoLock auto_lock(external_files_lock_);
auto it = external_files_.find(path.value());
if (it != external_files_.end()) {
*out = it->second->path();

View File

@@ -11,6 +11,7 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/synchronization/lock.h"
namespace base {
class DictionaryValue;
@@ -21,7 +22,7 @@ namespace asar {
class ScopedTemporaryFile;
// This class represents an asar package, and provides methods to read
// information from it.
// information from it. It is thread-safe after |Init| has been called.
class Archive {
public:
struct FileInfo {
@@ -46,16 +47,17 @@ class Archive {
bool Init();
// Get the info of a file.
bool GetFileInfo(const base::FilePath& path, FileInfo* info);
bool GetFileInfo(const base::FilePath& path, FileInfo* info) const;
// Fs.stat(path).
bool Stat(const base::FilePath& path, Stats* stats);
bool Stat(const base::FilePath& path, Stats* stats) const;
// Fs.readdir(path).
bool Readdir(const base::FilePath& path, std::vector<base::FilePath>* files);
bool Readdir(const base::FilePath& path,
std::vector<base::FilePath>* files) const;
// Fs.realpath(path).
bool Realpath(const base::FilePath& path, base::FilePath* realpath);
bool Realpath(const base::FilePath& path, base::FilePath* realpath) const;
// Copy the file into a temporary file, and return the new path.
// For unpacked file, this method will return its real path.
@@ -65,16 +67,17 @@ class Archive {
int GetFD() const;
base::FilePath path() const { return path_; }
base::DictionaryValue* header() const { return header_.get(); }
private:
base::FilePath path_;
bool initialized_;
const base::FilePath path_;
base::File file_;
int fd_ = -1;
uint32_t header_size_ = 0;
std::unique_ptr<base::DictionaryValue> header_;
// Cached external temporary files.
base::Lock external_files_lock_;
std::unordered_map<base::FilePath::StringType,
std::unique_ptr<ScopedTemporaryFile>>
external_files_;

View File

@@ -6,11 +6,14 @@
#include <map>
#include <string>
#include <utility>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/no_destructor.h"
#include "base/stl_util.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_restrictions.h"
#include "shell/common/asar/archive.h"
@@ -19,30 +22,41 @@ namespace asar {
namespace {
// The global instance of ArchiveMap, will be destroyed on exit.
typedef std::map<base::FilePath, std::shared_ptr<Archive>> ArchiveMap;
base::LazyInstance<base::ThreadLocalPointer<ArchiveMap>>::Leaky
g_archive_map_tls = LAZY_INSTANCE_INITIALIZER;
const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar");
std::map<base::FilePath, bool> g_is_directory_cache;
bool IsDirectoryCached(const base::FilePath& path) {
auto it = g_is_directory_cache.find(path);
if (it != g_is_directory_cache.end()) {
static base::NoDestructor<std::map<base::FilePath, bool>>
s_is_directory_cache;
static base::NoDestructor<base::Lock> lock;
base::AutoLock auto_lock(*lock);
auto& is_directory_cache = *s_is_directory_cache;
auto it = is_directory_cache.find(path);
if (it != is_directory_cache.end()) {
return it->second;
}
base::ThreadRestrictions::ScopedAllowIO allow_io;
return g_is_directory_cache[path] = base::DirectoryExists(path);
return is_directory_cache[path] = base::DirectoryExists(path);
}
} // namespace
ArchiveMap& GetArchiveCache() {
static base::NoDestructor<ArchiveMap> s_archive_map;
return *s_archive_map;
}
base::Lock& GetArchiveCacheLock() {
static base::NoDestructor<base::Lock> lock;
return *lock;
}
std::shared_ptr<Archive> GetOrCreateAsarArchive(const base::FilePath& path) {
if (!g_archive_map_tls.Pointer()->Get())
g_archive_map_tls.Pointer()->Set(new ArchiveMap);
ArchiveMap& map = *g_archive_map_tls.Pointer()->Get();
base::AutoLock auto_lock(GetArchiveCacheLock());
ArchiveMap& map = GetArchiveCache();
// if we have it, return it
const auto lower = map.lower_bound(path);
@@ -61,8 +75,10 @@ std::shared_ptr<Archive> GetOrCreateAsarArchive(const base::FilePath& path) {
}
void ClearArchives() {
if (g_archive_map_tls.Pointer()->Get())
delete g_archive_map_tls.Pointer()->Get();
base::AutoLock auto_lock(GetArchiveCacheLock());
ArchiveMap& map = GetArchiveCache();
map.clear();
}
bool GetAsarArchivePath(const base::FilePath& full_path,

View File

@@ -16,7 +16,7 @@ namespace asar {
class Archive;
// Gets or creates a new Archive from the path.
// Gets or creates and caches a new Archive from the path.
std::shared_ptr<Archive> GetOrCreateAsarArchive(const base::FilePath& path);
// Destroy cached Archive objects.

View File

@@ -187,10 +187,10 @@ bool Converter<blink::WebKeyboardEvent>::FromV8(v8::Isolate* isolate,
if (!dict.Get("keyCode", &str))
return false;
bool shifted = false;
ui::KeyboardCode keyCode = electron::KeyboardCodeFromStr(str, &shifted);
base::Optional<char16_t> shifted_char;
ui::KeyboardCode keyCode = electron::KeyboardCodeFromStr(str, &shifted_char);
out->windows_key_code = keyCode;
if (shifted)
if (shifted_char)
out->SetModifiers(out->GetModifiers() |
blink::WebInputEvent::Modifiers::kShiftKey);

View File

@@ -14,7 +14,7 @@
#include "shell/common/gin_helper/function_template.h"
#include "shell/common/gin_helper/locker.h"
#include "shell/common/gin_helper/microtasks_scope.h"
// Implements safe convertions between JS functions and base::RepeatingCallback.
// Implements safe conversions between JS functions and base::RepeatingCallback.
namespace gin_helper {
@@ -110,7 +110,7 @@ struct V8FunctionInvoker<ReturnType(ArgTypes...)> {
}
};
// Helper to pass a C++ funtion to JavaScript.
// Helper to pass a C++ function to JavaScript.
using Translater = base::RepeatingCallback<void(gin::Arguments* args)>;
v8::Local<v8::Value> CreateFunctionFromTranslater(v8::Isolate* isolate,
const Translater& translater,

View File

@@ -96,7 +96,7 @@ bool GetNextArgument(gin::Arguments* args,
}
// Support base::Optional as output, which would be empty and do not throw error
// when convertion to T fails.
// when conversion to T fails.
template <typename T>
bool GetNextArgument(gin::Arguments* args,
int create_flags,

View File

@@ -15,8 +15,9 @@ namespace electron {
namespace {
// Return key code represented by |str|.
ui::KeyboardCode KeyboardCodeFromKeyIdentifier(const std::string& s,
bool* shifted) {
ui::KeyboardCode KeyboardCodeFromKeyIdentifier(
const std::string& s,
base::Optional<char16_t>* shifted_char) {
std::string str = base::ToLowerASCII(s);
if (str == "ctrl" || str == "control") {
return ui::VKEY_CONTROL;
@@ -36,7 +37,7 @@ ui::KeyboardCode KeyboardCodeFromKeyIdentifier(const std::string& s,
} else if (str == "altgr") {
return ui::VKEY_ALTGR;
} else if (str == "plus") {
*shifted = true;
shifted_char->emplace('+');
return ui::VKEY_OEM_PLUS;
} else if (str == "capslock") {
return ui::VKEY_CAPITAL;
@@ -319,11 +320,17 @@ ui::KeyboardCode KeyboardCodeFromCharCode(char16_t c, bool* shifted) {
}
}
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted) {
if (str.size() == 1)
return KeyboardCodeFromCharCode(str[0], shifted);
else
return KeyboardCodeFromKeyIdentifier(str, shifted);
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str,
base::Optional<char16_t>* shifted_char) {
if (str.size() == 1) {
bool shifted = false;
auto ret = KeyboardCodeFromCharCode(str[0], &shifted);
if (shifted)
shifted_char->emplace(str[0]);
return ret;
} else {
return KeyboardCodeFromKeyIdentifier(str, shifted_char);
}
}
} // namespace electron

View File

@@ -7,6 +7,7 @@
#include <string>
#include "base/optional.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace electron {
@@ -15,9 +16,11 @@ namespace electron {
// pressed.
ui::KeyboardCode KeyboardCodeFromCharCode(char16_t c, bool* shifted);
// Return key code of the |str|, and also determine whether the SHIFT key is
// Return key code of the |str|, if the original key is a shifted character,
// for example + and /, set it in |shifted_char|.
// pressed.
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str, bool* shifted);
ui::KeyboardCode KeyboardCodeFromStr(const std::string& str,
base::Optional<char16_t>* shifted_char);
} // namespace electron

View File

@@ -131,6 +131,10 @@ void stop_and_close_uv_loop(uv_loop_t* loop) {
bool g_is_initialized = false;
bool IsPackagedApp() {
auto env = base::Environment::Create();
if (env->HasVar("ELECTRON_FORCE_IS_PACKAGED"))
return true;
base::FilePath exe_path;
base::PathService::Get(base::FILE_EXE, &exe_path);
base::FilePath::StringType base_name =

View File

@@ -76,7 +76,7 @@ class SpellCheckClient : public blink::WebSpellCheckPanelHostClient,
// Output variable contraction_words will contain individual
// words in the contraction.
bool IsContraction(const SpellCheckScope& scope,
const std::u16string& word,
const std::u16string& contraction,
std::vector<std::u16string>* contraction_words);
// Callback for the JS API which returns the list of misspelled words.

View File

@@ -11,7 +11,6 @@
#include "content/public/renderer/render_frame.h"
#include "electron/buildflags/buildflags.h"
#include "shell/common/api/electron_bindings.h"
#include "shell/common/asar/asar_util.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/event_emitter_caller.h"
#include "shell/common/node_bindings.h"
@@ -39,9 +38,7 @@ ElectronRendererClient::ElectronRendererClient()
NodeBindings::Create(NodeBindings::BrowserEnvironment::kRenderer)),
electron_bindings_(new ElectronBindings(node_bindings_->uv_loop())) {}
ElectronRendererClient::~ElectronRendererClient() {
asar::ClearArchives();
}
ElectronRendererClient::~ElectronRendererClient() = default;
void ElectronRendererClient::RenderFrameCreated(
content::RenderFrame* render_frame) {

View File

@@ -22,6 +22,7 @@
#include "electron/buildflags/buildflags.h"
#include "media/blink/multibuffer_data_source.h"
#include "printing/buildflags/buildflags.h"
#include "shell/browser/api/electron_api_protocol.h"
#include "shell/common/api/electron_api_native_image.h"
#include "shell/common/color_util.h"
#include "shell/common/gin_helper/dictionary.h"
@@ -111,6 +112,11 @@ RendererClientBase* g_renderer_client_base = nullptr;
RendererClientBase::RendererClientBase() {
auto* command_line = base::CommandLine::ForCurrentProcess();
// Parse --service-worker-schemes=scheme1,scheme2
std::vector<std::string> service_worker_schemes_list =
ParseSchemesCLISwitch(command_line, switches::kServiceWorkerSchemes);
for (const std::string& scheme : service_worker_schemes_list)
electron::api::AddServiceWorkerScheme(scheme);
// Parse --standard-schemes=scheme1,scheme2
std::vector<std::string> standard_schemes_list =
ParseSchemesCLISwitch(command_line, switches::kStandardSchemes);

View File

@@ -65,7 +65,7 @@ class RendererClientBase : public content::ContentRendererClient
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
// service_manager::LocalInterfaceProvider implementation.
void GetInterface(const std::string& name,
mojo::ScopedMessagePipeHandle request_handle) override;
mojo::ScopedMessagePipeHandle interface_pipe) override;
#endif
virtual void DidCreateScriptContext(v8::Handle<v8::Context> context,

View File

@@ -7,7 +7,6 @@
#include "base/lazy_instance.h"
#include "base/threading/thread_local.h"
#include "shell/common/api/electron_bindings.h"
#include "shell/common/asar/asar_util.h"
#include "shell/common/gin_helper/event_emitter_caller.h"
#include "shell/common/node_bindings.h"
#include "shell/common/node_includes.h"
@@ -39,7 +38,6 @@ WebWorkerObserver::~WebWorkerObserver() {
lazy_tls.Pointer()->Set(nullptr);
node::FreeEnvironment(node_bindings_->uv_env());
node::FreeIsolateData(node_bindings_->isolate_data());
asar::ClearArchives();
}
void WebWorkerObserver::WorkerScriptReadyForEvaluation(

View File

@@ -462,9 +462,9 @@ describe('MenuItems', () => {
{ label: 'text', accelerator: 'Alt+A' }
]);
expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? 'A' : 'Ctrl+A');
expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧A' : 'Shift+A');
expect(menu.getAcceleratorTextAt(2)).to.equal(isDarwin() ? '⌥A' : 'Alt+A');
expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? 'Command+A' : 'Ctrl+A');
expect(menu.getAcceleratorTextAt(1)).to.equal('Shift+A');
expect(menu.getAcceleratorTextAt(2)).to.equal('Alt+A');
});
it('should display modifiers correctly for special keys', () => {
@@ -474,9 +474,9 @@ describe('MenuItems', () => {
{ label: 'text', accelerator: 'Alt+Tab' }
]);
expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⌘⇥' : 'Ctrl+Tab');
expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧⇥' : 'Shift+Tab');
expect(menu.getAcceleratorTextAt(2)).to.equal(isDarwin() ? '⌥⇥' : 'Alt+Tab');
expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? 'Command+Tab' : 'Ctrl+Tab');
expect(menu.getAcceleratorTextAt(1)).to.equal('Shift+Tab');
expect(menu.getAcceleratorTextAt(2)).to.equal('Alt+Tab');
});
it('should not display modifiers twice', () => {
@@ -485,18 +485,26 @@ describe('MenuItems', () => {
{ label: 'text', accelerator: 'Shift+Shift+Tab' }
]);
expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⇧A' : 'Shift+A');
expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧⇥' : 'Shift+Tab');
expect(menu.getAcceleratorTextAt(0)).to.equal('Shift+A');
expect(menu.getAcceleratorTextAt(1)).to.equal('Shift+Tab');
});
it('should display correctly for edge cases', () => {
it('should display correctly for shifted keys', () => {
const menu = Menu.buildFromTemplate([
{ label: 'text', accelerator: 'Control+Shift+=' },
{ label: 'text', accelerator: 'Control+Plus' }
{ label: 'text', accelerator: 'Control+Plus' },
{ label: 'text', accelerator: 'Control+Shift+3' },
{ label: 'text', accelerator: 'Control+#' },
{ label: 'text', accelerator: 'Control+Shift+/' },
{ label: 'text', accelerator: 'Control+?' }
]);
expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⌃⇧=' : 'Ctrl+Shift+=');
expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⌃⇧=' : 'Ctrl+Shift+=');
expect(menu.getAcceleratorTextAt(0)).to.equal('Ctrl+Shift+=');
expect(menu.getAcceleratorTextAt(1)).to.equal('Ctrl++');
expect(menu.getAcceleratorTextAt(2)).to.equal('Ctrl+Shift+3');
expect(menu.getAcceleratorTextAt(3)).to.equal('Ctrl+#');
expect(menu.getAcceleratorTextAt(4)).to.equal('Ctrl+Shift+/');
expect(menu.getAcceleratorTextAt(5)).to.equal('Ctrl+?');
});
});
});

View File

@@ -586,6 +586,43 @@ describe('chromium features', () => {
w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'index.html'));
});
it('should register for custom scheme', (done) => {
const customSession = session.fromPartition('custom-scheme');
const { serviceWorkerScheme } = global as any;
customSession.protocol.registerFileProtocol(serviceWorkerScheme, (request, callback) => {
let file = url.parse(request.url).pathname!;
if (file[0] === '/' && process.platform === 'win32') file = file.slice(1);
callback({ path: path.normalize(file) } as any);
});
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
session: customSession,
contextIsolation: false
}
});
w.webContents.on('ipc-message', (event, channel, message) => {
if (channel === 'reload') {
w.webContents.reload();
} else if (channel === 'error') {
done(`unexpected error : ${message}`);
} else if (channel === 'response') {
expect(message).to.equal('Hello from serviceWorker!');
customSession.clearStorageData({
storages: ['serviceworkers']
}).then(() => {
customSession.protocol.uninterceptProtocol(serviceWorkerScheme);
done();
});
}
});
w.webContents.on('crashed', () => done(new Error('WebContents crashed.')));
w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'custom-scheme-index.html'));
});
it('should not crash when nodeIntegration is enabled', (done) => {
const w = new BrowserWindow({
show: false,

View File

@@ -0,0 +1,121 @@
# Electron Support
## Finding Support
If you have a security concern,
please see the [security document](https://github.com/electron/electron/tree/master/SECURITY.md).
If you're looking for programming help,
for answers to questions,
or to join in discussion with other developers who use Electron,
you can interact with the community in these locations:
* [`Electron's Discord`](https://discord.com/invite/electron) has channels for:
* Getting help
* Ecosystem apps like [Electron Forge](https://github.com/electron-userland/electron-forge) and [Electron Fiddle](https://github.com/electron/fiddle)
* Sharing ideas with other Electron app developers
* And more!
* [`electron`](https://discuss.atom.io/c/electron) category on the Atom forums
* `#atom-shell` channel on Freenode
* `#electron` channel on [Atom's Slack](https://discuss.atom.io/t/join-us-on-slack/16638?source_topic_id=25406)
* [`electron-ru`](https://telegram.me/electron_ru) *(Russian)*
* [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)*
* [`electron-kr`](https://electron-kr.github.io/electron-kr) *(Korean)*
* [`electron-jp`](https://electron-jp.slack.com) *(Japanese)*
* [`electron-tr`](https://electron-tr.herokuapp.com) *(Turkish)*
* [`electron-id`](https://electron-id.slack.com) *(Indonesia)*
* [`electron-pl`](https://electronpl.github.io) *(Poland)*
If you'd like to contribute to Electron,
see the [contributing document](https://github.com/electron/electron/blob/master/CONTRIBUTING.md).
If you've found a bug in a [supported version](#supported-versions) of Electron,
please report it with the [issue tracker](../development/issues.md).
[awesome-electron](https://github.com/sindresorhus/awesome-electron)
is a community-maintained list of useful example apps,
tools and resources.
## Supported Versions
The latest three *stable* major versions are supported by the Electron team.
For example, if the latest release is 6.1.x, then the 5.0.x as well
as the 4.2.x series are supported. We only support the latest minor release
for each stable release series. This means that in the case of a security fix
6.1.x will receive the fix, but we will not release a new version of 6.0.x.
The latest stable release unilaterally receives all fixes from `master`,
and the version prior to that receives the vast majority of those fixes
as time and bandwidth warrants. The oldest supported release line will receive
only security fixes directly.
All supported release lines will accept external pull requests to backport
fixes previously merged to `master`, though this may be on a case-by-case
basis for some older supported lines. All contested decisions around release
line backports will be resolved by the [Releases Working Group](https://github.com/electron/governance/tree/master/wg-releases) as an agenda item at their weekly meeting the week the backport PR is raised.
When an API is changed or removed in a way that breaks existing functionality, the
previous functionality will be supported for a minimum of two major versions when
possible before being removed. For example, if a function takes three arguments,
and that number is reduced to two in major version 10, the three-argument version would
continue to work until, at minimum, major version 12. Past the minimum two-version
threshold, we will attempt to support backwards compatibility beyond two versions
until the maintainers feel the maintenance burden is too high to continue doing so.
### Currently supported versions
* 3.x.y
* 2.x.y
* 1.x.y
### End-of-life
When a release branch reaches the end of its support cycle, the series
will be deprecated in NPM and a final end-of-support release will be
made. This release will add a warning to inform that an unsupported
version of Electron is in use.
These steps are to help app developers learn when a branch they're
using becomes unsupported, but without being excessively intrusive
to end users.
If an application has exceptional circumstances and needs to stay
on an unsupported series of Electron, developers can silence the
end-of-support warning by omitting the final release from the app's
`package.json` `devDependencies`. For example, since the 1-6-x series
ended with an end-of-support 1.6.18 release, developers could choose
to stay in the 1-6-x series without warnings with `devDependency` of
`"electron": 1.6.0 - 1.6.17`.
## Supported Platforms
Following platforms are supported by Electron:
### macOS
Only 64bit binaries are provided for macOS, and the minimum macOS version
supported is macOS 10.11 (El Capitan).
Native support for Apple Silicon (`arm64`) devices was added in Electron 11.0.0.
### Windows
Windows 7 and later are supported, older operating systems are not supported
(and do not work).
Both `ia32` (`x86`) and `x64` (`amd64`) binaries are provided for Windows.
[Native support for Windows on Arm (`arm64`) devices was added in Electron 6.0.8.](windows-arm.md).
Running apps packaged with previous versions is possible using the ia32 binary.
### Linux
The prebuilt binaries of Electron are built on Ubuntu 18.04.
Whether the prebuilt binary can run on a distribution depends on whether the
distribution includes the libraries that Electron is linked to on the building
platform, so only Ubuntu 18.04 is guaranteed to work, but following platforms
are also verified to be able to run the prebuilt binaries of Electron:
* Ubuntu 14.04 and newer
* Fedora 24 and newer
* Debian 8 and newer

View File

@@ -132,6 +132,29 @@ describe('node feature', () => {
child.stderr.on('data', listener);
child.stdout.on('data', listener);
});
it('does allow --require in non-packaged apps', async () => {
const appPath = path.join(fixtures, 'module', 'noop.js');
const env = Object.assign({}, process.env, {
NODE_OPTIONS: `--require=${path.join(fixtures, 'module', 'fail.js')}`
});
// App should exit with code 1.
const child = childProcess.spawn(process.execPath, [appPath], { env });
const [code] = await emittedOnce(child, 'exit');
expect(code).to.equal(1);
});
it('does not allow --require in packaged apps', async () => {
const appPath = path.join(fixtures, 'module', 'noop.js');
const env = Object.assign({}, process.env, {
ELECTRON_FORCE_IS_PACKAGED: 'true',
NODE_OPTIONS: `--require=${path.join(fixtures, 'module', 'fail.js')}`
});
// App should exit with code 0.
const child = childProcess.spawn(process.execPath, [appPath], { env });
const [code] = await emittedOnce(child, 'exit');
expect(code).to.equal(0);
});
});
ifdescribe(features.isRunAsNodeEnabled())('Node.js cli flags', () => {

View File

@@ -1,7 +1,13 @@
import { expect } from 'chai';
import { nextVersion } from '../script/release/version-bumper';
import { nextVersion, shouldUpdateSupported, updateSupported } from '../script/release/version-bumper';
import * as utils from '../script/release/version-utils';
import { ifdescribe } from './spec-helpers';
const { promises: fs } = require('fs');
const path = require('path');
const fixtureDir = path.resolve(__dirname, 'fixtures', 'version-bumper', 'fixture_support.md');
const readFile = fs.readFile;
const writeFile = fs.writeFile;
describe('version-bumper', () => {
describe('makeVersion', () => {
@@ -41,6 +47,94 @@ describe('version-bumper', () => {
});
});
describe('updateSupported', () => {
let restore: any;
before(async () => {
restore = await readFile(fixtureDir, 'utf8');
});
afterEach(async () => {
await writeFile(fixtureDir, restore, 'utf8');
});
it('updates correctly when a new stable version is promoted from beta', async () => {
const version = '4.0.0';
const currentVersion = '4.0.0-beta.29';
if (shouldUpdateSupported('stable', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('4.x.y\n* 3.x.y\n* 2.x.y');
});
it('should not update when a new stable patch version is promoted', async () => {
const version = '3.0.1';
const currentVersion = '3.0.0';
if (shouldUpdateSupported('stable', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
it('should not update when a new stable minor version is promoted', async () => {
const version = '3.1.0';
const currentVersion = '3.0.0';
if (shouldUpdateSupported('minor', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
it('should not update when a new beta.1 version is promoted', async () => {
const version = '5.0.0-beta.1';
const currentVersion = '4.0.0-beta.29';
if (shouldUpdateSupported('beta', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
it('should not update when a new beta.12 version is promoted', async () => {
const version = '4.0.0-beta.12';
const currentVersion = '4.0.0-beta.11';
if (shouldUpdateSupported('beta', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
it('should update when a new major nightly version is promoted', async () => {
const version = '4.0.0-nightly.19950901';
const currentVersion = '3.0.0-nightly.19950828';
if (shouldUpdateSupported('nightly', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('4.x.y\n* 3.x.y\n* 2.x.y');
});
it('should not update when a new nightly version is promoted', async () => {
const version = '3.0.0-nightly.19950901';
const currentVersion = '3.0.0-nightly.19950828';
if (shouldUpdateSupported('nightly', currentVersion, version)) {
await updateSupported(version, fixtureDir);
}
const contents = await readFile(fixtureDir, 'utf8');
expect(contents).to.contain('3.x.y\n* 2.x.y\n* 1.x.y');
});
});
// On macOS Circle CI we don't have a real git environment due to running
// gclient sync on a linux machine. These tests therefore don't run as expected.
ifdescribe(!(process.platform === 'linux' && process.arch === 'arm') && process.platform !== 'darwin')('nextVersion', () => {

1
spec/fixtures/module/fail.js vendored Normal file
View File

@@ -0,0 +1 @@
process.exit(1);

View File

@@ -0,0 +1,21 @@
<script>
const ipcRenderer = require('electron').ipcRenderer;
navigator.serviceWorker.register('service-worker.js', {scope: './'}).then(() => {
if (navigator.serviceWorker.controller) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'sw://dummy/echo');
xhr.setRequestHeader('X-Mock-Response', 'yes');
xhr.addEventListener('error', error => {
ipcRenderer.send('error', `${error.message}\n${error.stack}`);
})
xhr.addEventListener('load', () => {
ipcRenderer.send('response', xhr.responseText);
});
xhr.send();
} else {
ipcRenderer.send('reload');
}
}).catch(error => {
ipcRenderer.send('error', `${error.message}\n${error.stack}`);
})
</script>

View File

@@ -56,12 +56,7 @@ declare namespace Electron {
}
interface WebContents {
_getURL(): string;
_loadURL(url: string, options: ElectronInternal.LoadURLOptions): void;
_stop(): void;
_goBack(): void;
_goForward(): void;
_goToOffset(offset: number): void;
getOwnerBrowserWindow(): Electron.BrowserWindow;
getWebPreferences(): Electron.WebPreferences;
getLastWebPreferences(): Electron.WebPreferences;