mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
39 Commits
miniak/set
...
v14.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdbf78dca8 | ||
|
|
40dbeb6836 | ||
|
|
3ff100521d | ||
|
|
f1752a0b6f | ||
|
|
e67b244571 | ||
|
|
fef79701e0 | ||
|
|
c2877a342c | ||
|
|
4e610b1948 | ||
|
|
4d30e7618a | ||
|
|
7381738d85 | ||
|
|
0f2ad3e384 | ||
|
|
b8812c8942 | ||
|
|
7a11390b8a | ||
|
|
6ab2684234 | ||
|
|
80f051d859 | ||
|
|
95e7c6d53a | ||
|
|
1b4c3428a9 | ||
|
|
8edb7b456f | ||
|
|
6362736703 | ||
|
|
670ae438b9 | ||
|
|
10c6959c7f | ||
|
|
1066dce975 | ||
|
|
d584afdf5b | ||
|
|
0d69ba8ca2 | ||
|
|
c2ba3ab114 | ||
|
|
72a33e79d0 | ||
|
|
3ee0536b1d | ||
|
|
846a12056d | ||
|
|
e0f6313739 | ||
|
|
8866b312ad | ||
|
|
cb8fada7a0 | ||
|
|
b983bda721 | ||
|
|
3125ec093d | ||
|
|
a27329d9ad | ||
|
|
5362882cf6 | ||
|
|
c58446d9d7 | ||
|
|
1c2ed2ba95 | ||
|
|
1c0e496ee2 | ||
|
|
0f32b0f1ce |
8
BUILD.gn
8
BUILD.gn
@@ -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") {
|
||||
|
||||
@@ -1 +1 @@
|
||||
14.0.0-nightly.20210524
|
||||
14.0.0-beta.4
|
||||
@@ -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
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 |
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
If you open the Console and then click the notification, you will see the
|
||||
message that was generated after triggering the `onclick` event:
|
||||
|
||||

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

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

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

|
||||
|
||||
### 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
|
||||
|
||||
|
||||
@@ -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)*
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,6 +6,7 @@ import sys
|
||||
|
||||
PLATFORM = {
|
||||
'cygwin': 'win32',
|
||||
'msys': 'win32',
|
||||
'darwin': 'darwin',
|
||||
'linux': 'linux',
|
||||
'linux2': 'linux',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,7 +16,7 @@ class CocoaNotification;
|
||||
|
||||
class NotificationPresenterMac : public NotificationPresenter {
|
||||
public:
|
||||
CocoaNotification* GetNotification(NSUserNotification* notif);
|
||||
CocoaNotification* GetNotification(NSUserNotification* ns_notification);
|
||||
|
||||
NotificationPresenterMac();
|
||||
~NotificationPresenterMac() override;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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+?');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
121
spec-main/fixtures/version-bumper/fixture_support.md
Normal file
121
spec-main/fixtures/version-bumper/fixture_support.md
Normal 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
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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
1
spec/fixtures/module/fail.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
process.exit(1);
|
||||
21
spec/fixtures/pages/service-worker/custom-scheme-index.html
vendored
Normal file
21
spec/fixtures/pages/service-worker/custom-scheme-index.html
vendored
Normal 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>
|
||||
5
typings/internal-electron.d.ts
vendored
5
typings/internal-electron.d.ts
vendored
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user