mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09677ab592 | ||
|
|
2cd8848b97 | ||
|
|
82151b4f0d | ||
|
|
37ece8b71a | ||
|
|
e4c2be03b2 | ||
|
|
d1735ee2f3 | ||
|
|
4c8eb34bab | ||
|
|
14ded68b75 | ||
|
|
220b356233 | ||
|
|
858d74bc36 | ||
|
|
c32cb09a68 | ||
|
|
194edd0270 | ||
|
|
6c3c9b5a51 | ||
|
|
a43d4cacde | ||
|
|
a7372e8f53 | ||
|
|
3aa207c2dc | ||
|
|
f96153a4d1 | ||
|
|
1aeba811cc | ||
|
|
f6dced63b5 | ||
|
|
489aafc1f9 | ||
|
|
a6ed6480d8 | ||
|
|
81c1a6ed29 | ||
|
|
fe3378db92 | ||
|
|
fe85a94035 | ||
|
|
48779feb91 | ||
|
|
5e289cee04 | ||
|
|
5f4ccec09a | ||
|
|
20f58fddf1 | ||
|
|
75f193a089 | ||
|
|
7de2539399 | ||
|
|
a90397f348 | ||
|
|
6e734570ac | ||
|
|
79c1c8d21f | ||
|
|
35b348a7d7 | ||
|
|
213f2fc838 | ||
|
|
fb03807cd2 | ||
|
|
89523b2504 | ||
|
|
243dbda31c | ||
|
|
587157e933 | ||
|
|
15f9300a64 | ||
|
|
88898acfef | ||
|
|
8a69889048 | ||
|
|
b032c415c1 | ||
|
|
e8643c1144 | ||
|
|
f8af08c279 | ||
|
|
713ff4c471 | ||
|
|
99abcba062 | ||
|
|
086086b4e5 | ||
|
|
6e7425164f | ||
|
|
4a03aa5bd2 | ||
|
|
99b4fd1ce0 | ||
|
|
a60211333c | ||
|
|
732ce44e61 | ||
|
|
0f70d096fc | ||
|
|
56cde790e1 | ||
|
|
7dc7b3627f | ||
|
|
123f4087ca | ||
|
|
b5720bb2b8 | ||
|
|
84f05ee0a4 | ||
|
|
694e8d8e66 | ||
|
|
1529bf7ce5 | ||
|
|
d688b77187 | ||
|
|
7651036bce | ||
|
|
294825837c | ||
|
|
945defd47e | ||
|
|
cbd91b1fba | ||
|
|
1aebbe9538 | ||
|
|
508d2cb1f5 | ||
|
|
f769da64ad | ||
|
|
e16decd6a1 | ||
|
|
03c7a54dc5 | ||
|
|
eefcbda641 | ||
|
|
ea118c3984 | ||
|
|
b204da6603 | ||
|
|
cf321a9b55 | ||
|
|
358d6e1116 | ||
|
|
12934c911f | ||
|
|
e2c4e341cc | ||
|
|
79f6ba4b18 | ||
|
|
3e835c1247 | ||
|
|
91849fccbe | ||
|
|
8dc38cb973 | ||
|
|
f799b6eb37 | ||
|
|
7063ba73df | ||
|
|
f01bb5f43b | ||
|
|
0c2cb59b62 | ||
|
|
310495212a | ||
|
|
c7e00b933f | ||
|
|
81d26ac1b7 | ||
|
|
a5795824ef | ||
|
|
0107cab3d1 | ||
|
|
f65756bd4f | ||
|
|
05343a8972 | ||
|
|
72d921e453 | ||
|
|
7564453101 | ||
|
|
720e49d73d |
@@ -61,7 +61,7 @@ parameters:
|
||||
# Build machines configs.
|
||||
docker-image: &docker-image
|
||||
docker:
|
||||
- image: electronjs/build:d09fd95029bd8c1c73069888231b29688ef385ed
|
||||
- image: electron.azurecr.io/build:4cec2c5ab66765caa724e37bae2bffb9b29722a5
|
||||
|
||||
machine-linux-medium: &machine-linux-medium
|
||||
<<: *docker-image
|
||||
@@ -702,8 +702,6 @@ step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols
|
||||
if [ "$GENERATE_SYMBOLS" == "true" ]; then
|
||||
cd src
|
||||
ninja -C out/Default electron:electron_symbols
|
||||
cd out/Default/breakpad_symbols
|
||||
find . -name \*.sym -print0 | xargs -0 npx @sentry/cli@1.51.1 difutil bundle-sources
|
||||
fi
|
||||
|
||||
step-maybe-zip-symbols: &step-maybe-zip-symbols
|
||||
|
||||
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -2,3 +2,13 @@
|
||||
# files to be checked out with LF endings even if core.autocrlf is true.
|
||||
*.patch text eol=lf
|
||||
patches/**/.patches merge=union
|
||||
|
||||
# Source code and markdown files should always use LF as line ending.
|
||||
*.cc text eol=lf
|
||||
*.mm text eol=lf
|
||||
*.h text eol=lf
|
||||
*.js text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.py text eol=lf
|
||||
*.ps1 text eol=lf
|
||||
*.md text eol=lf
|
||||
|
||||
13
BUILD.gn
13
BUILD.gn
@@ -1115,20 +1115,17 @@ if (is_mac) {
|
||||
"//components/crash/core/app:run_as_crashpad_handler",
|
||||
]
|
||||
|
||||
ldflags = []
|
||||
|
||||
libs = [
|
||||
"comctl32.lib",
|
||||
"uiautomationcore.lib",
|
||||
"wtsapi32.lib",
|
||||
]
|
||||
|
||||
configs += [ "//build/config/win:windowed" ]
|
||||
|
||||
ldflags = [
|
||||
# Windows 7 doesn't have these DLLs.
|
||||
# TODO: are there other DLLs we need to list here to be win7
|
||||
# compatible?
|
||||
"/DELAYLOAD:api-ms-win-core-winrt-l1-1-0.dll",
|
||||
"/DELAYLOAD:api-ms-win-core-winrt-string-l1-1-0.dll",
|
||||
configs += [
|
||||
"//build/config/win:windowed",
|
||||
"//build/config/win:delayloads",
|
||||
]
|
||||
|
||||
# This is to support renaming of electron.exe. node-gyp has hard-coded
|
||||
|
||||
@@ -1 +1 @@
|
||||
9.1.2
|
||||
9.3.2
|
||||
@@ -10,10 +10,9 @@ config.output = {
|
||||
filename: path.basename(outPath)
|
||||
}
|
||||
|
||||
const { wrapInitWithProfilingTimeout } = config;
|
||||
delete config.wrapInitWithProfilingTimeout;
|
||||
const { wrapInitWithProfilingTimeout, wrapInitWithTryCatch, ...webpackConfig } = config;
|
||||
|
||||
webpack(config, (err, stats) => {
|
||||
webpack(webpackConfig, (err, stats) => {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
@@ -21,9 +20,17 @@ webpack(config, (err, stats) => {
|
||||
console.error(stats.toString('normal'))
|
||||
process.exit(1)
|
||||
} else {
|
||||
let contents = fs.readFileSync(outPath, 'utf8');
|
||||
if (wrapInitWithTryCatch) {
|
||||
contents = `try {
|
||||
${contents}
|
||||
} catch (err) {
|
||||
console.error('Electron ${webpackConfig.output.filename} script failed to run');
|
||||
console.error(err);
|
||||
}`;
|
||||
}
|
||||
if (wrapInitWithProfilingTimeout) {
|
||||
const contents = fs.readFileSync(outPath, 'utf8');
|
||||
const newContents = `function ___electron_webpack_init__() {
|
||||
contents = `function ___electron_webpack_init__() {
|
||||
${contents}
|
||||
};
|
||||
if ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) {
|
||||
@@ -31,8 +38,8 @@ if ((globalThis.process || binding.process).argv.includes("--profile-electron-in
|
||||
} else {
|
||||
___electron_webpack_init__();
|
||||
}`;
|
||||
fs.writeFileSync(outPath, newContents);
|
||||
}
|
||||
fs.writeFileSync(outPath, contents)
|
||||
process.exit(0)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -86,7 +86,8 @@ module.exports = ({
|
||||
loadElectronFromAlternateTarget,
|
||||
targetDeletesNodeGlobals,
|
||||
target,
|
||||
wrapInitWithProfilingTimeout
|
||||
wrapInitWithProfilingTimeout,
|
||||
wrapInitWithTryCatch
|
||||
}) => {
|
||||
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts')
|
||||
if (!fs.existsSync(entry)) {
|
||||
@@ -102,6 +103,7 @@ module.exports = ({
|
||||
filename: `${target}.bundle.js`
|
||||
},
|
||||
wrapInitWithProfilingTimeout,
|
||||
wrapInitWithTryCatch,
|
||||
resolve: {
|
||||
alias: {
|
||||
'@electron/internal': path.resolve(electronRoot, 'lib'),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'isolated_renderer',
|
||||
alwaysHasNode: false
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithTryCatch: true
|
||||
})
|
||||
|
||||
@@ -2,5 +2,6 @@ module.exports = require('./webpack.config.base')({
|
||||
target: 'renderer',
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true,
|
||||
wrapInitWithProfilingTimeout: true
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
wrapInitWithTryCatch: true
|
||||
})
|
||||
|
||||
@@ -2,4 +2,5 @@ module.exports = require('./webpack.config.base')({
|
||||
target: 'sandboxed_renderer',
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
wrapInitWithTryCatch: true
|
||||
})
|
||||
|
||||
@@ -2,5 +2,6 @@ module.exports = require('./webpack.config.base')({
|
||||
target: 'worker',
|
||||
loadElectronFromAlternateTarget: 'renderer',
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true
|
||||
targetDeletesNodeGlobals: true,
|
||||
wrapInitWithTryCatch: true
|
||||
})
|
||||
|
||||
@@ -36,6 +36,7 @@ net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
|
||||
// Linux has only a single persistent slot compared to ChromeOS's separate
|
||||
// public and private slot.
|
||||
// Redirect any slot usage to this persistent slot on Linux.
|
||||
crypto::EnsureNSSInit();
|
||||
g_nss_cert_database = new net::NSSCertDatabase(
|
||||
crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* public slot */,
|
||||
crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* private slot */);
|
||||
|
||||
@@ -348,6 +348,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
You can access this context in the dev tools by selecting the
|
||||
'Electron Isolated Context' entry in the combo box at the top of the
|
||||
Console tab.
|
||||
* `worldSafeExecuteJavaScript` Boolean (optional) - If true, values returned from `webFrame.executeJavaScript` will be sanitized to ensure JS values
|
||||
can't unsafely cross between worlds when using `contextIsolation`. The default
|
||||
is `false`. In Electron 12, the default will be changed to `true`. _Deprecated_
|
||||
* `nativeWindowOpen` Boolean (optional) - Whether to use native
|
||||
`window.open()`. Defaults to `false`. Child windows will always have node
|
||||
integration disabled unless `nodeIntegrationInSubFrames` is true. **Note:** This option is currently
|
||||
@@ -1666,9 +1669,12 @@ Sets whether the menu bar should be visible. If the menu bar is auto-hide, users
|
||||
|
||||
Returns `Boolean` - Whether the menu bar is visible.
|
||||
|
||||
#### `win.setVisibleOnAllWorkspaces(visible)`
|
||||
#### `win.setVisibleOnAllWorkspaces(visible[, options])`
|
||||
|
||||
* `visible` Boolean
|
||||
* `options` Object (optional)
|
||||
* `visibleOnFullScreen` Boolean (optional) _macOS_ - Sets whether
|
||||
the window should be visible above fullscreen windows
|
||||
|
||||
Sets whether the window should be visible on all workspaces.
|
||||
|
||||
|
||||
@@ -157,10 +157,16 @@ parameters in a renderer process will not result in those parameters being sent
|
||||
with crashes that occur in other renderer processes or in the main process.
|
||||
|
||||
**Note:** Parameters have limits on the length of the keys and values. Key
|
||||
names must be no longer than 39 bytes, and values must be no longer than 127
|
||||
names must be no longer than 39 bytes, and values must be no longer than 20320
|
||||
bytes. Keys with names longer than the maximum will be silently ignored. Key
|
||||
values longer than the maximum length will be truncated.
|
||||
|
||||
**Note:** On linux values that are longer than 127 bytes will be chunked into
|
||||
multiple keys, each 127 bytes in length. E.g. `addExtraParameter('foo', 'a'.repeat(130))`
|
||||
will result in two chunked keys `foo__1` and `foo__2`, the first will contain
|
||||
the first 127 bytes and the second will contain the remaining 3 bytes. On
|
||||
your crash reporting backend you should stitch together keys in this format.
|
||||
|
||||
### `crashReporter.removeExtraParameter(key)`
|
||||
|
||||
* `key` String - Parameter key, must be no longer than 39 bytes.
|
||||
|
||||
@@ -28,6 +28,9 @@ session.loadExtension('path/to/unpacked/extension').then(({ id }) => {
|
||||
Loaded extensions will not be automatically remembered across exits; if you do
|
||||
not call `loadExtension` when the app runs, the extension will not be loaded.
|
||||
|
||||
Note that loading extensions is only supported in persistent sessions.
|
||||
Attempting to load an extension into an in-memory session will throw an error.
|
||||
|
||||
See the [`session`](session.md) documentation for more information about
|
||||
loading, unloading, and querying active extensions.
|
||||
|
||||
@@ -99,3 +102,15 @@ The following methods of `chrome.tabs` are supported:
|
||||
> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active
|
||||
> tab". Since Electron has no such concept, passing `-1` as a tab ID is not
|
||||
> supported and will raise an error.
|
||||
|
||||
### `chrome.management`
|
||||
|
||||
The following methods of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.getAll`
|
||||
- `chrome.management.get`
|
||||
- `chrome.management.getSelf`
|
||||
- `chrome.management.getPermissionWarningsById`
|
||||
- `chrome.management.getPermissionWarningsByManifest`
|
||||
- `chrome.management.onEnabled`
|
||||
- `chrome.management.onDisabled`
|
||||
|
||||
@@ -24,19 +24,19 @@ app.whenReady().then(() => {
|
||||
|
||||
The `powerMonitor` module emits the following events:
|
||||
|
||||
### Event: 'suspend' _Linux_ _Windows_
|
||||
### Event: 'suspend' _macOS_ _Windows_
|
||||
|
||||
Emitted when the system is suspending.
|
||||
|
||||
### Event: 'resume' _Linux_ _Windows_
|
||||
### Event: 'resume' _macOS_ _Windows_
|
||||
|
||||
Emitted when system is resuming.
|
||||
|
||||
### Event: 'on-ac' _Windows_
|
||||
### Event: 'on-ac' _macOS_ _Windows_
|
||||
|
||||
Emitted when the system changes to AC power.
|
||||
|
||||
### Event: 'on-battery' _Windows_
|
||||
### Event: 'on-battery' _macOS_ _Windows_
|
||||
|
||||
Emitted when system changes to battery power.
|
||||
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
* `contentLengths` Number[] - The total size of the content, in bytes.
|
||||
* `price` Number - The cost of the product in the local currency.
|
||||
* `formattedPrice` String - The locale formatted price of the product.
|
||||
* `currencyCode` String - 3 character code presenting a product's currency based on the ISO 4217 standard.
|
||||
* `isDownloadable` Boolean - A Boolean value that indicates whether the App Store has downloadable content for this product. `true` if at least one file has been associated with the product.
|
||||
|
||||
@@ -1,95 +1,95 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('open-error-dialog', event => {
|
||||
dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
|
||||
})
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('open-error-dialog', event => {
|
||||
dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
|
||||
})
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
const errorBtn = document.getElementById('error-dialog')
|
||||
|
||||
errorBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-error-dialog')
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
const errorBtn = document.getElementById('error-dialog')
|
||||
|
||||
errorBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-error-dialog')
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,70 +1,70 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const informationBtn = document.getElementById('information-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
informationBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-information-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('information-dialog-selection', (event, index) => {
|
||||
let message = 'You selected '
|
||||
if (index === 0) message += 'yes.'
|
||||
else message += 'no.'
|
||||
document.getElementById('info-selection').innerHTML = message
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const informationBtn = document.getElementById('information-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
informationBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-information-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('information-dialog-selection', (event, index) => {
|
||||
let message = 'You selected '
|
||||
if (index === 0) message += 'yes.'
|
||||
else message += 'no.'
|
||||
document.getElementById('info-selection').innerHTML = message
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,70 +1,70 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const selectDirBtn = document.getElementById('select-directory')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
selectDirBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-file-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('selected-directory', (event, path) => {
|
||||
document.getElementById('selected-file').innerHTML = `You selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const selectDirBtn = document.getElementById('select-directory')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
selectDirBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-file-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('selected-directory', (event, path) => {
|
||||
document.getElementById('selected-file').innerHTML = `You selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,66 +1,66 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const saveBtn = document.getElementById('save-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
saveBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('save-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('saved-file', (event, path) => {
|
||||
if (!path) path = 'No path'
|
||||
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const saveBtn = document.getElementById('save-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
saveBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('save-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('saved-file', (event, path) => {
|
||||
if (!path) path = 'No path'
|
||||
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
const { shell } = require('electron')
|
||||
const os = require('os')
|
||||
|
||||
const exLinksBtn = document.getElementById('open-ex-links')
|
||||
const fileManagerBtn = document.getElementById('open-file-manager')
|
||||
|
||||
fileManagerBtn.addEventListener('click', (event) => {
|
||||
shell.showItemInFolder(os.homedir())
|
||||
})
|
||||
|
||||
exLinksBtn.addEventListener('click', (event) => {
|
||||
shell.openExternal('http://electron.atom.io')
|
||||
const { shell } = require('electron')
|
||||
const os = require('os')
|
||||
|
||||
const exLinksBtn = document.getElementById('open-ex-links')
|
||||
const fileManagerBtn = document.getElementById('open-file-manager')
|
||||
|
||||
fileManagerBtn.addEventListener('click', (event) => {
|
||||
shell.showItemInFolder(os.homedir())
|
||||
})
|
||||
|
||||
exLinksBtn.addEventListener('click', (event) => {
|
||||
shell.openExternal('http://electron.atom.io')
|
||||
})
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
const basicNotification = {
|
||||
title: 'Basic Notification',
|
||||
body: 'Short message part'
|
||||
}
|
||||
|
||||
const notification = {
|
||||
title: 'Notification with image',
|
||||
body: 'Short message plus a custom image',
|
||||
icon: 'https://via.placeholder.com/150'
|
||||
}
|
||||
|
||||
const basicNotificationButton = document.getElementById('basic-noti')
|
||||
const notificationButton = document.getElementById('advanced-noti')
|
||||
|
||||
notificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(notification.title, notification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
|
||||
basicNotificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(basicNotification.title, basicNotification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
const basicNotification = {
|
||||
title: 'Basic Notification',
|
||||
body: 'Short message part'
|
||||
}
|
||||
|
||||
const notification = {
|
||||
title: 'Notification with image',
|
||||
body: 'Short message plus a custom image',
|
||||
icon: 'https://via.placeholder.com/150'
|
||||
}
|
||||
|
||||
const basicNotificationButton = document.getElementById('basic-noti')
|
||||
const notificationButton = document.getElementById('advanced-noti')
|
||||
|
||||
notificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(notification.title, notification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
|
||||
basicNotificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(basicNotification.title, basicNotification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,35 +1,35 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const trayBtn = document.getElementById('put-in-tray')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let trayOn = false
|
||||
|
||||
trayBtn.addEventListener('click', function (event) {
|
||||
if (trayOn) {
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
ipcRenderer.send('remove-tray')
|
||||
} else {
|
||||
trayOn = true
|
||||
const message = 'Click demo again to remove.'
|
||||
document.getElementById('tray-countdown').innerHTML = message
|
||||
ipcRenderer.send('put-in-tray')
|
||||
}
|
||||
})
|
||||
// Tray removed from context menu on icon
|
||||
ipcRenderer.on('tray-removed', function () {
|
||||
ipcRenderer.send('remove-tray')
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const trayBtn = document.getElementById('put-in-tray')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let trayOn = false
|
||||
|
||||
trayBtn.addEventListener('click', function (event) {
|
||||
if (trayOn) {
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
ipcRenderer.send('remove-tray')
|
||||
} else {
|
||||
trayOn = true
|
||||
const message = 'Click demo again to remove.'
|
||||
document.getElementById('tray-countdown').innerHTML = message
|
||||
ipcRenderer.send('put-in-tray')
|
||||
}
|
||||
})
|
||||
// Tray removed from context menu on icon
|
||||
ipcRenderer.on('tray-removed', function () {
|
||||
ipcRenderer.send('remove-tray')
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const framelessWindowBtn = document.getElementById('frameless-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
framelessWindowBtn.addEventListener('click', (event) => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
let win = new BrowserWindow({ frame: false })
|
||||
|
||||
win.on('close', () => { win = null })
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const framelessWindowBtn = document.getElementById('frameless-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
framelessWindowBtn.addEventListener('click', (event) => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
let win = new BrowserWindow({ frame: false })
|
||||
|
||||
win.on('close', () => { win = null })
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const manageWindowBtn = document.getElementById('manage-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let win
|
||||
|
||||
manageWindowBtn.addEventListener('click', (event) => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
win = new BrowserWindow({ width: 400, height: 275 })
|
||||
|
||||
win.on('resize', updateReply)
|
||||
win.on('move', updateReply)
|
||||
win.on('close', () => { win = null })
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
|
||||
function updateReply () {
|
||||
const manageWindowReply = document.getElementById('manage-window-reply')
|
||||
const message = `Size: ${win.getSize()} Position: ${win.getPosition()}`
|
||||
manageWindowReply.innerText = message
|
||||
}
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const manageWindowBtn = document.getElementById('manage-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let win
|
||||
|
||||
manageWindowBtn.addEventListener('click', (event) => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
win = new BrowserWindow({ width: 400, height: 275 })
|
||||
|
||||
win.on('resize', updateReply)
|
||||
win.on('move', updateReply)
|
||||
win.on('close', () => { win = null })
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
|
||||
function updateReply () {
|
||||
const manageWindowReply = document.getElementById('manage-window-reply')
|
||||
const message = `Size: ${win.getSize()} Position: ${win.getPosition()}`
|
||||
manageWindowReply.innerText = message
|
||||
}
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const listenToWindowBtn = document.getElementById('listen-to-window')
|
||||
const focusModalBtn = document.getElementById('focus-on-modal-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let win
|
||||
|
||||
listenToWindowBtn.addEventListener('click', () => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
win = new BrowserWindow({ width: 600, height: 400 })
|
||||
|
||||
const hideFocusBtn = () => {
|
||||
focusModalBtn.classList.add('disappear')
|
||||
focusModalBtn.classList.remove('smooth-appear')
|
||||
focusModalBtn.removeEventListener('click', clickHandler)
|
||||
}
|
||||
|
||||
const showFocusBtn = (btn) => {
|
||||
if (!win) return
|
||||
focusModalBtn.classList.add('smooth-appear')
|
||||
focusModalBtn.classList.remove('disappear')
|
||||
focusModalBtn.addEventListener('click', clickHandler)
|
||||
}
|
||||
|
||||
win.on('focus', hideFocusBtn)
|
||||
win.on('blur', showFocusBtn)
|
||||
win.on('close', () => {
|
||||
hideFocusBtn()
|
||||
win = null
|
||||
})
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
|
||||
const clickHandler = () => { win.focus() }
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const listenToWindowBtn = document.getElementById('listen-to-window')
|
||||
const focusModalBtn = document.getElementById('focus-on-modal-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let win
|
||||
|
||||
listenToWindowBtn.addEventListener('click', () => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
win = new BrowserWindow({ width: 600, height: 400 })
|
||||
|
||||
const hideFocusBtn = () => {
|
||||
focusModalBtn.classList.add('disappear')
|
||||
focusModalBtn.classList.remove('smooth-appear')
|
||||
focusModalBtn.removeEventListener('click', clickHandler)
|
||||
}
|
||||
|
||||
const showFocusBtn = (btn) => {
|
||||
if (!win) return
|
||||
focusModalBtn.classList.add('smooth-appear')
|
||||
focusModalBtn.classList.remove('disappear')
|
||||
focusModalBtn.addEventListener('click', clickHandler)
|
||||
}
|
||||
|
||||
win.on('focus', hideFocusBtn)
|
||||
win.on('blur', showFocusBtn)
|
||||
win.on('close', () => {
|
||||
hideFocusBtn()
|
||||
win = null
|
||||
})
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
|
||||
const clickHandler = () => { win.focus() }
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -614,6 +614,8 @@ filenames = {
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.h",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.cc",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.h",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.cc",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.h",
|
||||
"shell/browser/extensions/api/streams_private/streams_private_api.cc",
|
||||
|
||||
@@ -459,6 +459,10 @@ const addReturnValueToEvent = (event) => {
|
||||
});
|
||||
};
|
||||
|
||||
const loggingEnabled = () => {
|
||||
return process.env.ELECTRON_ENABLE_LOGGING || app.commandLine.hasSwitch('enable-logging');
|
||||
};
|
||||
|
||||
// Add JavaScript wrappers for WebContents class.
|
||||
WebContents.prototype._init = function () {
|
||||
// The navigation controller.
|
||||
@@ -527,8 +531,13 @@ WebContents.prototype._init = function () {
|
||||
app.emit('renderer-process-crashed', event, this, ...args);
|
||||
});
|
||||
|
||||
this.on('render-process-gone', (event, ...args) => {
|
||||
app.emit('render-process-gone', event, this, ...args);
|
||||
this.on('render-process-gone', (event, details) => {
|
||||
app.emit('render-process-gone', event, this, details);
|
||||
|
||||
// Log out a hint to help users better debug renderer crashes.
|
||||
if (loggingEnabled()) {
|
||||
console.info(`Renderer process ${details.reason} - see https://www.electronjs.org/docs/tutorial/application-debugging for potential debugging information.`);
|
||||
}
|
||||
});
|
||||
|
||||
// The devtools requests the webContents to reload.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import deprecate from '@electron/internal/common/api/deprecate';
|
||||
|
||||
const binding = process.electronBinding('web_frame');
|
||||
|
||||
@@ -47,14 +48,26 @@ class WebFrame extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
const { hasSwitch } = process.electronBinding('command_line');
|
||||
const worldSafeJS = hasSwitch('world-safe-execute-javascript') && hasSwitch('context-isolation');
|
||||
|
||||
// Populate the methods.
|
||||
for (const name in binding) {
|
||||
if (!name.startsWith('_')) { // some methods are manually populated above
|
||||
// TODO(felixrieseberg): Once we can type web_frame natives, we could
|
||||
// use a neat `keyof` here
|
||||
(WebFrame as any).prototype[name] = function (...args: Array<any>) {
|
||||
if (!worldSafeJS && name.startsWith('executeJavaScript')) {
|
||||
deprecate.log(`Security Warning: webFrame.${name} was called without worldSafeExecuteJavaScript enabled. This is considered unsafe. worldSafeExecuteJavaScript will be enabled by default in Electron 12.`);
|
||||
}
|
||||
return binding[name](this.context, ...args);
|
||||
};
|
||||
// TODO(MarshallOfSound): Remove once the above deprecation is removed
|
||||
if (name.startsWith('executeJavaScript')) {
|
||||
(WebFrame as any).prototype[`_${name}`] = function (...args: Array<any>) {
|
||||
return binding[name](this.context, ...args);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ export const webFrameInit = () => {
|
||||
ipcRendererUtils.handle('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (
|
||||
event, method: keyof WebFrameMethod, ...args: any[]
|
||||
) => {
|
||||
// TODO(MarshallOfSound): Remove once the world-safe-execute-javascript deprecation warning is removed
|
||||
if (method.startsWith('executeJavaScript')) {
|
||||
return (webFrame as any)[`_${method}`](...args);
|
||||
}
|
||||
|
||||
// The TypeScript compiler cannot handle the sheer number of
|
||||
// call signatures here and simply gives up. Incorrect invocations
|
||||
// will be caught by "keyof WebFrameMethod" though.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "9.1.2",
|
||||
"version": "9.3.2",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
2
patches/angle/.patches
Normal file
2
patches/angle/.patches
Normal file
@@ -0,0 +1,2 @@
|
||||
fix_stale_validation_cache_on_buffer_deletion.patch
|
||||
d3d11_fix_bug_with_static_vertex_attributes.patch
|
||||
@@ -0,0 +1,41 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jamie Madill <jmadill@chromium.org>
|
||||
Date: Wed, 22 Jul 2020 13:58:50 -0400
|
||||
Subject: D3D11: Fix bug with static vertex attributes.
|
||||
|
||||
In some specific cases after binding a zero size buffer we could end
|
||||
up trying to use a buffer storage that was no longer valid. Fix this
|
||||
by ensuring we don't flush dirty bits when we have an early exit due
|
||||
to a zero size buffer.
|
||||
|
||||
Also adds a regression test.
|
||||
|
||||
Bug: chromium:1107433
|
||||
Change-Id: I9db560e8dd3699abed2bb7fe6d91060148ba1817
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2314216
|
||||
Commit-Queue: Jamie Madill <jmadill@chromium.org>
|
||||
Reviewed-by: Geoff Lang <geofflang@chromium.org>
|
||||
|
||||
diff --git a/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp b/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp
|
||||
index 5f834b5c8dbc63d3bcc374eca15a17ee442d77e3..b0e27c9f0c6c316fe347a44776a6926b093a81c4 100644
|
||||
--- a/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp
|
||||
+++ b/src/libANGLE/renderer/d3d/d3d11/VertexArray11.cpp
|
||||
@@ -250,8 +250,6 @@ angle::Result VertexArray11::updateDirtyAttribs(const gl::Context *context,
|
||||
|
||||
for (size_t dirtyAttribIndex : activeDirtyAttribs)
|
||||
{
|
||||
- mAttribsToTranslate.reset(dirtyAttribIndex);
|
||||
-
|
||||
auto *translatedAttrib = &mTranslatedAttribs[dirtyAttribIndex];
|
||||
const auto ¤tValue = glState.getVertexAttribCurrentValue(dirtyAttribIndex);
|
||||
|
||||
@@ -279,6 +277,9 @@ angle::Result VertexArray11::updateDirtyAttribs(const gl::Context *context,
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
+
|
||||
+ // Make sure we reset the dirty bit after the switch because STATIC can early exit.
|
||||
+ mAttribsToTranslate.reset(dirtyAttribIndex);
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
@@ -0,0 +1,39 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jamie Madill <jmadill@chromium.org>
|
||||
Date: Tue, 14 Jul 2020 17:20:18 -0400
|
||||
Subject: Fix stale validation cache on buffer deletion.
|
||||
|
||||
When we would delete the currently bound element array buffer we
|
||||
would neglect to invalidate a specific validation cache variable.
|
||||
This incorrectly would let us skip buffer size validation and lead
|
||||
to internal invalid memory accesses.
|
||||
|
||||
Bug: chromium:1105202
|
||||
Change-Id: I23ab28ccd3ac6b5d461cb8745b930f4d42d53b35
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2323644
|
||||
Reviewed-by: Jamie Madill <jmadill@chromium.org>
|
||||
|
||||
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
|
||||
index d8bccaf1e920e261c45c777993b77b8c40a011ad..3233d12dd233786b0988f299ace57d932e0d0fe6 100644
|
||||
--- a/src/libANGLE/Context.cpp
|
||||
+++ b/src/libANGLE/Context.cpp
|
||||
@@ -8468,6 +8468,7 @@ void StateCache::onVertexArrayStateChange(Context *context)
|
||||
updateActiveAttribsMask(context);
|
||||
updateVertexElementLimits(context);
|
||||
updateBasicDrawStatesError();
|
||||
+ updateBasicDrawElementsError();
|
||||
}
|
||||
|
||||
void StateCache::onVertexArrayBufferStateChange(Context *context)
|
||||
diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h
|
||||
index 201010f9f5555faa79cab480a34caf63bf5ffbf2..06eeff3b94c937067e674fc127afdeab34e63f21 100644
|
||||
--- a/src/libANGLE/Context.h
|
||||
+++ b/src/libANGLE/Context.h
|
||||
@@ -202,6 +202,7 @@ class StateCache final : angle::NonCopyable
|
||||
// 1. onActiveTransformFeedbackChange.
|
||||
// 2. onVertexArrayBufferStateChange.
|
||||
// 3. onBufferBindingChange.
|
||||
+ // 4. onVertexArrayStateChange.
|
||||
intptr_t getBasicDrawElementsError(const Context *context) const
|
||||
{
|
||||
if (mCachedBasicDrawElementsError != kInvalidPointer)
|
||||
@@ -116,3 +116,25 @@ backport_1073409.patch
|
||||
backport_1074340.patch
|
||||
backport_1016278.patch
|
||||
backport_1042986.patch
|
||||
a11y_axplatformnodebase_getindexinparent_returns_base_optional.patch
|
||||
worker_feat_add_hook_to_notify_script_ready.patch
|
||||
reconnect_p2p_socket_dispatcher_if_network_service_dies.patch
|
||||
allow_focus_to_move_into_an_editable_combobox_s_listbox.patch
|
||||
cherry-pick-70579363ce7b.patch
|
||||
cherry-pick-138b748dd0a4.patch
|
||||
cherry-pick-9d100199c92b.patch
|
||||
cherry-pick-bee371eeaf66.patch
|
||||
cherry-pick-9746a4cde14a.patch
|
||||
avoid_loading_dri_via_gbm_when_gpumemorybuffers_are_disabled.patch
|
||||
cherry-pick-72ee7c437c88.patch
|
||||
cherry-pick-9ad8c9610d0a.patch
|
||||
indexeddb_fix_crash_in_webidbgetdbnamescallbacksimpl.patch
|
||||
indexeddb_reset_async_tasks_in_webidbgetdbnamescallbacksimpl.patch
|
||||
reland_fix_uaf_in_selecttype.patch
|
||||
cherry-pick-f6cb89728f04.patch
|
||||
backport_1081874.patch
|
||||
backport_1122684.patch
|
||||
backport_1111737.patch
|
||||
cherry-pick-0e61c69ebd47.patch
|
||||
cherry-pick-814a27f8522b.patch
|
||||
cherry-pick-52dceba66599.patch
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Julie Jeongeun Kim <jkim@igalia.com>
|
||||
Date: Wed, 8 Apr 2020 11:18:34 +0000
|
||||
Subject: a11y: AXPlatformNodeBase::GetIndexInParent() returns base::Optional
|
||||
|
||||
This fixes the regression issue from the CL[1] which moved
|
||||
the logic of GetIndexInParent to AXPlatformNodeBase.
|
||||
|
||||
Before the CL[1], atk_object::GetIndexInParent returns -1 as an
|
||||
invalid value but AXPlatformNodeBase::GetIndexInParent returns
|
||||
positive numbers or 0 except the case when it's not implemented
|
||||
or doesn't have |delegate_|. AXPlatformNodeBase::GetIndexInParent
|
||||
is also used for the internal logic in AXPlatformNodeBase with
|
||||
the assumption that the return value is not less than 0.
|
||||
The CL[1] moved the logic to AXPlatformNodeBase::GetIndexInParent
|
||||
with keeping the rule that the return value is not less than 0.
|
||||
|
||||
The problem is that screen reader such as Orca expects that it
|
||||
returns -1 when it's invalid.
|
||||
|
||||
With this CL, AXPlatformNodeBase::GetIndexInParent() returns
|
||||
base::Optional and the caller has -1 if GetIndexInParent() returns
|
||||
base::nullopt. It also uses GetFirstChild and GetNextSibling for
|
||||
children iteration in GetHypertextOffsetFromChild() and
|
||||
GetHypertextOffsetFromEndpoint() instead of GetIndexInParent and
|
||||
GetChildAt.
|
||||
|
||||
[1]https://crrev.com/c/2087234
|
||||
|
||||
Bug: 1065534
|
||||
Change-Id: I00e2ed421ed263cf379ba22f55049fd52d32df9b
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2129667
|
||||
Commit-Queue: Julie Kim <jkim@igalia.com>
|
||||
Reviewed-by: Nektarios Paisios <nektar@chromium.org>
|
||||
Reviewed-by: Martin Robinson <mrobinson@igalia.com>
|
||||
Reviewed-by: Joanmarie Diggs <jdiggs@igalia.com>
|
||||
Cr-Commit-Position: refs/heads/master@{#757381}
|
||||
|
||||
diff --git a/content/test/data/accessibility/event/css-flex-text-update-expected-auralinux.txt b/content/test/data/accessibility/event/css-flex-text-update-expected-auralinux.txt
|
||||
index c68cfe98572b9b2edf3658053be3dea2040524e4..483d5bb7eb7be40fa0dbd81cb336fe41fe665efe 100644
|
||||
--- a/content/test/data/accessibility/event/css-flex-text-update-expected-auralinux.txt
|
||||
+++ b/content/test/data/accessibility/event/css-flex-text-update-expected-auralinux.txt
|
||||
@@ -1,3 +1,3 @@
|
||||
-CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
+CHILDREN-CHANGED index:-1 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
NAME-CHANGED:new role=ROLE_STATIC name='new' ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
|
||||
diff --git a/content/test/data/accessibility/event/text-changed-expected-auralinux.txt b/content/test/data/accessibility/event/text-changed-expected-auralinux.txt
|
||||
index b1573d5a3e84bc168403ad989632333473a5c0a8..0fe6700c90e82846aa862cc09a567d5ad36bccc6 100644
|
||||
--- a/content/test/data/accessibility/event/text-changed-expected-auralinux.txt
|
||||
+++ b/content/test/data/accessibility/event/text-changed-expected-auralinux.txt
|
||||
@@ -1,7 +1,7 @@
|
||||
+CHILDREN-CHANGED index:-1 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
+CHILDREN-CHANGED index:-1 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_PARAGRAPH ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_PARAGRAPH ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
-CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
-CHILDREN-CHANGED index:0 CHILD:(role=ROLE_STATIC) role=ROLE_STATIC ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
NAME-CHANGED:Modified Div role=ROLE_STATIC name='Modified Div' ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
NAME-CHANGED:Modified Heading role=ROLE_STATIC name='Modified Heading' ENABLED,SENSITIVE,SHOWING,VISIBLE
|
||||
STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
|
||||
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
|
||||
index 2f150dbbae32f9faae0f8b55a65133fb333aa5e5..f3e9ab6b2bb8ab0c4a688fe84861fe9d7e766a5a 100644
|
||||
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
|
||||
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
|
||||
@@ -2077,7 +2077,7 @@ gint GetIndexInParent(AtkObject* atk_object) {
|
||||
if (!obj)
|
||||
return -1;
|
||||
|
||||
- return obj->GetIndexInParent();
|
||||
+ return obj->GetIndexInParent().value_or(-1);
|
||||
}
|
||||
|
||||
gint AtkGetIndexInParent(AtkObject* atk_object) {
|
||||
@@ -3774,7 +3774,7 @@ void AXPlatformNodeAuraLinux::OnSubtreeCreated() {
|
||||
return;
|
||||
|
||||
g_signal_emit_by_name(GetParent(), "children-changed::add",
|
||||
- GetIndexInParent(), atk_object);
|
||||
+ GetIndexInParent().value_or(-1), atk_object);
|
||||
}
|
||||
|
||||
void AXPlatformNodeAuraLinux::OnSubtreeWillBeDeleted() {
|
||||
@@ -3788,7 +3788,7 @@ void AXPlatformNodeAuraLinux::OnSubtreeWillBeDeleted() {
|
||||
return;
|
||||
|
||||
g_signal_emit_by_name(GetParent(), "children-changed::remove",
|
||||
- GetIndexInParent(), atk_object);
|
||||
+ GetIndexInParent().value_or(-1), atk_object);
|
||||
}
|
||||
|
||||
void AXPlatformNodeAuraLinux::OnParentChanged() {
|
||||
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
|
||||
index 12ec1674a79bdc1f28d5d2b1bb4792b81cd8608f..d946b07612f55efebf85615063411d262552944b 100644
|
||||
--- a/ui/accessibility/platform/ax_platform_node_base.cc
|
||||
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
|
||||
@@ -149,15 +149,16 @@ base::string16 AXPlatformNodeBase::GetNameAsString16() const {
|
||||
return base::UTF8ToUTF16(name);
|
||||
}
|
||||
|
||||
-int AXPlatformNodeBase::GetIndexInParent() {
|
||||
+base::Optional<int> AXPlatformNodeBase::GetIndexInParent() {
|
||||
AXPlatformNodeBase* parent = FromNativeViewAccessible(GetParent());
|
||||
if (!parent)
|
||||
- return 0;
|
||||
+ return base::nullopt;
|
||||
|
||||
int child_count = parent->GetChildCount();
|
||||
if (child_count == 0) {
|
||||
- // |child_count| could be 0 if the node is PlatformIsLeaf.
|
||||
- return 0;
|
||||
+ // |child_count| could be 0 if the parent is IsLeaf.
|
||||
+ DCHECK(parent->IsLeaf());
|
||||
+ return base::nullopt;
|
||||
}
|
||||
|
||||
// Ask the delegate for the index in parent, and return it if it's plausible.
|
||||
@@ -176,7 +177,9 @@ int AXPlatformNodeBase::GetIndexInParent() {
|
||||
if (parent->ChildAtIndex(i) == current)
|
||||
return i;
|
||||
}
|
||||
- return -1;
|
||||
+ NOTREACHED()
|
||||
+ << "Unable to find the child in the list of its parent's children.";
|
||||
+ return base::nullopt;
|
||||
}
|
||||
|
||||
// AXPlatformNode overrides.
|
||||
@@ -1429,12 +1432,8 @@ int32_t AXPlatformNodeBase::GetHypertextOffsetFromChild(
|
||||
// cross-tree traversal is necessary.
|
||||
if (child->IsTextOnlyObject()) {
|
||||
int32_t hypertext_offset = 0;
|
||||
- int32_t index_in_parent = child->GetIndexInParent();
|
||||
- DCHECK_GE(index_in_parent, 0);
|
||||
- DCHECK_LT(index_in_parent, static_cast<int32_t>(GetChildCount()));
|
||||
- for (uint32_t i = 0; i < static_cast<uint32_t>(index_in_parent); ++i) {
|
||||
- auto* sibling = static_cast<AXPlatformNodeBase*>(
|
||||
- FromNativeViewAccessible(ChildAtIndex(i)));
|
||||
+ for (AXPlatformNodeBase* sibling = GetFirstChild(); sibling != child;
|
||||
+ sibling = sibling->GetNextSibling()) {
|
||||
DCHECK(sibling);
|
||||
if (sibling->IsTextOnlyObject()) {
|
||||
hypertext_offset += (int32_t)sibling->GetHypertext().size();
|
||||
@@ -1510,7 +1509,7 @@ int AXPlatformNodeBase::GetHypertextOffsetFromEndpoint(
|
||||
}
|
||||
|
||||
AXPlatformNodeBase* common_parent = this;
|
||||
- int32_t index_in_common_parent = GetIndexInParent();
|
||||
+ base::Optional<int> index_in_common_parent = GetIndexInParent();
|
||||
while (common_parent && !endpoint_object->IsDescendantOf(common_parent)) {
|
||||
index_in_common_parent = common_parent->GetIndexInParent();
|
||||
common_parent = static_cast<AXPlatformNodeBase*>(
|
||||
@@ -1519,7 +1518,6 @@ int AXPlatformNodeBase::GetHypertextOffsetFromEndpoint(
|
||||
if (!common_parent)
|
||||
return -1;
|
||||
|
||||
- DCHECK_GE(index_in_common_parent, 0);
|
||||
DCHECK(!(common_parent->IsTextOnlyObject()));
|
||||
|
||||
// Case 2. Is the selection endpoint inside a descendant of this object?
|
||||
@@ -1547,17 +1545,14 @@ int AXPlatformNodeBase::GetHypertextOffsetFromEndpoint(
|
||||
//
|
||||
// We can safely assume that the endpoint is in another part of the tree or
|
||||
// at common parent, and that this object is a descendant of common parent.
|
||||
- int32_t endpoint_index_in_common_parent = -1;
|
||||
- for (int i = 0; i < common_parent->GetDelegate()->GetChildCount(); ++i) {
|
||||
- auto* child = static_cast<AXPlatformNodeBase*>(FromNativeViewAccessible(
|
||||
- common_parent->GetDelegate()->ChildAtIndex(i)));
|
||||
- DCHECK(child);
|
||||
+ base::Optional<int> endpoint_index_in_common_parent;
|
||||
+ for (AXPlatformNodeBase* child = common_parent->GetFirstChild();
|
||||
+ child != nullptr; child = child->GetNextSibling()) {
|
||||
if (endpoint_object->IsDescendantOf(child)) {
|
||||
endpoint_index_in_common_parent = child->GetIndexInParent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
- DCHECK_GE(endpoint_index_in_common_parent, 0);
|
||||
|
||||
if (endpoint_index_in_common_parent < index_in_common_parent)
|
||||
return 0;
|
||||
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
|
||||
index 0355f281a600979f59a25086fd7400201693bf89..9cb3364d43cb93f5e612a3a076161b4f2e11adaf 100644
|
||||
--- a/ui/accessibility/platform/ax_platform_node_base.h
|
||||
+++ b/ui/accessibility/platform/ax_platform_node_base.h
|
||||
@@ -69,8 +69,9 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
|
||||
std::string GetName() const;
|
||||
base::string16 GetNameAsString16() const;
|
||||
|
||||
- // This returns 0 if there's no parent.
|
||||
- virtual int GetIndexInParent();
|
||||
+ // This returns nullopt if there's no parent, it's unable to find the child in
|
||||
+ // the list of its parent's children, or its parent doesn't have children.
|
||||
+ virtual base::Optional<int> GetIndexInParent();
|
||||
|
||||
// AXPlatformNode.
|
||||
void Destroy() override;
|
||||
diff --git a/ui/accessibility/platform/ax_platform_node_mac.h b/ui/accessibility/platform/ax_platform_node_mac.h
|
||||
index a5624cb789a7b686c1b139d1845127114befd7e2..920f0a0536364d26b99dbe54cf372abaf7d34439 100644
|
||||
--- a/ui/accessibility/platform/ax_platform_node_mac.h
|
||||
+++ b/ui/accessibility/platform/ax_platform_node_mac.h
|
||||
@@ -27,7 +27,6 @@ class AXPlatformNodeMac : public AXPlatformNodeBase {
|
||||
|
||||
// AXPlatformNodeBase.
|
||||
void Destroy() override;
|
||||
- int GetIndexInParent() override;
|
||||
|
||||
protected:
|
||||
void AddAttributeToList(const char* name,
|
||||
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
|
||||
index 0454164364c6a4a1b1c11011603af5978d2b8ef5..bd9e17f1e21bd17a68988ee3c9ab08f2a1d99b2e 100644
|
||||
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
|
||||
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
|
||||
@@ -1257,11 +1257,6 @@ void AXPlatformNodeMac::AnnounceText(const base::string16& text) {
|
||||
[native_node_ AXWindow], false);
|
||||
}
|
||||
|
||||
-int AXPlatformNodeMac::GetIndexInParent() {
|
||||
- // TODO(dmazzoni): implement this. http://crbug.com/396137
|
||||
- return -1;
|
||||
-}
|
||||
-
|
||||
bool IsNameExposedInAXValueForRole(ax::mojom::Role role) {
|
||||
switch (role) {
|
||||
case ax::mojom::Role::kListBoxOption:
|
||||
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
|
||||
index a61d4121d9eeb336e72dbb6a3e2bf884a9b4e799..222c9358ca8e9958d6e89faf8779e706862e1f8c 100644
|
||||
--- a/ui/accessibility/platform/ax_platform_node_win.cc
|
||||
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
|
||||
@@ -1370,10 +1370,11 @@ IFACEMETHODIMP AXPlatformNodeWin::get_attributes(BSTR* attributes) {
|
||||
IFACEMETHODIMP AXPlatformNodeWin::get_indexInParent(LONG* index_in_parent) {
|
||||
WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_INDEX_IN_PARENT);
|
||||
COM_OBJECT_VALIDATE_1_ARG(index_in_parent);
|
||||
- *index_in_parent = GetIndexInParent();
|
||||
- if (*index_in_parent < 0)
|
||||
+ base::Optional<int> index = GetIndexInParent();
|
||||
+ if (!index.has_value())
|
||||
return E_FAIL;
|
||||
|
||||
+ *index_in_parent = index.value();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
|
||||
index 2092d5c263dafa452c4977d53678ad81aa6575e1..8e2cf3d13fd4f936dd7c1e0a21bd245b589857d0 100644
|
||||
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
|
||||
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
|
||||
@@ -1149,8 +1149,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessible2IndexInParent) {
|
||||
ComPtr<IAccessible2> right_iaccessible2 = ToIAccessible2(right_iaccessible);
|
||||
|
||||
LONG index;
|
||||
- EXPECT_EQ(S_OK, root_iaccessible2->get_indexInParent(&index));
|
||||
- EXPECT_EQ(0, index);
|
||||
+ EXPECT_EQ(E_FAIL, root_iaccessible2->get_indexInParent(&index));
|
||||
|
||||
EXPECT_EQ(S_OK, left_iaccessible2->get_indexInParent(&index));
|
||||
EXPECT_EQ(0, index);
|
||||
@@ -0,0 +1,115 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aaron Leventhal <aleventhal@chromium.org>
|
||||
Date: Tue, 21 Jul 2020 19:41:11 +0000
|
||||
Subject: Allow focus to move into an editable combobox's listbox
|
||||
|
||||
In bug 593646 (https://codereview.chromium.org/2024053003/) a rule was
|
||||
added, apparently similar to a rule in WebKit, that prevents
|
||||
aria-activedescendant on an editable combobox field from moving focus
|
||||
into its listbox. However, removing this condition fixes an issue where
|
||||
the first item is not read. It also improves the verbalization,
|
||||
providing the user with the positional info, e.g. "2 of 5".
|
||||
Removing this line also does not seem to break the example attached to
|
||||
bug 593646.
|
||||
|
||||
Bug: 1082865
|
||||
Change-Id: I4250fb152f4b06f3c57b300ebe7ef5549c58d624
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2303789
|
||||
Reviewed-by: Dominic Mazzoni <dmazzoni@chromium.org>
|
||||
Commit-Queue: Aaron Leventhal <aleventhal@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#790502}
|
||||
|
||||
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
|
||||
index 33324bbca5953f6f2f9d829e4c7b5d7daa3f49ea..a27cacfd303706ef3ee637107d2de0c8839bfa20 100644
|
||||
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
|
||||
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
|
||||
@@ -143,11 +143,6 @@ BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus() const {
|
||||
if (!focus)
|
||||
return nullptr;
|
||||
|
||||
- // For editable combo boxes, focus should stay on the combo box so the user
|
||||
- // will not be taken out of the combo box while typing.
|
||||
- if (focus->GetRole() == ax::mojom::Role::kTextFieldWithComboBox)
|
||||
- return focus;
|
||||
-
|
||||
// Otherwise, follow the active descendant.
|
||||
return GetActiveDescendant(focus);
|
||||
}
|
||||
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt b/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt
|
||||
index d28f2235dedeedab23bf23db0475087c3d3ec73e..9dd9803232490edb95c80abcb2f084b8c42ceb7e 100644
|
||||
--- a/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt
|
||||
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-mac.txt
|
||||
@@ -1,7 +1,7 @@
|
||||
AXWebArea
|
||||
++AXGroup
|
||||
++++AXStaticText AXValue='State'
|
||||
-++AXComboBox AXTitle='State' AXAutocompleteValue='list' AXFocused='1'
|
||||
+++AXComboBox AXTitle='State' AXAutocompleteValue='list'
|
||||
++AXList
|
||||
++++AXStaticText AXValue='Alabama'
|
||||
-++++AXStaticText AXValue='Alaska'
|
||||
\ No newline at end of file
|
||||
+++++AXStaticText AXFocused='1' AXValue='Alaska'
|
||||
diff --git a/content/test/data/accessibility/event/aria-combo-box-collapse-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-collapse-expected-mac.txt
|
||||
index 97c3c417f30812abecdde273f130088df86bd4ae..93ef79e311977bb4277842fa5332f6097faba837 100644
|
||||
--- a/content/test/data/accessibility/event/aria-combo-box-collapse-expected-mac.txt
|
||||
+++ b/content/test/data/accessibility/event/aria-combo-box-collapse-expected-mac.txt
|
||||
@@ -1,2 +1,3 @@
|
||||
AXExpandedChanged on AXComboBox
|
||||
-AXSelectedChildrenChanged on AXComboBox
|
||||
+AXFocusedUIElementChanged on AXComboBox
|
||||
+AXSelectedChildrenChanged on AXComboBox
|
||||
\ No newline at end of file
|
||||
diff --git a/content/test/data/accessibility/event/aria-combo-box-delay-add-list-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-delay-add-list-expected-mac.txt
|
||||
index 47ff72a2689baa23600ededfe38d79fd6b5bedcf..f7d4c30f49fe3f3f62132cddf59942181b8127b8 100644
|
||||
--- a/content/test/data/accessibility/event/aria-combo-box-delay-add-list-expected-mac.txt
|
||||
+++ b/content/test/data/accessibility/event/aria-combo-box-delay-add-list-expected-mac.txt
|
||||
@@ -1 +1,2 @@
|
||||
-AXSelectedChildrenChanged on AXComboBox
|
||||
+AXFocusedUIElementChanged on AXStaticText AXValue="Apple"
|
||||
+AXSelectedChildrenChanged on AXComboBox
|
||||
\ No newline at end of file
|
||||
diff --git a/content/test/data/accessibility/event/aria-combo-box-delay-show-list-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-delay-show-list-expected-mac.txt
|
||||
index 47ff72a2689baa23600ededfe38d79fd6b5bedcf..ab757bb0687ff49affcc67076eddd52e18b0eacc 100644
|
||||
--- a/content/test/data/accessibility/event/aria-combo-box-delay-show-list-expected-mac.txt
|
||||
+++ b/content/test/data/accessibility/event/aria-combo-box-delay-show-list-expected-mac.txt
|
||||
@@ -1 +1,2 @@
|
||||
+AXFocusedUIElementChanged on AXStaticText AXValue="Apple"
|
||||
AXSelectedChildrenChanged on AXComboBox
|
||||
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt
|
||||
index 97cdb8b8b67d46e0a952d22765c823bd346aef3a..343feae3d79a48981cc1e27015ba2a93423eedc0 100644
|
||||
--- a/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt
|
||||
+++ b/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt
|
||||
@@ -1,3 +1,4 @@
|
||||
AXExpandedChanged on AXComboBox
|
||||
+AXFocusedUIElementChanged on AXStaticText AXValue="Apple"
|
||||
AXSelectedChildrenChanged on AXComboBox
|
||||
-AXSelectedChildrenChanged on AXList
|
||||
+AXSelectedChildrenChanged on AXList
|
||||
\ No newline at end of file
|
||||
diff --git a/content/test/data/accessibility/event/aria-combo-box-focus-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-focus-expected-mac.txt
|
||||
index 2bfc70f5fecea2c2a5e7268cef641d6d0e7d4a47..ad5e2bf2c8029185c51eecc94cac1dbe7608c99e 100644
|
||||
--- a/content/test/data/accessibility/event/aria-combo-box-focus-expected-mac.txt
|
||||
+++ b/content/test/data/accessibility/event/aria-combo-box-focus-expected-mac.txt
|
||||
@@ -1,3 +1,3 @@
|
||||
-AXFocusedUIElementChanged on AXComboBox
|
||||
-AXSelectedTextChanged on AXComboBox
|
||||
-AXSelectedTextChanged on AXWebArea
|
||||
+AXFocusedUIElementChanged on AXStaticText AXValue="Apple not selected"
|
||||
+AXSelectedTextChanged on AXStaticText AXValue="Apple not selected"
|
||||
+AXSelectedTextChanged on AXWebArea
|
||||
\ No newline at end of file
|
||||
diff --git a/content/test/data/accessibility/event/aria-combo-box-next-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-next-expected-mac.txt
|
||||
index d5f21183c3d0a1c24cb6665194a93c3299dbfd56..9e7d0c0aaeb1c52dc1f1b3afed36f287851b89ff 100644
|
||||
--- a/content/test/data/accessibility/event/aria-combo-box-next-expected-mac.txt
|
||||
+++ b/content/test/data/accessibility/event/aria-combo-box-next-expected-mac.txt
|
||||
@@ -1,5 +1,7 @@
|
||||
+AXFocusedUIElementChanged on AXStaticText AXValue="Orange"
|
||||
AXSelectedChildrenChanged on AXComboBox
|
||||
AXSelectedChildrenChanged on AXList
|
||||
=== Start Continuation ===
|
||||
+AXFocusedUIElementChanged on AXStaticText AXValue="Banana"
|
||||
AXSelectedChildrenChanged on AXComboBox
|
||||
-AXSelectedChildrenChanged on AXList
|
||||
+AXSelectedChildrenChanged on AXList
|
||||
\ No newline at end of file
|
||||
@@ -0,0 +1,41 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Anderson <thomasanderson@chromium.org>
|
||||
Date: Thu, 21 May 2020 01:20:33 +0000
|
||||
Subject: Avoid loading DRI via GBM when GpuMemoryBuffers are disabled
|
||||
|
||||
We haven't yet whitelisted the necessary dri files in the GPU sandbox,
|
||||
which is leading to issues like 1077609 and 1077626. Since GMBs are
|
||||
not yet supported, avoid loading GBM unless
|
||||
--enable-native-gpu-memory-buffers is passed.
|
||||
|
||||
Bug: 1077609, 1077626, 1031269
|
||||
Change-Id: Ic052d2e89330c6558da86a91b77637229808102f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2211120
|
||||
Auto-Submit: Thomas Anderson <thomasanderson@chromium.org>
|
||||
Reviewed-by: Kenneth Russell <kbr@chromium.org>
|
||||
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#770878}
|
||||
|
||||
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
|
||||
index 9b22493abbd81c2f592e75fe32f7ab7efbb281d9..d650cef5e081da9cd00896db7d59f00635f13279 100644
|
||||
--- a/content/gpu/gpu_main.cc
|
||||
+++ b/content/gpu/gpu_main.cc
|
||||
@@ -366,11 +366,13 @@ int GpuMain(const MainFunctionParams& parameters) {
|
||||
#if defined(USE_X11)
|
||||
// ui::GbmDevice() takes >50ms with amdgpu, so kick off
|
||||
// GpuMemoryBufferSupportX11 creation on another thread now.
|
||||
- base::PostTask(
|
||||
- FROM_HERE, base::BindOnce([]() {
|
||||
- SCOPED_UMA_HISTOGRAM_TIMER("Linux.X11.GbmSupportX11CreationTime");
|
||||
- ui::GpuMemoryBufferSupportX11::GetInstance();
|
||||
- }));
|
||||
+ if (gpu_preferences.enable_native_gpu_memory_buffers) {
|
||||
+ base::PostTask(
|
||||
+ FROM_HERE, base::BindOnce([]() {
|
||||
+ SCOPED_UMA_HISTOGRAM_TIMER("Linux.X11.GbmSupportX11CreationTime");
|
||||
+ ui::GpuMemoryBufferSupportX11::GetInstance();
|
||||
+ }));
|
||||
+ }
|
||||
#endif
|
||||
|
||||
if (client)
|
||||
549
patches/chromium/backport_1081874.patch
Normal file
549
patches/chromium/backport_1081874.patch
Normal file
@@ -0,0 +1,549 @@
|
||||
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: fix: data race on NodeChannel destruction
|
||||
|
||||
[1081874] [High] [CVE-2020-6575]: Double free on NodeChannel
|
||||
Backport https://chromium.googlesource.com/chromium/src/+/5e61913985df0cc621bf72f7fa75e76759ffde15.
|
||||
|
||||
diff --git a/mojo/core/BUILD.gn b/mojo/core/BUILD.gn
|
||||
index e6fcb96256f6fdb867a362588547a7829f28efe8..6282eed9158c691a9ca49d1ec9a57cedc4392741 100644
|
||||
--- a/mojo/core/BUILD.gn
|
||||
+++ b/mojo/core/BUILD.gn
|
||||
@@ -18,6 +18,7 @@ component("embedder_internal") {
|
||||
":test_sources",
|
||||
"//mojo:*",
|
||||
"//mojo/core/embedder",
|
||||
+ "//mojo/core/test:test_support",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -57,6 +58,7 @@ template("core_impl_source_set") {
|
||||
"handle_table.h",
|
||||
"invitation_dispatcher.h",
|
||||
"message_pipe_dispatcher.h",
|
||||
+ "node_channel.h",
|
||||
"node_controller.h",
|
||||
"options_validation.h",
|
||||
"platform_handle_dispatcher.h",
|
||||
@@ -86,7 +88,6 @@ template("core_impl_source_set") {
|
||||
"invitation_dispatcher.cc",
|
||||
"message_pipe_dispatcher.cc",
|
||||
"node_channel.cc",
|
||||
- "node_channel.h",
|
||||
"node_controller.cc",
|
||||
"platform_handle_dispatcher.cc",
|
||||
"platform_handle_in_transit.cc",
|
||||
@@ -289,6 +290,7 @@ source_set("test_sources") {
|
||||
"handle_table_unittest.cc",
|
||||
"message_pipe_unittest.cc",
|
||||
"message_unittest.cc",
|
||||
+ "node_channel_unittest.cc",
|
||||
"options_validation_unittest.cc",
|
||||
"platform_handle_dispatcher_unittest.cc",
|
||||
"quota_unittest.cc",
|
||||
@@ -387,6 +389,7 @@ fuzzer_test("mojo_core_node_channel_fuzzer") {
|
||||
deps = [
|
||||
":core_impl_for_fuzzers",
|
||||
"//base",
|
||||
+ "//mojo/core/test:test_support",
|
||||
"//mojo/public/cpp/platform",
|
||||
]
|
||||
}
|
||||
diff --git a/mojo/core/embedder/embedder.cc b/mojo/core/embedder/embedder.cc
|
||||
index ec68e37d09888f672759f79961e3e6e4d18f0c5c..f70c73be4df529cce09cb503dfce28f2a04e7e1a 100644
|
||||
--- a/mojo/core/embedder/embedder.cc
|
||||
+++ b/mojo/core/embedder/embedder.cc
|
||||
@@ -35,7 +35,7 @@ void SetDefaultProcessErrorCallback(ProcessErrorCallback callback) {
|
||||
Core::Get()->SetDefaultProcessErrorCallback(std::move(callback));
|
||||
}
|
||||
|
||||
-scoped_refptr<base::TaskRunner> GetIOTaskRunner() {
|
||||
+scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() {
|
||||
return Core::Get()->GetNodeController()->io_task_runner();
|
||||
}
|
||||
|
||||
diff --git a/mojo/core/embedder/embedder.h b/mojo/core/embedder/embedder.h
|
||||
index 5d65400987728940a296ba2a84d40158f52d6d34..96dc44c0a78e0e6cb6e5aa511bd2fd4f1252cf16 100644
|
||||
--- a/mojo/core/embedder/embedder.h
|
||||
+++ b/mojo/core/embedder/embedder.h
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "base/component_export.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/process/process_handle.h"
|
||||
-#include "base/task_runner.h"
|
||||
+#include "base/single_thread_task_runner.h"
|
||||
#include "build/build_config.h"
|
||||
#include "mojo/core/embedder/configuration.h"
|
||||
|
||||
@@ -42,9 +42,10 @@ void SetDefaultProcessErrorCallback(ProcessErrorCallback callback);
|
||||
|
||||
// Initialialization/shutdown for interprocess communication (IPC) -------------
|
||||
|
||||
-// Retrieves the TaskRunner used for IPC I/O, as set by ScopedIPCSupport.
|
||||
+// Retrieves the SequencedTaskRunner used for IPC I/O, as set by
|
||||
+// ScopedIPCSupport.
|
||||
COMPONENT_EXPORT(MOJO_CORE_EMBEDDER)
|
||||
-scoped_refptr<base::TaskRunner> GetIOTaskRunner();
|
||||
+scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner();
|
||||
|
||||
} // namespace core
|
||||
} // namespace mojo
|
||||
diff --git a/mojo/core/node_channel.cc b/mojo/core/node_channel.cc
|
||||
index e898b044286e019b5e423b941502030ce3094582..061ea1026e95d1b1f80a762ce377aebdd97e1b42 100644
|
||||
--- a/mojo/core/node_channel.cc
|
||||
+++ b/mojo/core/node_channel.cc
|
||||
@@ -228,7 +228,7 @@ void NodeChannel::NotifyBadMessage(const std::string& error) {
|
||||
}
|
||||
|
||||
void NodeChannel::SetRemoteProcessHandle(ScopedProcessHandle process_handle) {
|
||||
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
|
||||
+ DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
|
||||
{
|
||||
base::AutoLock lock(channel_lock_);
|
||||
if (channel_)
|
||||
@@ -253,7 +253,7 @@ ScopedProcessHandle NodeChannel::CloneRemoteProcessHandle() {
|
||||
}
|
||||
|
||||
void NodeChannel::SetRemoteNodeName(const ports::NodeName& name) {
|
||||
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
|
||||
+ DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
|
||||
remote_node_name_ = name;
|
||||
}
|
||||
|
||||
@@ -468,15 +468,15 @@ NodeChannel::NodeChannel(
|
||||
Channel::HandlePolicy channel_handle_policy,
|
||||
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
|
||||
const ProcessErrorCallback& process_error_callback)
|
||||
- : delegate_(delegate),
|
||||
- io_task_runner_(io_task_runner),
|
||||
+ : base::RefCountedDeleteOnSequence<NodeChannel>(io_task_runner),
|
||||
+ delegate_(delegate),
|
||||
process_error_callback_(process_error_callback)
|
||||
#if !defined(OS_NACL_SFI)
|
||||
,
|
||||
channel_(Channel::Create(this,
|
||||
std::move(connection_params),
|
||||
channel_handle_policy,
|
||||
- io_task_runner_))
|
||||
+ std::move(io_task_runner)))
|
||||
#endif
|
||||
{
|
||||
}
|
||||
@@ -499,15 +499,10 @@ void NodeChannel::CreateAndBindLocalBrokerHost(
|
||||
void NodeChannel::OnChannelMessage(const void* payload,
|
||||
size_t payload_size,
|
||||
std::vector<PlatformHandle> handles) {
|
||||
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
|
||||
+ DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
|
||||
|
||||
RequestContext request_context(RequestContext::Source::SYSTEM);
|
||||
|
||||
- // Ensure this NodeChannel stays alive through the extent of this method. The
|
||||
- // delegate may have the only other reference to this object and it may choose
|
||||
- // to drop it here in response to, e.g., a malformed message.
|
||||
- scoped_refptr<NodeChannel> keepalive = this;
|
||||
-
|
||||
if (payload_size <= sizeof(Header)) {
|
||||
delegate_->OnChannelError(remote_node_name_, this);
|
||||
return;
|
||||
@@ -739,7 +734,7 @@ void NodeChannel::OnChannelMessage(const void* payload,
|
||||
}
|
||||
|
||||
void NodeChannel::OnChannelError(Channel::Error error) {
|
||||
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
|
||||
+ DCHECK(owning_task_runner()->RunsTasksInCurrentSequence());
|
||||
|
||||
RequestContext request_context(RequestContext::Source::SYSTEM);
|
||||
|
||||
diff --git a/mojo/core/node_channel.h b/mojo/core/node_channel.h
|
||||
index ea91f927049befa258884b13e7f7966c09518687..04501da0fb6cd36df1b332135282104dd442b41e 100644
|
||||
--- a/mojo/core/node_channel.h
|
||||
+++ b/mojo/core/node_channel.h
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "base/callback.h"
|
||||
#include "base/containers/queue.h"
|
||||
#include "base/macros.h"
|
||||
-#include "base/memory/ref_counted.h"
|
||||
+#include "base/memory/ref_counted_delete_on_sequence.h"
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
@@ -21,13 +21,15 @@
|
||||
#include "mojo/core/embedder/process_error_callback.h"
|
||||
#include "mojo/core/ports/name.h"
|
||||
#include "mojo/core/scoped_process_handle.h"
|
||||
+#include "mojo/core/system_impl_export.h"
|
||||
|
||||
namespace mojo {
|
||||
namespace core {
|
||||
|
||||
// Wraps a Channel to send and receive Node control messages.
|
||||
-class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>,
|
||||
- public Channel::Delegate {
|
||||
+class MOJO_SYSTEM_IMPL_EXPORT NodeChannel
|
||||
+ : public base::RefCountedDeleteOnSequence<NodeChannel>,
|
||||
+ public Channel::Delegate {
|
||||
public:
|
||||
class Delegate {
|
||||
public:
|
||||
@@ -92,8 +94,6 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>,
|
||||
void** data,
|
||||
size_t* num_data_bytes);
|
||||
|
||||
- Channel* channel() const { return channel_.get(); }
|
||||
-
|
||||
// Start receiving messages.
|
||||
void Start();
|
||||
|
||||
@@ -155,7 +155,8 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>,
|
||||
#endif
|
||||
|
||||
private:
|
||||
- friend class base::RefCountedThreadSafe<NodeChannel>;
|
||||
+ friend class base::RefCountedDeleteOnSequence<NodeChannel>;
|
||||
+ friend class base::DeleteHelper<NodeChannel>;
|
||||
|
||||
using PendingMessageQueue = base::queue<Channel::MessagePtr>;
|
||||
using PendingRelayMessageQueue =
|
||||
@@ -181,13 +182,12 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>,
|
||||
void WriteChannelMessage(Channel::MessagePtr message);
|
||||
|
||||
Delegate* const delegate_;
|
||||
- const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
|
||||
const ProcessErrorCallback process_error_callback_;
|
||||
|
||||
base::Lock channel_lock_;
|
||||
- scoped_refptr<Channel> channel_;
|
||||
+ scoped_refptr<Channel> channel_ GUARDED_BY(channel_lock_);
|
||||
|
||||
- // Must only be accessed from |io_task_runner_|'s thread.
|
||||
+ // Must only be accessed from the owning task runner's thread.
|
||||
ports::NodeName remote_node_name_;
|
||||
|
||||
base::Lock remote_process_handle_lock_;
|
||||
diff --git a/mojo/core/node_channel_fuzzer.cc b/mojo/core/node_channel_fuzzer.cc
|
||||
index 99047c000dbe6be90f1d28b4b6817e608f9853a6..54fe757e0dec8aa138af96aa1a0afe596563c26c 100644
|
||||
--- a/mojo/core/node_channel_fuzzer.cc
|
||||
+++ b/mojo/core/node_channel_fuzzer.cc
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "mojo/core/connection_params.h"
|
||||
#include "mojo/core/entrypoints.h"
|
||||
#include "mojo/core/node_channel.h" // nogncheck
|
||||
+#include "mojo/core/test/mock_node_channel_delegate.h"
|
||||
#include "mojo/public/cpp/platform/platform_channel.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
@@ -24,60 +25,6 @@ using mojo::core::Channel;
|
||||
using mojo::core::ConnectionParams;
|
||||
using mojo::core::ports::NodeName;
|
||||
|
||||
-// Implementation of NodeChannel::Delegate which does nothing. All of the
|
||||
-// interesting NodeChannel control message message parsing is done by
|
||||
-// NodeChannel by the time any of the delegate methods are invoked, so there's
|
||||
-// no need for this to do any work.
|
||||
-class FakeNodeChannelDelegate : public mojo::core::NodeChannel::Delegate {
|
||||
- public:
|
||||
- FakeNodeChannelDelegate() = default;
|
||||
- ~FakeNodeChannelDelegate() override = default;
|
||||
-
|
||||
- void OnAcceptInvitee(const NodeName& from_node,
|
||||
- const NodeName& inviter_name,
|
||||
- const NodeName& token) override {}
|
||||
- void OnAcceptInvitation(const NodeName& from_node,
|
||||
- const NodeName& token,
|
||||
- const NodeName& invitee_name) override {}
|
||||
- void OnAddBrokerClient(const NodeName& from_node,
|
||||
- const NodeName& client_name,
|
||||
- base::ProcessHandle process_handle) override {}
|
||||
- void OnBrokerClientAdded(const NodeName& from_node,
|
||||
- const NodeName& client_name,
|
||||
- mojo::PlatformHandle broker_channel) override {}
|
||||
- void OnAcceptBrokerClient(const NodeName& from_node,
|
||||
- const NodeName& broker_name,
|
||||
- mojo::PlatformHandle broker_channel) override {}
|
||||
- void OnEventMessage(const NodeName& from_node,
|
||||
- Channel::MessagePtr message) override {}
|
||||
- void OnRequestPortMerge(
|
||||
- const NodeName& from_node,
|
||||
- const mojo::core::ports::PortName& connector_port_name,
|
||||
- const std::string& token) override {}
|
||||
- void OnRequestIntroduction(const NodeName& from_node,
|
||||
- const NodeName& name) override {}
|
||||
- void OnIntroduce(const NodeName& from_node,
|
||||
- const NodeName& name,
|
||||
- mojo::PlatformHandle channel_handle) override {}
|
||||
- void OnBroadcast(const NodeName& from_node,
|
||||
- Channel::MessagePtr message) override {}
|
||||
-#if defined(OS_WIN)
|
||||
- void OnRelayEventMessage(const NodeName& from_node,
|
||||
- base::ProcessHandle from_process,
|
||||
- const NodeName& destination,
|
||||
- Channel::MessagePtr message) override {}
|
||||
- void OnEventMessageFromRelay(const NodeName& from_node,
|
||||
- const NodeName& source_node,
|
||||
- Channel::MessagePtr message) override {}
|
||||
-#endif
|
||||
- void OnAcceptPeer(const NodeName& from_node,
|
||||
- const NodeName& token,
|
||||
- const NodeName& peer_name,
|
||||
- const mojo::core::ports::PortName& port_name) override {}
|
||||
- void OnChannelError(const NodeName& node,
|
||||
- mojo::core::NodeChannel* channel) override {}
|
||||
-};
|
||||
-
|
||||
// A fake delegate for the sending Channel endpoint. The sending Channel is not
|
||||
// being fuzzed and won't receive any interesting messages, so this doesn't need
|
||||
// to do anything.
|
||||
@@ -109,7 +56,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
|
||||
// used to carry messages between processes.
|
||||
mojo::PlatformChannel channel;
|
||||
|
||||
- FakeNodeChannelDelegate receiver_delegate;
|
||||
+ mojo::core::MockNodeChannelDelegate receiver_delegate;
|
||||
auto receiver = mojo::core::NodeChannel::Create(
|
||||
&receiver_delegate, ConnectionParams(channel.TakeLocalEndpoint()),
|
||||
Channel::HandlePolicy::kRejectHandles,
|
||||
diff --git a/mojo/core/node_channel_unittest.cc b/mojo/core/node_channel_unittest.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..13c46f13fea6342316534a7a843debbf7586108d
|
||||
--- /dev/null
|
||||
+++ b/mojo/core/node_channel_unittest.cc
|
||||
@@ -0,0 +1,72 @@
|
||||
+// Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#include "mojo/core/node_channel.h"
|
||||
+
|
||||
+#include "base/bind_helpers.h"
|
||||
+#include "base/memory/scoped_refptr.h"
|
||||
+#include "base/message_loop/message_pump_type.h"
|
||||
+#include "base/test/task_environment.h"
|
||||
+#include "base/threading/thread.h"
|
||||
+#include "mojo/core/embedder/embedder.h"
|
||||
+#include "mojo/core/test/mock_node_channel_delegate.h"
|
||||
+#include "mojo/public/cpp/platform/platform_channel.h"
|
||||
+#include "mojo/public/cpp/platform/platform_channel_endpoint.h"
|
||||
+#include "testing/gtest/include/gtest/gtest.h"
|
||||
+
|
||||
+namespace mojo {
|
||||
+namespace core {
|
||||
+namespace {
|
||||
+
|
||||
+using NodeChannelTest = testing::Test;
|
||||
+using ports::NodeName;
|
||||
+
|
||||
+scoped_refptr<NodeChannel> CreateNodeChannel(NodeChannel::Delegate* delegate,
|
||||
+ PlatformChannelEndpoint endpoint) {
|
||||
+ return NodeChannel::Create(delegate, ConnectionParams(std::move(endpoint)),
|
||||
+ Channel::HandlePolicy::kAcceptHandles,
|
||||
+ GetIOTaskRunner(), base::NullCallback());
|
||||
+}
|
||||
+
|
||||
+TEST_F(NodeChannelTest, DestructionIsSafe) {
|
||||
+ // Regression test for https://crbug.com/1081874.
|
||||
+ base::test::TaskEnvironment task_environment;
|
||||
+
|
||||
+ PlatformChannel channel;
|
||||
+ MockNodeChannelDelegate local_delegate;
|
||||
+ auto local_channel =
|
||||
+ CreateNodeChannel(&local_delegate, channel.TakeLocalEndpoint());
|
||||
+ local_channel->Start();
|
||||
+ MockNodeChannelDelegate remote_delegate;
|
||||
+ auto remote_channel =
|
||||
+ CreateNodeChannel(&remote_delegate, channel.TakeRemoteEndpoint());
|
||||
+ remote_channel->Start();
|
||||
+
|
||||
+ // Verify end-to-end operation
|
||||
+ const NodeName kRemoteNodeName{123, 456};
|
||||
+ const NodeName kToken{987, 654};
|
||||
+ base::RunLoop loop;
|
||||
+ EXPECT_CALL(local_delegate,
|
||||
+ OnAcceptInvitee(ports::kInvalidNodeName, kRemoteNodeName, kToken))
|
||||
+ .WillRepeatedly([&] { loop.Quit(); });
|
||||
+ remote_channel->AcceptInvitee(kRemoteNodeName, kToken);
|
||||
+ loop.Run();
|
||||
+
|
||||
+ // Now send another message to the local endpoint but tear it down
|
||||
+ // immediately. This will race with the message being received on the IO
|
||||
+ // thread, and although the corresponding delegate call may or may not
|
||||
+ // dispatch as a result, the race should still be memory-safe.
|
||||
+ remote_channel->AcceptInvitee(kRemoteNodeName, kToken);
|
||||
+
|
||||
+ base::RunLoop error_loop;
|
||||
+ EXPECT_CALL(remote_delegate, OnChannelError).WillOnce([&] {
|
||||
+ error_loop.Quit();
|
||||
+ });
|
||||
+ local_channel.reset();
|
||||
+ error_loop.Run();
|
||||
+}
|
||||
+
|
||||
+} // namespace
|
||||
+} // namespace core
|
||||
+} // namespace mojo
|
||||
diff --git a/mojo/core/test/BUILD.gn b/mojo/core/test/BUILD.gn
|
||||
index 1abadfc503d5176d2af1dcecfde153f17488546d..9429c61853059e10ca2430ad79ec8d9a39d90906 100644
|
||||
--- a/mojo/core/test/BUILD.gn
|
||||
+++ b/mojo/core/test/BUILD.gn
|
||||
@@ -7,6 +7,8 @@ import("//third_party/protobuf/proto_library.gni")
|
||||
static_library("test_support") {
|
||||
testonly = true
|
||||
sources = [
|
||||
+ "mock_node_channel_delegate.cc",
|
||||
+ "mock_node_channel_delegate.h",
|
||||
"mojo_test_base.cc",
|
||||
"mojo_test_base.h",
|
||||
"test_utils.h",
|
||||
@@ -27,8 +29,10 @@ static_library("test_support") {
|
||||
public_deps = [
|
||||
"//base",
|
||||
"//base/test:test_support",
|
||||
+ "//mojo/core:embedder_internal",
|
||||
"//mojo/core/embedder",
|
||||
"//mojo/public/cpp/system",
|
||||
+ "//testing/gmock",
|
||||
"//testing/gtest",
|
||||
]
|
||||
}
|
||||
diff --git a/mojo/core/test/mock_node_channel_delegate.cc b/mojo/core/test/mock_node_channel_delegate.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..d257c3e1dc03857f91e94807328a0dc176f332f4
|
||||
--- /dev/null
|
||||
+++ b/mojo/core/test/mock_node_channel_delegate.cc
|
||||
@@ -0,0 +1,15 @@
|
||||
+// Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#include "mojo/core/test/mock_node_channel_delegate.h"
|
||||
+
|
||||
+namespace mojo {
|
||||
+namespace core {
|
||||
+
|
||||
+MockNodeChannelDelegate::MockNodeChannelDelegate() = default;
|
||||
+
|
||||
+MockNodeChannelDelegate::~MockNodeChannelDelegate() = default;
|
||||
+
|
||||
+} // namespace core
|
||||
+} // namespace mojo
|
||||
diff --git a/mojo/core/test/mock_node_channel_delegate.h b/mojo/core/test/mock_node_channel_delegate.h
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..06ca96857b9472682d577a802c68a56a7af0aacd
|
||||
--- /dev/null
|
||||
+++ b/mojo/core/test/mock_node_channel_delegate.h
|
||||
@@ -0,0 +1,114 @@
|
||||
+// Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#ifndef MOJO_CORE_TEST_MOCK_NODE_CHANNEL_DELEGATE_H_
|
||||
+#define MOJO_CORE_TEST_MOCK_NODE_CHANNEL_DELEGATE_H_
|
||||
+
|
||||
+#include "build/build_config.h"
|
||||
+#include "mojo/core/node_channel.h"
|
||||
+#include "testing/gmock/include/gmock/gmock.h"
|
||||
+
|
||||
+namespace mojo {
|
||||
+namespace core {
|
||||
+
|
||||
+// A NodeChannel Delegate implementation which can be used by NodeChannel unit
|
||||
+// tests and fuzzers.
|
||||
+class MockNodeChannelDelegate
|
||||
+ : public testing::NiceMock<NodeChannel::Delegate> {
|
||||
+ public:
|
||||
+ using NodeName = ports::NodeName;
|
||||
+ using PortName = ports::PortName;
|
||||
+
|
||||
+ MockNodeChannelDelegate();
|
||||
+ MockNodeChannelDelegate(const MockNodeChannelDelegate&) = delete;
|
||||
+ MockNodeChannelDelegate& operator=(const MockNodeChannelDelegate&) = delete;
|
||||
+ ~MockNodeChannelDelegate() override;
|
||||
+
|
||||
+ // testing::NiceMock<NodeChannel::Delegate> implementation:
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnAcceptInvitee,
|
||||
+ (const NodeName& from_node,
|
||||
+ const NodeName& inviter_name,
|
||||
+ const NodeName& token),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnAcceptInvitation,
|
||||
+ (const NodeName& from_node,
|
||||
+ const NodeName& token,
|
||||
+ const NodeName& invitee_name),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnAddBrokerClient,
|
||||
+ (const NodeName& from_node,
|
||||
+ const NodeName& client_name,
|
||||
+ base::ProcessHandle process_handle),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnBrokerClientAdded,
|
||||
+ (const NodeName& from_node,
|
||||
+ const NodeName& client_name,
|
||||
+ PlatformHandle broker_channel),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnAcceptBrokerClient,
|
||||
+ (const NodeName& from_node,
|
||||
+ const NodeName& broker_name,
|
||||
+ PlatformHandle broker_channel),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnEventMessage,
|
||||
+ (const NodeName& from_node, Channel::MessagePtr message),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnRequestPortMerge,
|
||||
+ (const NodeName& from_node,
|
||||
+ const PortName& connector_port_name,
|
||||
+ const std::string& token),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnRequestIntroduction,
|
||||
+ (const NodeName& from_node, const NodeName& name),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnIntroduce,
|
||||
+ (const NodeName& from_node,
|
||||
+ const NodeName& name,
|
||||
+ PlatformHandle channel_handle),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnBroadcast,
|
||||
+ (const NodeName& from_node, Channel::MessagePtr message),
|
||||
+ (override));
|
||||
+#if defined(OS_WIN)
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnRelayEventMessage,
|
||||
+ (const NodeName& from_node,
|
||||
+ base::ProcessHandle from_process,
|
||||
+ const NodeName& destination,
|
||||
+ Channel::MessagePtr message),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnEventMessageFromRelay,
|
||||
+ (const NodeName& from_node,
|
||||
+ const NodeName& source_node,
|
||||
+ Channel::MessagePtr message),
|
||||
+ (override));
|
||||
+#endif
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnAcceptPeer,
|
||||
+ (const NodeName& from_node,
|
||||
+ const NodeName& token,
|
||||
+ const NodeName& peer_name,
|
||||
+ const PortName& port_name),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnChannelError,
|
||||
+ (const NodeName& node, NodeChannel* channel),
|
||||
+ (override));
|
||||
+};
|
||||
+
|
||||
+} // namespace core
|
||||
+} // namespace mojo
|
||||
+
|
||||
+#endif // MOJO_CORE_TEST_MOCK_NODE_CHANNEL_DELEGATE_H_
|
||||
23
patches/chromium/backport_1111737.patch
Normal file
23
patches/chromium/backport_1111737.patch
Normal file
@@ -0,0 +1,23 @@
|
||||
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: fix: remove references to launched device before it is reset
|
||||
|
||||
[1111737] [High] [CVE-2020-6576]: Security: OffscreenCanvas - Use After Free in OffscreenCanvasRenderingContext2D::DrawTextInternal()
|
||||
Backport https://chromium.googlesource.com/chromium/src/+/1283160e334f78c5eed4668d95e04f2ed2e2a4a3.
|
||||
|
||||
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
|
||||
index 8a3f1c3be3d804e3879a8a6aa5921bee99b0879e..85ed13b0008c58ba45cc28be36441234d928ed30 100644
|
||||
--- a/content/browser/renderer_host/media/video_capture_controller.cc
|
||||
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
|
||||
@@ -709,6 +709,10 @@ void VideoCaptureController::ReleaseDeviceAsync(base::OnceClosure done_cb) {
|
||||
device_launcher_->AbortLaunch();
|
||||
return;
|
||||
}
|
||||
+ // |buffer_contexts_| contain references to |launched_device_| as observers.
|
||||
+ // Clear those observer references prior to resetting |launced_device_|.
|
||||
+ for (auto& entry : buffer_contexts_)
|
||||
+ entry.set_consumer_feedback_observer(nullptr);
|
||||
launched_device_.reset();
|
||||
}
|
||||
|
||||
149
patches/chromium/backport_1122684.patch
Normal file
149
patches/chromium/backport_1122684.patch
Normal file
@@ -0,0 +1,149 @@
|
||||
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: fix: elide headers in QuicHttp3Logger
|
||||
|
||||
[1122684] [High] [CVE-2020-15959]: Insufficient policy enforcement in networking.
|
||||
Backport https://chromium.googlesource.com/chromium/src/+/c1a7439efcc7626c34d3e38503c974e4c215c489.
|
||||
|
||||
diff --git a/net/quic/quic_http3_logger.cc b/net/quic/quic_http3_logger.cc
|
||||
index 8240046a419ed41fa340c106f45d6e9468ec7bf9..4cebe54bce4df8daaffe1a31f07ffe431a96bff6 100644
|
||||
--- a/net/quic/quic_http3_logger.cc
|
||||
+++ b/net/quic/quic_http3_logger.cc
|
||||
@@ -9,10 +9,13 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
+#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
+#include "net/http/http_log_util.h"
|
||||
#include "net/log/net_log_capture_mode.h"
|
||||
#include "net/log/net_log_event_type.h"
|
||||
#include "net/log/net_log_values.h"
|
||||
+#include "net/spdy/spdy_log_util.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
@@ -64,20 +67,19 @@ base::Value NetLogThreeIntParams(base::StringPiece name1,
|
||||
return dict;
|
||||
}
|
||||
|
||||
-base::Value NetLogHeadersToDict(const quic::QuicHeaderList& headers) {
|
||||
- base::Value dict(base::Value::Type::DICTIONARY);
|
||||
- for (auto header : headers) {
|
||||
- dict.SetStringKey(header.first, header.second);
|
||||
- }
|
||||
- return dict;
|
||||
-}
|
||||
-
|
||||
-base::Value NetLogHeadersToDict(const spdy::SpdyHeaderBlock& headers) {
|
||||
- base::Value dict(base::Value::Type::DICTIONARY);
|
||||
- for (auto header : headers) {
|
||||
- dict.SetStringKey(header.first, header.second);
|
||||
+base::ListValue ElideQuicHeaderListForNetLog(
|
||||
+ const quic::QuicHeaderList& headers,
|
||||
+ NetLogCaptureMode capture_mode) {
|
||||
+ base::ListValue headers_list;
|
||||
+ for (const auto& header : headers) {
|
||||
+ base::StringPiece key = header.first;
|
||||
+ base::StringPiece value = header.second;
|
||||
+ headers_list.Append(NetLogStringValue(
|
||||
+ base::StrCat({key, ": ",
|
||||
+ ElideHeaderValueForNetLog(capture_mode, key.as_string(),
|
||||
+ value.as_string())})));
|
||||
}
|
||||
- return dict;
|
||||
+ return headers_list;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -235,11 +237,13 @@ void QuicHttp3Logger::OnHeadersDecoded(quic::QuicStreamId stream_id,
|
||||
return;
|
||||
}
|
||||
net_log_.AddEvent(
|
||||
- NetLogEventType::HTTP3_HEADERS_DECODED, [stream_id, &headers] {
|
||||
+ NetLogEventType::HTTP3_HEADERS_DECODED,
|
||||
+ [stream_id, &headers](NetLogCaptureMode capture_mode) {
|
||||
base::Value dict(base::Value::Type::DICTIONARY);
|
||||
dict.SetKey("stream_id",
|
||||
NetLogNumberValue(static_cast<uint64_t>(stream_id)));
|
||||
- dict.SetKey("headers", NetLogHeadersToDict(headers));
|
||||
+ dict.SetKey("headers",
|
||||
+ ElideQuicHeaderListForNetLog(headers, capture_mode));
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
@@ -266,16 +270,18 @@ void QuicHttp3Logger::OnPushPromiseDecoded(quic::QuicStreamId stream_id,
|
||||
if (!net_log_.IsCapturing()) {
|
||||
return;
|
||||
}
|
||||
- net_log_.AddEvent(NetLogEventType::HTTP3_PUSH_PROMISE_DECODED, [stream_id,
|
||||
- push_id,
|
||||
- &headers] {
|
||||
- base::Value dict(base::Value::Type::DICTIONARY);
|
||||
- dict.SetKey("stream_id",
|
||||
- NetLogNumberValue(static_cast<uint64_t>(stream_id)));
|
||||
- dict.SetKey("push_id", NetLogNumberValue(static_cast<uint64_t>(push_id)));
|
||||
- dict.SetKey("headers", NetLogHeadersToDict(headers));
|
||||
- return dict;
|
||||
- });
|
||||
+ net_log_.AddEvent(
|
||||
+ NetLogEventType::HTTP3_PUSH_PROMISE_DECODED,
|
||||
+ [stream_id, push_id, &headers](NetLogCaptureMode capture_mode) {
|
||||
+ base::Value dict(base::Value::Type::DICTIONARY);
|
||||
+ dict.SetKey("stream_id",
|
||||
+ NetLogNumberValue(static_cast<uint64_t>(stream_id)));
|
||||
+ dict.SetKey("push_id",
|
||||
+ NetLogNumberValue(static_cast<uint64_t>(push_id)));
|
||||
+ dict.SetKey("headers",
|
||||
+ ElideQuicHeaderListForNetLog(headers, capture_mode));
|
||||
+ return dict;
|
||||
+ });
|
||||
}
|
||||
|
||||
void QuicHttp3Logger::OnUnknownFrameReceived(
|
||||
@@ -344,11 +350,13 @@ void QuicHttp3Logger::OnHeadersFrameSent(
|
||||
return;
|
||||
}
|
||||
net_log_.AddEvent(
|
||||
- NetLogEventType::HTTP3_HEADERS_SENT, [stream_id, &header_block] {
|
||||
+ NetLogEventType::HTTP3_HEADERS_SENT,
|
||||
+ [stream_id, &header_block](NetLogCaptureMode capture_mode) {
|
||||
base::Value dict(base::Value::Type::DICTIONARY);
|
||||
dict.SetKey("stream_id",
|
||||
NetLogNumberValue(static_cast<uint64_t>(stream_id)));
|
||||
- dict.SetKey("headers", NetLogHeadersToDict(header_block));
|
||||
+ dict.SetKey("headers",
|
||||
+ ElideSpdyHeaderBlockForNetLog(header_block, capture_mode));
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
@@ -360,16 +368,18 @@ void QuicHttp3Logger::OnPushPromiseFrameSent(
|
||||
if (!net_log_.IsCapturing()) {
|
||||
return;
|
||||
}
|
||||
- net_log_.AddEvent(NetLogEventType::HTTP3_PUSH_PROMISE_SENT, [stream_id,
|
||||
- push_id,
|
||||
- &header_block] {
|
||||
- base::Value dict(base::Value::Type::DICTIONARY);
|
||||
- dict.SetKey("stream_id",
|
||||
- NetLogNumberValue(static_cast<uint64_t>(stream_id)));
|
||||
- dict.SetKey("push_id", NetLogNumberValue(static_cast<uint64_t>(push_id)));
|
||||
- dict.SetKey("headers", NetLogHeadersToDict(header_block));
|
||||
- return dict;
|
||||
- });
|
||||
+ net_log_.AddEvent(
|
||||
+ NetLogEventType::HTTP3_PUSH_PROMISE_SENT,
|
||||
+ [stream_id, push_id, &header_block](NetLogCaptureMode capture_mode) {
|
||||
+ base::Value dict(base::Value::Type::DICTIONARY);
|
||||
+ dict.SetKey("stream_id",
|
||||
+ NetLogNumberValue(static_cast<uint64_t>(stream_id)));
|
||||
+ dict.SetKey("push_id",
|
||||
+ NetLogNumberValue(static_cast<uint64_t>(push_id)));
|
||||
+ dict.SetKey("headers",
|
||||
+ ElideSpdyHeaderBlockForNetLog(header_block, capture_mode));
|
||||
+ return dict;
|
||||
+ });
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
750
patches/chromium/cherry-pick-0e61c69ebd47.patch
Normal file
750
patches/chromium/cherry-pick-0e61c69ebd47.patch
Normal file
@@ -0,0 +1,750 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ramin Halavati <rhalavati@chromium.org>
|
||||
Date: Wed, 9 Sep 2020 05:10:19 +0000
|
||||
Subject: Reland Run ObfuscatedFileUtilMemoryDelegate entirely on TaskRunner.
|
||||
|
||||
MemoryFileStreamWriter called some ObfuscatedFileUtilMemoryDelegate
|
||||
functions through IO thread while other functions in OFUMD are called
|
||||
on a threadpool sequence. This could result in races in updating
|
||||
directory structure.
|
||||
|
||||
To fix the issue, MemoryFileStreamWriter and MemoryFileStreamReader are
|
||||
updated to call all OFUMD on the default task runner of the file system
|
||||
context.
|
||||
|
||||
This CL was landed in crrev.com/c/2308721 and reverted due to flakiness.
|
||||
The flaky crashes are believed to be because the buffer passed to
|
||||
MemoryFileStreamReader::Read and MemoryFileStreamWrite::Write are not
|
||||
thread safe.
|
||||
|
||||
Patchset1 is a copy of the previous CL and the issue is fixed in the
|
||||
next patchsets.
|
||||
|
||||
Bug: 1100136
|
||||
Change-Id: I619b82c2f4d23a020e9ce7e5e6c16980907b501b
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2398701
|
||||
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
|
||||
Commit-Queue: Ramin Halavati <rhalavati@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#805198}
|
||||
(cherry picked from commit 0e61c69ebd476e5b688f341f8d0bf69fe814c515)
|
||||
|
||||
diff --git a/storage/browser/file_system/file_stream_reader.h b/storage/browser/file_system/file_stream_reader.h
|
||||
index b3cfc7f751be2e8654ba6d5e51849a4f35863d7a..d9ac296c94a4df765417d71b0c80b798ff7c888c 100644
|
||||
--- a/storage/browser/file_system/file_stream_reader.h
|
||||
+++ b/storage/browser/file_system/file_stream_reader.h
|
||||
@@ -60,6 +60,7 @@ class FileStreamReader {
|
||||
// ERR_UPLOAD_FILE_CHANGED error.
|
||||
COMPONENT_EXPORT(STORAGE_BROWSER)
|
||||
static std::unique_ptr<FileStreamReader> CreateForMemoryFile(
|
||||
+ scoped_refptr<base::TaskRunner> task_runner,
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
|
||||
const base::FilePath& file_path,
|
||||
int64_t initial_offset,
|
||||
diff --git a/storage/browser/file_system/file_stream_test_utils.cc b/storage/browser/file_system/file_stream_test_utils.cc
|
||||
index 835a423c9c913ed46e8b68e7a9d3b323b3263695..e66dfc716aae031cc9c6e00d660753d4487cb60a 100644
|
||||
--- a/storage/browser/file_system/file_stream_test_utils.cc
|
||||
+++ b/storage/browser/file_system/file_stream_test_utils.cc
|
||||
@@ -40,6 +40,14 @@ void ReadFromReader(FileStreamReader* reader,
|
||||
}
|
||||
}
|
||||
|
||||
+int64_t GetLengthFromReader(FileStreamReader* reader) {
|
||||
+ EXPECT_NE(nullptr, reader);
|
||||
+ net::TestInt64CompletionCallback callback;
|
||||
+
|
||||
+ int rv = reader->GetLength(callback.callback());
|
||||
+ return callback.GetResult(rv);
|
||||
+}
|
||||
+
|
||||
int WriteStringToWriter(FileStreamWriter* writer, const std::string& data) {
|
||||
scoped_refptr<net::StringIOBuffer> buffer =
|
||||
base::MakeRefCounted<net::StringIOBuffer>(data);
|
||||
diff --git a/storage/browser/file_system/file_stream_test_utils.h b/storage/browser/file_system/file_stream_test_utils.h
|
||||
index 5714f7a1e7a1f6e91628e9f958a1b13324d7ec8e..d6425f15af6309a0891a10ca54cc092b8c1180f1 100644
|
||||
--- a/storage/browser/file_system/file_stream_test_utils.h
|
||||
+++ b/storage/browser/file_system/file_stream_test_utils.h
|
||||
@@ -20,8 +20,12 @@ void ReadFromReader(FileStreamReader* reader,
|
||||
size_t size,
|
||||
int* result);
|
||||
|
||||
-// Writes |data| to |writer|, an intialized FileStreamWriter. Returns net::OK if
|
||||
-// successful, otherwise a net error.
|
||||
+// Returns the length of the file if it could be successfully retrieved,
|
||||
+// otherwise a net error.
|
||||
+int64_t GetLengthFromReader(FileStreamReader* reader);
|
||||
+
|
||||
+// Writes |data| to |writer|, an initialized FileStreamWriter. Returns net::OK
|
||||
+// if successful, otherwise a net error.
|
||||
int WriteStringToWriter(FileStreamWriter* writer, const std::string& data);
|
||||
|
||||
} // namespace storage
|
||||
diff --git a/storage/browser/file_system/file_stream_writer.h b/storage/browser/file_system/file_stream_writer.h
|
||||
index 2ddbecc6587d94d16ad547e3b2249c103621ee7e..11ce21c64d9b0f43d761b45ae7a710be60f03316 100644
|
||||
--- a/storage/browser/file_system/file_stream_writer.h
|
||||
+++ b/storage/browser/file_system/file_stream_writer.h
|
||||
@@ -48,10 +48,9 @@ class FileStreamWriter {
|
||||
|
||||
// Creates a writer for the existing memory file in the path |file_path|
|
||||
// starting from |initial_offset|.
|
||||
- // TODO(mek): Remove or use |open_or_create| field here, as it is not
|
||||
- // currently used. https://crbug.com/1041048
|
||||
COMPONENT_EXPORT(STORAGE_BROWSER)
|
||||
static std::unique_ptr<FileStreamWriter> CreateForMemoryFile(
|
||||
+ scoped_refptr<base::TaskRunner> task_runner,
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
|
||||
const base::FilePath& file_path,
|
||||
int64_t initial_offset);
|
||||
diff --git a/storage/browser/file_system/file_system_file_stream_reader.cc b/storage/browser/file_system/file_system_file_stream_reader.cc
|
||||
index 8a3b85166bf85fb9661953c37b2942eddd5a61e1..9d37b8075199fe5c93c058610bb1f9d99730b526 100644
|
||||
--- a/storage/browser/file_system/file_system_file_stream_reader.cc
|
||||
+++ b/storage/browser/file_system/file_system_file_stream_reader.cc
|
||||
@@ -112,6 +112,7 @@ void FileSystemFileStreamReader::DidCreateSnapshot(
|
||||
file_system_context_->sandbox_delegate()->memory_file_util_delegate();
|
||||
}
|
||||
file_reader_ = FileStreamReader::CreateForMemoryFile(
|
||||
+ file_system_context_->default_file_task_runner(),
|
||||
memory_file_util_delegate, platform_path, initial_offset_,
|
||||
expected_modification_time_);
|
||||
} else {
|
||||
diff --git a/storage/browser/file_system/memory_file_stream_reader.cc b/storage/browser/file_system/memory_file_stream_reader.cc
|
||||
index f5d895c6cc97e883024e854395a24f094c797ed4..0ca229bb8e8e853d96710fc5946e7a5d854c2180 100644
|
||||
--- a/storage/browser/file_system/memory_file_stream_reader.cc
|
||||
+++ b/storage/browser/file_system/memory_file_stream_reader.cc
|
||||
@@ -8,68 +8,114 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
+#include "base/task_runner_util.h"
|
||||
+#include "net/base/io_buffer.h"
|
||||
#include "net/base/net_errors.h"
|
||||
|
||||
namespace storage {
|
||||
|
||||
std::unique_ptr<FileStreamReader> FileStreamReader::CreateForMemoryFile(
|
||||
+ scoped_refptr<base::TaskRunner> task_runner,
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
|
||||
const base::FilePath& file_path,
|
||||
int64_t initial_offset,
|
||||
const base::Time& expected_modification_time) {
|
||||
- return base::WrapUnique(
|
||||
- new MemoryFileStreamReader(std::move(memory_file_util), file_path,
|
||||
- initial_offset, expected_modification_time));
|
||||
+ return base::WrapUnique(new MemoryFileStreamReader(
|
||||
+ std::move(task_runner), std::move(memory_file_util), file_path,
|
||||
+ initial_offset, expected_modification_time));
|
||||
}
|
||||
|
||||
MemoryFileStreamReader::MemoryFileStreamReader(
|
||||
+ scoped_refptr<base::TaskRunner> task_runner,
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
|
||||
const base::FilePath& file_path,
|
||||
int64_t initial_offset,
|
||||
const base::Time& expected_modification_time)
|
||||
: memory_file_util_(std::move(memory_file_util)),
|
||||
+ task_runner_(std::move(task_runner)),
|
||||
file_path_(file_path),
|
||||
expected_modification_time_(expected_modification_time),
|
||||
offset_(initial_offset) {
|
||||
- DCHECK(memory_file_util_);
|
||||
+ DCHECK(memory_file_util_.MaybeValid());
|
||||
}
|
||||
|
||||
MemoryFileStreamReader::~MemoryFileStreamReader() = default;
|
||||
|
||||
int MemoryFileStreamReader::Read(net::IOBuffer* buf,
|
||||
int buf_len,
|
||||
- net::CompletionOnceCallback /*callback*/) {
|
||||
- base::File::Info file_info;
|
||||
- if (memory_file_util_->GetFileInfo(file_path_, &file_info) !=
|
||||
- base::File::FILE_OK) {
|
||||
- return net::ERR_FILE_NOT_FOUND;
|
||||
- }
|
||||
-
|
||||
- if (!FileStreamReader::VerifySnapshotTime(expected_modification_time_,
|
||||
- file_info)) {
|
||||
- return net::ERR_UPLOAD_FILE_CHANGED;
|
||||
- }
|
||||
-
|
||||
- int result = memory_file_util_->ReadFile(file_path_, offset_, buf, buf_len);
|
||||
+ net::CompletionOnceCallback callback) {
|
||||
+ task_runner_->PostTaskAndReplyWithResult(
|
||||
+ FROM_HERE,
|
||||
+ base::BindOnce(
|
||||
+ [](base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> util,
|
||||
+ const base::FilePath& path, base::Time expected_modification_time,
|
||||
+ int64_t offset, scoped_refptr<net::IOBuffer> buf,
|
||||
+ int buf_len) -> int {
|
||||
+ if (!util)
|
||||
+ return net::ERR_FILE_NOT_FOUND;
|
||||
+ base::File::Info file_info;
|
||||
+ if (util->GetFileInfo(path, &file_info) != base::File::FILE_OK)
|
||||
+ return net::ERR_FILE_NOT_FOUND;
|
||||
+
|
||||
+ if (!FileStreamReader::VerifySnapshotTime(
|
||||
+ expected_modification_time, file_info)) {
|
||||
+ return net::ERR_UPLOAD_FILE_CHANGED;
|
||||
+ }
|
||||
+
|
||||
+ return util->ReadFile(path, offset, std::move(buf), buf_len);
|
||||
+ },
|
||||
+ memory_file_util_, file_path_, expected_modification_time_, offset_,
|
||||
+ base::WrapRefCounted(buf), buf_len),
|
||||
+ base::BindOnce(&MemoryFileStreamReader::OnReadCompleted,
|
||||
+ weak_factory_.GetWeakPtr(), std::move(callback)));
|
||||
+
|
||||
+ return net::ERR_IO_PENDING;
|
||||
+}
|
||||
+
|
||||
+void MemoryFileStreamReader::OnReadCompleted(
|
||||
+ net::CompletionOnceCallback callback,
|
||||
+ int result) {
|
||||
if (result > 0)
|
||||
offset_ += result;
|
||||
- return result;
|
||||
+
|
||||
+ std::move(callback).Run(result);
|
||||
}
|
||||
|
||||
int64_t MemoryFileStreamReader::GetLength(
|
||||
- net::Int64CompletionOnceCallback /*callback*/) {
|
||||
- base::File::Info file_info;
|
||||
- if (memory_file_util_->GetFileInfo(file_path_, &file_info) !=
|
||||
- base::File::FILE_OK) {
|
||||
- return net::ERR_FILE_NOT_FOUND;
|
||||
- }
|
||||
-
|
||||
- if (!FileStreamReader::VerifySnapshotTime(expected_modification_time_,
|
||||
- file_info)) {
|
||||
- return net::ERR_UPLOAD_FILE_CHANGED;
|
||||
- }
|
||||
-
|
||||
- return file_info.size;
|
||||
+ net::Int64CompletionOnceCallback callback) {
|
||||
+ task_runner_->PostTaskAndReplyWithResult(
|
||||
+ FROM_HERE,
|
||||
+ base::BindOnce(
|
||||
+ [](base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> util,
|
||||
+ const base::FilePath& path,
|
||||
+ base::Time expected_modification_time) -> int64_t {
|
||||
+ if (!util)
|
||||
+ return net::ERR_FILE_NOT_FOUND;
|
||||
+ base::File::Info file_info;
|
||||
+ if (util->GetFileInfo(path, &file_info) != base::File::FILE_OK) {
|
||||
+ return net::ERR_FILE_NOT_FOUND;
|
||||
+ }
|
||||
+
|
||||
+ if (!FileStreamReader::VerifySnapshotTime(
|
||||
+ expected_modification_time, file_info)) {
|
||||
+ return net::ERR_UPLOAD_FILE_CHANGED;
|
||||
+ }
|
||||
+
|
||||
+ return file_info.size;
|
||||
+ },
|
||||
+ memory_file_util_, file_path_, expected_modification_time_),
|
||||
+ // |callback| is not directly used to make sure that it is not called if
|
||||
+ // stream is deleted while this function is in flight.
|
||||
+ base::BindOnce(&MemoryFileStreamReader::OnGetLengthCompleted,
|
||||
+ weak_factory_.GetWeakPtr(), std::move(callback)));
|
||||
+
|
||||
+ return net::ERR_IO_PENDING;
|
||||
+}
|
||||
+
|
||||
+void MemoryFileStreamReader::OnGetLengthCompleted(
|
||||
+ net::Int64CompletionOnceCallback callback,
|
||||
+ int64_t result) {
|
||||
+ std::move(callback).Run(result);
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
diff --git a/storage/browser/file_system/memory_file_stream_reader.h b/storage/browser/file_system/memory_file_stream_reader.h
|
||||
index 909db6b1178bc329af5e4694538045bba243310b..4f05d450522613e668549e59d58c36552650773e 100644
|
||||
--- a/storage/browser/file_system/memory_file_stream_reader.h
|
||||
+++ b/storage/browser/file_system/memory_file_stream_reader.h
|
||||
@@ -32,17 +32,25 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) MemoryFileStreamReader
|
||||
friend class FileStreamReader;
|
||||
|
||||
MemoryFileStreamReader(
|
||||
+ scoped_refptr<base::TaskRunner> task_runner,
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
|
||||
const base::FilePath& file_path,
|
||||
int64_t initial_offset,
|
||||
const base::Time& expected_modification_time);
|
||||
|
||||
+ void OnReadCompleted(net::CompletionOnceCallback callback, int result);
|
||||
+ void OnGetLengthCompleted(net::Int64CompletionOnceCallback callback,
|
||||
+ int64_t result);
|
||||
+
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util_;
|
||||
|
||||
+ const scoped_refptr<base::TaskRunner> task_runner_;
|
||||
const base::FilePath file_path_;
|
||||
const base::Time expected_modification_time_;
|
||||
int64_t offset_;
|
||||
|
||||
+ base::WeakPtrFactory<MemoryFileStreamReader> weak_factory_{this};
|
||||
+
|
||||
DISALLOW_COPY_AND_ASSIGN(MemoryFileStreamReader);
|
||||
};
|
||||
|
||||
diff --git a/storage/browser/file_system/memory_file_stream_reader_unittest.cc b/storage/browser/file_system/memory_file_stream_reader_unittest.cc
|
||||
index 7cbaf6e06f82e792a52d549f963a0044a0a5fbd5..99bcfcbeb7e5dcdced47be3df1820f518558a78b 100644
|
||||
--- a/storage/browser/file_system/memory_file_stream_reader_unittest.cc
|
||||
+++ b/storage/browser/file_system/memory_file_stream_reader_unittest.cc
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/files/scoped_temp_dir.h"
|
||||
#include "base/macros.h"
|
||||
+#include "base/test/task_environment.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "storage/browser/file_system/file_stream_reader.h"
|
||||
@@ -62,9 +63,9 @@ class MemoryFileStreamReaderTest : public testing::Test {
|
||||
const base::FilePath& path,
|
||||
int64_t initial_offset,
|
||||
const base::Time& expected_modification_time) {
|
||||
- return FileStreamReader::CreateForMemoryFile(file_util_->GetWeakPtr(), path,
|
||||
- initial_offset,
|
||||
- expected_modification_time);
|
||||
+ return FileStreamReader::CreateForMemoryFile(
|
||||
+ base::ThreadTaskRunnerHandle::Get(), file_util_->GetWeakPtr(), path,
|
||||
+ initial_offset, expected_modification_time);
|
||||
}
|
||||
|
||||
void TouchTestFile(base::TimeDelta delta) {
|
||||
@@ -83,6 +84,7 @@ class MemoryFileStreamReaderTest : public testing::Test {
|
||||
}
|
||||
|
||||
private:
|
||||
+ base::test::TaskEnvironment task_environment_;
|
||||
base::ScopedTempDir file_system_directory_;
|
||||
std::unique_ptr<ObfuscatedFileUtilMemoryDelegate> file_util_;
|
||||
base::Time test_file_modification_time_;
|
||||
@@ -113,14 +115,14 @@ TEST_F(MemoryFileStreamReaderTest, Empty) {
|
||||
ASSERT_EQ(net::OK, result);
|
||||
ASSERT_EQ(0U, data.size());
|
||||
|
||||
- int64_t length_result = reader->GetLength(base::DoNothing());
|
||||
+ int64_t length_result = GetLengthFromReader(reader.get());
|
||||
ASSERT_EQ(0, length_result);
|
||||
}
|
||||
|
||||
TEST_F(MemoryFileStreamReaderTest, GetLengthNormal) {
|
||||
std::unique_ptr<FileStreamReader> reader(
|
||||
CreateFileReader(test_path(), 0, test_file_modification_time()));
|
||||
- int64_t result = reader->GetLength(base::DoNothing());
|
||||
+ int64_t result = GetLengthFromReader(reader.get());
|
||||
ASSERT_EQ(kTestDataSize, result);
|
||||
}
|
||||
|
||||
@@ -131,7 +133,7 @@ TEST_F(MemoryFileStreamReaderTest, GetLengthAfterModified) {
|
||||
|
||||
std::unique_ptr<FileStreamReader> reader(
|
||||
CreateFileReader(test_path(), 0, test_file_modification_time()));
|
||||
- int64_t result = reader->GetLength(base::DoNothing());
|
||||
+ int64_t result = GetLengthFromReader(reader.get());
|
||||
ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result);
|
||||
}
|
||||
|
||||
@@ -142,14 +144,14 @@ TEST_F(MemoryFileStreamReaderTest, GetLengthAfterModifiedWithNoExpectedTime) {
|
||||
|
||||
std::unique_ptr<FileStreamReader> reader(
|
||||
CreateFileReader(test_path(), 0, base::Time()));
|
||||
- int64_t result = reader->GetLength(base::DoNothing());
|
||||
+ int64_t result = GetLengthFromReader(reader.get());
|
||||
ASSERT_EQ(kTestDataSize, result);
|
||||
}
|
||||
|
||||
TEST_F(MemoryFileStreamReaderTest, GetLengthWithOffset) {
|
||||
std::unique_ptr<FileStreamReader> reader(
|
||||
CreateFileReader(test_path(), 3, base::Time()));
|
||||
- int64_t result = reader->GetLength(base::DoNothing());
|
||||
+ int64_t result = GetLengthFromReader(reader.get());
|
||||
// Initial offset does not affect the result of GetLength.
|
||||
ASSERT_EQ(kTestDataSize, result);
|
||||
}
|
||||
diff --git a/storage/browser/file_system/memory_file_stream_writer.cc b/storage/browser/file_system/memory_file_stream_writer.cc
|
||||
index 9c421145866cd4425f752e343abcfca6260178a2..b36c4b5e4bbb5d7280fba11ab73e8c4a4d39c088 100644
|
||||
--- a/storage/browser/file_system/memory_file_stream_writer.cc
|
||||
+++ b/storage/browser/file_system/memory_file_stream_writer.cc
|
||||
@@ -8,43 +8,68 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
+#include "base/task_runner_util.h"
|
||||
+#include "net/base/io_buffer.h"
|
||||
#include "net/base/net_errors.h"
|
||||
|
||||
namespace storage {
|
||||
|
||||
std::unique_ptr<FileStreamWriter> FileStreamWriter::CreateForMemoryFile(
|
||||
+ scoped_refptr<base::TaskRunner> task_runner,
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
|
||||
const base::FilePath& file_path,
|
||||
int64_t initial_offset) {
|
||||
return base::WrapUnique(new MemoryFileStreamWriter(
|
||||
- std::move(memory_file_util), file_path, initial_offset));
|
||||
+ std::move(task_runner), std::move(memory_file_util), file_path,
|
||||
+ initial_offset));
|
||||
}
|
||||
|
||||
MemoryFileStreamWriter::MemoryFileStreamWriter(
|
||||
+ scoped_refptr<base::TaskRunner> task_runner,
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
|
||||
const base::FilePath& file_path,
|
||||
int64_t initial_offset)
|
||||
: memory_file_util_(std::move(memory_file_util)),
|
||||
+ task_runner_(std::move(task_runner)),
|
||||
file_path_(file_path),
|
||||
offset_(initial_offset) {
|
||||
- DCHECK(memory_file_util_);
|
||||
+ DCHECK(memory_file_util_.MaybeValid());
|
||||
}
|
||||
|
||||
MemoryFileStreamWriter::~MemoryFileStreamWriter() = default;
|
||||
|
||||
int MemoryFileStreamWriter::Write(net::IOBuffer* buf,
|
||||
int buf_len,
|
||||
- net::CompletionOnceCallback /*callback*/) {
|
||||
- base::File::Info file_info;
|
||||
- if (memory_file_util_->GetFileInfo(file_path_, &file_info) !=
|
||||
- base::File::FILE_OK) {
|
||||
- return net::ERR_FILE_NOT_FOUND;
|
||||
- }
|
||||
-
|
||||
- int result = memory_file_util_->WriteFile(file_path_, offset_, buf, buf_len);
|
||||
+ net::CompletionOnceCallback callback) {
|
||||
+ task_runner_->PostTaskAndReplyWithResult(
|
||||
+ FROM_HERE,
|
||||
+ base::BindOnce(
|
||||
+ [](base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> util,
|
||||
+ const base::FilePath& path, int64_t offset,
|
||||
+ scoped_refptr<net::IOBuffer> buf, int buf_len) -> int {
|
||||
+ if (!util)
|
||||
+ return net::ERR_FILE_NOT_FOUND;
|
||||
+ base::File::Info file_info;
|
||||
+ if (util->GetFileInfo(path, &file_info) != base::File::FILE_OK)
|
||||
+ return net::ERR_FILE_NOT_FOUND;
|
||||
+
|
||||
+ return util->WriteFile(path, offset, std::move(buf), buf_len);
|
||||
+ },
|
||||
+ memory_file_util_, file_path_, offset_, base::WrapRefCounted(buf),
|
||||
+ buf_len),
|
||||
+ base::BindOnce(&MemoryFileStreamWriter::OnWriteCompleted,
|
||||
+ weak_factory_.GetWeakPtr(), std::move(callback)));
|
||||
+
|
||||
+ return net::ERR_IO_PENDING;
|
||||
+}
|
||||
+
|
||||
+void MemoryFileStreamWriter::OnWriteCompleted(
|
||||
+ net::CompletionOnceCallback callback,
|
||||
+ int result) {
|
||||
if (result > 0)
|
||||
offset_ += result;
|
||||
- return result;
|
||||
+
|
||||
+ std::move(callback).Run(result);
|
||||
}
|
||||
|
||||
int MemoryFileStreamWriter::Cancel(net::CompletionOnceCallback /*callback*/) {
|
||||
diff --git a/storage/browser/file_system/memory_file_stream_writer.h b/storage/browser/file_system/memory_file_stream_writer.h
|
||||
index fe1c9d17932e2c4398295bc0ed6359f86281be91..74f6213f0f80b619083779184d04a62c0f983835 100644
|
||||
--- a/storage/browser/file_system/memory_file_stream_writer.h
|
||||
+++ b/storage/browser/file_system/memory_file_stream_writer.h
|
||||
@@ -30,15 +30,21 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) MemoryFileStreamWriter
|
||||
private:
|
||||
friend class FileStreamWriter;
|
||||
MemoryFileStreamWriter(
|
||||
+ scoped_refptr<base::TaskRunner> task_runner,
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util,
|
||||
const base::FilePath& file_path,
|
||||
int64_t initial_offset);
|
||||
|
||||
+ void OnWriteCompleted(net::CompletionOnceCallback callback, int result);
|
||||
+
|
||||
base::WeakPtr<ObfuscatedFileUtilMemoryDelegate> memory_file_util_;
|
||||
|
||||
+ const scoped_refptr<base::TaskRunner> task_runner_;
|
||||
const base::FilePath file_path_;
|
||||
int64_t offset_;
|
||||
|
||||
+ base::WeakPtrFactory<MemoryFileStreamWriter> weak_factory_{this};
|
||||
+
|
||||
DISALLOW_COPY_AND_ASSIGN(MemoryFileStreamWriter);
|
||||
};
|
||||
|
||||
diff --git a/storage/browser/file_system/memory_file_stream_writer_unittest.cc b/storage/browser/file_system/memory_file_stream_writer_unittest.cc
|
||||
index 7fcda3dfd5e533e8d501a5b56ae5b2abd9486f2f..44342a33c66f21eed35287a4bb9b94f82de21521 100644
|
||||
--- a/storage/browser/file_system/memory_file_stream_writer_unittest.cc
|
||||
+++ b/storage/browser/file_system/memory_file_stream_writer_unittest.cc
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/files/scoped_temp_dir.h"
|
||||
+#include "base/test/task_environment.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "storage/browser/file_system/file_stream_test_utils.h"
|
||||
@@ -59,11 +60,13 @@ class MemoryFileStreamWriterTest : public testing::Test {
|
||||
|
||||
std::unique_ptr<FileStreamWriter> CreateWriter(const base::FilePath& path,
|
||||
int64_t offset) {
|
||||
- return FileStreamWriter::CreateForMemoryFile(file_util_->GetWeakPtr(), path,
|
||||
- offset);
|
||||
+ return FileStreamWriter::CreateForMemoryFile(
|
||||
+ base::ThreadTaskRunnerHandle::Get(), file_util_->GetWeakPtr(), path,
|
||||
+ offset);
|
||||
}
|
||||
|
||||
private:
|
||||
+ base::test::TaskEnvironment task_environment_;
|
||||
base::ScopedTempDir file_system_directory_;
|
||||
std::unique_ptr<ObfuscatedFileUtilMemoryDelegate> file_util_;
|
||||
};
|
||||
diff --git a/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc b/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
|
||||
index 5919d1f1c2c4c80fd73cfbb0586af0e505d43152..3a3d8563e3c91d3b9ca15a980a709a8fd96e8c6c 100644
|
||||
--- a/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
|
||||
+++ b/storage/browser/file_system/obfuscated_file_util_memory_delegate.cc
|
||||
@@ -56,13 +56,17 @@ struct ObfuscatedFileUtilMemoryDelegate::DecomposedPath {
|
||||
ObfuscatedFileUtilMemoryDelegate::ObfuscatedFileUtilMemoryDelegate(
|
||||
const base::FilePath& file_system_directory)
|
||||
: root_(std::make_unique<Entry>(Entry::kDirectory)) {
|
||||
+ DETACH_FROM_SEQUENCE(sequence_checker_);
|
||||
file_system_directory.GetComponents(&root_path_components_);
|
||||
}
|
||||
|
||||
-ObfuscatedFileUtilMemoryDelegate::~ObfuscatedFileUtilMemoryDelegate() = default;
|
||||
+ObfuscatedFileUtilMemoryDelegate::~ObfuscatedFileUtilMemoryDelegate() {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
+}
|
||||
|
||||
base::Optional<ObfuscatedFileUtilMemoryDelegate::DecomposedPath>
|
||||
ObfuscatedFileUtilMemoryDelegate::ParsePath(const base::FilePath& path) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
DecomposedPath dp;
|
||||
|
||||
path.GetComponents(&dp.components);
|
||||
@@ -118,6 +122,7 @@ ObfuscatedFileUtilMemoryDelegate::ParsePath(const base::FilePath& path) {
|
||||
|
||||
bool ObfuscatedFileUtilMemoryDelegate::DirectoryExists(
|
||||
const base::FilePath& path) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
return dp && dp->entry && dp->entry->type == Entry::kDirectory;
|
||||
}
|
||||
@@ -126,6 +131,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CreateDirectory(
|
||||
const base::FilePath& path,
|
||||
bool exclusive,
|
||||
bool recursive) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
if (!dp)
|
||||
return base::File::FILE_ERROR_NOT_FOUND;
|
||||
@@ -169,6 +175,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CreateDirectory(
|
||||
bool ObfuscatedFileUtilMemoryDelegate::DeleteFileOrDirectory(
|
||||
const base::FilePath& path,
|
||||
bool recursive) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
if (!dp)
|
||||
return false;
|
||||
@@ -185,11 +192,13 @@ bool ObfuscatedFileUtilMemoryDelegate::DeleteFileOrDirectory(
|
||||
}
|
||||
|
||||
bool ObfuscatedFileUtilMemoryDelegate::IsLink(const base::FilePath& file_path) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
// In-memory file system does not support links.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObfuscatedFileUtilMemoryDelegate::PathExists(const base::FilePath& path) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
return dp && dp->entry;
|
||||
}
|
||||
@@ -197,6 +206,7 @@ bool ObfuscatedFileUtilMemoryDelegate::PathExists(const base::FilePath& path) {
|
||||
base::File ObfuscatedFileUtilMemoryDelegate::CreateOrOpen(
|
||||
const base::FilePath& path,
|
||||
int file_flags) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
// TODO:(https://crbug.com/936722): Once the output of this function is
|
||||
// changed to base::File::Error, it can use CreateOrOpenInternal to perform
|
||||
// the task and return the result.
|
||||
@@ -206,6 +216,7 @@ base::File ObfuscatedFileUtilMemoryDelegate::CreateOrOpen(
|
||||
void ObfuscatedFileUtilMemoryDelegate::CreateOrOpenInternal(
|
||||
const DecomposedPath& dp,
|
||||
int file_flags) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (!dp.entry) {
|
||||
dp.parent->directory_content.emplace(dp.components.back(), Entry::kFile);
|
||||
return;
|
||||
@@ -221,6 +232,7 @@ void ObfuscatedFileUtilMemoryDelegate::CreateOrOpenInternal(
|
||||
|
||||
base::File::Error ObfuscatedFileUtilMemoryDelegate::DeleteFile(
|
||||
const base::FilePath& path) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
if (!dp || !dp->entry)
|
||||
return base::File::FILE_ERROR_NOT_FOUND;
|
||||
@@ -235,6 +247,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::DeleteFile(
|
||||
base::File::Error ObfuscatedFileUtilMemoryDelegate::EnsureFileExists(
|
||||
const base::FilePath& path,
|
||||
bool* created) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
*created = false;
|
||||
if (!dp || !dp->parent)
|
||||
@@ -253,6 +266,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::EnsureFileExists(
|
||||
base::File::Error ObfuscatedFileUtilMemoryDelegate::GetFileInfo(
|
||||
const base::FilePath& path,
|
||||
base::File::Info* file_info) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
if (!dp || !dp->entry)
|
||||
return base::File::FILE_ERROR_NOT_FOUND;
|
||||
@@ -272,6 +286,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::Touch(
|
||||
const base::FilePath& path,
|
||||
const base::Time& last_access_time,
|
||||
const base::Time& last_modified_time) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
if (!dp || !dp->entry)
|
||||
return base::File::FILE_ERROR_FAILED;
|
||||
@@ -285,6 +300,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::Touch(
|
||||
base::File::Error ObfuscatedFileUtilMemoryDelegate::Truncate(
|
||||
const base::FilePath& path,
|
||||
int64_t length) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
if (!dp || !dp->entry || dp->entry->type != Entry::kFile)
|
||||
return base::File::FILE_ERROR_NOT_FOUND;
|
||||
@@ -297,6 +313,7 @@ NativeFileUtil::CopyOrMoveMode
|
||||
ObfuscatedFileUtilMemoryDelegate::CopyOrMoveModeForDestination(
|
||||
const FileSystemURL& /*dest_url*/,
|
||||
bool copy) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
return copy ? NativeFileUtil::CopyOrMoveMode::COPY_SYNC
|
||||
: NativeFileUtil::CopyOrMoveMode::MOVE;
|
||||
}
|
||||
@@ -306,6 +323,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFile(
|
||||
const base::FilePath& dest_path,
|
||||
FileSystemOperation::CopyOrMoveOption option,
|
||||
NativeFileUtil::CopyOrMoveMode mode) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> src_dp = ParsePath(src_path);
|
||||
base::Optional<DecomposedPath> dest_dp = ParsePath(dest_path);
|
||||
|
||||
@@ -361,6 +379,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFile(
|
||||
bool ObfuscatedFileUtilMemoryDelegate::MoveDirectoryInternal(
|
||||
const DecomposedPath& src_dp,
|
||||
const DecomposedPath& dest_dp) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
DCHECK(src_dp.entry->type == Entry::kDirectory);
|
||||
if (!dest_dp.entry) {
|
||||
dest_dp.parent->directory_content.insert(
|
||||
@@ -379,6 +398,7 @@ bool ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFileInternal(
|
||||
const DecomposedPath& src_dp,
|
||||
const DecomposedPath& dest_dp,
|
||||
bool move) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
DCHECK(src_dp.entry->type == Entry::kFile);
|
||||
if (dest_dp.entry)
|
||||
dest_dp.parent->directory_content.erase(dest_dp.components.back());
|
||||
@@ -404,6 +424,7 @@ bool ObfuscatedFileUtilMemoryDelegate::CopyOrMoveFileInternal(
|
||||
|
||||
size_t ObfuscatedFileUtilMemoryDelegate::ComputeDirectorySize(
|
||||
const base::FilePath& path) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
if (!dp || !dp->entry || dp->entry->type != Entry::kDirectory)
|
||||
return 0;
|
||||
@@ -427,8 +448,9 @@ size_t ObfuscatedFileUtilMemoryDelegate::ComputeDirectorySize(
|
||||
|
||||
int ObfuscatedFileUtilMemoryDelegate::ReadFile(const base::FilePath& path,
|
||||
int64_t offset,
|
||||
- net::IOBuffer* buf,
|
||||
+ scoped_refptr<net::IOBuffer> buf,
|
||||
int buf_len) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
if (!dp || dp->entry->type != Entry::kFile)
|
||||
return net::ERR_FILE_NOT_FOUND;
|
||||
@@ -445,13 +467,15 @@ int ObfuscatedFileUtilMemoryDelegate::ReadFile(const base::FilePath& path,
|
||||
return buf_len;
|
||||
}
|
||||
|
||||
-int ObfuscatedFileUtilMemoryDelegate::WriteFile(const base::FilePath& path,
|
||||
- int64_t offset,
|
||||
- net::IOBuffer* buf,
|
||||
- int buf_len) {
|
||||
+int ObfuscatedFileUtilMemoryDelegate::WriteFile(
|
||||
+ const base::FilePath& path,
|
||||
+ int64_t offset,
|
||||
+ scoped_refptr<net::IOBuffer> buf,
|
||||
+ int buf_len) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dp = ParsePath(path);
|
||||
|
||||
- if (!dp || dp->entry->type != Entry::kFile)
|
||||
+ if (!dp || !dp->entry || dp->entry->type != Entry::kFile)
|
||||
return net::ERR_FILE_NOT_FOUND;
|
||||
|
||||
size_t offset_u = static_cast<size_t>(offset);
|
||||
@@ -479,6 +503,7 @@ int ObfuscatedFileUtilMemoryDelegate::WriteFile(const base::FilePath& path,
|
||||
base::File::Error ObfuscatedFileUtilMemoryDelegate::CreateFileForTesting(
|
||||
const base::FilePath& path,
|
||||
base::span<const char> content) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
bool created;
|
||||
base::File::Error result = EnsureFileExists(path, &created);
|
||||
if (result != base::File::FILE_OK)
|
||||
@@ -498,6 +523,7 @@ base::File::Error ObfuscatedFileUtilMemoryDelegate::CopyInForeignFile(
|
||||
const base::FilePath& dest_path,
|
||||
FileSystemOperation::CopyOrMoveOption /* option */,
|
||||
NativeFileUtil::CopyOrMoveMode /* mode */) {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
base::Optional<DecomposedPath> dest_dp = ParsePath(dest_path);
|
||||
|
||||
if (!dest_dp || !dest_dp->parent)
|
||||
diff --git a/storage/browser/file_system/obfuscated_file_util_memory_delegate.h b/storage/browser/file_system/obfuscated_file_util_memory_delegate.h
|
||||
index 4dd25b48affa901251ec4ec54dc6221a60626d19..d1240511303860e67603543e5795c893ef0db482 100644
|
||||
--- a/storage/browser/file_system/obfuscated_file_util_memory_delegate.h
|
||||
+++ b/storage/browser/file_system/obfuscated_file_util_memory_delegate.h
|
||||
@@ -88,7 +88,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) ObfuscatedFileUtilMemoryDelegate
|
||||
// bytes are returned. Otherwise a net::Error value is returned.
|
||||
int ReadFile(const base::FilePath& path,
|
||||
int64_t offset,
|
||||
- net::IOBuffer* buf,
|
||||
+ scoped_refptr<net::IOBuffer> buf,
|
||||
int buf_len);
|
||||
|
||||
// Writes |buf_len| bytes to the file at |path|, starting from |offset|.
|
||||
@@ -96,7 +96,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) ObfuscatedFileUtilMemoryDelegate
|
||||
// net::Error value is returned.
|
||||
int WriteFile(const base::FilePath& path,
|
||||
int64_t offset,
|
||||
- net::IOBuffer* buf,
|
||||
+ scoped_refptr<net::IOBuffer> buf,
|
||||
int buf_len);
|
||||
|
||||
base::File::Error CreateFileForTesting(const base::FilePath& path,
|
||||
@@ -126,6 +126,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) ObfuscatedFileUtilMemoryDelegate
|
||||
const DecomposedPath& dest_dp,
|
||||
bool move);
|
||||
|
||||
+ SEQUENCE_CHECKER(sequence_checker_);
|
||||
+
|
||||
// The root of the directory tree.
|
||||
std::unique_ptr<Entry> root_;
|
||||
|
||||
diff --git a/storage/browser/file_system/sandbox_file_stream_writer.cc b/storage/browser/file_system/sandbox_file_stream_writer.cc
|
||||
index 21495aee42684fcbe7c79fa5d26ad4f2006da875..d344e8ae71e254c2fc758e6a8f6b219d4b145805 100644
|
||||
--- a/storage/browser/file_system/sandbox_file_stream_writer.cc
|
||||
+++ b/storage/browser/file_system/sandbox_file_stream_writer.cc
|
||||
@@ -155,6 +155,7 @@ void SandboxFileStreamWriter::DidCreateSnapshotFile(
|
||||
file_system_context_->sandbox_delegate()->memory_file_util_delegate();
|
||||
}
|
||||
file_writer_ = FileStreamWriter::CreateForMemoryFile(
|
||||
+ file_system_context_->default_file_task_runner(),
|
||||
memory_file_util_delegate, platform_path, initial_offset_);
|
||||
|
||||
} else {
|
||||
64
patches/chromium/cherry-pick-138b748dd0a4.patch
Normal file
64
patches/chromium/cherry-pick-138b748dd0a4.patch
Normal file
@@ -0,0 +1,64 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Cooper <alcooper@chromium.org>
|
||||
Date: Tue, 4 Aug 2020 00:31:54 +0000
|
||||
Subject: Update FocusChanged notifiers to operate on a copy
|
||||
|
||||
These focus changed calls ultimately trigger javascript events. These
|
||||
events could potentially run code that would modify the list of items
|
||||
that the FocusChanged notifiers are notifying, and thus invalidate their
|
||||
in-use iterators.
|
||||
|
||||
Fix this by having these methods iterate over a copy instead of the
|
||||
member list.
|
||||
|
||||
(cherry picked from commit d8f526f4e25c24ed29e60b46b3416bfabd5e8f11)
|
||||
|
||||
Fixed: 1107815
|
||||
Change-Id: I03fa08eeadc60736f3a3fae079253dbd3ee26476
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2314158
|
||||
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
|
||||
Reviewed-by: Klaus Weidner <klausw@chromium.org>
|
||||
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
|
||||
Auto-Submit: Alexander Cooper <alcooper@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#791261}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2335893
|
||||
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
|
||||
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4147@{#1015}
|
||||
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/page/focus_controller.cc b/third_party/blink/renderer/core/page/focus_controller.cc
|
||||
index 225ff4339c3a9b0bd79b3a188e28cf615e6ed97c..19215d532094c340dd146660b062aeb3293b7bc3 100644
|
||||
--- a/third_party/blink/renderer/core/page/focus_controller.cc
|
||||
+++ b/third_party/blink/renderer/core/page/focus_controller.cc
|
||||
@@ -1335,7 +1335,12 @@ void FocusController::RegisterFocusChangedObserver(
|
||||
}
|
||||
|
||||
void FocusController::NotifyFocusChangedObservers() const {
|
||||
- for (const auto& it : focus_changed_observers_)
|
||||
+ // Since this eventually dispatches an event to the page, the page could add
|
||||
+ // new observer, which would invalidate our iterators; so iterate over a copy
|
||||
+ // of the observer list.
|
||||
+ HeapHashSet<WeakMember<FocusChangedObserver>> observers =
|
||||
+ focus_changed_observers_;
|
||||
+ for (const auto& it : observers)
|
||||
it->FocusedFrameChanged();
|
||||
}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/xr/xr_system.cc b/third_party/blink/renderer/modules/xr/xr_system.cc
|
||||
index a94164360dcfe0e0686e5d48e64ee8a4dc9ef125..8b22f66e7f0c9e905d9a6503abb557287c6e456b 100644
|
||||
--- a/third_party/blink/renderer/modules/xr/xr_system.cc
|
||||
+++ b/third_party/blink/renderer/modules/xr/xr_system.cc
|
||||
@@ -682,7 +682,11 @@ XRSystem::XRSystem(LocalFrame& frame, int64_t ukm_source_id)
|
||||
|
||||
void XRSystem::FocusedFrameChanged() {
|
||||
// Tell all sessions that focus changed.
|
||||
- for (const auto& session : sessions_) {
|
||||
+ // Since this eventually dispatches an event to the page, the page could
|
||||
+ // create a new session which would invalidate our iterators; so iterate over
|
||||
+ // a copy of the session map.
|
||||
+ HeapHashSet<WeakMember<XRSession>> processing_sessions = sessions_;
|
||||
+ for (const auto& session : processing_sessions) {
|
||||
session->OnFocusChanged();
|
||||
}
|
||||
|
||||
115
patches/chromium/cherry-pick-52dceba66599.patch
Normal file
115
patches/chromium/cherry-pick-52dceba66599.patch
Normal file
@@ -0,0 +1,115 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Rahul Arakeri <arakeri@microsoft.com>
|
||||
Date: Tue, 8 Sep 2020 20:36:24 +0000
|
||||
Subject: Fix for UAF when referencing a deleted scrollbar layer.
|
||||
|
||||
This CL fixes an issue where autoscroll may be called on a scrollbar
|
||||
layer that is deleted. When the pointer is pressed down, an autoscroll
|
||||
task is scheduled to be executed after ~250ms. The task will execute if
|
||||
the pointer remains held down. In LayerTreeImpl::UnregisterScrollbar,
|
||||
DidUnregisterScrollbarLayer only gets called after both scrollbars are
|
||||
removed. So if you go from 2 to 1 scrollbar while an autoscroll task is
|
||||
queued, the autoscroll task won't get cancelled. At this point, the
|
||||
ScrollbarController tries to access the deleted scrollbar and that
|
||||
leads to an access violation. The fix here is to ensure that the call
|
||||
to DidUnregisterScrollbarLayer happens for each scrollbar.
|
||||
|
||||
Bug: 1106612
|
||||
Change-Id: I4f1d684830012db8fdd73258c9a9e5231807bcb6
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2356809
|
||||
Reviewed-by: Robert Flack <flackr@chromium.org>
|
||||
Commit-Queue: Rahul Arakeri <arakeri@microsoft.com>
|
||||
Cr-Commit-Position: refs/heads/master@{#805057}
|
||||
(cherry picked from commit 52dceba66599f0892fb649717fc462ff016d2fa2)
|
||||
|
||||
diff --git a/cc/input/scrollbar_controller.cc b/cc/input/scrollbar_controller.cc
|
||||
index b4d2ef531e2f5ccee157babb67522c454c720c9e..be80b5e07f5330ac636946f32f6cf713e0bb77a5 100644
|
||||
--- a/cc/input/scrollbar_controller.cc
|
||||
+++ b/cc/input/scrollbar_controller.cc
|
||||
@@ -433,9 +433,12 @@ void ScrollbarController::ResetState() {
|
||||
captured_scrollbar_metadata_ = base::nullopt;
|
||||
}
|
||||
|
||||
-void ScrollbarController::DidUnregisterScrollbar(ElementId element_id) {
|
||||
+void ScrollbarController::DidUnregisterScrollbar(
|
||||
+ ElementId element_id,
|
||||
+ ScrollbarOrientation orientation) {
|
||||
if (captured_scrollbar_metadata_.has_value() &&
|
||||
- captured_scrollbar_metadata_->scroll_element_id == element_id)
|
||||
+ captured_scrollbar_metadata_->scroll_element_id == element_id &&
|
||||
+ captured_scrollbar_metadata_->orientation == orientation)
|
||||
ResetState();
|
||||
}
|
||||
|
||||
@@ -531,6 +534,7 @@ void ScrollbarController::StartAutoScrollAnimation(
|
||||
// the same time.
|
||||
DCHECK(!drag_state_.has_value());
|
||||
DCHECK_NE(velocity, 0);
|
||||
+ DCHECK(ScrollbarLayer());
|
||||
|
||||
// scroll_node is set up while handling GSB. If there's no node to scroll, we
|
||||
// don't need to create any animation for it.
|
||||
diff --git a/cc/input/scrollbar_controller.h b/cc/input/scrollbar_controller.h
|
||||
index ab93bde692dfc48f3d2e2f95ca56c984e8d0eb88..c9faf8ca505776b1b2764225427e28008ae74c8e 100644
|
||||
--- a/cc/input/scrollbar_controller.h
|
||||
+++ b/cc/input/scrollbar_controller.h
|
||||
@@ -135,7 +135,8 @@ class CC_EXPORT ScrollbarController {
|
||||
const ScrollbarLayerImplBase* scrollbar,
|
||||
ScrollbarPart pressed_scrollbar_part);
|
||||
bool ScrollbarScrollIsActive() { return scrollbar_scroll_is_active_; }
|
||||
- void DidUnregisterScrollbar(ElementId element_id);
|
||||
+ void DidUnregisterScrollbar(ElementId element_id,
|
||||
+ ScrollbarOrientation orientation);
|
||||
ScrollbarLayerImplBase* ScrollbarLayer();
|
||||
void WillBeginImplFrame();
|
||||
|
||||
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
|
||||
index 688a0905c997c86b63bcc607033ec82a74741114..46821befb4f1720a93cb9e4b8e9d33bd45a6292a 100644
|
||||
--- a/cc/trees/layer_tree_host_impl.cc
|
||||
+++ b/cc/trees/layer_tree_host_impl.cc
|
||||
@@ -5326,9 +5326,10 @@ void LayerTreeHostImpl::RegisterScrollbarAnimationController(
|
||||
}
|
||||
|
||||
void LayerTreeHostImpl::DidUnregisterScrollbarLayer(
|
||||
- ElementId scroll_element_id) {
|
||||
+ ElementId scroll_element_id,
|
||||
+ ScrollbarOrientation orientation) {
|
||||
scrollbar_animation_controllers_.erase(scroll_element_id);
|
||||
- scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id);
|
||||
+ scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id, orientation);
|
||||
}
|
||||
|
||||
ScrollbarAnimationController*
|
||||
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
|
||||
index 2352d8e17c316542f2d65f6e1c6b075db9cf66bd..f2272d0c760f3301b0b64d81d6372758110798e9 100644
|
||||
--- a/cc/trees/layer_tree_host_impl.h
|
||||
+++ b/cc/trees/layer_tree_host_impl.h
|
||||
@@ -464,7 +464,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
|
||||
|
||||
void RegisterScrollbarAnimationController(ElementId scroll_element_id,
|
||||
float initial_opacity);
|
||||
- void DidUnregisterScrollbarLayer(ElementId scroll_element_id);
|
||||
+ void DidUnregisterScrollbarLayer(ElementId scroll_element_id,
|
||||
+ ScrollbarOrientation orientation);
|
||||
ScrollbarAnimationController* ScrollbarAnimationControllerForElementId(
|
||||
ElementId scroll_element_id) const;
|
||||
void FlashAllScrollbars(bool did_scroll);
|
||||
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
|
||||
index 45f7e5707a0ab5b983294964369d549e2981bf7d..2b6ea7532e954babe7150386209e958546b9bc40 100644
|
||||
--- a/cc/trees/layer_tree_impl.cc
|
||||
+++ b/cc/trees/layer_tree_impl.cc
|
||||
@@ -1933,9 +1933,11 @@ void LayerTreeImpl::UnregisterScrollbar(
|
||||
if (scrollbar_ids.horizontal == Layer::INVALID_ID &&
|
||||
scrollbar_ids.vertical == Layer::INVALID_ID) {
|
||||
element_id_to_scrollbar_layer_ids_.erase(scroll_element_id);
|
||||
- if (IsActiveTree()) {
|
||||
- host_impl_->DidUnregisterScrollbarLayer(scroll_element_id);
|
||||
- }
|
||||
+ }
|
||||
+
|
||||
+ if (IsActiveTree()) {
|
||||
+ host_impl_->DidUnregisterScrollbarLayer(scroll_element_id,
|
||||
+ scrollbar_layer->orientation());
|
||||
}
|
||||
}
|
||||
|
||||
78
patches/chromium/cherry-pick-70579363ce7b.patch
Normal file
78
patches/chromium/cherry-pick-70579363ce7b.patch
Normal file
@@ -0,0 +1,78 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Reilly Grant <reillyg@chromium.org>
|
||||
Date: Wed, 22 Jul 2020 04:00:16 +0000
|
||||
Subject: usb: Prevent iterator invalidation during Promise resolution
|
||||
|
||||
This change swaps sets of ScriptPromiseResolvers into local variables in
|
||||
a number of places where it was possible for script to execute during
|
||||
the call to Resolve() or Reject() and modify the set being iterated
|
||||
over, thus invalidating the iterator.
|
||||
|
||||
(cherry picked from commit dbc6c3c3652680e287c60b3c6551622748543439)
|
||||
|
||||
Bug: 1106773
|
||||
Change-Id: Id4eb0cd444a7dbb5de23038ec80f44fee649cfe4
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2304538
|
||||
Auto-Submit: Reilly Grant <reillyg@chromium.org>
|
||||
Commit-Queue: James Hollyer <jameshollyer@chromium.org>
|
||||
Reviewed-by: James Hollyer <jameshollyer@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#790217}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2310964
|
||||
Reviewed-by: Reilly Grant <reillyg@chromium.org>
|
||||
Commit-Queue: Reilly Grant <reillyg@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4183@{#842}
|
||||
Cr-Branched-From: 740e9e8a40505392ba5c8e022a8024b3d018ca65-refs/heads/master@{#782793}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/webusb/usb.cc b/third_party/blink/renderer/modules/webusb/usb.cc
|
||||
index de66613d20851dee9a96a8fdcfaec30da9f05b53..e3dc44eb208946aac4a2f836365127ba88c08edd 100644
|
||||
--- a/third_party/blink/renderer/modules/webusb/usb.cc
|
||||
+++ b/third_party/blink/renderer/modules/webusb/usb.cc
|
||||
@@ -254,15 +254,22 @@ void USB::OnDeviceRemoved(UsbDeviceInfoPtr device_info) {
|
||||
void USB::OnServiceConnectionError() {
|
||||
service_.reset();
|
||||
client_receiver_.reset();
|
||||
- for (ScriptPromiseResolver* resolver : get_devices_requests_)
|
||||
+
|
||||
+ // Move the set to a local variable to prevent script execution in Resolve()
|
||||
+ // from invalidating the iterator used by the loop.
|
||||
+ HeapHashSet<Member<ScriptPromiseResolver>> get_devices_requests;
|
||||
+ get_devices_requests.swap(get_devices_requests_);
|
||||
+ for (auto& resolver : get_devices_requests)
|
||||
resolver->Resolve(HeapVector<Member<USBDevice>>(0));
|
||||
- get_devices_requests_.clear();
|
||||
|
||||
- for (ScriptPromiseResolver* resolver : get_permission_requests_) {
|
||||
+ // Move the set to a local variable to prevent script execution in Reject()
|
||||
+ // from invalidating the iterator used by the loop.
|
||||
+ HeapHashSet<Member<ScriptPromiseResolver>> get_permission_requests;
|
||||
+ get_permission_requests.swap(get_permission_requests_);
|
||||
+ for (auto& resolver : get_permission_requests) {
|
||||
resolver->Reject(MakeGarbageCollected<DOMException>(
|
||||
DOMExceptionCode::kNotFoundError, kNoDeviceSelected));
|
||||
}
|
||||
- get_permission_requests_.clear();
|
||||
}
|
||||
|
||||
void USB::AddedEventListener(const AtomicString& event_type,
|
||||
diff --git a/third_party/blink/renderer/modules/webusb/usb_device.cc b/third_party/blink/renderer/modules/webusb/usb_device.cc
|
||||
index 655212ee5dd6c27498b83b8eb5b9e13a14c38a40..17f52e86a134ceb137923b65d1686ce3cbf3bf85 100644
|
||||
--- a/third_party/blink/renderer/modules/webusb/usb_device.cc
|
||||
+++ b/third_party/blink/renderer/modules/webusb/usb_device.cc
|
||||
@@ -1125,11 +1125,15 @@ void USBDevice::AsyncReset(ScriptPromiseResolver* resolver, bool success) {
|
||||
void USBDevice::OnConnectionError() {
|
||||
device_.reset();
|
||||
opened_ = false;
|
||||
- for (ScriptPromiseResolver* resolver : device_requests_) {
|
||||
+
|
||||
+ // Move the set to a local variable to prevent script execution in Reject()
|
||||
+ // from invalidating the iterator used by the loop.
|
||||
+ HeapHashSet<Member<ScriptPromiseResolver>> device_requests;
|
||||
+ device_requests.swap(device_requests_);
|
||||
+ for (auto& resolver : device_requests) {
|
||||
resolver->Reject(MakeGarbageCollected<DOMException>(
|
||||
DOMExceptionCode::kNotFoundError, kDeviceDisconnected));
|
||||
}
|
||||
- device_requests_.clear();
|
||||
}
|
||||
|
||||
bool USBDevice::MarkRequestComplete(ScriptPromiseResolver* resolver) {
|
||||
83
patches/chromium/cherry-pick-72ee7c437c88.patch
Normal file
83
patches/chromium/cherry-pick-72ee7c437c88.patch
Normal file
@@ -0,0 +1,83 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Hiroki Nakagawa <nhiroki@chromium.org>
|
||||
Date: Fri, 7 Aug 2020 15:27:06 +0000
|
||||
Subject: Worker: Fix a race condition on task runner handling
|
||||
|
||||
WebSharedWorkerImpl accesses WorkerScheduler from the main thread to
|
||||
take a task runner, and then dispatches a connect event to
|
||||
SharedWorkerGlobalScope using the task runner.
|
||||
|
||||
This causes a race condition if close() is called on the global scope
|
||||
on the worker thread while the task runner is being taken on the main
|
||||
thread: close() call disposes of WorkerScheduler, and accessing the
|
||||
scheduler after that is not allowed. See the issue for details.
|
||||
|
||||
To fix this, this CL makes WebSharedWorkerImpl capture the task runner
|
||||
between starting a worker thread (initializing WorkerScheduler) and
|
||||
posting a task to evaluate worker scripts that may call close(). This
|
||||
ensures that WebSharedWorkerImpl accesses WorkerScheduler before the
|
||||
scheduler is disposed of.
|
||||
|
||||
(cherry picked from commit c7bbec3e595c4359e36e5472b7265c4b6d047f2c)
|
||||
|
||||
Bug: 1104046
|
||||
Change-Id: I145cd39f706019c33220fcb01ed81f76963ffff0
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2308550
|
||||
Commit-Queue: Hiroki Nakagawa <nhiroki@chromium.org>
|
||||
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#790284}
|
||||
Tbr: bashi@chromium.org
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2342337
|
||||
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4147@{#1050}
|
||||
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
|
||||
index 7582cdbd3a1ee5e8a58686715020f304319860d8..9942e60d41f4add60bf28388902e08bc1d071dc7 100644
|
||||
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
|
||||
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
|
||||
@@ -145,11 +145,8 @@ void WebSharedWorkerImpl::Connect(MessagePortChannel web_channel) {
|
||||
DCHECK(IsMainThread());
|
||||
if (asked_to_terminate_)
|
||||
return;
|
||||
- // The HTML spec requires to queue a connect event using the DOM manipulation
|
||||
- // task source.
|
||||
- // https://html.spec.whatwg.org/C/#shared-workers-and-the-sharedworker-interface
|
||||
PostCrossThreadTask(
|
||||
- *GetWorkerThread()->GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE,
|
||||
+ *task_runner_for_connect_event_, FROM_HERE,
|
||||
CrossThreadBindOnce(&WebSharedWorkerImpl::ConnectTaskOnWorkerThread,
|
||||
WTF::CrossThreadUnretained(this),
|
||||
WTF::Passed(std::move(web_channel))));
|
||||
@@ -269,6 +266,18 @@ void WebSharedWorkerImpl::StartWorkerContext(
|
||||
|
||||
GetWorkerThread()->Start(std::move(creation_params), thread_startup_data,
|
||||
std::move(devtools_params));
|
||||
+
|
||||
+ // Capture the task runner for dispatching connect events. This is necessary
|
||||
+ // for avoiding race condition with WorkerScheduler termination induced by
|
||||
+ // close() call on SharedWorkerGlobalScope. See https://crbug.com/1104046 for
|
||||
+ // details.
|
||||
+ //
|
||||
+ // The HTML spec requires to queue a connect event using the DOM manipulation
|
||||
+ // task source.
|
||||
+ // https://html.spec.whatwg.org/C/#shared-workers-and-the-sharedworker-interface
|
||||
+ task_runner_for_connect_event_ =
|
||||
+ GetWorkerThread()->GetTaskRunner(TaskType::kDOMManipulation);
|
||||
+
|
||||
switch (script_type) {
|
||||
case mojom::ScriptType::kClassic:
|
||||
GetWorkerThread()->FetchAndRunClassicScript(
|
||||
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
|
||||
index 13006f67bd3e10b8d6c38e504d4bf235aa2f9022..98374fb9031e200a225e6e2982ff2d364fa007f5 100644
|
||||
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
|
||||
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
|
||||
@@ -114,6 +114,8 @@ class CORE_EXPORT WebSharedWorkerImpl final : public WebSharedWorker {
|
||||
// |client_| owns |this|.
|
||||
WebSharedWorkerClient* client_;
|
||||
|
||||
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_connect_event_;
|
||||
+
|
||||
bool asked_to_terminate_ = false;
|
||||
|
||||
base::WeakPtrFactory<WebSharedWorkerImpl> weak_ptr_factory_{this};
|
||||
54
patches/chromium/cherry-pick-814a27f8522b.patch
Normal file
54
patches/chromium/cherry-pick-814a27f8522b.patch
Normal file
@@ -0,0 +1,54 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Andrey Kosyakov <caseq@chromium.org>
|
||||
Date: Fri, 28 Aug 2020 18:55:17 +0000
|
||||
Subject: Delegate TargetHandler::Session permission checks to the root client
|
||||
|
||||
Bug: 1114636
|
||||
Change-Id: Iba3865206d7e80b363ec69180ac05e20b56aade2
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2380855
|
||||
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
|
||||
Reviewed-by: Devlin <rdevlin.cronin@chromium.org>
|
||||
Commit-Queue: Andrey Kosyakov <caseq@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#802736}
|
||||
(cherry picked from commit 814a27f8522b6ccddcce1a8f6a3b8fb37128ecf9)
|
||||
|
||||
diff --git a/content/browser/devtools/protocol/target_handler.cc b/content/browser/devtools/protocol/target_handler.cc
|
||||
index 4614ecae2110e5577539cffba02fcab153af301f..bcd054f2dc388908cf92bae3ff0254422960dc12 100644
|
||||
--- a/content/browser/devtools/protocol/target_handler.cc
|
||||
+++ b/content/browser/devtools/protocol/target_handler.cc
|
||||
@@ -436,7 +436,7 @@ class TargetHandler::Session : public DevToolsAgentHostClient {
|
||||
// was introduced. Try a DCHECK instead and possibly remove the check.
|
||||
if (!handler_->root_session_->HasChildSession(id_))
|
||||
return;
|
||||
- handler_->root_session_->GetClient()->DispatchProtocolMessage(
|
||||
+ GetRootClient()->DispatchProtocolMessage(
|
||||
handler_->root_session_->GetAgentHost(), message);
|
||||
return;
|
||||
}
|
||||
@@ -454,6 +454,26 @@ class TargetHandler::Session : public DevToolsAgentHostClient {
|
||||
Detach(true);
|
||||
}
|
||||
|
||||
+ bool MayAttachToURL(const GURL& url, bool is_webui) override {
|
||||
+ return GetRootClient()->MayAttachToURL(url, is_webui);
|
||||
+ }
|
||||
+
|
||||
+ bool MayAttachToBrowser() override {
|
||||
+ return GetRootClient()->MayAttachToBrowser();
|
||||
+ }
|
||||
+
|
||||
+ bool MayReadLocalFiles() override {
|
||||
+ return GetRootClient()->MayReadLocalFiles();
|
||||
+ }
|
||||
+
|
||||
+ bool MayWriteLocalFiles() override {
|
||||
+ return GetRootClient()->MayWriteLocalFiles();
|
||||
+ }
|
||||
+
|
||||
+ content::DevToolsAgentHostClient* GetRootClient() {
|
||||
+ return handler_->root_session_->GetClient();
|
||||
+ }
|
||||
+
|
||||
TargetHandler* handler_;
|
||||
scoped_refptr<DevToolsAgentHost> agent_host_;
|
||||
std::string id_;
|
||||
29
patches/chromium/cherry-pick-9746a4cde14a.patch
Normal file
29
patches/chromium/cherry-pick-9746a4cde14a.patch
Normal file
@@ -0,0 +1,29 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Andres Calderon Jaramillo <andrescj@chromium.org>
|
||||
Date: Mon, 20 Jul 2020 19:49:42 +0000
|
||||
Subject: blink: Disable WebP YUV image decoding by default.
|
||||
|
||||
JPEG YUV image decoding is already disabled by default.
|
||||
|
||||
Bug: 1102054,1091347
|
||||
Test: WebP images in bug 1091347 don't have artifacts on Linux
|
||||
Change-Id: Icf7f9ef46da97ba80d4e3b4aac4196f789bdf6d5
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2283409
|
||||
Reviewed-by: Andres Calderon Jaramillo <andrescj@chromium.org>
|
||||
Commit-Queue: Andres Calderon Jaramillo <andrescj@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4147@{#901}
|
||||
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
|
||||
|
||||
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
|
||||
index ad1c48f2d727f5fee33b2f5bd9d39682da2ba085..5f87b90e6cf3a3e3cb5f68e13fcbed06d039b18f 100644
|
||||
--- a/third_party/blink/common/features.cc
|
||||
+++ b/third_party/blink/common/features.cc
|
||||
@@ -257,7 +257,7 @@ const base::Feature kDecodeJpeg420ImagesToYUV{
|
||||
// Decodes lossy WebP images to YUV instead of RGBX and stores in this format
|
||||
// in the image decode cache. See crbug.com/900264 for details on the feature.
|
||||
const base::Feature kDecodeLossyWebPImagesToYUV{
|
||||
- "DecodeLossyWebPImagesToYUV", base::FEATURE_ENABLED_BY_DEFAULT};
|
||||
+ "DecodeLossyWebPImagesToYUV", base::FEATURE_DISABLED_BY_DEFAULT};
|
||||
|
||||
// Enables cache-aware WebFonts loading. See https://crbug.com/570205.
|
||||
// The feature is disabled on Android for WebView API issue discussed at
|
||||
42
patches/chromium/cherry-pick-9ad8c9610d0a.patch
Normal file
42
patches/chromium/cherry-pick-9ad8c9610d0a.patch
Normal file
@@ -0,0 +1,42 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Guido Urdaneta <guidou@chromium.org>
|
||||
Date: Tue, 4 Aug 2020 21:25:10 +0000
|
||||
Subject: Use copy of source map in
|
||||
MediaElementElementListener::UpdateSources()
|
||||
|
||||
Prior to this CL, this function iterated over a source map that could
|
||||
be modified by a re-entrant call triggered by JS code.
|
||||
|
||||
(cherry picked from commit 292ac9aa5ba263f63f761e03b8214cae21e667c9)
|
||||
|
||||
Bug: 1105426
|
||||
Change-Id: I47e49e4132cba98e12ee7c195720ac9ecc1f485b
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2312703
|
||||
Reviewed-by: Marina Ciocea <marinaciocea@chromium.org>
|
||||
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#790894}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2332823
|
||||
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4147@{#1026}
|
||||
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
|
||||
index 03ba68236c511ba2d2767af3c796b37a90dce476..80f36cb236adee50aefc505ff64f092cbc4e82b9 100644
|
||||
--- a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
|
||||
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
|
||||
@@ -242,9 +242,14 @@ void MediaElementEventListener::UpdateSources(ExecutionContext* context) {
|
||||
for (auto track : media_stream_->getTracks())
|
||||
sources_.insert(track->Component()->Source());
|
||||
|
||||
+ // Handling of the ended event in JS triggered by DidStopMediaStreamSource()
|
||||
+ // may cause a reentrant call to this function, which can modify |sources_|.
|
||||
+ // Iterate over a copy of |sources_| to avoid invalidation of the iterator
|
||||
+ // when a reentrant call occurs.
|
||||
+ auto sources_copy = sources_;
|
||||
if (!media_element_->currentSrc().IsEmpty() &&
|
||||
!media_element_->IsMediaDataCorsSameOrigin()) {
|
||||
- for (auto source : sources_)
|
||||
+ for (auto source : sources_copy)
|
||||
DidStopMediaStreamSource(source.Get());
|
||||
}
|
||||
}
|
||||
160
patches/chromium/cherry-pick-9d100199c92b.patch
Normal file
160
patches/chromium/cherry-pick-9d100199c92b.patch
Normal file
@@ -0,0 +1,160 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yutaka Hirano <yhirano@chromium.org>
|
||||
Date: Fri, 7 Aug 2020 09:06:23 +0000
|
||||
Subject: Fix UAF in ScriptPromiseProperty caused by reentrant code
|
||||
|
||||
v8::Promise::Resolve can run user code synchronously, which caused a UAF
|
||||
in ScriptPromiseProperty. Fix it.
|
||||
|
||||
Bug: 1108518
|
||||
Change-Id: Ia9baec6eef0887323cd88ceb1d3fa0c14fdb77ef
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2325499
|
||||
Reviewed-by: Yuki Shiino <yukishiino@chromium.org>
|
||||
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#792661}
|
||||
(cherry picked from commit 6d18e924b9c426905434cc280d7b602b3a3379ed)
|
||||
|
||||
TBR=yhirano@chromium.org
|
||||
|
||||
Change-Id: I3b7bfd5e8d932fb59c292159a4526cf70b44c58b
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2342489
|
||||
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
|
||||
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4147@{#1049}
|
||||
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
|
||||
|
||||
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_property.h b/third_party/blink/renderer/bindings/core/v8/script_promise_property.h
|
||||
index 7c83ee35246486cf2a43227bf180904b2d9eb5f3..49ec9edc0d897e6f6e842a425cc2b0a4272702e2 100644
|
||||
--- a/third_party/blink/renderer/bindings/core/v8/script_promise_property.h
|
||||
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise_property.h
|
||||
@@ -102,10 +102,11 @@ class ScriptPromiseProperty final
|
||||
}
|
||||
state_ = kResolved;
|
||||
resolved_ = value;
|
||||
- for (const Member<ScriptPromiseResolver>& resolver : resolvers_) {
|
||||
+ HeapVector<Member<ScriptPromiseResolver>> resolvers;
|
||||
+ resolvers.swap(resolvers_);
|
||||
+ for (const Member<ScriptPromiseResolver>& resolver : resolvers) {
|
||||
resolver->Resolve(resolved_);
|
||||
}
|
||||
- resolvers_.clear();
|
||||
}
|
||||
|
||||
void ResolveWithUndefined() {
|
||||
@@ -116,10 +117,11 @@ class ScriptPromiseProperty final
|
||||
}
|
||||
state_ = kResolved;
|
||||
resolved_with_undefined_ = true;
|
||||
- for (const Member<ScriptPromiseResolver>& resolver : resolvers_) {
|
||||
+ HeapVector<Member<ScriptPromiseResolver>> resolvers;
|
||||
+ resolvers.swap(resolvers_);
|
||||
+ for (const Member<ScriptPromiseResolver>& resolver : resolvers) {
|
||||
resolver->Resolve();
|
||||
}
|
||||
- resolvers_.clear();
|
||||
}
|
||||
|
||||
template <typename PassRejectedType>
|
||||
@@ -131,10 +133,11 @@ class ScriptPromiseProperty final
|
||||
}
|
||||
state_ = kRejected;
|
||||
rejected_ = value;
|
||||
- for (const Member<ScriptPromiseResolver>& resolver : resolvers_) {
|
||||
+ HeapVector<Member<ScriptPromiseResolver>> resolvers;
|
||||
+ resolvers.swap(resolvers_);
|
||||
+ for (const Member<ScriptPromiseResolver>& resolver : resolvers) {
|
||||
resolver->Reject(rejected_);
|
||||
}
|
||||
- resolvers_.clear();
|
||||
}
|
||||
|
||||
// Resets this property by unregistering the Promise property from the
|
||||
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc b/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc
|
||||
index c2dd607686e4ef76885036b4a678103c3869241e..95b6b1b83638b30918032fc4f76fec56a781c492 100644
|
||||
--- a/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc
|
||||
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise_property_test.cc
|
||||
@@ -96,6 +96,35 @@ class GarbageCollectedHolder final : public GarbageCollectedScriptWrappable {
|
||||
Member<Property> property_;
|
||||
};
|
||||
|
||||
+class ScriptPromisePropertyResetter : public ScriptFunction {
|
||||
+ public:
|
||||
+ using Property =
|
||||
+ ScriptPromiseProperty<Member<GarbageCollectedScriptWrappable>,
|
||||
+ Member<GarbageCollectedScriptWrappable>>;
|
||||
+ static v8::Local<v8::Function> CreateFunction(ScriptState* script_state,
|
||||
+ Property* property) {
|
||||
+ auto* self = MakeGarbageCollected<ScriptPromisePropertyResetter>(
|
||||
+ script_state, property);
|
||||
+ return self->BindToV8Function();
|
||||
+ }
|
||||
+
|
||||
+ ScriptPromisePropertyResetter(ScriptState* script_state, Property* property)
|
||||
+ : ScriptFunction(script_state), property_(property) {}
|
||||
+
|
||||
+ void Trace(Visitor* visitor) override {
|
||||
+ visitor->Trace(property_);
|
||||
+ ScriptFunction::Trace(visitor);
|
||||
+ }
|
||||
+
|
||||
+ private:
|
||||
+ ScriptValue Call(ScriptValue arg) override {
|
||||
+ property_->Reset();
|
||||
+ return ScriptValue();
|
||||
+ }
|
||||
+
|
||||
+ const Member<Property> property_;
|
||||
+};
|
||||
+
|
||||
class ScriptPromisePropertyTestBase {
|
||||
public:
|
||||
ScriptPromisePropertyTestBase()
|
||||
@@ -520,6 +549,48 @@ TEST_F(ScriptPromisePropertyGarbageCollectedTest, MarkAsHandled) {
|
||||
}
|
||||
}
|
||||
|
||||
+TEST_F(ScriptPromisePropertyGarbageCollectedTest, SyncResolve) {
|
||||
+ // Call getters to create resolvers in the property.
|
||||
+ GetProperty()->Promise(DOMWrapperWorld::MainWorld());
|
||||
+ GetProperty()->Promise(OtherWorld());
|
||||
+
|
||||
+ auto* resolution =
|
||||
+ MakeGarbageCollected<GarbageCollectedScriptWrappable>("hi");
|
||||
+ v8::HandleScope handle_scope(GetIsolate());
|
||||
+ v8::Local<v8::Object> main_v8_resolution;
|
||||
+ v8::Local<v8::Object> other_v8_resolution;
|
||||
+ {
|
||||
+ ScriptState::Scope scope(MainScriptState());
|
||||
+ main_v8_resolution = ToV8(resolution, MainScriptState()).As<v8::Object>();
|
||||
+ v8::PropertyDescriptor descriptor(
|
||||
+ ScriptPromisePropertyResetter::CreateFunction(MainScriptState(),
|
||||
+ GetProperty()),
|
||||
+ v8::Undefined(GetIsolate()));
|
||||
+ ASSERT_EQ(
|
||||
+ v8::Just(true),
|
||||
+ main_v8_resolution->DefineProperty(
|
||||
+ MainScriptState()->GetContext(),
|
||||
+ v8::String::NewFromUtf8Literal(GetIsolate(), "then"), descriptor));
|
||||
+ }
|
||||
+ {
|
||||
+ ScriptState::Scope scope(OtherScriptState());
|
||||
+ other_v8_resolution = ToV8(resolution, OtherScriptState()).As<v8::Object>();
|
||||
+ v8::PropertyDescriptor descriptor(
|
||||
+ ScriptPromisePropertyResetter::CreateFunction(OtherScriptState(),
|
||||
+ GetProperty()),
|
||||
+ v8::Undefined(GetIsolate()));
|
||||
+ ASSERT_EQ(
|
||||
+ v8::Just(true),
|
||||
+ other_v8_resolution->DefineProperty(
|
||||
+ OtherScriptState()->GetContext(),
|
||||
+ v8::String::NewFromUtf8Literal(GetIsolate(), "then"), descriptor));
|
||||
+ }
|
||||
+
|
||||
+ // This shouldn't crash.
|
||||
+ GetProperty()->Resolve(resolution);
|
||||
+ EXPECT_EQ(GetProperty()->GetState(), Property::State::kPending);
|
||||
+}
|
||||
+
|
||||
TEST_F(ScriptPromisePropertyNonScriptWrappableResolutionTargetTest,
|
||||
ResolveWithUndefined) {
|
||||
Test(ToV8UndefinedGenerator(), "undefined", __FILE__, __LINE__);
|
||||
112
patches/chromium/cherry-pick-bee371eeaf66.patch
Normal file
112
patches/chromium/cherry-pick-bee371eeaf66.patch
Normal file
@@ -0,0 +1,112 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Hongchan Choi <hongchan@chromium.org>
|
||||
Date: Tue, 4 Aug 2020 16:13:40 +0000
|
||||
Subject: Use SupportWeakPtr in OfflineAudioDestinationHandler
|
||||
|
||||
OfflineAudioDestinationHandler's render thread notifies the
|
||||
main thread when the rendering state changes. In this process,
|
||||
the associated audio context can be deleted when a posted task
|
||||
is performed sometime later in the task runner's queue.
|
||||
|
||||
By using WeakPtr, the task runner will not perform a scheduled task
|
||||
in the queue when the target object is no longer valid.
|
||||
|
||||
(cherry picked from commit 4f309b864587890acaefa9da5d580abb21ff9ca0)
|
||||
|
||||
Bug: 1095584
|
||||
Test: Locally confirmed that the repro case does not crash after 30 min.
|
||||
Change-Id: Ic1814b97f8d9a8d1027ef04f475112874cfa8137
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2285473
|
||||
Reviewed-by: Robert Sesek <rsesek@chromium.org>
|
||||
Reviewed-by: Raymond Toy <rtoy@chromium.org>
|
||||
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#786381}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2335487
|
||||
Reviewed-by: Hongchan Choi <hongchan@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4147@{#1019}
|
||||
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
|
||||
index ec610b99c6758bc66cda07bd344f29825c8376a3..2b0d87395cef31793273ef69f61eb540be897fcf 100644
|
||||
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
|
||||
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
|
||||
@@ -51,17 +51,14 @@ OfflineAudioDestinationHandler::OfflineAudioDestinationHandler(
|
||||
frames_to_process_(frames_to_process),
|
||||
is_rendering_started_(false),
|
||||
number_of_channels_(number_of_channels),
|
||||
- sample_rate_(sample_rate) {
|
||||
- channel_count_ = number_of_channels;
|
||||
+ sample_rate_(sample_rate),
|
||||
+ main_thread_task_runner_(Context()->GetExecutionContext()->GetTaskRunner(
|
||||
+ TaskType::kInternalMedia)) {
|
||||
+ DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
||||
|
||||
+ channel_count_ = number_of_channels;
|
||||
SetInternalChannelCountMode(kExplicit);
|
||||
SetInternalChannelInterpretation(AudioBus::kSpeakers);
|
||||
-
|
||||
- if (Context()->GetExecutionContext()) {
|
||||
- main_thread_task_runner_ = Context()->GetExecutionContext()->GetTaskRunner(
|
||||
- TaskType::kMiscPlatformAPI);
|
||||
- DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
||||
- }
|
||||
}
|
||||
|
||||
scoped_refptr<OfflineAudioDestinationHandler>
|
||||
@@ -218,7 +215,7 @@ void OfflineAudioDestinationHandler::SuspendOfflineRendering() {
|
||||
PostCrossThreadTask(
|
||||
*main_thread_task_runner_, FROM_HERE,
|
||||
CrossThreadBindOnce(&OfflineAudioDestinationHandler::NotifySuspend,
|
||||
- WrapRefCounted(this),
|
||||
+ GetWeakPtr(),
|
||||
Context()->CurrentSampleFrame()));
|
||||
}
|
||||
|
||||
@@ -229,7 +226,7 @@ void OfflineAudioDestinationHandler::FinishOfflineRendering() {
|
||||
PostCrossThreadTask(
|
||||
*main_thread_task_runner_, FROM_HERE,
|
||||
CrossThreadBindOnce(&OfflineAudioDestinationHandler::NotifyComplete,
|
||||
- WrapRefCounted(this)));
|
||||
+ GetWeakPtr()));
|
||||
}
|
||||
|
||||
void OfflineAudioDestinationHandler::NotifySuspend(size_t frame) {
|
||||
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h
|
||||
index e4d2d8bfcd5d90d233ca8c761f07e8f518f83018..07d80e4cff535ee7230ea14d96c292bacaa7c19e 100644
|
||||
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h
|
||||
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
+#include "base/memory/weak_ptr.h"
|
||||
#include "third_party/blink/renderer/modules/webaudio/audio_buffer.h"
|
||||
#include "third_party/blink/renderer/modules/webaudio/audio_destination_node.h"
|
||||
#include "third_party/blink/renderer/modules/webaudio/offline_audio_context.h"
|
||||
@@ -124,6 +125,17 @@ class OfflineAudioDestinationHandler final : public AudioDestinationHandler {
|
||||
// from AudioWorkletThread will be used until the rendering is finished.
|
||||
void PrepareTaskRunnerForRendering();
|
||||
|
||||
+ // For cross-thread posting, this object uses two different targets.
|
||||
+ // 1. rendering thread -> main thread: WeakPtr
|
||||
+ // When the main thread starts deleting this object, a task posted with
|
||||
+ // a WeakPtr from the rendering thread will be cancelled.
|
||||
+ // 2. main thread -> rendering thread: scoped_refptr
|
||||
+ // |render_thread_| is owned by this object, so it is safe to target with
|
||||
+ // WrapRefCounted() instead of GetWeakPtr().
|
||||
+ base::WeakPtr<OfflineAudioDestinationHandler> GetWeakPtr() {
|
||||
+ return weak_factory_.GetWeakPtr();
|
||||
+ }
|
||||
+
|
||||
// This AudioHandler renders into this SharedAudioBuffer.
|
||||
std::unique_ptr<SharedAudioBuffer> shared_render_target_;
|
||||
// Temporary AudioBus for each render quantum.
|
||||
@@ -148,6 +160,8 @@ class OfflineAudioDestinationHandler final : public AudioDestinationHandler {
|
||||
|
||||
scoped_refptr<base::SingleThreadTaskRunner> render_thread_task_runner_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
|
||||
+
|
||||
+ base::WeakPtrFactory<OfflineAudioDestinationHandler> weak_factory_{this};
|
||||
};
|
||||
|
||||
class OfflineAudioDestinationNode final : public AudioDestinationNode {
|
||||
284
patches/chromium/cherry-pick-f6cb89728f04.patch
Normal file
284
patches/chromium/cherry-pick-f6cb89728f04.patch
Normal file
@@ -0,0 +1,284 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Lei Zhang <thestig@chromium.org>
|
||||
Date: Tue, 28 Jul 2020 22:47:48 +0000
|
||||
Subject: M85: Correctly retrieve the plugin when printing.
|
||||
|
||||
The logic in PrintRenderFrameHelper to retrieve a plugin is out of sync
|
||||
with the logic in WebLocalFrameImpl::PrintBegin(). If
|
||||
PrintRenderFrameHelper thinks it is printing a webpage, while
|
||||
WebLocalFrameImpl thinks it is printing a plugin, bad things happen.
|
||||
|
||||
Fix this by adding WebLocalFrame::GetPluginToPrint(), to expose the
|
||||
plugin finding logic in WebLocalFrameImpl. With GetPluginToPrint()
|
||||
available, PrintRenderFrameHelper can delete its own GetPlugin() helper,
|
||||
and switch the GetPlugin() callers to use GetPluginToPrint() instead.
|
||||
|
||||
Once synchronized, some use cases for printing Flash now work correctly.
|
||||
|
||||
(cherry picked from commit f8d7d428b1549ff1f87e3d34c5ca0b53d6ce4e84)
|
||||
|
||||
Tbr: japhet@chromium.org
|
||||
Bug: 1098860
|
||||
Change-Id: I9500db9ed2d6da0f87dad84c197f738d3a1e3c84
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2317168
|
||||
Reviewed-by: Nate Chapin <japhet@chromium.org>
|
||||
Commit-Queue: Lei Zhang <thestig@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#791564}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2324664
|
||||
Reviewed-by: Lei Zhang <thestig@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4183@{#1009}
|
||||
Cr-Branched-From: 740e9e8a40505392ba5c8e022a8024b3d018ca65-refs/heads/master@{#782793}
|
||||
|
||||
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
|
||||
index 8456ec0c453345e5cab96728e501d341053d8cf2..9dd56e9df0d91ccd1d6789e3b3f261a533d373e8 100644
|
||||
--- a/components/printing/renderer/print_render_frame_helper.cc
|
||||
+++ b/components/printing/renderer/print_render_frame_helper.cc
|
||||
@@ -61,8 +61,6 @@
|
||||
#include "third_party/blink/public/web/web_local_frame_client.h"
|
||||
#include "third_party/blink/public/web/web_navigation_control.h"
|
||||
#include "third_party/blink/public/web/web_plugin.h"
|
||||
-#include "third_party/blink/public/web/web_plugin_container.h"
|
||||
-#include "third_party/blink/public/web/web_plugin_document.h"
|
||||
#include "third_party/blink/public/web/web_print_params.h"
|
||||
#include "third_party/blink/public/web/web_print_preset_options.h"
|
||||
#include "third_party/blink/public/web/web_script_source.h"
|
||||
@@ -343,28 +341,14 @@ void ComputeWebKitPrintParamsInDesiredDpi(
|
||||
webkit_print_params->pages_per_sheet = print_params.pages_per_sheet;
|
||||
}
|
||||
|
||||
-blink::WebPlugin* GetPlugin(const blink::WebLocalFrame* frame) {
|
||||
- return frame->GetDocument().IsPluginDocument()
|
||||
- ? frame->GetDocument().To<blink::WebPluginDocument>().Plugin()
|
||||
- : nullptr;
|
||||
-}
|
||||
-
|
||||
-bool IsPrintingNodeOrPdfFrame(const blink::WebLocalFrame* frame,
|
||||
+bool IsPrintingNodeOrPdfFrame(blink::WebLocalFrame* frame,
|
||||
const blink::WebNode& node) {
|
||||
- if (!node.IsNull())
|
||||
- return true;
|
||||
- blink::WebPlugin* plugin = GetPlugin(frame);
|
||||
+ blink::WebPlugin* plugin = frame->GetPluginToPrint(node);
|
||||
return plugin && plugin->SupportsPaginatedPrint();
|
||||
}
|
||||
|
||||
bool IsPrintingPdf(blink::WebLocalFrame* frame, const blink::WebNode& node) {
|
||||
- blink::WebPlugin* plugin;
|
||||
- if (node.IsNull()) {
|
||||
- plugin = GetPlugin(frame);
|
||||
- } else {
|
||||
- blink::WebPluginContainer* plugin_container = node.PluginContainer();
|
||||
- plugin = plugin_container ? plugin_container->Plugin() : nullptr;
|
||||
- }
|
||||
+ blink::WebPlugin* plugin = frame->GetPluginToPrint(node);
|
||||
return plugin && plugin->IsPdfPlugin();
|
||||
}
|
||||
|
||||
@@ -2337,7 +2321,7 @@ void PrintRenderFrameHelper::RequestPrintPreview(PrintPreviewRequestType type) {
|
||||
// 2. PrintHostMsg_ShowScriptedPrintPreview shows preview once the
|
||||
// document has been loaded.
|
||||
is_scripted_preview_delayed_ = true;
|
||||
- if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
|
||||
+ if (is_loading_ && print_preview_context_.IsPlugin()) {
|
||||
// Wait for DidStopLoading. Plugins may not know the correct
|
||||
// |is_modifiable| value until they are fully loaded, which occurs when
|
||||
// DidStopLoading() is called. Defer showing the preview until then.
|
||||
@@ -2364,7 +2348,7 @@ void PrintRenderFrameHelper::RequestPrintPreview(PrintPreviewRequestType type) {
|
||||
// Wait for DidStopLoading. Continuing with this function while
|
||||
// |is_loading_| is true will cause print preview to hang when try to
|
||||
// print a PDF document.
|
||||
- if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
|
||||
+ if (is_loading_ && print_preview_context_.IsPlugin()) {
|
||||
on_stop_loading_closure_ =
|
||||
base::BindOnce(&PrintRenderFrameHelper::RequestPrintPreview,
|
||||
weak_ptr_factory_.GetWeakPtr(), type);
|
||||
@@ -2375,12 +2359,12 @@ void PrintRenderFrameHelper::RequestPrintPreview(PrintPreviewRequestType type) {
|
||||
}
|
||||
case PRINT_PREVIEW_USER_INITIATED_SELECTION: {
|
||||
DCHECK(has_selection);
|
||||
- DCHECK(!GetPlugin(print_preview_context_.source_frame()));
|
||||
+ DCHECK(!print_preview_context_.IsPlugin());
|
||||
params.selection_only = has_selection;
|
||||
break;
|
||||
}
|
||||
case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE: {
|
||||
- if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) {
|
||||
+ if (is_loading_ && print_preview_context_.IsPlugin()) {
|
||||
on_stop_loading_closure_ =
|
||||
base::BindOnce(&PrintRenderFrameHelper::RequestPrintPreview,
|
||||
weak_ptr_factory_.GetWeakPtr(), type);
|
||||
@@ -2465,8 +2449,7 @@ void PrintRenderFrameHelper::PrintPreviewContext::InitWithFrame(
|
||||
state_ = INITIALIZED;
|
||||
source_frame_.Reset(web_frame);
|
||||
source_node_.Reset();
|
||||
- CalculateIsModifiable();
|
||||
- CalculateIsPdf();
|
||||
+ CalculatePluginAttributes();
|
||||
}
|
||||
|
||||
void PrintRenderFrameHelper::PrintPreviewContext::InitWithNode(
|
||||
@@ -2477,8 +2460,7 @@ void PrintRenderFrameHelper::PrintPreviewContext::InitWithNode(
|
||||
state_ = INITIALIZED;
|
||||
source_frame_.Reset(web_node.GetDocument().GetFrame());
|
||||
source_node_ = web_node;
|
||||
- CalculateIsModifiable();
|
||||
- CalculateIsPdf();
|
||||
+ CalculatePluginAttributes();
|
||||
}
|
||||
|
||||
void PrintRenderFrameHelper::PrintPreviewContext::OnPrintPreview() {
|
||||
@@ -2621,6 +2603,11 @@ bool PrintRenderFrameHelper::PrintPreviewContext::IsForArc() const {
|
||||
return is_for_arc_;
|
||||
}
|
||||
|
||||
+bool PrintRenderFrameHelper::PrintPreviewContext::IsPlugin() const {
|
||||
+ DCHECK(state_ != UNINITIALIZED);
|
||||
+ return is_plugin_;
|
||||
+}
|
||||
+
|
||||
bool PrintRenderFrameHelper::PrintPreviewContext::IsModifiable() const {
|
||||
DCHECK(state_ != UNINITIALIZED);
|
||||
return is_modifiable_;
|
||||
@@ -2713,11 +2700,9 @@ void PrintRenderFrameHelper::PrintPreviewContext::ClearContext() {
|
||||
error_ = PREVIEW_ERROR_NONE;
|
||||
}
|
||||
|
||||
-void PrintRenderFrameHelper::PrintPreviewContext::CalculateIsModifiable() {
|
||||
+void PrintRenderFrameHelper::PrintPreviewContext::CalculatePluginAttributes() {
|
||||
+ is_plugin_ = !!source_frame()->GetPluginToPrint(source_node_);
|
||||
is_modifiable_ = !IsPrintingNodeOrPdfFrame(source_frame(), source_node_);
|
||||
-}
|
||||
-
|
||||
-void PrintRenderFrameHelper::PrintPreviewContext::CalculateIsPdf() {
|
||||
is_pdf_ = IsPrintingPdf(source_frame(), source_node_);
|
||||
}
|
||||
|
||||
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
|
||||
index 81df566a5908f1b35b9da5c2aad35ea7dec1dc3b..30cb90cab8a971b1b4eb11bb89e05b09853b7721 100644
|
||||
--- a/components/printing/renderer/print_render_frame_helper.h
|
||||
+++ b/components/printing/renderer/print_render_frame_helper.h
|
||||
@@ -503,6 +503,7 @@ class PrintRenderFrameHelper
|
||||
int GetNextPageNumber();
|
||||
bool IsRendering() const;
|
||||
bool IsForArc() const;
|
||||
+ bool IsPlugin() const;
|
||||
bool IsModifiable() const;
|
||||
bool IsPdf() const;
|
||||
bool HasSelection();
|
||||
@@ -543,9 +544,7 @@ class PrintRenderFrameHelper
|
||||
// Reset some of the internal rendering context.
|
||||
void ClearContext();
|
||||
|
||||
- void CalculateIsModifiable();
|
||||
-
|
||||
- void CalculateIsPdf();
|
||||
+ void CalculatePluginAttributes();
|
||||
|
||||
// Specifies what to render for print preview.
|
||||
FrameReference source_frame_;
|
||||
@@ -565,6 +564,9 @@ class PrintRenderFrameHelper
|
||||
// List of page indices that need to be rendered.
|
||||
std::vector<int> pages_to_render_;
|
||||
|
||||
+ // True, if the document source is a plugin.
|
||||
+ bool is_plugin_ = false;
|
||||
+
|
||||
// True, if the document source is modifiable. e.g. HTML and not PDF.
|
||||
bool is_modifiable_ = true;
|
||||
|
||||
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
|
||||
index c5d9cd52989c0d776ac12a5c0b88782d2c40b213..ecb810fecebd4ecb8c93630490cfb8ad5fe856c6 100644
|
||||
--- a/third_party/blink/public/web/web_local_frame.h
|
||||
+++ b/third_party/blink/public/web/web_local_frame.h
|
||||
@@ -59,6 +59,7 @@ class WebLocalFrameClient;
|
||||
class WebFrameWidget;
|
||||
class WebInputMethodController;
|
||||
class WebPerformance;
|
||||
+class WebPlugin;
|
||||
class WebRange;
|
||||
class WebSecurityOrigin;
|
||||
class WebScriptExecutionCallback;
|
||||
@@ -655,13 +656,16 @@ class WebLocalFrame : public WebFrame {
|
||||
// This function should be called before pairs of PrintBegin() and PrintEnd().
|
||||
virtual void DispatchBeforePrintEvent() = 0;
|
||||
|
||||
+ // Get the plugin to print, if any. The |constrain_to_node| parameter is the
|
||||
+ // same as the one for PrintBegin() below.
|
||||
+ virtual WebPlugin* GetPluginToPrint(const WebNode& constrain_to_node) = 0;
|
||||
+
|
||||
// Reformats the WebFrame for printing. WebPrintParams specifies the printable
|
||||
// content size, paper size, printable area size, printer DPI and print
|
||||
- // scaling option. If constrainToNode node is specified, then only the given
|
||||
+ // scaling option. If |constrain_to_node| is specified, then only the given
|
||||
// node is printed (for now only plugins are supported), instead of the entire
|
||||
// frame.
|
||||
- // Returns the number of pages that can be printed at the given
|
||||
- // page size.
|
||||
+ // Returns the number of pages that can be printed at the given page size.
|
||||
virtual int PrintBegin(const WebPrintParams&,
|
||||
const WebNode& constrain_to_node = WebNode()) = 0;
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
|
||||
index 9cb4f3d02fccd6f1098c9b8080f1057642aead5f..52e57b11169ec19c2f890545560a5784af071029 100644
|
||||
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
|
||||
@@ -1537,19 +1537,29 @@ void WebLocalFrameImpl::DispatchPrintEventRecursively(
|
||||
}
|
||||
}
|
||||
|
||||
-int WebLocalFrameImpl::PrintBegin(const WebPrintParams& print_params,
|
||||
- const WebNode& constrain_to_node) {
|
||||
- WebPluginContainerImpl* plugin_container = nullptr;
|
||||
+WebPluginContainerImpl* WebLocalFrameImpl::GetPluginToPrintHelper(
|
||||
+ const WebNode& constrain_to_node) {
|
||||
if (constrain_to_node.IsNull()) {
|
||||
// If this is a plugin document, check if the plugin supports its own
|
||||
// printing. If it does, we will delegate all printing to that.
|
||||
- plugin_container = GetFrame()->GetWebPluginContainer();
|
||||
- } else {
|
||||
- // We only support printing plugin nodes for now.
|
||||
- plugin_container =
|
||||
- To<WebPluginContainerImpl>(constrain_to_node.PluginContainer());
|
||||
+ return GetFrame()->GetWebPluginContainer();
|
||||
}
|
||||
|
||||
+ // We only support printing plugin nodes for now.
|
||||
+ return To<WebPluginContainerImpl>(constrain_to_node.PluginContainer());
|
||||
+}
|
||||
+
|
||||
+WebPlugin* WebLocalFrameImpl::GetPluginToPrint(
|
||||
+ const WebNode& constrain_to_node) {
|
||||
+ WebPluginContainerImpl* plugin_container =
|
||||
+ GetPluginToPrintHelper(constrain_to_node);
|
||||
+ return plugin_container ? plugin_container->Plugin() : nullptr;
|
||||
+}
|
||||
+
|
||||
+int WebLocalFrameImpl::PrintBegin(const WebPrintParams& print_params,
|
||||
+ const WebNode& constrain_to_node) {
|
||||
+ WebPluginContainerImpl* plugin_container =
|
||||
+ GetPluginToPrintHelper(constrain_to_node);
|
||||
if (plugin_container && plugin_container->SupportsPaginatedPrint()) {
|
||||
print_context_ = MakeGarbageCollected<ChromePluginPrintContext>(
|
||||
GetFrame(), plugin_container, print_params);
|
||||
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
|
||||
index d7582f9c2f733d4beeb294df34dcbf09ce3970ce..f93ce60b7dd67c49feadc7679f2dc40dab0fb47e 100644
|
||||
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
|
||||
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
|
||||
@@ -279,6 +279,7 @@ class CORE_EXPORT WebLocalFrameImpl final
|
||||
bool HasVisibleContent() const override;
|
||||
WebRect VisibleContentRect() const override;
|
||||
void DispatchBeforePrintEvent() override;
|
||||
+ WebPlugin* GetPluginToPrint(const WebNode& constrain_to_node) override;
|
||||
int PrintBegin(const WebPrintParams&,
|
||||
const WebNode& constrain_to_node) override;
|
||||
float GetPrintPageShrink(int page) override;
|
||||
@@ -470,6 +471,9 @@ class CORE_EXPORT WebLocalFrameImpl final
|
||||
// A helper for DispatchBeforePrintEvent() and DispatchAfterPrintEvent().
|
||||
void DispatchPrintEventRecursively(const AtomicString& event_type);
|
||||
|
||||
+ WebPluginContainerImpl* GetPluginToPrintHelper(
|
||||
+ const WebNode& constrain_to_node);
|
||||
+
|
||||
Node* ContextMenuNodeInner() const;
|
||||
|
||||
WebLocalFrameClient* client_;
|
||||
@@ -0,0 +1,48 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Adrienne Walker <enne@chromium.org>
|
||||
Date: Tue, 4 Aug 2020 20:10:23 +0000
|
||||
Subject: indexeddb: fix crash in WebIDBGetDBNamesCallbacksImpl
|
||||
|
||||
Resolve() can end up freeing WebIDBGetDBNamesCallbacksImpl by throwing a
|
||||
mojo error that deletes the self-owned associated receiver that owns it.
|
||||
So, don't call any other functions after it.
|
||||
|
||||
As the promise resolver can only resolve/reject once, it is safe to
|
||||
not clear it.
|
||||
|
||||
(cherry picked from commit da90fc39f5ca0f8dc1c665fbabad8ec229826f89)
|
||||
|
||||
Bug: 1106682
|
||||
Change-Id: Iea943f3c5c1e57adb6ad399baff49522f54d264b
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2311620
|
||||
Commit-Queue: Daniel Murphy <dmurph@chromium.org>
|
||||
Reviewed-by: Daniel Murphy <dmurph@chromium.org>
|
||||
Auto-Submit: enne <enne@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#790857}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2337096
|
||||
Reviewed-by: enne <enne@chromium.org>
|
||||
Commit-Queue: enne <enne@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4147@{#1023}
|
||||
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
|
||||
index 46c5b3432931f6caef98f05dbb6f3543b2711874..af1b4c4234c62671ed56b52d7bd13c3557d397e9 100644
|
||||
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
|
||||
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
|
||||
@@ -104,7 +104,6 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
|
||||
promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
|
||||
DOMExceptionCode::kUnknownError,
|
||||
"The databases() promise was rejected."));
|
||||
- promise_resolver_.Clear();
|
||||
}
|
||||
|
||||
void SuccessNamesAndVersionsList(
|
||||
@@ -128,7 +127,7 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
|
||||
ExecutionContext::From(promise_resolver_->GetScriptState()),
|
||||
&async_task_id_, "success");
|
||||
promise_resolver_->Resolve(name_and_version_list);
|
||||
- promise_resolver_.Clear();
|
||||
+ // Note: Resolve may cause |this| to be deleted.
|
||||
}
|
||||
|
||||
void SuccessStringList(const Vector<String>&) override { NOTREACHED(); }
|
||||
@@ -0,0 +1,62 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Adrienne Walker <enne@chromium.org>
|
||||
Date: Wed, 5 Aug 2020 00:44:52 +0000
|
||||
Subject: indexeddb: reset async tasks in ~WebIDBGetDBNamesCallbacksImpl
|
||||
|
||||
Since sometimes the WebIDBGetDBNamesCallbacksImpl can be destroyed when
|
||||
the promise is resolved, make sure that no code that could reference it
|
||||
is still around. Store the async task as an optional member so it can
|
||||
be cleared during the destructor.
|
||||
|
||||
Followup to:
|
||||
https://chromium-review.googlesource.com/c/chromium/src/+/2311620
|
||||
|
||||
(cherry picked from commit 4422ec665ddca3ac05ad90bac5d5ebee7cfc5536)
|
||||
|
||||
Bug: 1106682,1109467
|
||||
Change-Id: Id6a0ff0a3703fab94e9684e41f16d5a1bac20468
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2321332
|
||||
Reviewed-by: Daniel Murphy <dmurph@chromium.org>
|
||||
Commit-Queue: enne <enne@chromium.org>
|
||||
Auto-Submit: enne <enne@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#792121}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2337110
|
||||
Reviewed-by: enne <enne@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4147@{#1029}
|
||||
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
|
||||
index af1b4c4234c62671ed56b52d7bd13c3557d397e9..d0ea6c191643ad51ab9e1d97e7a81ca9a074092e 100644
|
||||
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
|
||||
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
|
||||
@@ -110,6 +110,7 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
|
||||
Vector<mojom::blink::IDBNameAndVersionPtr> names_and_versions) override {
|
||||
if (!promise_resolver_)
|
||||
return;
|
||||
+ DCHECK(!async_task_.has_value());
|
||||
|
||||
HeapVector<Member<IDBDatabaseInfo>> name_and_version_list;
|
||||
name_and_version_list.ReserveInitialCapacity(name_and_version_list.size());
|
||||
@@ -123,11 +124,12 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
|
||||
name_and_version_list.push_back(idb_info);
|
||||
}
|
||||
|
||||
- probe::AsyncTask async_task(
|
||||
+ async_task_.emplace(
|
||||
ExecutionContext::From(promise_resolver_->GetScriptState()),
|
||||
&async_task_id_, "success");
|
||||
promise_resolver_->Resolve(name_and_version_list);
|
||||
- // Note: Resolve may cause |this| to be deleted.
|
||||
+ // Note: Resolve may cause |this| to be deleted. async_task_ will be
|
||||
+ // completed in the destructor.
|
||||
}
|
||||
|
||||
void SuccessStringList(const Vector<String>&) override { NOTREACHED(); }
|
||||
@@ -189,6 +191,7 @@ class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
|
||||
|
||||
private:
|
||||
probe::AsyncTaskId async_task_id_;
|
||||
+ base::Optional<probe::AsyncTask> async_task_;
|
||||
Persistent<ScriptPromiseResolver> promise_resolver_;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taylor Brandstetter <deadbeef@chromium.org>
|
||||
Date: Wed, 12 Aug 2020 05:24:50 +0000
|
||||
Subject: Reconnect P2P socket dispatcher if network service dies.
|
||||
|
||||
Bug: chromium:1113227
|
||||
Change-Id: Ifc8e856fde4cf4eee25149f0a1e86a3cad71ea83
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2344747
|
||||
Commit-Queue: Taylor <deadbeef@chromium.org>
|
||||
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#797125}
|
||||
|
||||
diff --git a/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc b/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc
|
||||
index 6c3adda46f77bc15d7d1998871b0daeb34780a77..29884a255f24556f4dc8b41b22304938a4f0d775 100644
|
||||
--- a/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc
|
||||
+++ b/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc
|
||||
@@ -110,6 +110,18 @@ void P2PSocketDispatcher::RequestNetworkEventsIfNecessary() {
|
||||
void P2PSocketDispatcher::OnConnectionError() {
|
||||
base::AutoLock lock(p2p_socket_manager_lock_);
|
||||
p2p_socket_manager_.reset();
|
||||
+ // Attempt to reconnect in case the network service crashed in his being
|
||||
+ // restarted.
|
||||
+ PostCrossThreadTask(
|
||||
+ *main_task_runner_.get(), FROM_HERE,
|
||||
+ CrossThreadBindOnce(&P2PSocketDispatcher::ReconnectP2PSocketManager,
|
||||
+ scoped_refptr<P2PSocketDispatcher>(this)));
|
||||
+}
|
||||
+
|
||||
+void P2PSocketDispatcher::ReconnectP2PSocketManager() {
|
||||
+ network_notification_client_receiver_.reset();
|
||||
+ GetP2PSocketManager()->StartNetworkNotifications(
|
||||
+ network_notification_client_receiver_.BindNewPipeAndPassRemote());
|
||||
}
|
||||
|
||||
} // namespace blink
|
||||
diff --git a/third_party/blink/renderer/platform/p2p/socket_dispatcher.h b/third_party/blink/renderer/platform/p2p/socket_dispatcher.h
|
||||
index de9b6690e0b27d3c4a263e8f908c0a95a675a089..c250d2af99d5291f34a7e3bfdb63fcbb70a5bd73 100644
|
||||
--- a/third_party/blink/renderer/platform/p2p/socket_dispatcher.h
|
||||
+++ b/third_party/blink/renderer/platform/p2p/socket_dispatcher.h
|
||||
@@ -83,6 +83,7 @@ class PLATFORM_EXPORT P2PSocketDispatcher
|
||||
void RequestNetworkEventsIfNecessary();
|
||||
|
||||
void OnConnectionError();
|
||||
+ void ReconnectP2PSocketManager();
|
||||
|
||||
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
|
||||
|
||||
93
patches/chromium/reland_fix_uaf_in_selecttype.patch
Normal file
93
patches/chromium/reland_fix_uaf_in_selecttype.patch
Normal file
@@ -0,0 +1,93 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mason Freed <masonfreed@chromium.org>
|
||||
Date: Mon, 20 Jul 2020 19:55:49 +0000
|
||||
Subject: Reland "Fix UAF in SelectType"
|
||||
|
||||
This is a reland of 72158deaf3751325f1983c87829f65441ee32de3
|
||||
|
||||
The only change made here is to add the new test to LeakExpectations,
|
||||
pointing to crbug.com/1103082. Local testing shows that this leak
|
||||
is triggered by the new test, both before and after this patch. And
|
||||
since the patch fixes a UAF security bug, I'd like to land it with the
|
||||
test, and then work on the leak.
|
||||
|
||||
Fixed: 1102408
|
||||
Bug: 1103082
|
||||
|
||||
TBR=masonfreed@chromium.org
|
||||
|
||||
Original change's description:
|
||||
> Fix UAF in SelectType
|
||||
>
|
||||
> This fixes the UAF detected by ClusterFuzz in [1], caused by [2].
|
||||
> The test case added here is a minimized version of the clusterfuzz
|
||||
> case, and I verified that it crashes (ASAN UAF) before this patch
|
||||
> and no longer crashes after.
|
||||
>
|
||||
> [1] https://clusterfuzz.com/testcase-detail/6224868955193344
|
||||
> [2] https://chromium-review.googlesource.com/c/chromium/src/+/1912682
|
||||
>
|
||||
> Fixed: 1102408
|
||||
> Change-Id: Ieb6a9582ff5b9676596048920bbcff881fdc2eb2
|
||||
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2283901
|
||||
> Commit-Queue: Mason Freed <masonfreed@chromium.org>
|
||||
> Auto-Submit: Mason Freed <masonfreed@chromium.org>
|
||||
> Reviewed-by: Kent Tamura <tkent@chromium.org>
|
||||
> Cr-Commit-Position: refs/heads/master@{#785970}
|
||||
|
||||
(cherry picked from commit e1c45006a8e5a97778eeed0010a7f57d86e70ca4)
|
||||
|
||||
Change-Id: I471cb4abc98a7627803de4e434e0453cb729c15f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2288372
|
||||
Auto-Submit: Mason Freed <masonfreed@chromium.org>
|
||||
Reviewed-by: Kent Tamura <tkent@chromium.org>
|
||||
Commit-Queue: Mason Freed <masonfreed@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/master@{#786562}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2307621
|
||||
Reviewed-by: Mason Freed <masonfreed@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/4147@{#902}
|
||||
Cr-Branched-From: 16307825352720ae04d898f37efa5449ad68b606-refs/heads/master@{#768962}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/html/forms/select_type.cc b/third_party/blink/renderer/core/html/forms/select_type.cc
|
||||
index 345cbeb26823c5f111f2bc2b98fc15f99efe3314..b5a7e169964b40a51b073ae13b2922019bee0927 100644
|
||||
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
|
||||
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
|
||||
@@ -709,7 +709,7 @@ bool ListBoxSelectType::DefaultEventHandler(const Event& event) {
|
||||
|
||||
if (Page* page = select_->GetDocument().GetPage()) {
|
||||
page->GetAutoscrollController().StartAutoscrollForSelection(
|
||||
- layout_object);
|
||||
+ select_->GetLayoutObject());
|
||||
}
|
||||
}
|
||||
// Mousedown didn't happen in this element.
|
||||
diff --git a/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash-expected.txt b/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash-expected.txt
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..cbe81f73ef4a3684565b70d51fe460b22702964f
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash-expected.txt
|
||||
@@ -0,0 +1 @@
|
||||
+PASS - this test passes if it does not crash (ASAN)
|
||||
diff --git a/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash.html b/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..8e9361dd7cd1f954a2c482aab7c182789ddc0701
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/fast/forms/select/select-change-layout-object-crash.html
|
||||
@@ -0,0 +1,17 @@
|
||||
+<style>
|
||||
+.c:hover { display: block; }
|
||||
+</style>
|
||||
+
|
||||
+<select id=target autofocus=autofocus size=2 class=c></select>
|
||||
+
|
||||
+<script>
|
||||
+if (window.testRunner)
|
||||
+ testRunner.dumpAsText();
|
||||
+
|
||||
+window.onload = function() {
|
||||
+ eventSender.beginDragWithFiles( ["resources/file-for-drag-to-navigate.html"]);
|
||||
+ eventSender.mouseMoveTo(target.offsetLeft + 5, target.offsetTop + 5);
|
||||
+};
|
||||
+</script>
|
||||
+
|
||||
+<p>PASS - this test passes if it does not crash (ASAN)</p>
|
||||
@@ -0,0 +1,92 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Thu, 17 Oct 2019 18:00:32 -0700
|
||||
Subject: feat: add hook to notify script ready from WorkerScriptController
|
||||
|
||||
In Off-the-main-thread fetch, the WorkerGlobalScope will be in a half
|
||||
initialized state until the script is finished downloading.
|
||||
|
||||
Doc: https://docs.google.com/document/d/1JCv8TD2nPLNC2iRCp_D1OM4I3uTS0HoEobuTymaMqgw/edit
|
||||
|
||||
During this stage if the global object is transformed for ex: copying properties
|
||||
in DidInitializeWorkerContextOnWorkerThread hook then an access to property like
|
||||
location will result in a crash WorkerGlobalScope::Url() because the script has
|
||||
not been set with response URL yet.
|
||||
|
||||
This issue cannot happen in chromium with existing usage, but can surface when an
|
||||
embedder tries to integrate Node.js in the worker. Hence, this new hook is proposed
|
||||
that clearly establishes the worker script is ready for evaluation with the scope
|
||||
initialized.
|
||||
|
||||
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
|
||||
index a94b2b289eb1b5da820dd3bf620911f2edef7c68..e11d29b43553574425c524e722f31bb8c2996fcb 100644
|
||||
--- a/content/public/renderer/content_renderer_client.h
|
||||
+++ b/content/public/renderer/content_renderer_client.h
|
||||
@@ -385,6 +385,11 @@ class CONTENT_EXPORT ContentRendererClient {
|
||||
virtual void DidInitializeWorkerContextOnWorkerThread(
|
||||
v8::Local<v8::Context> context) {}
|
||||
|
||||
+ // Notifies that a worker script has been downloaded, scope initialized and
|
||||
+ // ready for evaluation. This function is called from the worker thread.
|
||||
+ virtual void WorkerScriptReadyForEvaluationOnWorkerThread(
|
||||
+ v8::Local<v8::Context> context) {}
|
||||
+
|
||||
// Notifies that a worker context will be destroyed. This function is called
|
||||
// from the worker thread.
|
||||
virtual void WillDestroyWorkerContextOnWorkerThread(
|
||||
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
|
||||
index a2fe4cc09ffdcdf93199c01c316a4173a6f84d32..f8360eb68629a5d6b56471ef714312e1c5826025 100644
|
||||
--- a/content/renderer/renderer_blink_platform_impl.cc
|
||||
+++ b/content/renderer/renderer_blink_platform_impl.cc
|
||||
@@ -876,6 +876,12 @@ void RendererBlinkPlatformImpl::WorkerContextCreated(
|
||||
worker);
|
||||
}
|
||||
|
||||
+void RendererBlinkPlatformImpl::WorkerScriptReadyForEvaluation(
|
||||
+ const v8::Local<v8::Context>& worker) {
|
||||
+ GetContentClient()->renderer()->WorkerScriptReadyForEvaluationOnWorkerThread(
|
||||
+ worker);
|
||||
+}
|
||||
+
|
||||
bool RendererBlinkPlatformImpl::IsExcludedHeaderForServiceWorkerFetchEvent(
|
||||
const blink::WebString& header_name) {
|
||||
return GetContentClient()
|
||||
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
|
||||
index 236894dc0dd7242c1f47caeb982c4f24b784ce95..35f229faa607bc3f06c524619e385d9aa356e2f1 100644
|
||||
--- a/content/renderer/renderer_blink_platform_impl.h
|
||||
+++ b/content/renderer/renderer_blink_platform_impl.h
|
||||
@@ -182,6 +182,8 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl {
|
||||
void DidStartWorkerThread() override;
|
||||
void WillStopWorkerThread() override;
|
||||
void WorkerContextCreated(const v8::Local<v8::Context>& worker) override;
|
||||
+ void WorkerScriptReadyForEvaluation(
|
||||
+ const v8::Local<v8::Context>& worker) override;
|
||||
void WorkerContextWillDestroy(const v8::Local<v8::Context>& worker) override;
|
||||
bool IsExcludedHeaderForServiceWorkerFetchEvent(
|
||||
const blink::WebString& header_name) override;
|
||||
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
|
||||
index 3edcb4715c2dd8b2cf33f093710f14816b061117..79336bcad4d18e617677b2fb80d898680618e03b 100644
|
||||
--- a/third_party/blink/public/platform/platform.h
|
||||
+++ b/third_party/blink/public/platform/platform.h
|
||||
@@ -615,6 +615,8 @@ class BLINK_PLATFORM_EXPORT Platform {
|
||||
virtual void DidStartWorkerThread() {}
|
||||
virtual void WillStopWorkerThread() {}
|
||||
virtual void WorkerContextCreated(const v8::Local<v8::Context>& worker) {}
|
||||
+ virtual void WorkerScriptReadyForEvaluation(
|
||||
+ const v8::Local<v8::Context>& worker) {}
|
||||
virtual void WorkerContextWillDestroy(const v8::Local<v8::Context>& worker) {}
|
||||
virtual bool AllowScriptExtensionForServiceWorker(
|
||||
const WebSecurityOrigin& script_origin) {
|
||||
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
|
||||
index 83db0d2cba80184ecea9314b5869f836c0e3ea20..a25e2631bfd23bf1c212943d05b5f93a1c0201ed 100644
|
||||
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
|
||||
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
|
||||
@@ -323,6 +323,8 @@ void WorkerOrWorkletScriptController::PrepareForEvaluation() {
|
||||
wrapper_type_info->InstallConditionalFeatures(
|
||||
context, *world_, global_object, v8::Local<v8::Object>(),
|
||||
v8::Local<v8::Function>(), global_interface_template);
|
||||
+
|
||||
+ Platform::Current()->WorkerScriptReadyForEvaluation(context);
|
||||
}
|
||||
|
||||
void WorkerOrWorkletScriptController::DisableEvalInternal(
|
||||
@@ -7,11 +7,15 @@
|
||||
|
||||
"src/electron/patches/node": "src/third_party/electron_node",
|
||||
|
||||
"src/electron/patches/swiftshader": "src/third_party/swiftshader",
|
||||
|
||||
"src/electron/patches/pdfium": "src/third_party/pdfium",
|
||||
|
||||
"src/electron/patches/webrtc": "src/third_party/webrtc",
|
||||
|
||||
"src/electron/patches/skia": "src/third_party/skia",
|
||||
|
||||
"src/electron/patches/angle": "src/third_party/angle",
|
||||
|
||||
"src/electron/patches/ffmpeg": "src/third_party/ffmpeg"
|
||||
}
|
||||
|
||||
@@ -41,3 +41,6 @@ win_use_rtlgenrandom_from_advapi32_dll_directly.patch
|
||||
tools_update_certdata_txt.patch
|
||||
crypto_update_root_certificates.patch
|
||||
darwin_work_around_clock_jumping_back_in_time.patch
|
||||
lib_use_non-symbols_in_isurlinstance_check.patch
|
||||
fix_enable_tls_renegotiation.patch
|
||||
crypto_update_certdata_to_nss_3_56.patch
|
||||
|
||||
2407
patches/node/crypto_update_certdata_to_nss_3_56.patch
Normal file
2407
patches/node/crypto_update_certdata_to_nss_3_56.patch
Normal file
File diff suppressed because it is too large
Load Diff
27
patches/node/fix_enable_tls_renegotiation.patch
Normal file
27
patches/node/fix_enable_tls_renegotiation.patch
Normal file
@@ -0,0 +1,27 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jeremy Rose <nornagon@nornagon.net>
|
||||
Date: Tue, 18 Aug 2020 09:51:46 -0700
|
||||
Subject: fix: enable TLS renegotiation
|
||||
|
||||
This configures BoringSSL to behave more similarly to OpenSSL.
|
||||
See https://github.com/electron/electron/issues/18380.
|
||||
|
||||
This should be upstreamed.
|
||||
|
||||
diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc
|
||||
index 42b9469e38189f04745732afdeadd59e3ce6ad4c..f664f280d605a32d9f97121ab2816fab0fbe28c9 100644
|
||||
--- a/src/tls_wrap.cc
|
||||
+++ b/src/tls_wrap.cc
|
||||
@@ -125,6 +125,12 @@ void TLSWrap::InitSSL() {
|
||||
// - https://wiki.openssl.org/index.php/TLS1.3#Non-application_data_records
|
||||
SSL_set_mode(ssl_.get(), SSL_MODE_AUTO_RETRY);
|
||||
|
||||
+#ifdef OPENSSL_IS_BORINGSSL
|
||||
+ // OpenSSL allows renegotiation by default, but BoringSSL disables it.
|
||||
+ // Configure BoringSSL to match OpenSSL's behavior.
|
||||
+ SSL_set_renegotiate_mode(ssl_.get(), ssl_renegotiate_freely);
|
||||
+#endif
|
||||
+
|
||||
SSL_set_app_data(ssl_.get(), this);
|
||||
// Using InfoCallback isn't how we are supposed to check handshake progress:
|
||||
// https://github.com/openssl/openssl/issues/7199#issuecomment-420915993
|
||||
@@ -0,0 +1,39 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Tue, 4 Aug 2020 09:17:06 -0700
|
||||
Subject: lib: use non-symbols in isURLInstance check
|
||||
|
||||
This slightly changes the conditional used to determine whether or
|
||||
not something is a URL instance. Since Node.js adds symbols to the URL
|
||||
not specified by the WHATWG, those symbols are not present in other
|
||||
implementations (like Blink's) and therefore can cause false negatives.
|
||||
|
||||
This fixes that by slightly changing the check to properties present
|
||||
in all URL instances as specified in the WHATWG spec.
|
||||
|
||||
Upstreamed at: https://github.com/nodejs/node/pull/34622.
|
||||
|
||||
diff --git a/lib/internal/url.js b/lib/internal/url.js
|
||||
index 860fa4d7ad01b391d7d8e4c5ffe432d4d1d5983e..7d557a51930b7f225d128425fcc9db959c13392c 100644
|
||||
--- a/lib/internal/url.js
|
||||
+++ b/lib/internal/url.js
|
||||
@@ -1334,8 +1334,7 @@ function getPathFromURLPosix(url) {
|
||||
function fileURLToPath(path) {
|
||||
if (typeof path === 'string')
|
||||
path = new URL(path);
|
||||
- else if (path == null || !path[searchParams] ||
|
||||
- !path[searchParams][searchParams])
|
||||
+ else if (path == null || !path['origin'] || !path['href'])
|
||||
throw new ERR_INVALID_ARG_TYPE('path', ['string', 'URL'], path);
|
||||
if (path.protocol !== 'file:')
|
||||
throw new ERR_INVALID_URL_SCHEME('file');
|
||||
@@ -1383,8 +1382,7 @@ function pathToFileURL(filepath) {
|
||||
}
|
||||
|
||||
function toPathIfFileURL(fileURLOrPath) {
|
||||
- if (fileURLOrPath == null || !fileURLOrPath[searchParams] ||
|
||||
- !fileURLOrPath[searchParams][searchParams])
|
||||
+ if (fileURLOrPath == null || !fileURLOrPath['origin'] || !fileURLOrPath['href'])
|
||||
return fileURLOrPath;
|
||||
return fileURLToPath(fileURLOrPath);
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
backport_1080481.patch
|
||||
mallocpixelref_should_always_allocate_as_large_as_computebytesize.patch
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mike Reed <reed@google.com>
|
||||
Date: Mon, 20 Jul 2020 18:12:00 -0400
|
||||
Subject: MallocPixelRef should always allocate as large as computeByteSize()
|
||||
says
|
||||
|
||||
Bug: 1103827
|
||||
Change-Id: I837f92cf10a1a389fe1b0ba55ae1323e7e68f741
|
||||
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/304416
|
||||
Reviewed-by: Ben Wagner <bungeman@google.com>
|
||||
Commit-Queue: Mike Reed <reed@google.com>
|
||||
(cherry picked from commit c1eb58de32c016eb60e7a46046321ffe351e8222)
|
||||
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/308761
|
||||
Reviewed-by: Heather Miller <hcm@google.com>
|
||||
|
||||
diff --git a/src/core/SkMallocPixelRef.cpp b/src/core/SkMallocPixelRef.cpp
|
||||
index d998029a2be43b0ecd913ef8e7b2a52f419812dd..02fd9c458012f8b8b7982d2fe3c971e585b54ac2 100644
|
||||
--- a/src/core/SkMallocPixelRef.cpp
|
||||
+++ b/src/core/SkMallocPixelRef.cpp
|
||||
@@ -30,12 +30,9 @@ sk_sp<SkPixelRef> SkMallocPixelRef::MakeAllocate(const SkImageInfo& info, size_t
|
||||
if (!is_valid(info) || !info.validRowBytes(rowBytes)) {
|
||||
return nullptr;
|
||||
}
|
||||
- size_t size = 0;
|
||||
- if (!info.isEmpty() && rowBytes) {
|
||||
- size = info.computeByteSize(rowBytes);
|
||||
- if (SkImageInfo::ByteSizeOverflowed(size)) {
|
||||
- return nullptr;
|
||||
- }
|
||||
+ size_t size = info.computeByteSize(rowBytes);
|
||||
+ if (SkImageInfo::ByteSizeOverflowed(size)) {
|
||||
+ return nullptr;
|
||||
}
|
||||
void* addr = sk_calloc_canfail(size);
|
||||
if (nullptr == addr) {
|
||||
diff --git a/tests/BitmapTest.cpp b/tests/BitmapTest.cpp
|
||||
index 94bb5af818c518a1a732679659d85d3ae9d7af26..c9d215cb9b504f0f137ff156cf25950263a0277e 100644
|
||||
--- a/tests/BitmapTest.cpp
|
||||
+++ b/tests/BitmapTest.cpp
|
||||
@@ -398,3 +398,38 @@ DEF_TEST(getalphaf, reporter) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+/* computeByteSize() is documented to return 0 if height is zero, but does not
|
||||
+ * special-case width==0, so computeByteSize() can return non-zero for that
|
||||
+ * (since it is defined to return (height-1)*rb + ...
|
||||
+ *
|
||||
+ * Test that allocPixels() respects this, and allocates a buffer as large as
|
||||
+ * computeByteSize()... even though the bitmap is logicallly empty.
|
||||
+ */
|
||||
+DEF_TEST(bitmap_zerowidth_crbug_1103827, reporter) {
|
||||
+ const size_t big_rb = 1 << 16;
|
||||
+
|
||||
+ struct {
|
||||
+ int width, height;
|
||||
+ size_t rowbytes, expected_size;
|
||||
+ } rec[] = {
|
||||
+ { 2, 0, big_rb, 0 }, // zero-height means zero-size
|
||||
+ { 0, 2, big_rb, big_rb }, // zero-width is computed normally
|
||||
+ };
|
||||
+
|
||||
+ for (const auto& r : rec) {
|
||||
+ auto info = SkImageInfo::Make(r.width, r.height,
|
||||
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
||||
+ size_t size = info.computeByteSize(r.rowbytes);
|
||||
+ REPORTER_ASSERT(reporter, size == r.expected_size);
|
||||
+
|
||||
+ SkBitmap bm;
|
||||
+ bm.setInfo(info, r.rowbytes);
|
||||
+ REPORTER_ASSERT(reporter, size == bm.computeByteSize());
|
||||
+
|
||||
+ // Be sure we can actually write to that much memory. If the bitmap underallocated
|
||||
+ // the buffer, this should trash memory and crash (we hope).
|
||||
+ bm.allocPixels();
|
||||
+ sk_bzero(bm.getPixels(), size);
|
||||
+ }
|
||||
+}
|
||||
1
patches/swiftshader/.patches
Normal file
1
patches/swiftshader/.patches
Normal file
@@ -0,0 +1 @@
|
||||
fix_copying_cubemap_textures_out_of_bounds.patch
|
||||
@@ -0,0 +1,36 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Alexis Hetu <sugoi@google.com>
|
||||
Date: Wed, 12 Aug 2020 17:43:18 -0400
|
||||
Subject: Fix copying cubemap textures out of bounds
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Cubemap textures are created with a border, which means that their
|
||||
size is larger. Their range is also different. Instead of [0, dim-1],
|
||||
they have a [-1, dim] range. This means that if we copy them starting
|
||||
at [0, 0] for their full size, we'll overflow at the end, since the
|
||||
buffer starts at [-1, -1]. This cl prevents going through the fast
|
||||
copy path when we have a border.
|
||||
|
||||
Bug: chromium:1115345
|
||||
Change-Id: I333acfd6094645231eb111634359d82ed3d5c787
|
||||
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/47668
|
||||
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
|
||||
Reviewed-by: Corentin Wallez <cwallez@google.com>
|
||||
Reviewed-by: Chris Forbes <chrisforbes@google.com>
|
||||
Tested-by: Alexis Hétu <sugoi@google.com>
|
||||
|
||||
diff --git a/src/OpenGL/libGLESv2/Device.cpp b/src/OpenGL/libGLESv2/Device.cpp
|
||||
index 0758428fb8c2aa91a21539d533e8b32d78d15e2a..8d80db73d5e43cb7463455f85beabaaf0d773bff 100644
|
||||
--- a/src/OpenGL/libGLESv2/Device.cpp
|
||||
+++ b/src/OpenGL/libGLESv2/Device.cpp
|
||||
@@ -575,7 +575,7 @@ namespace es2
|
||||
bool fullCopy = (sRect.x0 == 0.0f) && (sRect.y0 == 0.0f) && (dRect.x0 == 0) && (dRect.y0 == 0) &&
|
||||
(sRect.x1 == (float)sWidth) && (sRect.y1 == (float)sHeight) && (dRect.x1 == dWidth) && (dRect.y1 == dHeight);
|
||||
bool alpha0xFF = false;
|
||||
- bool equalSlice = sourceSliceB == destSliceB;
|
||||
+ bool equalSlice = (sourceSliceB == destSliceB) && (source->getBorder() == 0) && (dest->getBorder() == 0);
|
||||
bool smallMargin = sourcePitchB <= source->getWidth() * Surface::bytes(source->getInternalFormat()) + 16;
|
||||
|
||||
if((source->getInternalFormat() == FORMAT_A8R8G8B8 && dest->getInternalFormat() == FORMAT_X8R8G8B8) ||
|
||||
@@ -9,3 +9,5 @@ revert_cleanup_switch_offset_of_to_offsetof_where_possible.patch
|
||||
fix_build_deprecated_attirbute_for_older_msvc_versions.patch
|
||||
backport_1084820.patch
|
||||
backport_986051.patch
|
||||
fix_alreadycalled_checking_in_element_closures.patch
|
||||
wasm_do_not_log_code_of_functions_whose_module_is_not_fully_loaded.patch
|
||||
|
||||
110
patches/v8/fix_alreadycalled_checking_in_element_closures.patch
Normal file
110
patches/v8/fix_alreadycalled_checking_in_element_closures.patch
Normal file
@@ -0,0 +1,110 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shu-yu Guo <syg@chromium.org>
|
||||
Date: Wed, 15 Jul 2020 17:12:44 -0700
|
||||
Subject: Fix [[AlreadyCalled]] checking in element closures
|
||||
|
||||
Bug: chromium:1105318
|
||||
Change-Id: I7b1c57b7ff7beaaa53c19a270d5a8c36b11baf17
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2301082
|
||||
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
|
||||
Commit-Queue: Shu-yu Guo <syg@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#68903}
|
||||
|
||||
diff --git a/src/builtins/promise-all-element-closure.tq b/src/builtins/promise-all-element-closure.tq
|
||||
index c320b24f036c1cf2afb2fb9d0906690cc90bdb82..bb65760bd16c4d7510e4d1267b6037aef32767e7 100644
|
||||
--- a/src/builtins/promise-all-element-closure.tq
|
||||
+++ b/src/builtins/promise-all-element-closure.tq
|
||||
@@ -81,9 +81,9 @@ namespace promise {
|
||||
generates 'PropertyArray::HashField::kMax';
|
||||
|
||||
transitioning macro PromiseAllResolveElementClosure<F: type>(
|
||||
- implicit context:
|
||||
- Context)(value: JSAny, function: JSFunction, wrapResultFunctor: F):
|
||||
- JSAny {
|
||||
+ implicit context: Context)(
|
||||
+ value: JSAny, function: JSFunction, wrapResultFunctor: F,
|
||||
+ hasResolveAndRejectClosures: constexpr bool): JSAny {
|
||||
// We use the {function}s context as the marker to remember whether this
|
||||
// resolve element closure was already called. It points to the resolve
|
||||
// element context (which is a FunctionContext) until it was called the
|
||||
@@ -99,10 +99,6 @@ namespace promise {
|
||||
const nativeContext = LoadNativeContext(context);
|
||||
function.context = nativeContext;
|
||||
|
||||
- // Update the value depending on whether Promise.all or
|
||||
- // Promise.allSettled is called.
|
||||
- const updatedValue = wrapResultFunctor.Call(nativeContext, value);
|
||||
-
|
||||
// Determine the index from the {function}.
|
||||
assert(kPropertyArrayNoHashSentinel == 0);
|
||||
const identityHash =
|
||||
@@ -117,13 +113,41 @@ namespace promise {
|
||||
const elements = UnsafeCast<FixedArray>(valuesArray.elements);
|
||||
const valuesLength = Convert<intptr>(valuesArray.length);
|
||||
if (index < valuesLength) {
|
||||
- // The {index} is in bounds of the {values_array},
|
||||
- // just store the {value} and continue.
|
||||
+ // The {index} is in bounds of the {values_array}, check if this element has
|
||||
+ // already been resolved, and store the {value} if not.
|
||||
+ //
|
||||
+ // Promise.allSettled, for each input element, has both a resolve and a
|
||||
+ // reject closure that share an [[AlreadyCalled]] boolean. That is, the
|
||||
+ // input element can only be settled once: after resolve is called, reject
|
||||
+ // returns early, and vice versa. Using {function}'s context as the marker
|
||||
+ // only tracks per-closure instead of per-element. When the second
|
||||
+ // resolve/reject closure is called on the same index, values.object[index]
|
||||
+ // will already exist and will not be the hole value. In that case, return
|
||||
+ // early. Everything up to this point is not yet observable to user code.
|
||||
+ // This is not a problem for Promise.all since Promise.all has a single
|
||||
+ // resolve closure (no reject) per element.
|
||||
+ if (hasResolveAndRejectClosures) {
|
||||
+ if (elements.objects[index] != TheHole) deferred {
|
||||
+ return Undefined;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Update the value depending on whether Promise.all or
|
||||
+ // Promise.allSettled is called.
|
||||
+ const updatedValue = wrapResultFunctor.Call(nativeContext, value);
|
||||
elements.objects[index] = updatedValue;
|
||||
} else {
|
||||
// Check if we need to grow the backing store.
|
||||
+ //
|
||||
+ // There's no need to check if this element has already been resolved for
|
||||
+ // Promise.allSettled if {values_array} has not yet grown to the index.
|
||||
const newLength = index + 1;
|
||||
const elementsLength = elements.length_intptr;
|
||||
+
|
||||
+ // Update the value depending on whether Promise.all or
|
||||
+ // Promise.allSettled is called.
|
||||
+ const updatedValue = wrapResultFunctor.Call(nativeContext, value);
|
||||
+
|
||||
if (index < elementsLength) {
|
||||
// The {index} is within bounds of the {elements} backing store, so
|
||||
// just store the {value} and update the "length" of the {values_array}.
|
||||
@@ -168,7 +192,7 @@ namespace promise {
|
||||
js-implicit context: Context, receiver: JSAny,
|
||||
target: JSFunction)(value: JSAny): JSAny {
|
||||
return PromiseAllResolveElementClosure(
|
||||
- value, target, PromiseAllWrapResultAsFulfilledFunctor{});
|
||||
+ value, target, PromiseAllWrapResultAsFulfilledFunctor{}, false);
|
||||
}
|
||||
|
||||
transitioning javascript builtin
|
||||
@@ -176,7 +200,7 @@ namespace promise {
|
||||
js-implicit context: Context, receiver: JSAny,
|
||||
target: JSFunction)(value: JSAny): JSAny {
|
||||
return PromiseAllResolveElementClosure(
|
||||
- value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{});
|
||||
+ value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}, true);
|
||||
}
|
||||
|
||||
transitioning javascript builtin
|
||||
@@ -184,6 +208,6 @@ namespace promise {
|
||||
js-implicit context: Context, receiver: JSAny,
|
||||
target: JSFunction)(value: JSAny): JSAny {
|
||||
return PromiseAllResolveElementClosure(
|
||||
- value, target, PromiseAllSettledWrapResultAsRejectedFunctor{});
|
||||
+ value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Emanuel Ziegler <ecmziegler@chromium.org>
|
||||
Date: Fri, 5 Jun 2020 19:54:58 +0200
|
||||
Subject: Do not log code of functions whose module is not fully loaded
|
||||
|
||||
This is a reland of change Idb1061cafcba7a2a654a207402dca520f79a3bbe.
|
||||
The access to wire_bytes has been protected by using atomic operations.
|
||||
|
||||
Under some circumstances, Wasm is trying to log code for which the
|
||||
wire bytes are not fully loaded yet. This can happen during streaming
|
||||
compilation when a few functions are already fully compiled but the
|
||||
engine is still streaming the remaining functions.
|
||||
|
||||
If the profiler now kicks in, it will attempt to log these freshly
|
||||
compiled functions. As these functions will not be executed before
|
||||
the module is fully compiled, we can simply defer the logging in this
|
||||
case.
|
||||
|
||||
R=clemensb@chromium.org
|
||||
|
||||
Bug: chromium:1085852
|
||||
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel
|
||||
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_isolates_rel_ng
|
||||
Change-Id: Iccb6607e8adb9fdaf6138d4ccd30de58d6a6cdff
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2230536
|
||||
Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org>
|
||||
Reviewed-by: Clemens Backes <clemensb@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#68336}
|
||||
|
||||
diff --git a/src/wasm/module-compiler.cc b/src/wasm/module-compiler.cc
|
||||
index 369dcfd9f79309ce10f0906bdcf11f53ef608deb..815a29640f51ba272a03c30526c0519ec1abd53f 100644
|
||||
--- a/src/wasm/module-compiler.cc
|
||||
+++ b/src/wasm/module-compiler.cc
|
||||
@@ -1080,7 +1080,10 @@ bool ExecuteCompilationUnits(
|
||||
}
|
||||
}
|
||||
|
||||
- native_module->engine()->LogCode(VectorOf(code_vector));
|
||||
+ // Defer logging code in case wire bytes were not fully received yet.
|
||||
+ if (native_module->HasWireBytes()) {
|
||||
+ native_module->engine()->LogCode(VectorOf(code_vector));
|
||||
+ }
|
||||
|
||||
compile_scope->compilation_state()->OnFinishedUnits(
|
||||
VectorOf(code_vector), VectorOf(results_to_publish));
|
||||
@@ -2357,6 +2360,7 @@ void AsyncStreamingProcessor::OnFinishedStream(OwnedVector<uint8_t> bytes) {
|
||||
} else {
|
||||
job_->native_module_->SetWireBytes(
|
||||
{std::move(job_->bytes_copy_), job_->wire_bytes_.length()});
|
||||
+ job_->native_module_->LogWasmCodes(job_->isolate_);
|
||||
}
|
||||
const bool needs_finish = job_->DecrementAndCheckFinisherCount();
|
||||
DCHECK_IMPLIES(!has_code_section, needs_finish);
|
||||
diff --git a/src/wasm/wasm-code-manager.cc b/src/wasm/wasm-code-manager.cc
|
||||
index 99cf484b17821b7b1a09cb52bcc206c4c839e919..99cf3561c8622cd83970d30ea6718362d5f3e58b 100644
|
||||
--- a/src/wasm/wasm-code-manager.cc
|
||||
+++ b/src/wasm/wasm-code-manager.cc
|
||||
@@ -1314,7 +1314,9 @@ class NativeModuleWireBytesStorage final : public WireBytesStorage {
|
||||
: wire_bytes_(std::move(wire_bytes)) {}
|
||||
|
||||
Vector<const uint8_t> GetCode(WireBytesRef ref) const final {
|
||||
- return wire_bytes_->as_vector().SubVector(ref.offset(), ref.end_offset());
|
||||
+ return std::atomic_load(&wire_bytes_)
|
||||
+ ->as_vector()
|
||||
+ .SubVector(ref.offset(), ref.end_offset());
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1325,7 +1327,7 @@ class NativeModuleWireBytesStorage final : public WireBytesStorage {
|
||||
void NativeModule::SetWireBytes(OwnedVector<const uint8_t> wire_bytes) {
|
||||
auto shared_wire_bytes =
|
||||
std::make_shared<OwnedVector<const uint8_t>>(std::move(wire_bytes));
|
||||
- wire_bytes_ = shared_wire_bytes;
|
||||
+ std::atomic_store(&wire_bytes_, shared_wire_bytes);
|
||||
if (!shared_wire_bytes->empty()) {
|
||||
compilation_state_->SetWireBytesStorage(
|
||||
std::make_shared<NativeModuleWireBytesStorage>(
|
||||
diff --git a/src/wasm/wasm-code-manager.h b/src/wasm/wasm-code-manager.h
|
||||
index 4b176f3ba61bcdfb922c5f5cbb92981d79393ffa..6960c224c32d3c8b237f973ecc399c49793af3e6 100644
|
||||
--- a/src/wasm/wasm-code-manager.h
|
||||
+++ b/src/wasm/wasm-code-manager.h
|
||||
@@ -537,7 +537,9 @@ class V8_EXPORT_PRIVATE NativeModule final {
|
||||
UseTrapHandler use_trap_handler() const { return use_trap_handler_; }
|
||||
void set_lazy_compile_frozen(bool frozen) { lazy_compile_frozen_ = frozen; }
|
||||
bool lazy_compile_frozen() const { return lazy_compile_frozen_; }
|
||||
- Vector<const uint8_t> wire_bytes() const { return wire_bytes_->as_vector(); }
|
||||
+ Vector<const uint8_t> wire_bytes() const {
|
||||
+ return std::atomic_load(&wire_bytes_)->as_vector();
|
||||
+ }
|
||||
const WasmModule* module() const { return module_.get(); }
|
||||
std::shared_ptr<const WasmModule> shared_module() const { return module_; }
|
||||
size_t committed_code_space() const {
|
||||
@@ -545,6 +547,10 @@ class V8_EXPORT_PRIVATE NativeModule final {
|
||||
}
|
||||
WasmEngine* engine() const { return engine_; }
|
||||
|
||||
+ bool HasWireBytes() const {
|
||||
+ auto wire_bytes = std::atomic_load(&wire_bytes_);
|
||||
+ return wire_bytes && !wire_bytes->empty();
|
||||
+ }
|
||||
void SetWireBytes(OwnedVector<const uint8_t> wire_bytes);
|
||||
|
||||
WasmCode* Lookup(Address) const;
|
||||
diff --git a/test/cctest/wasm/test-streaming-compilation.cc b/test/cctest/wasm/test-streaming-compilation.cc
|
||||
index 4d3f83daff766edb96d25934bb7ab5564f193046..87da8ea1d62b14f792e9525f5707fb4b4e05cd94 100644
|
||||
--- a/test/cctest/wasm/test-streaming-compilation.cc
|
||||
+++ b/test/cctest/wasm/test-streaming-compilation.cc
|
||||
@@ -1248,6 +1248,48 @@ STREAM_TEST(TestSetModuleCodeSection) {
|
||||
CHECK(tester.IsPromiseFulfilled());
|
||||
}
|
||||
|
||||
+// Test that profiler does not crash when module is only partly compiled.
|
||||
+STREAM_TEST(TestProfilingMidStreaming) {
|
||||
+ StreamTester tester;
|
||||
+ v8::Isolate* isolate = CcTest::isolate();
|
||||
+ Isolate* i_isolate = CcTest::i_isolate();
|
||||
+ Zone* zone = tester.zone();
|
||||
+
|
||||
+ // Build module with one exported (named) function.
|
||||
+ ZoneBuffer buffer(zone);
|
||||
+ {
|
||||
+ TestSignatures sigs;
|
||||
+ WasmModuleBuilder builder(zone);
|
||||
+ WasmFunctionBuilder* f = builder.AddFunction(sigs.v_v());
|
||||
+ uint8_t code[] = {kExprEnd};
|
||||
+ f->EmitCode(code, arraysize(code));
|
||||
+ builder.AddExport(VectorOf("foo", 3), f);
|
||||
+ builder.WriteTo(&buffer);
|
||||
+ }
|
||||
+
|
||||
+ // Start profiler to force code logging.
|
||||
+ v8::CpuProfiler* cpu_profiler = v8::CpuProfiler::New(isolate);
|
||||
+ v8::CpuProfilingOptions profile_options;
|
||||
+ cpu_profiler->StartProfiling(v8::String::Empty(isolate), profile_options);
|
||||
+
|
||||
+ // Send incomplete wire bytes and start compilation.
|
||||
+ tester.OnBytesReceived(buffer.begin(), buffer.end() - buffer.begin());
|
||||
+ tester.RunCompilerTasks();
|
||||
+
|
||||
+ // Trigger code logging explicitly like the profiler would do.
|
||||
+ CHECK(WasmCode::ShouldBeLogged(i_isolate));
|
||||
+ i_isolate->wasm_engine()->LogOutstandingCodesForIsolate(i_isolate);
|
||||
+ CHECK(tester.IsPromisePending());
|
||||
+
|
||||
+ // Finalize stream, stop profiler and clean up.
|
||||
+ tester.FinishStream();
|
||||
+ CHECK(tester.IsPromiseFulfilled());
|
||||
+ v8::CpuProfile* profile =
|
||||
+ cpu_profiler->StopProfiling(v8::String::Empty(isolate));
|
||||
+ profile->Delete();
|
||||
+ cpu_profiler->Dispose();
|
||||
+}
|
||||
+
|
||||
#undef STREAM_TEST
|
||||
|
||||
} // namespace wasm
|
||||
@@ -1,2 +1,3 @@
|
||||
backport_1076703.patch
|
||||
backport_978779.patch
|
||||
check_for_null_before_accessing_sctptransport_map.patch
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taylor Brandstetter <deadbeef@webrtc.org>
|
||||
Date: Mon, 13 Jul 2020 11:51:49 -0700
|
||||
Subject: Check for null before accessing SctpTransport map.
|
||||
|
||||
Bug: chromium:1104061
|
||||
Change-Id: I52d44ff1603341777a873e747c625665bc11bfa5
|
||||
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/179161
|
||||
Commit-Queue: Taylor <deadbeef@webrtc.org>
|
||||
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
|
||||
Cr-Commit-Position: refs/heads/master@{#31720}
|
||||
|
||||
diff --git a/media/sctp/sctp_transport.cc b/media/sctp/sctp_transport.cc
|
||||
index 1a6dc334e1fd1f69a094e763d13f71f4c5292a5e..ad68c37ac97263aec8de5fdd1657c1c1a1373895 100644
|
||||
--- a/media/sctp/sctp_transport.cc
|
||||
+++ b/media/sctp/sctp_transport.cc
|
||||
@@ -302,18 +302,21 @@ class SctpTransport::UsrSctpWrapper {
|
||||
}
|
||||
|
||||
static void UninitializeUsrSctp() {
|
||||
- delete g_transport_map_;
|
||||
RTC_LOG(LS_INFO) << __FUNCTION__;
|
||||
// usrsctp_finish() may fail if it's called too soon after the transports
|
||||
// are
|
||||
// closed. Wait and try again until it succeeds for up to 3 seconds.
|
||||
for (size_t i = 0; i < 300; ++i) {
|
||||
if (usrsctp_finish() == 0) {
|
||||
+ delete g_transport_map_;
|
||||
+ g_transport_map_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
rtc::Thread::SleepMs(10);
|
||||
}
|
||||
+ delete g_transport_map_;
|
||||
+ g_transport_map_ = nullptr;
|
||||
RTC_LOG(LS_ERROR) << "Failed to shutdown usrsctp.";
|
||||
}
|
||||
|
||||
@@ -340,6 +343,11 @@ class SctpTransport::UsrSctpWrapper {
|
||||
size_t length,
|
||||
uint8_t tos,
|
||||
uint8_t set_df) {
|
||||
+ if (!g_transport_map_) {
|
||||
+ RTC_LOG(LS_ERROR)
|
||||
+ << "OnSctpOutboundPacket called after usrsctp uninitialized?";
|
||||
+ return EINVAL;
|
||||
+ }
|
||||
SctpTransport* transport =
|
||||
g_transport_map_->Retrieve(reinterpret_cast<uintptr_t>(addr));
|
||||
if (!transport) {
|
||||
@@ -463,6 +471,12 @@ class SctpTransport::UsrSctpWrapper {
|
||||
// id of the transport that created them, so [0] is as good as any other.
|
||||
struct sockaddr_conn* sconn =
|
||||
reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
|
||||
+ if (!g_transport_map_) {
|
||||
+ RTC_LOG(LS_ERROR)
|
||||
+ << "GetTransportFromSocket called after usrsctp uninitialized?";
|
||||
+ usrsctp_freeladdrs(addrs);
|
||||
+ return nullptr;
|
||||
+ }
|
||||
SctpTransport* transport = g_transport_map_->Retrieve(
|
||||
reinterpret_cast<uintptr_t>(sconn->sconn_addr));
|
||||
usrsctp_freeladdrs(addrs);
|
||||
@@ -8,9 +8,10 @@ const semver = require('semver');
|
||||
const { ELECTRON_DIR } = require('../../lib/utils');
|
||||
const notesGenerator = require('./notes.js');
|
||||
|
||||
const semverify = version => version.replace(/^origin\//, '').replace('x', '0').replace(/-/g, '.');
|
||||
const semverify = version => version.replace(/^origin\//, '').replace(/[xy]/g, '0').replace(/-/g, '.');
|
||||
|
||||
const runGit = async (args) => {
|
||||
console.info(`Running: git ${args.join(' ')}`);
|
||||
const response = await GitProcess.exec(args, ELECTRON_DIR);
|
||||
if (response.exitCode !== 0) {
|
||||
throw new Error(response.stderr.trim());
|
||||
@@ -19,15 +20,20 @@ const runGit = async (args) => {
|
||||
};
|
||||
|
||||
const tagIsSupported = tag => tag && !tag.includes('nightly') && !tag.includes('unsupported');
|
||||
const tagIsBeta = tag => tag.includes('beta');
|
||||
const tagIsBeta = tag => tag && tag.includes('beta');
|
||||
const tagIsStable = tag => tagIsSupported(tag) && !tagIsBeta(tag);
|
||||
|
||||
const getTagsOf = async (point) => {
|
||||
return (await runGit(['tag', '--merged', point]))
|
||||
.split('\n')
|
||||
.map(tag => tag.trim())
|
||||
.filter(tag => semver.valid(tag))
|
||||
.sort(semver.compare);
|
||||
try {
|
||||
const tags = await runGit(['tag', '--merged', point]);
|
||||
return tags.split('\n')
|
||||
.map(tag => tag.trim())
|
||||
.filter(tag => semver.valid(tag))
|
||||
.sort(semver.compare);
|
||||
} catch (err) {
|
||||
console.error(`Failed to fetch tags for point ${point}`);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const getTagsOnBranch = async (point) => {
|
||||
@@ -41,26 +47,36 @@ const getTagsOnBranch = async (point) => {
|
||||
};
|
||||
|
||||
const getBranchOf = async (point) => {
|
||||
const branches = (await runGit(['branch', '-a', '--contains', point]))
|
||||
.split('\n')
|
||||
.map(branch => branch.trim())
|
||||
.filter(branch => !!branch);
|
||||
const current = branches.find(branch => branch.startsWith('* '));
|
||||
return current ? current.slice(2) : branches.shift();
|
||||
try {
|
||||
const branches = (await runGit(['branch', '-a', '--contains', point]))
|
||||
.split('\n')
|
||||
.map(branch => branch.trim())
|
||||
.filter(branch => !!branch);
|
||||
const current = branches.find(branch => branch.startsWith('* '));
|
||||
return current ? current.slice(2) : branches.shift();
|
||||
} catch (err) {
|
||||
console.error(`Failed to fetch branch for ${point}: `, err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const getAllBranches = async () => {
|
||||
return (await runGit(['branch', '--remote']))
|
||||
.split('\n')
|
||||
.map(branch => branch.trim())
|
||||
.filter(branch => !!branch)
|
||||
.filter(branch => branch !== 'origin/HEAD -> origin/master')
|
||||
.sort();
|
||||
try {
|
||||
const branches = await runGit(['branch', '--remote']);
|
||||
return branches.split('\n')
|
||||
.map(branch => branch.trim())
|
||||
.filter(branch => !!branch)
|
||||
.filter(branch => branch !== 'origin/HEAD -> origin/master')
|
||||
.sort();
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch all branches');
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const getStabilizationBranches = async () => {
|
||||
return (await getAllBranches())
|
||||
.filter(branch => /^origin\/\d+-\d+-x$/.test(branch));
|
||||
.filter(branch => /^origin\/\d+-\d+-x$/.test(branch) || /^origin\/\d+-x-y$/.test(branch));
|
||||
};
|
||||
|
||||
const getPreviousStabilizationBranch = async (current) => {
|
||||
@@ -120,7 +136,7 @@ const getPreviousPoint = async (point) => {
|
||||
}
|
||||
};
|
||||
|
||||
async function getReleaseNotes (range, newVersion, explicitLinks) {
|
||||
async function getReleaseNotes (range, newVersion) {
|
||||
const rangeList = range.split('..') || ['HEAD'];
|
||||
const to = rangeList.pop();
|
||||
const from = rangeList.pop() || (await getPreviousPoint(to));
|
||||
@@ -129,10 +145,9 @@ async function getReleaseNotes (range, newVersion, explicitLinks) {
|
||||
newVersion = to;
|
||||
}
|
||||
|
||||
console.log(`Generating release notes between ${from} and ${to} for version ${newVersion}`);
|
||||
const notes = await notesGenerator.get(from, to, newVersion);
|
||||
const ret = {
|
||||
text: notesGenerator.render(notes, explicitLinks)
|
||||
text: notesGenerator.render(notes)
|
||||
};
|
||||
|
||||
if (notes.unknown.length) {
|
||||
@@ -144,7 +159,7 @@ async function getReleaseNotes (range, newVersion, explicitLinks) {
|
||||
|
||||
async function main () {
|
||||
const opts = minimist(process.argv.slice(2), {
|
||||
boolean: ['explicit-links', 'help'],
|
||||
boolean: ['help'],
|
||||
string: ['version']
|
||||
});
|
||||
opts.range = opts._.shift();
|
||||
@@ -153,14 +168,13 @@ async function main () {
|
||||
console.log(`
|
||||
easy usage: ${name} version
|
||||
|
||||
full usage: ${name} [begin..]end [--version version] [--explicit-links]
|
||||
full usage: ${name} [begin..]end [--version version]
|
||||
|
||||
* 'begin' and 'end' are two git references -- tags, branches, etc --
|
||||
from which the release notes are generated.
|
||||
* if omitted, 'begin' defaults to the previous tag in end's branch.
|
||||
* if omitted, 'version' defaults to 'end'. Specifying a version is
|
||||
useful if you're making notes on a new version that isn't tagged yet.
|
||||
* 'explicit-links' makes every note's issue, commit, or pull an MD link
|
||||
|
||||
For example, these invocations are equivalent:
|
||||
${process.argv[1]} v4.0.1
|
||||
@@ -169,7 +183,7 @@ For example, these invocations are equivalent:
|
||||
return 0;
|
||||
}
|
||||
|
||||
const notes = await getReleaseNotes(opts.range, opts.version, opts['explicit-links']);
|
||||
const notes = await getReleaseNotes(opts.range, opts.version);
|
||||
console.log(notes.text);
|
||||
if (notes.warning) {
|
||||
throw new Error(notes.warning);
|
||||
|
||||
@@ -2,25 +2,23 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const childProcess = require('child_process');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const { GitProcess } = require('dugite');
|
||||
|
||||
const octokit = require('@octokit/rest')({
|
||||
auth: process.env.ELECTRON_GITHUB_TOKEN
|
||||
});
|
||||
const semver = require('semver');
|
||||
|
||||
const { ELECTRON_VERSION, SRC_DIR } = require('../../lib/utils');
|
||||
const { ELECTRON_DIR } = require('../../lib/utils');
|
||||
|
||||
const MAX_FAIL_COUNT = 3;
|
||||
const CHECK_INTERVAL = 5000;
|
||||
|
||||
const CACHE_DIR = path.resolve(__dirname, '.cache');
|
||||
const TROP_LOGIN = 'trop[bot]';
|
||||
|
||||
const NO_NOTES = 'No notes';
|
||||
const FOLLOW_REPOS = ['electron/electron', 'electron/node'];
|
||||
|
||||
const docTypes = new Set(['doc', 'docs']);
|
||||
const featTypes = new Set(['feat', 'feature']);
|
||||
@@ -28,6 +26,8 @@ const fixTypes = new Set(['fix']);
|
||||
const otherTypes = new Set(['spec', 'build', 'test', 'chore', 'deps', 'refactor', 'tools', 'vendor', 'perf', 'style', 'ci']);
|
||||
const knownTypes = new Set([...docTypes.keys(), ...featTypes.keys(), ...fixTypes.keys(), ...otherTypes.keys()]);
|
||||
|
||||
const getCacheDir = () => process.env.NOTES_CACHE_PATH || path.resolve(__dirname, '.cache');
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
@@ -39,6 +39,13 @@ class GHKey {
|
||||
this.repo = repo;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
static NewFromPull (pull) {
|
||||
const owner = pull.base.repo.owner.login;
|
||||
const repo = pull.base.repo.name;
|
||||
const number = pull.number;
|
||||
return new GHKey(owner, repo, number);
|
||||
}
|
||||
}
|
||||
|
||||
class Commit {
|
||||
@@ -47,10 +54,13 @@ class Commit {
|
||||
this.owner = owner; // string
|
||||
this.repo = repo; // string
|
||||
|
||||
this.body = null; // string
|
||||
this.isBreakingChange = false;
|
||||
this.issueNumber = null; // number
|
||||
this.note = null; // string
|
||||
|
||||
// A set of branches to which this change has been merged.
|
||||
// '8-x-y' => GHKey { owner: 'electron', repo: 'electron', number: 23714 }
|
||||
this.trops = new Map(); // Map<string,GHKey>
|
||||
|
||||
this.prKeys = new Set(); // GHKey
|
||||
this.revertHash = null; // string
|
||||
this.semanticType = null; // string
|
||||
@@ -99,65 +109,31 @@ const getNoteFromClerk = async (ghKey) => {
|
||||
return NO_NOTES;
|
||||
}
|
||||
if (comment.body.startsWith(PERSIST_LEAD)) {
|
||||
return comment.body
|
||||
let lines = comment.body
|
||||
.slice(PERSIST_LEAD.length).trim() // remove PERSIST_LEAD
|
||||
.split('\r?\n') // break into lines
|
||||
.split(/\r?\n/) // split into lines
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.startsWith(QUOTE_LEAD)) // notes are quoted
|
||||
.map(line => line.slice(QUOTE_LEAD.length)) // unquote the lines
|
||||
.join(' ') // join the note lines
|
||||
.map(line => line.slice(QUOTE_LEAD.length)); // unquote the lines
|
||||
|
||||
const firstLine = lines.shift();
|
||||
// indent anything after the first line to ensure that
|
||||
// multiline notes with their own sub-lists don't get
|
||||
// parsed in the markdown as part of the top-level list
|
||||
// (example: https://github.com/electron/electron/pull/25216)
|
||||
lines = lines.map(line => ' ' + line);
|
||||
return [firstLine, ...lines]
|
||||
.join('\n') // join the lines
|
||||
.trim();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// copied from https://github.com/electron/clerk/blob/master/src/index.ts#L4-L13
|
||||
const OMIT_FROM_RELEASE_NOTES_KEYS = [
|
||||
'no-notes',
|
||||
'no notes',
|
||||
'no_notes',
|
||||
'none',
|
||||
'no',
|
||||
'nothing',
|
||||
'empty',
|
||||
'blank'
|
||||
];
|
||||
|
||||
const getNoteFromBody = body => {
|
||||
if (!body) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const NOTE_PREFIX = 'Notes: ';
|
||||
const NOTE_HEADER = '#### Release Notes';
|
||||
|
||||
let note = body
|
||||
.split(/\r?\n\r?\n/) // split into paragraphs
|
||||
.map(paragraph => paragraph.trim())
|
||||
.map(paragraph => paragraph.startsWith(NOTE_HEADER) ? paragraph.slice(NOTE_HEADER.length).trim() : paragraph)
|
||||
.find(paragraph => paragraph.startsWith(NOTE_PREFIX));
|
||||
|
||||
if (note) {
|
||||
note = note
|
||||
.slice(NOTE_PREFIX.length)
|
||||
.replace(/<!--.*-->/, '') // '<!-- change summary here-->'
|
||||
.replace(/\r?\n/, ' ') // remove newlines
|
||||
.trim();
|
||||
}
|
||||
|
||||
if (note && OMIT_FROM_RELEASE_NOTES_KEYS.includes(note.toLowerCase())) {
|
||||
return NO_NOTES;
|
||||
}
|
||||
|
||||
return note;
|
||||
};
|
||||
|
||||
/**
|
||||
* Looks for our project's conventions in the commit message:
|
||||
*
|
||||
* 'semantic: some description' -- sets semanticType, subject
|
||||
* 'some description (#99999)' -- sets subject, pr
|
||||
* 'Fixes #3333' -- sets issueNumber
|
||||
* 'Merge pull request #99999 from ${branchname}' -- sets pr
|
||||
* 'This reverts commit ${sha}' -- sets revertHash
|
||||
* line starting with 'BREAKING CHANGE' in body -- sets isBreakingChange
|
||||
@@ -175,13 +151,6 @@ const parseCommitMessage = (commitMessage, commit) => {
|
||||
subject = subject.slice(0, pos).trim();
|
||||
}
|
||||
|
||||
if (body) {
|
||||
commit.body = body;
|
||||
|
||||
const note = getNoteFromBody(body);
|
||||
if (note) { commit.note = note; }
|
||||
}
|
||||
|
||||
// if the subject ends in ' (#dddd)', treat it as a pull request id
|
||||
let match;
|
||||
if ((match = subject.match(/^(.*)\s\(#(\d+)\)$/))) {
|
||||
@@ -213,7 +182,6 @@ const parseCommitMessage = (commitMessage, commit) => {
|
||||
|
||||
// https://help.github.com/articles/closing-issues-using-keywords/
|
||||
if ((match = body.match(/\b(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved|for)\s#(\d+)\b/i))) {
|
||||
commit.issueNumber = parseInt(match[1]);
|
||||
commit.semanticType = commit.semanticType || 'fix';
|
||||
}
|
||||
|
||||
@@ -237,32 +205,36 @@ const parseCommitMessage = (commitMessage, commit) => {
|
||||
const parsePullText = (pull, commit) => parseCommitMessage(`${pull.data.title}\n\n${pull.data.body}`, commit);
|
||||
|
||||
const getLocalCommitHashes = async (dir, ref) => {
|
||||
const args = ['log', '-z', '--format=%H', ref];
|
||||
return (await runGit(dir, args)).split('\0').map(hash => hash.trim());
|
||||
const args = ['log', '--format=%H', ref];
|
||||
return (await runGit(dir, args))
|
||||
.split(/\r?\n/) // split into lines
|
||||
.map(hash => hash.trim());
|
||||
};
|
||||
|
||||
// return an array of Commits
|
||||
const getLocalCommits = async (module, point1, point2) => {
|
||||
const { owner, repo, dir } = module;
|
||||
|
||||
const fieldSep = '||';
|
||||
const format = ['%H', '%B'].join(fieldSep);
|
||||
const args = ['log', '-z', '--cherry-pick', '--right-only', '--first-parent', `--format=${format}`, `${point1}..${point2}`];
|
||||
const logs = (await runGit(dir, args)).split('\0').map(field => field.trim());
|
||||
const fieldSep = ',';
|
||||
const format = ['%H', '%s'].join(fieldSep);
|
||||
const args = ['log', '--cherry-pick', '--right-only', '--first-parent', `--format=${format}`, `${point1}..${point2}`];
|
||||
const logs = (await runGit(dir, args))
|
||||
.split(/\r?\n/) // split into lines
|
||||
.map(field => field.trim());
|
||||
|
||||
const commits = [];
|
||||
for (const log of logs) {
|
||||
if (!log) {
|
||||
continue;
|
||||
}
|
||||
const [hash, message] = log.split(fieldSep, 2).map(field => field.trim());
|
||||
commits.push(parseCommitMessage(message, new Commit(hash, owner, repo)));
|
||||
const [hash, subject] = log.split(fieldSep, 2).map(field => field.trim());
|
||||
commits.push(parseCommitMessage(subject, new Commit(hash, owner, repo)));
|
||||
}
|
||||
return commits;
|
||||
};
|
||||
|
||||
const checkCache = async (name, operation) => {
|
||||
const filename = path.resolve(CACHE_DIR, name);
|
||||
const filename = path.resolve(getCacheDir(), name);
|
||||
if (fs.existsSync(filename)) {
|
||||
return JSON.parse(fs.readFileSync(filename, 'utf8'));
|
||||
}
|
||||
@@ -286,12 +258,42 @@ async function runRetryable (fn, maxRetries) {
|
||||
}
|
||||
}
|
||||
// Silently eat 404s.
|
||||
if (lastError.status !== 404) throw lastError;
|
||||
// Silently eat 422s, which come from "No commit found for SHA"
|
||||
if (lastError.status !== 404 && lastError.status !== 422) throw lastError;
|
||||
}
|
||||
|
||||
const getPullCacheFilename = ghKey => `${ghKey.owner}-${ghKey.repo}-pull-${ghKey.number}`;
|
||||
|
||||
const getCommitPulls = async (owner, repo, hash) => {
|
||||
const name = `${owner}-${repo}-commit-${hash}`;
|
||||
const retryableFunc = () => octokit.repos.listPullRequestsAssociatedWithCommit({ owner, repo, commit_sha: hash });
|
||||
let ret = await checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT));
|
||||
|
||||
// only merged pulls belong in release notes
|
||||
if (ret && ret.data) {
|
||||
ret.data = ret.data.filter(pull => pull.merged_at);
|
||||
}
|
||||
|
||||
// cache the pulls
|
||||
if (ret && ret.data) {
|
||||
for (const pull of ret.data) {
|
||||
const cachefile = getPullCacheFilename(GHKey.NewFromPull(pull));
|
||||
const payload = { ...ret, data: pull };
|
||||
await checkCache(cachefile, () => payload);
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the return value has the expected structure, even on failure
|
||||
if (!ret || !ret.data) {
|
||||
ret = { data: [] };
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
const getPullRequest = async (ghKey) => {
|
||||
const { number, owner, repo } = ghKey;
|
||||
const name = `${owner}-${repo}-pull-${number}`;
|
||||
const name = getPullCacheFilename(ghKey);
|
||||
const retryableFunc = () => octokit.pulls.get({ pull_number: number, owner, repo });
|
||||
return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT));
|
||||
};
|
||||
@@ -306,10 +308,20 @@ const getComments = async (ghKey) => {
|
||||
const addRepoToPool = async (pool, repo, from, to) => {
|
||||
const commonAncestor = await getCommonAncestor(repo.dir, from, to);
|
||||
|
||||
// add the commits
|
||||
const oldHashes = await getLocalCommitHashes(repo.dir, from);
|
||||
oldHashes.forEach(hash => { pool.processedHashes.add(hash); });
|
||||
// mark the old branch's commits as old news
|
||||
for (const oldHash of await getLocalCommitHashes(repo.dir, from)) {
|
||||
pool.processedHashes.add(oldHash);
|
||||
}
|
||||
|
||||
// get the new branch's commits and the pulls associated with them
|
||||
const commits = await getLocalCommits(repo, commonAncestor, to);
|
||||
for (const commit of commits) {
|
||||
const { owner, repo, hash } = commit;
|
||||
for (const pull of (await getCommitPulls(owner, repo, hash)).data) {
|
||||
commit.prKeys.add(GHKey.NewFromPull(pull));
|
||||
}
|
||||
}
|
||||
|
||||
pool.commits.push(...commits);
|
||||
|
||||
// add the pulls
|
||||
@@ -317,95 +329,59 @@ const addRepoToPool = async (pool, repo, from, to) => {
|
||||
let prKey;
|
||||
for (prKey of commit.prKeys.values()) {
|
||||
const pull = await getPullRequest(prKey);
|
||||
if (!pull || !pull.data) break; // couldn't get it
|
||||
if (pool.pulls[prKey.number]) break; // already have it
|
||||
if (!pull || !pull.data) continue; // couldn't get it
|
||||
pool.pulls[prKey.number] = pull;
|
||||
parsePullText(pull, commit);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/***
|
||||
**** Other Repos
|
||||
***/
|
||||
// @return Map<string,GHKey>
|
||||
// where the key is a branch name (e.g. '7-1-x' or '8-x-y')
|
||||
// and the value is a GHKey to the PR
|
||||
async function getMergedTrops (commit, pool) {
|
||||
const branches = new Map();
|
||||
|
||||
// other repos - gn
|
||||
for (const prKey of commit.prKeys.values()) {
|
||||
const pull = pool.pulls[prKey.number];
|
||||
const mergedBranches = new Set(
|
||||
((pull && pull.data && pull.data.labels) ? pull.data.labels : [])
|
||||
.map(label => ((label && label.name) ? label.name : '').match(/merged\/([0-9]+-[x0-9]-[xy0-9])/))
|
||||
.filter(match => match)
|
||||
.map(match => match[1])
|
||||
);
|
||||
|
||||
const getDepsVariable = async (ref, key) => {
|
||||
// get a copy of that reference point's DEPS file
|
||||
const deps = await runGit(ELECTRON_VERSION, ['show', `${ref}:DEPS`]);
|
||||
const filename = path.resolve(os.tmpdir(), 'DEPS');
|
||||
fs.writeFileSync(filename, deps);
|
||||
if (mergedBranches.size > 0) {
|
||||
const isTropComment = (comment) => comment && comment.user && comment.user.login === TROP_LOGIN;
|
||||
|
||||
// query the DEPS file
|
||||
const response = childProcess.spawnSync(
|
||||
'gclient',
|
||||
['getdep', '--deps-file', filename, '--var', key],
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
const ghKey = GHKey.NewFromPull(pull.data);
|
||||
const backportRegex = /backported this PR to "(.*)",\s+please check out #(\d+)/;
|
||||
const getBranchNameAndPullKey = (comment) => {
|
||||
const match = ((comment && comment.body) ? comment.body : '').match(backportRegex);
|
||||
return match ? [match[1], new GHKey(ghKey.owner, ghKey.repo, parseInt(match[2]))] : null;
|
||||
};
|
||||
|
||||
// cleanup
|
||||
fs.unlinkSync(filename);
|
||||
return response.stdout.trim();
|
||||
};
|
||||
|
||||
const getDependencyCommitsGN = async (pool, fromRef, toRef) => {
|
||||
const repos = [{ // just node
|
||||
owner: 'electron',
|
||||
repo: 'node',
|
||||
dir: path.resolve(SRC_DIR, 'third_party', 'electron_node'),
|
||||
deps_variable_name: 'node_version'
|
||||
}];
|
||||
|
||||
for (const repo of repos) {
|
||||
// the 'DEPS' file holds the dependency reference point
|
||||
const key = repo.deps_variable_name;
|
||||
const from = await getDepsVariable(fromRef, key);
|
||||
const to = await getDepsVariable(toRef, key);
|
||||
await addRepoToPool(pool, repo, from, to);
|
||||
}
|
||||
};
|
||||
|
||||
// Changes are interesting if they make a change relative to a previous
|
||||
// release in the same series. For example if you fix a Y.0.0 bug, that
|
||||
// should be included in the Y.0.1 notes even if it's also tropped back
|
||||
// to X.0.1.
|
||||
//
|
||||
// The phrase 'previous release' is important: if this is the first
|
||||
// prerelease or first stable release in a series, we omit previous
|
||||
// branches' changes. Otherwise we will have an overwhelmingly long
|
||||
// list of mostly-irrelevant changes.
|
||||
const shouldIncludeMultibranchChanges = (version) => {
|
||||
let show = true;
|
||||
|
||||
if (semver.valid(version)) {
|
||||
const prerelease = semver.prerelease(version);
|
||||
show = prerelease
|
||||
? parseInt(prerelease.pop()) > 1
|
||||
: semver.patch(version) > 0;
|
||||
const comments = await getComments(ghKey);
|
||||
((comments && comments.data) ? comments.data : [])
|
||||
.filter(isTropComment)
|
||||
.map(getBranchNameAndPullKey)
|
||||
.filter(pair => pair)
|
||||
.filter(([branch]) => mergedBranches.has(branch))
|
||||
.forEach(([branch, key]) => branches.set(branch, key));
|
||||
}
|
||||
}
|
||||
|
||||
return show;
|
||||
};
|
||||
|
||||
function getOldestMajorBranchOfPull (pull) {
|
||||
return pull.data.labels
|
||||
.map(label => label.name.match(/merged\/(\d+)-(\d+)-x/) || label.name.match(/merged\/(\d+)-x-y/))
|
||||
.filter(label => !!label)
|
||||
.map(label => parseInt(label[1]))
|
||||
.filter(major => !!major)
|
||||
.sort()
|
||||
.shift();
|
||||
return branches;
|
||||
}
|
||||
|
||||
function getOldestMajorBranchOfCommit (commit, pool) {
|
||||
return [ ...commit.prKeys.values() ]
|
||||
.map(prKey => pool.pulls[prKey.number])
|
||||
.filter(pull => !!pull)
|
||||
.map(pull => getOldestMajorBranchOfPull(pull))
|
||||
.filter(major => !!major)
|
||||
.sort()
|
||||
.shift();
|
||||
// @return the shorthand name of the branch that `ref` is on,
|
||||
// e.g. a ref of '10.0.0-beta.1' will return '10-x-y'
|
||||
async function getBranchNameOfRef (ref, dir) {
|
||||
return (await runGit(dir, ['branch', '--all', '--contains', ref, '--sort', 'version:refname']))
|
||||
.split(/\r?\n/) // split into lines
|
||||
.shift() // we sorted by refname and want the first result
|
||||
.match(/(?:.*\/)?(.*)/)[1] // 'remote/origins/10-x-y' -> '10-x-y'
|
||||
.trim();
|
||||
}
|
||||
|
||||
/***
|
||||
@@ -413,26 +389,20 @@ function getOldestMajorBranchOfCommit (commit, pool) {
|
||||
***/
|
||||
|
||||
const getNotes = async (fromRef, toRef, newVersion) => {
|
||||
if (!fs.existsSync(CACHE_DIR)) {
|
||||
fs.mkdirSync(CACHE_DIR);
|
||||
const cacheDir = getCacheDir();
|
||||
if (!fs.existsSync(cacheDir)) {
|
||||
fs.mkdirSync(cacheDir);
|
||||
}
|
||||
|
||||
const pool = new Pool();
|
||||
const toBranch = await getBranchNameOfRef(toRef, ELECTRON_DIR);
|
||||
|
||||
console.log(`Generating release notes between ${fromRef} and ${toRef} for version ${newVersion} in branch ${toBranch}`);
|
||||
|
||||
// get the electron/electron commits
|
||||
const electron = { owner: 'electron', repo: 'electron', dir: ELECTRON_VERSION };
|
||||
const electron = { owner: 'electron', repo: 'electron', dir: ELECTRON_DIR };
|
||||
await addRepoToPool(pool, electron, fromRef, toRef);
|
||||
|
||||
// Don't include submodules if comparing across major versions;
|
||||
// there's just too much churn otherwise.
|
||||
const includeDeps = semver.valid(fromRef) &&
|
||||
semver.valid(toRef) &&
|
||||
semver.major(fromRef) === semver.major(toRef);
|
||||
|
||||
if (includeDeps) {
|
||||
await getDependencyCommitsGN(pool, fromRef, toRef);
|
||||
}
|
||||
|
||||
// remove any old commits
|
||||
pool.commits = pool.commits.filter(commit => !pool.processedHashes.has(commit.hash));
|
||||
|
||||
@@ -457,27 +427,24 @@ const getNotes = async (fromRef, toRef, newVersion) => {
|
||||
// ensure the commit has a note
|
||||
for (const commit of pool.commits) {
|
||||
for (const prKey of commit.prKeys.values()) {
|
||||
commit.note = commit.note || await getNoteFromClerk(prKey);
|
||||
if (commit.note) {
|
||||
break;
|
||||
}
|
||||
commit.note = await getNoteFromClerk(prKey);
|
||||
}
|
||||
// use a fallback note in case someone missed a 'Notes' comment
|
||||
commit.note = commit.note || commit.subject;
|
||||
}
|
||||
|
||||
// remove non-user-facing commits
|
||||
pool.commits = pool.commits
|
||||
.filter(commit => commit && commit.note)
|
||||
.filter(commit => commit.note !== NO_NOTES)
|
||||
.filter(commit => !((commit.note || commit.subject).match(/^[Bb]ump v\d+\.\d+\.\d+/)));
|
||||
.filter(commit => commit.note.match(/^[Bb]ump v\d+\.\d+\.\d+/) === null);
|
||||
|
||||
if (!shouldIncludeMultibranchChanges(newVersion)) {
|
||||
const currentMajor = semver.parse(newVersion).major;
|
||||
pool.commits = pool.commits
|
||||
.filter(commit => getOldestMajorBranchOfCommit(commit, pool) >= currentMajor);
|
||||
for (const commit of pool.commits) {
|
||||
commit.trops = await getMergedTrops(commit, pool);
|
||||
}
|
||||
|
||||
pool.commits = removeSupercededChromiumUpdates(pool.commits);
|
||||
pool.commits = removeSupercededStackUpdates(pool.commits);
|
||||
|
||||
const notes = {
|
||||
breaking: [],
|
||||
@@ -486,7 +453,8 @@ const getNotes = async (fromRef, toRef, newVersion) => {
|
||||
fix: [],
|
||||
other: [],
|
||||
unknown: [],
|
||||
name: newVersion
|
||||
name: newVersion,
|
||||
toBranch
|
||||
};
|
||||
|
||||
pool.commits.forEach(commit => {
|
||||
@@ -511,50 +479,76 @@ const getNotes = async (fromRef, toRef, newVersion) => {
|
||||
return notes;
|
||||
};
|
||||
|
||||
const removeSupercededChromiumUpdates = (commits) => {
|
||||
const chromiumRegex = /^Updated Chromium to \d+\.\d+\.\d+\.\d+/;
|
||||
const updates = commits.filter(commit => (commit.note || commit.subject).match(chromiumRegex));
|
||||
const keepers = commits.filter(commit => !updates.includes(commit));
|
||||
const removeSupercededStackUpdates = (commits) => {
|
||||
const updateRegex = /^Updated ([a-zA-Z.]+) to v?([\d.]+)/;
|
||||
const notupdates = [];
|
||||
|
||||
// keep the newest update.
|
||||
if (updates.length) {
|
||||
const compare = (a, b) => (a.note || a.subject).localeCompare(b.note || b.subject);
|
||||
keepers.push(updates.sort(compare).pop());
|
||||
const newest = {};
|
||||
for (const commit of commits) {
|
||||
const match = (commit.note || commit.subject).match(updateRegex);
|
||||
if (!match) {
|
||||
notupdates.push(commit);
|
||||
continue;
|
||||
}
|
||||
const [, dep, version] = match;
|
||||
if (!newest[dep] || newest[dep].version < version) {
|
||||
newest[dep] = { commit, version };
|
||||
}
|
||||
}
|
||||
|
||||
return keepers;
|
||||
return [...notupdates, ...Object.values(newest).map(o => o.commit)];
|
||||
};
|
||||
|
||||
/***
|
||||
**** Render
|
||||
***/
|
||||
|
||||
const renderLink = (commit, explicitLinks) => {
|
||||
let link;
|
||||
const { owner, repo } = commit;
|
||||
const keyIt = commit.prKeys.values().next();
|
||||
if (keyIt.done) /* no PRs */ {
|
||||
const { hash } = commit;
|
||||
const url = `https://github.com/${owner}/${repo}/commit/${hash}`;
|
||||
const text = owner === 'electron' && repo === 'electron'
|
||||
? `${hash.slice(0, 8)}`
|
||||
: `${owner}/${repo}@${hash.slice(0, 8)}`;
|
||||
link = explicitLinks ? `[${text}](${url})` : text;
|
||||
} else {
|
||||
const { number } = keyIt.value;
|
||||
const url = `https://github.com/${owner}/${repo}/pull/${number}`;
|
||||
const text = owner === 'electron' && repo === 'electron'
|
||||
? `#${number}`
|
||||
: `${owner}/${repo}#${number}`;
|
||||
link = explicitLinks ? `[${text}](${url})` : text;
|
||||
}
|
||||
return link;
|
||||
};
|
||||
// @return the pull request's GitHub URL
|
||||
const buildPullURL = ghKey => `https://github.com/${ghKey.owner}/${ghKey.repo}/pull/${ghKey.number}`;
|
||||
|
||||
const renderCommit = (commit, explicitLinks) => {
|
||||
// clean up the note
|
||||
let note = commit.note || commit.subject;
|
||||
const renderPull = ghKey => `[#${ghKey.number}](${buildPullURL(ghKey)})`;
|
||||
|
||||
// @return the commit's GitHub URL
|
||||
const buildCommitURL = commit => `https://github.com/${commit.owner}/${commit.repo}/commit/${commit.hash}`;
|
||||
|
||||
const renderCommit = commit => `[${commit.hash.slice(0, 8)}](${buildCommitURL(commit)})`;
|
||||
|
||||
// @return a markdown link to the PR if available; otherwise, the git commit
|
||||
function renderLink (commit) {
|
||||
const maybePull = commit.prKeys.values().next();
|
||||
return maybePull.value ? renderPull(maybePull.value) : renderCommit(commit);
|
||||
}
|
||||
|
||||
// @return a terser branch name,
|
||||
// e.g. '7-2-x' -> '7.2' and '8-x-y' -> '8'
|
||||
const renderBranchName = name => name.replace(/-[a-zA-Z]/g, '').replace('-', '.');
|
||||
|
||||
const renderTrop = (branch, ghKey) => `[${renderBranchName(branch)}](${buildPullURL(ghKey)})`;
|
||||
|
||||
// @return markdown-formatted links to other branches' trops,
|
||||
// e.g. "(Also in 7.2, 8, 9)"
|
||||
function renderTrops (commit, excludeBranch) {
|
||||
const body = [...commit.trops.entries()]
|
||||
.filter(([branch]) => branch !== excludeBranch)
|
||||
.sort(([branchA], [branchB]) => parseInt(branchA) - parseInt(branchB)) // sort by semver major
|
||||
.map(([branch, key]) => renderTrop(branch, key))
|
||||
.join(', ');
|
||||
return body ? `<span style="font-size:small;">(Also in ${body})</span>` : body;
|
||||
}
|
||||
|
||||
// @return a slightly cleaned-up human-readable change description
|
||||
function renderDescription (commit) {
|
||||
let note = commit.note || commit.subject || '';
|
||||
note = note.trim();
|
||||
|
||||
// release notes bullet point every change, so if the note author
|
||||
// manually started the content with a bullet point, that will confuse
|
||||
// the markdown renderer -- remove the redundant bullet point
|
||||
// (example: https://github.com/electron/electron/pull/25216)
|
||||
if (note.startsWith('*')) {
|
||||
note = note.slice(1).trim();
|
||||
}
|
||||
|
||||
if (note.length !== 0) {
|
||||
note = note[0].toUpperCase() + note.substr(1);
|
||||
|
||||
@@ -590,30 +584,24 @@ const renderCommit = (commit, explicitLinks) => {
|
||||
}
|
||||
}
|
||||
|
||||
const link = renderLink(commit, explicitLinks);
|
||||
return note;
|
||||
}
|
||||
|
||||
return { note, link };
|
||||
};
|
||||
// @return markdown-formatted release note line item,
|
||||
// e.g. '* Fixed a foo. #12345 (Also in 7.2, 8, 9)'
|
||||
const renderNote = (commit, excludeBranch) =>
|
||||
`* ${renderDescription(commit)} ${renderLink(commit)} ${renderTrops(commit, excludeBranch)}\n`;
|
||||
|
||||
const renderNotes = (notes, explicitLinks) => {
|
||||
const renderNotes = (notes) => {
|
||||
const rendered = [`# Release Notes for ${notes.name}\n\n`];
|
||||
|
||||
const renderSection = (title, commits) => {
|
||||
if (commits.length === 0) {
|
||||
return;
|
||||
if (commits.length > 0) {
|
||||
rendered.push(
|
||||
`## ${title}\n\n`,
|
||||
...(commits.map(commit => renderNote(commit, notes.toBranch)).sort())
|
||||
);
|
||||
}
|
||||
const notes = new Map();
|
||||
for (const note of commits.map(commit => renderCommit(commit, explicitLinks))) {
|
||||
if (!notes.has(note.note)) {
|
||||
notes.set(note.note, [note.link]);
|
||||
} else {
|
||||
notes.get(note.note).push(note.link);
|
||||
}
|
||||
}
|
||||
rendered.push(`## ${title}\n\n`);
|
||||
const lines = [];
|
||||
notes.forEach((links, key) => lines.push(` * ${key} ${links.map(link => link.toString()).sort().join(', ')}\n`));
|
||||
rendered.push(...lines.sort(), '\n');
|
||||
};
|
||||
|
||||
renderSection('Breaking Changes', notes.breaking);
|
||||
@@ -622,7 +610,7 @@ const renderNotes = (notes, explicitLinks) => {
|
||||
renderSection('Other Changes', notes.other);
|
||||
|
||||
if (notes.docs.length) {
|
||||
const docs = notes.docs.map(commit => renderLink(commit, explicitLinks)).sort();
|
||||
const docs = notes.docs.map(commit => renderLink(commit)).sort();
|
||||
rendered.push('## Documentation\n\n', ` * Documentation changes: ${docs.join(', ')}\n`, '\n');
|
||||
}
|
||||
|
||||
|
||||
@@ -211,4 +211,8 @@ async function prepareRelease (isBeta, notesOnly) {
|
||||
}
|
||||
}
|
||||
|
||||
prepareRelease(!args.stable, args.notesOnly);
|
||||
prepareRelease(!args.stable, args.notesOnly)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
def is_fs_case_sensitive():
|
||||
with tempfile.NamedTemporaryFile(prefix='TmP') as tmp_file:
|
||||
return(not os.path.exists(tmp_file.name.lower()))
|
||||
|
||||
sys.path.append(
|
||||
os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + "/../.."))
|
||||
@@ -22,15 +29,26 @@ PDB_LIST = [
|
||||
os.path.join(RELEASE_DIR, '{0}.exe.pdb'.format(PROJECT_NAME))
|
||||
]
|
||||
|
||||
NPX_CMD = "npx"
|
||||
if sys.platform == "win32":
|
||||
NPX_CMD += ".cmd"
|
||||
|
||||
|
||||
def main():
|
||||
os.chdir(ELECTRON_DIR)
|
||||
files = []
|
||||
if PLATFORM == 'win32':
|
||||
for pdb in PDB_LIST:
|
||||
run_symstore(pdb, SYMBOLS_DIR, PRODUCT_NAME)
|
||||
files = glob.glob(SYMBOLS_DIR + '/*.pdb/*/*.pdb')
|
||||
else:
|
||||
files = glob.glob(SYMBOLS_DIR + '/*/*/*.sym') + glob.glob(SYMBOLS_DIR + '/*/*/*.src.zip')
|
||||
|
||||
files += glob.glob(SYMBOLS_DIR + '/*/*/*.sym')
|
||||
|
||||
for symbol_file in files:
|
||||
print("Generating Sentry src bundle for: " + symbol_file)
|
||||
subprocess.check_output([NPX_CMD, '@sentry/cli@1.51.1', 'difutil', 'bundle-sources', symbol_file])
|
||||
|
||||
files += glob.glob(SYMBOLS_DIR + '/*/*/*.src.zip')
|
||||
|
||||
# The file upload needs to be atom-shell/symbols/:symbol_name/:hash/:symbol
|
||||
os.chdir(SYMBOLS_DIR)
|
||||
@@ -38,7 +56,19 @@ def main():
|
||||
|
||||
# The symbol server needs lowercase paths, it will fail otherwise
|
||||
# So lowercase all the file paths here
|
||||
if is_fs_case_sensitive():
|
||||
for f in files:
|
||||
lower_f = f.lower()
|
||||
if lower_f != f:
|
||||
if os.path.exists(lower_f):
|
||||
shutil.rmtree(lower_f)
|
||||
lower_dir = os.path.dirname(lower_f)
|
||||
if not os.path.exists(lower_dir):
|
||||
os.makedirs(lower_dir)
|
||||
shutil.copy2(f, lower_f)
|
||||
files = [f.lower() for f in files]
|
||||
for f in files:
|
||||
assert os.path.exists(f)
|
||||
|
||||
bucket, access_key, secret_key = s3_config()
|
||||
upload_symbols(bucket, access_key, secret_key, files)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
param([string]$gomaDir=$PWD)
|
||||
$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py"
|
||||
Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start"
|
||||
$timedOut = $false; $waitTime = 0; $waitIncrement = 5; $timeout=120;
|
||||
Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut)
|
||||
if ($timedOut) {
|
||||
write-error 'Timed out waiting for goma to start'; exit 1;
|
||||
} else {
|
||||
Write-Output "Successfully started goma!"
|
||||
}
|
||||
param([string]$gomaDir=$PWD)
|
||||
$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py"
|
||||
Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start"
|
||||
$timedOut = $false; $waitTime = 0; $waitIncrement = 5; $timeout=120;
|
||||
Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut)
|
||||
if ($timedOut) {
|
||||
write-error 'Timed out waiting for goma to start'; exit 1;
|
||||
} else {
|
||||
Write-Output "Successfully started goma!"
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "base/command_line.h"
|
||||
#include "base/environment.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/strings/string_split.h"
|
||||
@@ -147,7 +148,14 @@ bool ElectronCrashReporterClient::GetCrashDumpLocation(
|
||||
#else
|
||||
bool ElectronCrashReporterClient::GetCrashDumpLocation(
|
||||
base::FilePath* crash_dir) {
|
||||
return base::PathService::Get(electron::DIR_CRASH_DUMPS, crash_dir);
|
||||
bool result = base::PathService::Get(electron::DIR_CRASH_DUMPS, crash_dir);
|
||||
{
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
if (result && !base::PathExists(*crash_dir)) {
|
||||
return base::CreateDirectory(*crash_dir);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -159,7 +167,7 @@ bool ElectronCrashReporterClient::GetCrashMetricsLocation(
|
||||
#endif // OS_MACOSX || OS_LINUX
|
||||
|
||||
bool ElectronCrashReporterClient::IsRunningUnattended() {
|
||||
return false;
|
||||
return !collect_stats_consent_;
|
||||
}
|
||||
|
||||
bool ElectronCrashReporterClient::GetCollectStatsConsent() {
|
||||
|
||||
@@ -233,7 +233,7 @@ int NodeMain(int argc, char* argv[]) {
|
||||
|
||||
node::ResetStdio();
|
||||
|
||||
env->set_can_call_into_js(false);
|
||||
node::Stop(env);
|
||||
env->stop_sub_worker_contexts();
|
||||
env->RunCleanup();
|
||||
|
||||
|
||||
@@ -1094,12 +1094,12 @@ Browser::LoginItemSettings App::GetLoginItemSettings(
|
||||
#if defined(USE_NSS_CERTS)
|
||||
void App::ImportCertificate(const base::DictionaryValue& options,
|
||||
net::CompletionRepeatingCallback callback) {
|
||||
auto browser_context = ElectronBrowserContext::From("", false);
|
||||
auto* browser_context = ElectronBrowserContext::From("", false);
|
||||
if (!certificate_manager_model_) {
|
||||
auto copy = base::DictionaryValue::From(
|
||||
base::Value::ToUniquePtrValue(options.Clone()));
|
||||
CertificateManagerModel::Create(
|
||||
browser_context.get(),
|
||||
browser_context,
|
||||
base::BindRepeating(&App::OnCertificateManagerModelCreated,
|
||||
base::Unretained(this), base::Passed(©),
|
||||
callback));
|
||||
|
||||
@@ -159,12 +159,32 @@ void BrowserWindow::DidFirstVisuallyNonEmptyPaint() {
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE, base::BindOnce(
|
||||
[](base::WeakPtr<BrowserWindow> self) {
|
||||
if (self)
|
||||
if (self && !self->did_ready_to_show_fired_) {
|
||||
self->did_ready_to_show_fired_ = true;
|
||||
self->Emit("ready-to-show");
|
||||
}
|
||||
},
|
||||
GetWeakPtr()));
|
||||
}
|
||||
|
||||
void BrowserWindow::DidFinishLoad(content::RenderFrameHost* render_frame_host,
|
||||
const GURL& validated_url) {
|
||||
// The DidFirstVisuallyNonEmptyPaint event is not very stable that, sometimes
|
||||
// on some machines it might not be fired, and the actual behavior depends on
|
||||
// the version of Chromium.
|
||||
// To work around this bug, we ensure the ready-to-show event is emitted if it
|
||||
// has not been emitted in did-finish-load event.
|
||||
// Note that we use did-finish-load event instead of dom-ready event because
|
||||
// the latter may actually be emitted before the ready-to-show event.
|
||||
// See also https://github.com/electron/electron/issues/7779.
|
||||
if (window()->IsVisible() || did_ready_to_show_fired_)
|
||||
return;
|
||||
if (render_frame_host->GetParent()) // child frame
|
||||
return;
|
||||
did_ready_to_show_fired_ = true;
|
||||
Emit("ready-to-show");
|
||||
}
|
||||
|
||||
void BrowserWindow::BeforeUnloadDialogCancelled() {
|
||||
WindowList::WindowCloseCancelled(window());
|
||||
// Cancel unresponsive event when window close is cancelled.
|
||||
@@ -288,11 +308,11 @@ void BrowserWindow::OnWindowResize() {
|
||||
}
|
||||
|
||||
void BrowserWindow::OnWindowLeaveFullScreen() {
|
||||
TopLevelWindow::OnWindowLeaveFullScreen();
|
||||
#if defined(OS_MACOSX)
|
||||
if (web_contents()->IsFullscreenForCurrentTab())
|
||||
web_contents()->ExitFullscreen(true);
|
||||
#endif
|
||||
TopLevelWindow::OnWindowLeaveFullScreen();
|
||||
}
|
||||
|
||||
void BrowserWindow::Focus() {
|
||||
|
||||
@@ -49,6 +49,8 @@ class BrowserWindow : public TopLevelWindow,
|
||||
content::RenderViewHost* new_host) override;
|
||||
void RenderViewCreated(content::RenderViewHost* render_view_host) override;
|
||||
void DidFirstVisuallyNonEmptyPaint() override;
|
||||
void DidFinishLoad(content::RenderFrameHost* render_frame_host,
|
||||
const GURL& validated_url) override;
|
||||
void BeforeUnloadDialogCancelled() override;
|
||||
void OnRendererUnresponsive(content::RenderProcessHost*) override;
|
||||
|
||||
@@ -114,6 +116,8 @@ class BrowserWindow : public TopLevelWindow,
|
||||
// it should be cancelled when we can prove that the window is responsive.
|
||||
base::CancelableClosure window_unresponsive_closure_;
|
||||
|
||||
bool did_ready_to_show_fired_ = false;
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
std::vector<mojom::DraggableRegionPtr> draggable_regions_;
|
||||
#endif
|
||||
|
||||
@@ -188,8 +188,8 @@ v8::Local<v8::Promise> Cookies::Get(const gin_helper::Dictionary& filter) {
|
||||
gin_helper::Promise<net::CookieList> promise(isolate());
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
auto* storage_partition = content::BrowserContext::GetDefaultStoragePartition(
|
||||
browser_context_.get());
|
||||
auto* storage_partition =
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_);
|
||||
auto* manager = storage_partition->GetCookieManagerForBrowserProcess();
|
||||
|
||||
base::DictionaryValue dict;
|
||||
@@ -224,8 +224,8 @@ v8::Local<v8::Promise> Cookies::Remove(const GURL& url,
|
||||
cookie_deletion_filter->url = url;
|
||||
cookie_deletion_filter->cookie_name = name;
|
||||
|
||||
auto* storage_partition = content::BrowserContext::GetDefaultStoragePartition(
|
||||
browser_context_.get());
|
||||
auto* storage_partition =
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_);
|
||||
auto* manager = storage_partition->GetCookieManagerForBrowserProcess();
|
||||
|
||||
manager->DeleteCookies(
|
||||
@@ -280,8 +280,8 @@ v8::Local<v8::Promise> Cookies::Set(base::DictionaryValue details) {
|
||||
options.set_include_httponly();
|
||||
}
|
||||
|
||||
auto* storage_partition = content::BrowserContext::GetDefaultStoragePartition(
|
||||
browser_context_.get());
|
||||
auto* storage_partition =
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_);
|
||||
auto* manager = storage_partition->GetCookieManagerForBrowserProcess();
|
||||
manager->SetCanonicalCookie(
|
||||
*canonical_cookie, url.scheme(), options,
|
||||
@@ -303,8 +303,8 @@ v8::Local<v8::Promise> Cookies::FlushStore() {
|
||||
gin_helper::Promise<void> promise(isolate());
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
auto* storage_partition = content::BrowserContext::GetDefaultStoragePartition(
|
||||
browser_context_.get());
|
||||
auto* storage_partition =
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_);
|
||||
auto* manager = storage_partition->GetCookieManagerForBrowserProcess();
|
||||
|
||||
manager->FlushCookieStore(base::BindOnce(
|
||||
|
||||
@@ -58,7 +58,7 @@ class Cookies : public gin_helper::TrackableObject<Cookies> {
|
||||
std::unique_ptr<base::CallbackList<void(
|
||||
const net::CookieChangeInfo& change)>::Subscription>
|
||||
cookie_change_subscription_;
|
||||
scoped_refptr<ElectronBrowserContext> browser_context_;
|
||||
ElectronBrowserContext* browser_context_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Cookies);
|
||||
};
|
||||
|
||||
@@ -40,7 +40,11 @@
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
#include "base/containers/span.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/guid.h"
|
||||
#include "components/crash/core/app/breakpad_linux.h"
|
||||
#include "components/crash/core/common/crash_keys.h"
|
||||
#include "v8/include/v8-wasm-trap-handler-posix.h"
|
||||
#include "v8/include/v8.h"
|
||||
#endif
|
||||
@@ -81,6 +85,46 @@ bool IsCrashReporterEnabled() {
|
||||
const std::map<std::string, std::string>& GetGlobalCrashKeys() {
|
||||
return GetGlobalCrashKeysMutable();
|
||||
}
|
||||
|
||||
bool GetClientIdPath(base::FilePath* path) {
|
||||
if (base::PathService::Get(electron::DIR_CRASH_DUMPS, path)) {
|
||||
*path = path->Append("client_id");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ReadClientId() {
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
std::string client_id;
|
||||
// "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".length == 36
|
||||
base::FilePath client_id_path;
|
||||
if (GetClientIdPath(&client_id_path) &&
|
||||
(!base::ReadFileToStringWithMaxSize(client_id_path, &client_id, 36) ||
|
||||
client_id.size() != 36))
|
||||
return std::string();
|
||||
return client_id;
|
||||
}
|
||||
|
||||
void WriteClientId(const std::string& client_id) {
|
||||
DCHECK_EQ(client_id.size(), 36u);
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
base::FilePath client_id_path;
|
||||
if (GetClientIdPath(&client_id_path))
|
||||
base::WriteFile(client_id_path, client_id.data(), client_id.size());
|
||||
}
|
||||
|
||||
std::string GetClientId() {
|
||||
static base::NoDestructor<std::string> client_id;
|
||||
if (!client_id->empty())
|
||||
return *client_id;
|
||||
*client_id = ReadClientId();
|
||||
if (client_id->empty()) {
|
||||
*client_id = base::GenerateGUID();
|
||||
WriteClientId(*client_id);
|
||||
}
|
||||
return *client_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Start(const std::string& submit_url,
|
||||
@@ -107,6 +151,7 @@ void Start(const std::string& submit_url,
|
||||
? "node"
|
||||
: command_line->GetSwitchValueASCII(::switches::kProcessType);
|
||||
#if defined(OS_LINUX)
|
||||
::crash_keys::SetMetricsClientIdFromGUID(GetClientId());
|
||||
auto& global_crash_keys = GetGlobalCrashKeysMutable();
|
||||
for (const auto& pair : global_extra) {
|
||||
global_crash_keys[pair.first] = pair.second;
|
||||
|
||||
@@ -19,6 +19,7 @@ bool IsCrashReporterEnabled();
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
const std::map<std::string, std::string>& GetGlobalCrashKeys();
|
||||
std::string GetClientId();
|
||||
#endif
|
||||
|
||||
// JS bindings API; exposed publicly because it's also called from node_main.cc
|
||||
|
||||
@@ -60,6 +60,9 @@ struct Converter<in_app_purchase::Product> {
|
||||
dict.Set("price", val.price);
|
||||
dict.Set("formattedPrice", val.formattedPrice);
|
||||
|
||||
// Currency Information
|
||||
dict.Set("currencyCode", val.currencyCode);
|
||||
|
||||
// Downloadable Content Information
|
||||
dict.Set("isDownloadable", val.isDownloadable);
|
||||
|
||||
|
||||
@@ -24,29 +24,27 @@
|
||||
if ((self = [super init])) {
|
||||
NSDistributedNotificationCenter* distCenter =
|
||||
[NSDistributedNotificationCenter defaultCenter];
|
||||
// A notification that the screen was locked.
|
||||
[distCenter addObserver:self
|
||||
selector:@selector(onScreenLocked:)
|
||||
name:@"com.apple.screenIsLocked"
|
||||
object:nil];
|
||||
// A notification that the screen was unlocked by the user.
|
||||
[distCenter addObserver:self
|
||||
selector:@selector(onScreenUnlocked:)
|
||||
name:@"com.apple.screenIsUnlocked"
|
||||
object:nil];
|
||||
|
||||
// A notification that the workspace posts before the machine goes to sleep.
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter]
|
||||
addObserver:self
|
||||
selector:@selector(isSuspending:)
|
||||
name:NSWorkspaceWillSleepNotification
|
||||
object:nil];
|
||||
|
||||
[distCenter addObserver:self
|
||||
selector:@selector(isSuspending:)
|
||||
name:NSWorkspaceWillSleepNotification
|
||||
object:nil];
|
||||
// A notification that the workspace posts when the machine wakes from
|
||||
// sleep.
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter]
|
||||
addObserver:self
|
||||
selector:@selector(isResuming:)
|
||||
name:NSWorkspaceDidWakeNotification
|
||||
object:nil];
|
||||
[distCenter addObserver:self
|
||||
selector:@selector(isResuming:)
|
||||
name:NSWorkspaceDidWakeNotification
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,18 @@ void PowerMonitor::InitPlatformSpecificMonitors() {
|
||||
|
||||
// Tel windows we want to be notified with session events
|
||||
WTSRegisterSessionNotification(window_, NOTIFY_FOR_THIS_SESSION);
|
||||
|
||||
// For Windows 8 and later, a new "connected standy" mode has been added and
|
||||
// we must explicitly register for its notifications.
|
||||
auto RegisterSuspendResumeNotification =
|
||||
reinterpret_cast<decltype(&::RegisterSuspendResumeNotification)>(
|
||||
GetProcAddress(GetModuleHandle(L"user32.dll"),
|
||||
"RegisterSuspendResumeNotification"));
|
||||
|
||||
if (RegisterSuspendResumeNotification) {
|
||||
RegisterSuspendResumeNotification(static_cast<HANDLE>(window_),
|
||||
DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK PowerMonitor::WndProcStatic(HWND hwnd,
|
||||
|
||||
@@ -284,7 +284,7 @@ Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
|
||||
|
||||
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
|
||||
SpellcheckService* service =
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_.get());
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_);
|
||||
if (service) {
|
||||
service->SetHunspellObserver(this);
|
||||
}
|
||||
@@ -297,7 +297,7 @@ Session::~Session() {
|
||||
|
||||
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
|
||||
SpellcheckService* service =
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_.get());
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_);
|
||||
if (service) {
|
||||
service->SetHunspellObserver(nullptr);
|
||||
}
|
||||
@@ -366,7 +366,7 @@ v8::Local<v8::Promise> Session::GetCacheSize() {
|
||||
gin_helper::Promise<int64_t> promise(isolate);
|
||||
auto handle = promise.GetHandle();
|
||||
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
|
||||
->GetNetworkContext()
|
||||
->ComputeHttpCacheSize(
|
||||
base::Time(), base::Time::Max(),
|
||||
@@ -390,7 +390,7 @@ v8::Local<v8::Promise> Session::ClearCache() {
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
auto handle = promise.GetHandle();
|
||||
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
|
||||
->GetNetworkContext()
|
||||
->ClearHttpCache(base::Time(), base::Time::Max(), nullptr,
|
||||
base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
|
||||
@@ -486,17 +486,17 @@ void Session::EnableNetworkEmulation(const gin_helper::Dictionary& options) {
|
||||
conditions->latency = base::TimeDelta::FromMillisecondsD(latency);
|
||||
}
|
||||
|
||||
auto* network_context = content::BrowserContext::GetDefaultStoragePartition(
|
||||
browser_context_.get())
|
||||
->GetNetworkContext();
|
||||
auto* network_context =
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
|
||||
->GetNetworkContext();
|
||||
network_context->SetNetworkConditions(network_emulation_token_,
|
||||
std::move(conditions));
|
||||
}
|
||||
|
||||
void Session::DisableNetworkEmulation() {
|
||||
auto* network_context = content::BrowserContext::GetDefaultStoragePartition(
|
||||
browser_context_.get())
|
||||
->GetNetworkContext();
|
||||
auto* network_context =
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
|
||||
->GetNetworkContext();
|
||||
network_context->SetNetworkConditions(
|
||||
network_emulation_token_, network::mojom::NetworkConditions::New());
|
||||
}
|
||||
@@ -516,7 +516,7 @@ void Session::SetCertVerifyProc(v8::Local<v8::Value> val,
|
||||
std::make_unique<CertVerifierClient>(proc),
|
||||
cert_verifier_client_remote.InitWithNewPipeAndPassReceiver());
|
||||
}
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
|
||||
->GetNetworkContext()
|
||||
->SetCertVerifierClient(std::move(cert_verifier_client_remote));
|
||||
|
||||
@@ -569,7 +569,7 @@ v8::Local<v8::Promise> Session::ClearHostResolverCache(
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
|
||||
->GetNetworkContext()
|
||||
->ClearHostCache(nullptr,
|
||||
base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
|
||||
@@ -583,7 +583,7 @@ v8::Local<v8::Promise> Session::ClearAuthCache() {
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_.get())
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
|
||||
->GetNetworkContext()
|
||||
->ClearHttpAuthCache(
|
||||
base::Time(),
|
||||
@@ -609,9 +609,9 @@ void Session::AllowNTLMCredentialsForDomains(const std::string& domains) {
|
||||
void Session::SetUserAgent(const std::string& user_agent,
|
||||
gin_helper::Arguments* args) {
|
||||
browser_context_->SetUserAgent(user_agent);
|
||||
auto* network_context = content::BrowserContext::GetDefaultStoragePartition(
|
||||
browser_context_.get())
|
||||
->GetNetworkContext();
|
||||
auto* network_context =
|
||||
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
|
||||
->GetNetworkContext();
|
||||
network_context->SetUserAgent(user_agent);
|
||||
|
||||
std::string accept_lang;
|
||||
@@ -790,10 +790,9 @@ v8::Local<v8::Value> Session::NetLog(v8::Isolate* isolate) {
|
||||
return v8::Local<v8::Value>::New(isolate, net_log_);
|
||||
}
|
||||
|
||||
static void StartPreconnectOnUI(
|
||||
scoped_refptr<ElectronBrowserContext> browser_context,
|
||||
const GURL& url,
|
||||
int num_sockets_to_preconnect) {
|
||||
static void StartPreconnectOnUI(ElectronBrowserContext* browser_context,
|
||||
const GURL& url,
|
||||
int num_sockets_to_preconnect) {
|
||||
std::vector<predictors::PreconnectRequest> requests = {
|
||||
{url::Origin::Create(url), num_sockets_to_preconnect,
|
||||
net::NetworkIsolationKey()}};
|
||||
@@ -823,7 +822,7 @@ void Session::Preconnect(const gin_helper::Dictionary& options,
|
||||
DCHECK_GT(num_sockets_to_preconnect, 0);
|
||||
base::PostTask(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(&StartPreconnectOnUI, base::RetainedRef(browser_context_),
|
||||
base::BindOnce(&StartPreconnectOnUI, base::Unretained(browser_context_),
|
||||
url, num_sockets_to_preconnect));
|
||||
}
|
||||
|
||||
@@ -867,7 +866,7 @@ v8::Local<v8::Promise> Session::ListWordsInSpellCheckerDictionary() {
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
SpellcheckService* spellcheck =
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_.get());
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_);
|
||||
|
||||
if (!spellcheck)
|
||||
promise.RejectWithErrorMessage(
|
||||
@@ -893,7 +892,7 @@ bool Session::AddWordToSpellCheckerDictionary(const std::string& word) {
|
||||
return false;
|
||||
|
||||
SpellcheckService* spellcheck =
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_.get());
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_);
|
||||
if (!spellcheck)
|
||||
return false;
|
||||
|
||||
@@ -915,7 +914,7 @@ bool Session::RemoveWordFromSpellCheckerDictionary(const std::string& word) {
|
||||
return false;
|
||||
|
||||
SpellcheckService* service =
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_.get());
|
||||
SpellcheckServiceFactory::GetForContext(browser_context_);
|
||||
if (!service)
|
||||
return false;
|
||||
|
||||
@@ -952,7 +951,7 @@ gin::Handle<Session> Session::CreateFrom(
|
||||
gin::Handle<Session> Session::FromPartition(v8::Isolate* isolate,
|
||||
const std::string& partition,
|
||||
base::DictionaryValue options) {
|
||||
scoped_refptr<ElectronBrowserContext> browser_context;
|
||||
ElectronBrowserContext* browser_context;
|
||||
if (partition.empty()) {
|
||||
browser_context =
|
||||
ElectronBrowserContext::From("", false, std::move(options));
|
||||
@@ -965,7 +964,7 @@ gin::Handle<Session> Session::FromPartition(v8::Isolate* isolate,
|
||||
browser_context =
|
||||
ElectronBrowserContext::From(partition, true, std::move(options));
|
||||
}
|
||||
return CreateFrom(isolate, browser_context.get());
|
||||
return CreateFrom(isolate, browser_context);
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -57,9 +57,7 @@ class Session : public gin_helper::TrackableObject<Session>,
|
||||
const std::string& partition,
|
||||
base::DictionaryValue options = base::DictionaryValue());
|
||||
|
||||
ElectronBrowserContext* browser_context() const {
|
||||
return browser_context_.get();
|
||||
}
|
||||
ElectronBrowserContext* browser_context() const { return browser_context_; }
|
||||
|
||||
// gin_helper::TrackableObject:
|
||||
static void BuildPrototype(v8::Isolate* isolate,
|
||||
@@ -146,7 +144,7 @@ class Session : public gin_helper::TrackableObject<Session>,
|
||||
// The client id to enable the network throttler.
|
||||
base::UnguessableToken network_emulation_token_;
|
||||
|
||||
scoped_refptr<ElectronBrowserContext> browser_context_;
|
||||
ElectronBrowserContext* browser_context_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Session);
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user