Compare commits

...

23 Commits

Author SHA1 Message Date
Electron Bot
9e9cc8f606 Bump v11.0.0-beta.6 2020-09-10 08:02:15 -07:00
trop[bot]
1e1d35fb87 fix(extensions): devtools now open for background pages (#25366)
refactor(extensions): remove unused InitWithBrowserContext method

fix(extensions): release background page WebContents to avoid crash

The background page WebContents instance is managed by the ExtensionHost.

fix(extensions): open background page devtools detached by default

test(extensions): add background page devtools test

chore: test fix for null web_contents()

fix: close background page devtools in test after opening

Co-authored-by: samuelmaddock <samuel.maddock@gmail.com>
2020-09-10 11:38:52 +09:00
trop[bot]
119cd24a7f fix: bind fake mojo service for badging (#25370)
* fix: bind fake mojo service for badging

* Add a test

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-09-10 09:14:53 +09:00
Eryk Rakowski
138fa6cdf3 feat(extensions): add support for some chrome.management APIs (#25098) (#25342)
* fix: initialize management policy

* fix(extensions): crash when using chrome.management

* test: add tests

* docs: add a note about chrome.management

* fix: lint errors

* fix: lint errors

* fix: remove favicon_service include

* fix: add missing management permission

* docs: more supported apis

* fix: extensions.md line endings
2020-09-08 20:11:50 +09:00
trop[bot]
7745e48498 chore: graceful handling of notes with sub-lists (#25312)
* chore: graceful handling of notes with sub-lists

Handle multine release notes that contain their own bullet points.

Also, if a release note begins with a bullet point, remove it because it
will confuse the markdown parser to have two bullet points.

* chore: make lint happy

* test: add tests for release note changes

* chore: only target current octokit

* chore: add commits to release-notes test cache

No behavior change; just includes files that ought to be cached to prevent
hitting octokit for them.

Co-authored-by: Charles Kerr <charles@charleskerr.com>
2020-09-08 17:12:09 +09:00
trop[bot]
b62e00f0ae fix: handle electron script errors better (#25331)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-09-08 16:26:41 +09:00
Electron Bot
ad876ac1b1 Bump v11.0.0-beta.5 2020-09-07 08:01:02 -07:00
trop[bot]
c35ec6bc34 fix: only focus a webContents if the window was not initially hidden (#25323)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-09-04 14:47:08 -07:00
trop[bot]
a08b3682c3 fix: avoid creating client_id file for empty DIR_CRASH_DUMPS (#25310)
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2020-09-03 19:09:20 -07:00
trop[bot]
c3ad33c28b fix: multiple dock icons when calling dock.show/hide (#25300)
* fix: mulitple dock icons when calling dock.show/hide

* test: run dock.show tests after dock.hide tests

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-09-03 13:57:06 -07:00
Electron Bot
59f9e417d4 Bump v11.0.0-beta.4 2020-09-03 08:01:35 -07:00
trop[bot]
7931c8abfd ci: cleanup up test app directories (#25257)
* ci: cleanup up test app directories

* ci: use electron prefix for a testing apps so that the can be cleaned up

* Revert "ci: cleanup up test app directories"

This reverts commit a47daba812.

* fixup test due to app name change

Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-09-02 10:05:09 +09:00
trop[bot]
622f5f84cf chore: wrap add/remove view in extra check (#25258)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-09-02 10:04:10 +09:00
trop[bot]
c8944df576 chore: force source code and markdown files to use LF line ending (#25176)
* chore: force source code and markdown files to use LF line ending

* chore: replace CRLF with LF

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-09-01 12:48:18 +09:00
Robo
3efbee2061 fix: client area inset calculation when maximized for framless windows (#25217)
* adopt per monitor scale factor

* fix: client area inset calculation when maximized

* address review feedback

* pass correct glass insets to GetDwmFrameInsetsInPixels

* remove unused code

* Windows 8 and 10 use the same DWM frame calculation

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>

Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-31 13:20:28 -07:00
trop[bot]
aafada554f docs: fix supported platforms of powerMonitor (#25211)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-31 10:16:26 -07:00
Electron Bot
56e81665fb Bump v11.0.0-beta.3 2020-08-31 08:01:05 -07:00
trop[bot]
e68f086d95 fix: do not reset process_id in URLLoaderFactoryParams (#25180)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-08-31 10:08:52 +09:00
trop[bot]
74edd99570 fix: save dialog extensions should be deterministic (#25193)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-08-28 11:38:07 -07:00
trop[bot]
63720fd603 fix: resolve RegisterSuspendResumeNotification dynamically (#25168)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2020-08-28 10:15:40 +09:00
Markus Olsson
d6bea2a681 fix: make shell.moveItemToTrash return false on Windows when move unsuccessful (#25171) 2020-08-27 17:43:52 -07:00
Electron Bot
5b53a08132 Bump v11.0.0-beta.2 2020-08-27 08:01:11 -07:00
Electron Bot
09e33b3420 Bump v11.0.0-beta.1 2020-08-26 10:51:08 -07:00
98 changed files with 2155 additions and 1376 deletions

10
.gitattributes vendored
View File

@@ -2,3 +2,13 @@
# files to be checked out with LF endings even if core.autocrlf is true. # files to be checked out with LF endings even if core.autocrlf is true.
*.patch text eol=lf *.patch text eol=lf
patches/**/.patches merge=union 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

View File

@@ -1 +1 @@
11.0.0-nightly.20200826 11.0.0-beta.6

View File

@@ -10,10 +10,9 @@ config.output = {
filename: path.basename(outPath) filename: path.basename(outPath)
}; };
const { wrapInitWithProfilingTimeout } = config; const { wrapInitWithProfilingTimeout, wrapInitWithTryCatch, ...webpackConfig } = config;
delete config.wrapInitWithProfilingTimeout;
webpack(config, (err, stats) => { webpack(webpackConfig, (err, stats) => {
if (err) { if (err) {
console.error(err); console.error(err);
process.exit(1); process.exit(1);
@@ -21,9 +20,17 @@ webpack(config, (err, stats) => {
console.error(stats.toString('normal')); console.error(stats.toString('normal'));
process.exit(1); process.exit(1);
} else { } 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) { if (wrapInitWithProfilingTimeout) {
const contents = fs.readFileSync(outPath, 'utf8'); contents = `function ___electron_webpack_init__() {
const newContents = `function ___electron_webpack_init__() {
${contents} ${contents}
}; };
if ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) { 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 { } else {
___electron_webpack_init__(); ___electron_webpack_init__();
}`; }`;
fs.writeFileSync(outPath, newContents);
} }
fs.writeFileSync(outPath, contents);
process.exit(0); process.exit(0);
} }
}); });

View File

@@ -69,7 +69,8 @@ module.exports = ({
loadElectronFromAlternateTarget, loadElectronFromAlternateTarget,
targetDeletesNodeGlobals, targetDeletesNodeGlobals,
target, target,
wrapInitWithProfilingTimeout wrapInitWithProfilingTimeout,
wrapInitWithTryCatch
}) => { }) => {
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts'); let entry = path.resolve(electronRoot, 'lib', target, 'init.ts');
if (!fs.existsSync(entry)) { if (!fs.existsSync(entry)) {
@@ -87,6 +88,7 @@ module.exports = ({
filename: `${target}.bundle.js` filename: `${target}.bundle.js`
}, },
wrapInitWithProfilingTimeout, wrapInitWithProfilingTimeout,
wrapInitWithTryCatch,
resolve: { resolve: {
alias: { alias: {
'@electron/internal': path.resolve(electronRoot, 'lib'), '@electron/internal': path.resolve(electronRoot, 'lib'),

View File

@@ -1,4 +1,5 @@
module.exports = require('./webpack.config.base')({ module.exports = require('./webpack.config.base')({
target: 'isolated_renderer', target: 'isolated_renderer',
alwaysHasNode: false alwaysHasNode: false,
wrapInitWithTryCatch: true
}); });

View File

@@ -2,5 +2,6 @@ module.exports = require('./webpack.config.base')({
target: 'renderer', target: 'renderer',
alwaysHasNode: true, alwaysHasNode: true,
targetDeletesNodeGlobals: true, targetDeletesNodeGlobals: true,
wrapInitWithProfilingTimeout: true wrapInitWithProfilingTimeout: true,
wrapInitWithTryCatch: true
}); });

View File

@@ -1,5 +1,6 @@
module.exports = require('./webpack.config.base')({ module.exports = require('./webpack.config.base')({
target: 'sandboxed_renderer', target: 'sandboxed_renderer',
alwaysHasNode: false, alwaysHasNode: false,
wrapInitWithProfilingTimeout: true wrapInitWithProfilingTimeout: true,
wrapInitWithTryCatch: true
}); });

View File

@@ -2,5 +2,6 @@ module.exports = require('./webpack.config.base')({
target: 'worker', target: 'worker',
loadElectronFromAlternateTarget: 'renderer', loadElectronFromAlternateTarget: 'renderer',
alwaysHasNode: true, alwaysHasNode: true,
targetDeletesNodeGlobals: true targetDeletesNodeGlobals: true,
wrapInitWithTryCatch: true
}); });

View File

@@ -102,3 +102,15 @@ The following methods of `chrome.tabs` are supported:
> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active > **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 > tab". Since Electron has no such concept, passing `-1` as a tab ID is not
> supported and will raise an error. > 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`

View File

@@ -8,19 +8,19 @@ Process: [Main](../glossary.md#main-process)
The `powerMonitor` module emits the following events: The `powerMonitor` module emits the following events:
### Event: 'suspend' ### Event: 'suspend' _macOS_ _Windows_
Emitted when the system is suspending. Emitted when the system is suspending.
### Event: 'resume' ### Event: 'resume' _macOS_ _Windows_
Emitted when system is resuming. Emitted when system is resuming.
### Event: 'on-ac' _Windows_ ### Event: 'on-ac' _macOS_ _Windows_
Emitted when the system changes to AC power. Emitted when the system changes to AC power.
### Event: 'on-battery' _Windows_ ### Event: 'on-battery' _macOS_ _Windows_
Emitted when system changes to battery power. Emitted when system changes to battery power.

View File

@@ -1,95 +1,95 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = require('electron') const { app, BrowserWindow, ipcMain, dialog } = require('electron')
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow let mainWindow
function createWindow () { function createWindow () {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadFile('index.html') mainWindow.loadFile('index.html')
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time // in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null mainWindow = null
}) })
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow) app.whenReady().then(createWindow)
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', function () { app.on('activate', function () {
// On macOS it is common to re-create a window in the app when the // 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. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
createWindow() createWindow()
} }
}) })
ipcMain.on('open-error-dialog', event => { ipcMain.on('open-error-dialog', event => {
dialog.showErrorBox('An Error Message', 'Demonstrating an error message.') dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
}) })
ipcMain.on('open-information-dialog', event => { ipcMain.on('open-information-dialog', event => {
const options = { const options = {
type: 'info', type: 'info',
title: 'Information', title: 'Information',
message: "This is an information dialog. Isn't it nice?", message: "This is an information dialog. Isn't it nice?",
buttons: ['Yes', 'No'] buttons: ['Yes', 'No']
} }
dialog.showMessageBox(options, index => { dialog.showMessageBox(options, index => {
event.sender.send('information-dialog-selection', index) event.sender.send('information-dialog-selection', index)
}) })
}) })
ipcMain.on('open-file-dialog', event => { ipcMain.on('open-file-dialog', event => {
dialog.showOpenDialog( dialog.showOpenDialog(
{ {
properties: ['openFile', 'openDirectory'] properties: ['openFile', 'openDirectory']
}, },
files => { files => {
if (files) { if (files) {
event.sender.send('selected-directory', files) event.sender.send('selected-directory', files)
} }
} }
) )
}) })
ipcMain.on('save-dialog', event => { ipcMain.on('save-dialog', event => {
const options = { const options = {
title: 'Save an Image', title: 'Save an Image',
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }] filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
} }
dialog.showSaveDialog(options, filename => { dialog.showSaveDialog(options, filename => {
event.sender.send('saved-file', filename) event.sender.send('saved-file', filename)
}) })
}) })
// In this file you can include the rest of your app's specific main process // 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. // code. You can also put them in separate files and require them here.

View File

@@ -1,18 +1,18 @@
const { ipcRenderer, shell } = require('electron') const { ipcRenderer, shell } = require('electron')
const links = document.querySelectorAll('a[href]') const links = document.querySelectorAll('a[href]')
const errorBtn = document.getElementById('error-dialog') const errorBtn = document.getElementById('error-dialog')
errorBtn.addEventListener('click', event => { errorBtn.addEventListener('click', event => {
ipcRenderer.send('open-error-dialog') ipcRenderer.send('open-error-dialog')
}) })
Array.prototype.forEach.call(links, (link) => { Array.prototype.forEach.call(links, (link) => {
const url = link.getAttribute('href') const url = link.getAttribute('href')
if (url.indexOf('http') === 0) { if (url.indexOf('http') === 0) {
link.addEventListener('click', (e) => { link.addEventListener('click', (e) => {
e.preventDefault() e.preventDefault()
shell.openExternal(url) shell.openExternal(url)
}) })
} }
}) })

View File

@@ -1,70 +1,70 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = require('electron') const { app, BrowserWindow, ipcMain, dialog } = require('electron')
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow let mainWindow
function createWindow () { function createWindow () {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadFile('index.html') mainWindow.loadFile('index.html')
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time // in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null mainWindow = null
}) })
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow) app.whenReady().then(createWindow)
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', function () { app.on('activate', function () {
// On macOS it is common to re-create a window in the app when the // 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. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
createWindow() createWindow()
} }
}) })
ipcMain.on('open-information-dialog', event => { ipcMain.on('open-information-dialog', event => {
const options = { const options = {
type: 'info', type: 'info',
title: 'Information', title: 'Information',
message: "This is an information dialog. Isn't it nice?", message: "This is an information dialog. Isn't it nice?",
buttons: ['Yes', 'No'] buttons: ['Yes', 'No']
} }
dialog.showMessageBox(options, index => { dialog.showMessageBox(options, index => {
event.sender.send('information-dialog-selection', index) event.sender.send('information-dialog-selection', index)
}) })
}) })
// In this file you can include the rest of your app's specific main process // 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. // code. You can also put them in separate files and require them here.

View File

@@ -1,25 +1,25 @@
const { ipcRenderer, shell } = require('electron') const { ipcRenderer, shell } = require('electron')
const informationBtn = document.getElementById('information-dialog') const informationBtn = document.getElementById('information-dialog')
const links = document.querySelectorAll('a[href]') const links = document.querySelectorAll('a[href]')
informationBtn.addEventListener('click', event => { informationBtn.addEventListener('click', event => {
ipcRenderer.send('open-information-dialog') ipcRenderer.send('open-information-dialog')
}) })
ipcRenderer.on('information-dialog-selection', (event, index) => { ipcRenderer.on('information-dialog-selection', (event, index) => {
let message = 'You selected ' let message = 'You selected '
if (index === 0) message += 'yes.' if (index === 0) message += 'yes.'
else message += 'no.' else message += 'no.'
document.getElementById('info-selection').innerHTML = message document.getElementById('info-selection').innerHTML = message
}) })
Array.prototype.forEach.call(links, (link) => { Array.prototype.forEach.call(links, (link) => {
const url = link.getAttribute('href') const url = link.getAttribute('href')
if (url.indexOf('http') === 0) { if (url.indexOf('http') === 0) {
link.addEventListener('click', (e) => { link.addEventListener('click', (e) => {
e.preventDefault() e.preventDefault()
shell.openExternal(url) shell.openExternal(url)
}) })
} }
}) })

View File

@@ -1,70 +1,70 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = require('electron') const { app, BrowserWindow, ipcMain, dialog } = require('electron')
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow let mainWindow
function createWindow () { function createWindow () {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadFile('index.html') mainWindow.loadFile('index.html')
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time // in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null mainWindow = null
}) })
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow) app.whenReady().then(createWindow)
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', function () { app.on('activate', function () {
// On macOS it is common to re-create a window in the app when the // 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. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
createWindow() createWindow()
} }
}) })
ipcMain.on('open-file-dialog', event => { ipcMain.on('open-file-dialog', event => {
dialog.showOpenDialog( dialog.showOpenDialog(
{ {
properties: ['openFile', 'openDirectory'] properties: ['openFile', 'openDirectory']
}, },
files => { files => {
if (files) { if (files) {
event.sender.send('selected-directory', files) event.sender.send('selected-directory', files)
} }
} }
) )
}) })
// In this file you can include the rest of your app's specific main process // 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. // code. You can also put them in separate files and require them here.

View File

@@ -1,22 +1,22 @@
const { ipcRenderer, shell } = require('electron') const { ipcRenderer, shell } = require('electron')
const selectDirBtn = document.getElementById('select-directory') const selectDirBtn = document.getElementById('select-directory')
const links = document.querySelectorAll('a[href]') const links = document.querySelectorAll('a[href]')
selectDirBtn.addEventListener('click', event => { selectDirBtn.addEventListener('click', event => {
ipcRenderer.send('open-file-dialog') ipcRenderer.send('open-file-dialog')
}) })
ipcRenderer.on('selected-directory', (event, path) => { ipcRenderer.on('selected-directory', (event, path) => {
document.getElementById('selected-file').innerHTML = `You selected: ${path}` document.getElementById('selected-file').innerHTML = `You selected: ${path}`
}) })
Array.prototype.forEach.call(links, (link) => { Array.prototype.forEach.call(links, (link) => {
const url = link.getAttribute('href') const url = link.getAttribute('href')
if (url.indexOf('http') === 0) { if (url.indexOf('http') === 0) {
link.addEventListener('click', (e) => { link.addEventListener('click', (e) => {
e.preventDefault() e.preventDefault()
shell.openExternal(url) shell.openExternal(url)
}) })
} }
}) })

View File

@@ -1,66 +1,66 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain, dialog } = require('electron') const { app, BrowserWindow, ipcMain, dialog } = require('electron')
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow let mainWindow
function createWindow () { function createWindow () {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadFile('index.html') mainWindow.loadFile('index.html')
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time // in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null mainWindow = null
}) })
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow) app.whenReady().then(createWindow)
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', function () { app.on('activate', function () {
// On macOS it is common to re-create a window in the app when the // 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. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
createWindow() createWindow()
} }
}) })
ipcMain.on('save-dialog', event => { ipcMain.on('save-dialog', event => {
const options = { const options = {
title: 'Save an Image', title: 'Save an Image',
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }] filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
} }
dialog.showSaveDialog(options, filename => { dialog.showSaveDialog(options, filename => {
event.sender.send('saved-file', filename) event.sender.send('saved-file', filename)
}) })
}) })
// In this file you can include the rest of your app's specific main process // 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. // code. You can also put them in separate files and require them here.

View File

@@ -1,23 +1,23 @@
const { ipcRenderer, shell } = require('electron') const { ipcRenderer, shell } = require('electron')
const saveBtn = document.getElementById('save-dialog') const saveBtn = document.getElementById('save-dialog')
const links = document.querySelectorAll('a[href]') const links = document.querySelectorAll('a[href]')
saveBtn.addEventListener('click', event => { saveBtn.addEventListener('click', event => {
ipcRenderer.send('save-dialog') ipcRenderer.send('save-dialog')
}) })
ipcRenderer.on('saved-file', (event, path) => { ipcRenderer.on('saved-file', (event, path) => {
if (!path) path = 'No path' if (!path) path = 'No path'
document.getElementById('file-saved').innerHTML = `Path selected: ${path}` document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
}) })
Array.prototype.forEach.call(links, (link) => { Array.prototype.forEach.call(links, (link) => {
const url = link.getAttribute('href') const url = link.getAttribute('href')
if (url.indexOf('http') === 0) { if (url.indexOf('http') === 0) {
link.addEventListener('click', (e) => { link.addEventListener('click', (e) => {
e.preventDefault() e.preventDefault()
shell.openExternal(url) shell.openExternal(url)
}) })
} }
}) })

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron') const { app, BrowserWindow } = require('electron')
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow let mainWindow
function createWindow () { function createWindow () {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadFile('index.html') mainWindow.loadFile('index.html')
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time // in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null mainWindow = null
}) })
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow) app.whenReady().then(createWindow)
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', function () { app.on('activate', function () {
// On macOS it is common to re-create a window in the app when the // 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. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
createWindow() createWindow()
} }
}) })
// In this file you can include the rest of your app's specific main process // 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. // code. You can also put them in separate files and require them here.

View File

@@ -1,13 +1,13 @@
const { shell } = require('electron') const { shell } = require('electron')
const os = require('os') const os = require('os')
const exLinksBtn = document.getElementById('open-ex-links') const exLinksBtn = document.getElementById('open-ex-links')
const fileManagerBtn = document.getElementById('open-file-manager') const fileManagerBtn = document.getElementById('open-file-manager')
fileManagerBtn.addEventListener('click', (event) => { fileManagerBtn.addEventListener('click', (event) => {
shell.showItemInFolder(os.homedir()) shell.showItemInFolder(os.homedir())
}) })
exLinksBtn.addEventListener('click', (event) => { exLinksBtn.addEventListener('click', (event) => {
shell.openExternal('https://electronjs.org') shell.openExternal('https://electronjs.org')
}) })

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron') const { app, BrowserWindow } = require('electron')
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow let mainWindow
function createWindow () { function createWindow () {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadFile('index.html') mainWindow.loadFile('index.html')
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time // in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null mainWindow = null
}) })
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow) app.whenReady().then(createWindow)
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', function () { app.on('activate', function () {
// On macOS it is common to re-create a window in the app when the // 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. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
createWindow() createWindow()
} }
}) })
// In this file you can include the rest of your app's specific main process // 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. // code. You can also put them in separate files and require them here.

View File

@@ -1,29 +1,29 @@
const basicNotification = { const basicNotification = {
title: 'Basic Notification', title: 'Basic Notification',
body: 'Short message part' body: 'Short message part'
} }
const notification = { const notification = {
title: 'Notification with image', title: 'Notification with image',
body: 'Short message plus a custom image', body: 'Short message plus a custom image',
icon: 'https://via.placeholder.com/150' icon: 'https://via.placeholder.com/150'
} }
const basicNotificationButton = document.getElementById('basic-noti') const basicNotificationButton = document.getElementById('basic-noti')
const notificationButton = document.getElementById('advanced-noti') const notificationButton = document.getElementById('advanced-noti')
notificationButton.addEventListener('click', () => { notificationButton.addEventListener('click', () => {
const myNotification = new window.Notification(notification.title, notification) const myNotification = new window.Notification(notification.title, notification)
myNotification.onclick = () => { myNotification.onclick = () => {
console.log('Notification clicked') console.log('Notification clicked')
} }
}) })
basicNotificationButton.addEventListener('click', () => { basicNotificationButton.addEventListener('click', () => {
const myNotification = new window.Notification(basicNotification.title, basicNotification) const myNotification = new window.Notification(basicNotification.title, basicNotification)
myNotification.onclick = () => { myNotification.onclick = () => {
console.log('Notification clicked') console.log('Notification clicked')
} }
}) })

File diff suppressed because one or more lines are too long

View File

@@ -1,35 +1,35 @@
const { ipcRenderer, shell } = require('electron') const { ipcRenderer, shell } = require('electron')
const trayBtn = document.getElementById('put-in-tray') const trayBtn = document.getElementById('put-in-tray')
const links = document.querySelectorAll('a[href]') const links = document.querySelectorAll('a[href]')
let trayOn = false let trayOn = false
trayBtn.addEventListener('click', function (event) { trayBtn.addEventListener('click', function (event) {
if (trayOn) { if (trayOn) {
trayOn = false trayOn = false
document.getElementById('tray-countdown').innerHTML = '' document.getElementById('tray-countdown').innerHTML = ''
ipcRenderer.send('remove-tray') ipcRenderer.send('remove-tray')
} else { } else {
trayOn = true trayOn = true
const message = 'Click demo again to remove.' const message = 'Click demo again to remove.'
document.getElementById('tray-countdown').innerHTML = message document.getElementById('tray-countdown').innerHTML = message
ipcRenderer.send('put-in-tray') ipcRenderer.send('put-in-tray')
} }
}) })
// Tray removed from context menu on icon // Tray removed from context menu on icon
ipcRenderer.on('tray-removed', function () { ipcRenderer.on('tray-removed', function () {
ipcRenderer.send('remove-tray') ipcRenderer.send('remove-tray')
trayOn = false trayOn = false
document.getElementById('tray-countdown').innerHTML = '' document.getElementById('tray-countdown').innerHTML = ''
}) })
Array.prototype.forEach.call(links, (link) => { Array.prototype.forEach.call(links, (link) => {
const url = link.getAttribute('href') const url = link.getAttribute('href')
if (url.indexOf('http') === 0) { if (url.indexOf('http') === 0) {
link.addEventListener('click', (e) => { link.addEventListener('click', (e) => {
e.preventDefault() e.preventDefault()
shell.openExternal(url) shell.openExternal(url)
}) })
} }
}) })

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron') const { app, BrowserWindow } = require('electron')
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow let mainWindow
function createWindow () { function createWindow () {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadFile('index.html') mainWindow.loadFile('index.html')
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time // in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null mainWindow = null
}) })
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow) app.whenReady().then(createWindow)
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', function () { app.on('activate', function () {
// On macOS it is common to re-create a window in the app when the // 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. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
createWindow() createWindow()
} }
}) })
// In this file you can include the rest of your app's specific main process // 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. // code. You can also put them in separate files and require them here.

View File

@@ -1,25 +1,25 @@
const { BrowserWindow } = require('electron').remote const { BrowserWindow } = require('electron').remote
const shell = require('electron').shell const shell = require('electron').shell
const framelessWindowBtn = document.getElementById('frameless-window') const framelessWindowBtn = document.getElementById('frameless-window')
const links = document.querySelectorAll('a[href]') const links = document.querySelectorAll('a[href]')
framelessWindowBtn.addEventListener('click', (event) => { framelessWindowBtn.addEventListener('click', (event) => {
const modalPath = 'https://electronjs.org' const modalPath = 'https://electronjs.org'
let win = new BrowserWindow({ frame: false }) let win = new BrowserWindow({ frame: false })
win.on('close', () => { win = null }) win.on('close', () => { win = null })
win.loadURL(modalPath) win.loadURL(modalPath)
win.show() win.show()
}) })
Array.prototype.forEach.call(links, (link) => { Array.prototype.forEach.call(links, (link) => {
const url = link.getAttribute('href') const url = link.getAttribute('href')
if (url.indexOf('http') === 0) { if (url.indexOf('http') === 0) {
link.addEventListener('click', (e) => { link.addEventListener('click', (e) => {
e.preventDefault() e.preventDefault()
shell.openExternal(url) shell.openExternal(url)
}) })
} }
}) })

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron') const { app, BrowserWindow } = require('electron')
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow let mainWindow
function createWindow () { function createWindow () {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadFile('index.html') mainWindow.loadFile('index.html')
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time // in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null mainWindow = null
}) })
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow) app.whenReady().then(createWindow)
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', function () { app.on('activate', function () {
// On macOS it is common to re-create a window in the app when the // 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. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
createWindow() createWindow()
} }
}) })
// In this file you can include the rest of your app's specific main process // 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. // code. You can also put them in separate files and require them here.

View File

@@ -1,35 +1,35 @@
const { BrowserWindow } = require('electron').remote const { BrowserWindow } = require('electron').remote
const shell = require('electron').shell const shell = require('electron').shell
const manageWindowBtn = document.getElementById('manage-window') const manageWindowBtn = document.getElementById('manage-window')
const links = document.querySelectorAll('a[href]') const links = document.querySelectorAll('a[href]')
let win let win
manageWindowBtn.addEventListener('click', (event) => { manageWindowBtn.addEventListener('click', (event) => {
const modalPath = 'https://electronjs.org' const modalPath = 'https://electronjs.org'
win = new BrowserWindow({ width: 400, height: 275 }) win = new BrowserWindow({ width: 400, height: 275 })
win.on('resize', updateReply) win.on('resize', updateReply)
win.on('move', updateReply) win.on('move', updateReply)
win.on('close', () => { win = null }) win.on('close', () => { win = null })
win.loadURL(modalPath) win.loadURL(modalPath)
win.show() win.show()
function updateReply () { function updateReply () {
const manageWindowReply = document.getElementById('manage-window-reply') const manageWindowReply = document.getElementById('manage-window-reply')
const message = `Size: ${win.getSize()} Position: ${win.getPosition()}` const message = `Size: ${win.getSize()} Position: ${win.getPosition()}`
manageWindowReply.innerText = message manageWindowReply.innerText = message
} }
}) })
Array.prototype.forEach.call(links, (link) => { Array.prototype.forEach.call(links, (link) => {
const url = link.getAttribute('href') const url = link.getAttribute('href')
if (url.indexOf('http') === 0) { if (url.indexOf('http') === 0) {
link.addEventListener('click', (e) => { link.addEventListener('click', (e) => {
e.preventDefault() e.preventDefault()
shell.openExternal(url) shell.openExternal(url)
}) })
} }
}) })

View File

@@ -1,56 +1,56 @@
// Modules to control application life and create native browser window // Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron') const { app, BrowserWindow } = require('electron')
// Keep a global reference of the window object, if you don't, the window will // 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. // be closed automatically when the JavaScript object is garbage collected.
let mainWindow let mainWindow
function createWindow () { function createWindow () {
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow({ mainWindow = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadFile('index.html') mainWindow.loadFile('index.html')
// Open the DevTools. // Open the DevTools.
// mainWindow.webContents.openDevTools() // mainWindow.webContents.openDevTools()
// Emitted when the window is closed. // Emitted when the window is closed.
mainWindow.on('closed', function () { mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time // in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null mainWindow = null
}) })
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(createWindow) app.whenReady().then(createWindow)
// Quit when all windows are closed. // Quit when all windows are closed.
app.on('window-all-closed', function () { app.on('window-all-closed', function () {
// On macOS it is common for applications and their menu bar // On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', function () { app.on('activate', function () {
// On macOS it is common to re-create a window in the app when the // 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. // dock icon is clicked and there are no other windows open.
if (mainWindow === null) { if (mainWindow === null) {
createWindow() createWindow()
} }
}) })
// In this file you can include the rest of your app's specific main process // 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. // code. You can also put them in separate files and require them here.

View File

@@ -1,48 +1,48 @@
const { BrowserWindow } = require('electron').remote const { BrowserWindow } = require('electron').remote
const shell = require('electron').shell const shell = require('electron').shell
const listenToWindowBtn = document.getElementById('listen-to-window') const listenToWindowBtn = document.getElementById('listen-to-window')
const focusModalBtn = document.getElementById('focus-on-modal-window') const focusModalBtn = document.getElementById('focus-on-modal-window')
const links = document.querySelectorAll('a[href]') const links = document.querySelectorAll('a[href]')
let win let win
listenToWindowBtn.addEventListener('click', () => { listenToWindowBtn.addEventListener('click', () => {
const modalPath = 'https://electronjs.org' const modalPath = 'https://electronjs.org'
win = new BrowserWindow({ width: 600, height: 400 }) win = new BrowserWindow({ width: 600, height: 400 })
const hideFocusBtn = () => { const hideFocusBtn = () => {
focusModalBtn.classList.add('disappear') focusModalBtn.classList.add('disappear')
focusModalBtn.classList.remove('smooth-appear') focusModalBtn.classList.remove('smooth-appear')
focusModalBtn.removeEventListener('click', clickHandler) focusModalBtn.removeEventListener('click', clickHandler)
} }
const showFocusBtn = (btn) => { const showFocusBtn = (btn) => {
if (!win) return if (!win) return
focusModalBtn.classList.add('smooth-appear') focusModalBtn.classList.add('smooth-appear')
focusModalBtn.classList.remove('disappear') focusModalBtn.classList.remove('disappear')
focusModalBtn.addEventListener('click', clickHandler) focusModalBtn.addEventListener('click', clickHandler)
} }
win.on('focus', hideFocusBtn) win.on('focus', hideFocusBtn)
win.on('blur', showFocusBtn) win.on('blur', showFocusBtn)
win.on('close', () => { win.on('close', () => {
hideFocusBtn() hideFocusBtn()
win = null win = null
}) })
win.loadURL(modalPath) win.loadURL(modalPath)
win.show() win.show()
const clickHandler = () => { win.focus() } const clickHandler = () => { win.focus() }
}) })
Array.prototype.forEach.call(links, (link) => { Array.prototype.forEach.call(links, (link) => {
const url = link.getAttribute('href') const url = link.getAttribute('href')
if (url.indexOf('http') === 0) { if (url.indexOf('http') === 0) {
link.addEventListener('click', (e) => { link.addEventListener('click', (e) => {
e.preventDefault() e.preventDefault()
shell.openExternal(url) shell.openExternal(url)
}) })
} }
}) })

View File

@@ -625,6 +625,8 @@ filenames = {
"shell/browser/extensions/api/resources_private/resources_private_api.h", "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.cc",
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h", "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.cc",
"shell/browser/extensions/api/tabs/tabs_api.h", "shell/browser/extensions/api/tabs/tabs_api.h",
"shell/browser/extensions/api/streams_private/streams_private_api.cc", "shell/browser/extensions/api/streams_private/streams_private_api.cc",

View File

@@ -28,9 +28,11 @@ Object.setPrototypeOf(BrowserWindow.prototype, BaseWindow.prototype);
// Though this hack is only needed on macOS when the app is launched from // Though this hack is only needed on macOS when the app is launched from
// Finder, we still do it on all platforms in case of other bugs we don't // Finder, we still do it on all platforms in case of other bugs we don't
// know. // know.
this.webContents.once('load-url' as any, function (this: WebContents) { if (this.webContents._initiallyShown) {
this.focus(); this.webContents.once('load-url' as any, function (this: WebContents) {
}); this.focus();
});
}
// Redirect focus/blur event to app instance too. // Redirect focus/blur event to app instance too.
this.on('blur', (event: Event) => { this.on('blur', (event: Event) => {

View File

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

View File

@@ -6,6 +6,7 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const { GitProcess } = require('dugite'); const { GitProcess } = require('dugite');
const { Octokit } = require('@octokit/rest'); const { Octokit } = require('@octokit/rest');
const octokit = new Octokit({ const octokit = new Octokit({
auth: process.env.ELECTRON_GITHUB_TOKEN auth: process.env.ELECTRON_GITHUB_TOKEN
@@ -109,13 +110,21 @@ const getNoteFromClerk = async (ghKey) => {
return NO_NOTES; return NO_NOTES;
} }
if (comment.body.startsWith(PERSIST_LEAD)) { if (comment.body.startsWith(PERSIST_LEAD)) {
return comment.body let lines = comment.body
.slice(PERSIST_LEAD.length).trim() // remove PERSIST_LEAD .slice(PERSIST_LEAD.length).trim() // remove PERSIST_LEAD
.split(/\r?\n/) // split into lines .split(/\r?\n/) // split into lines
.map(line => line.trim()) .map(line => line.trim())
.filter(line => line.startsWith(QUOTE_LEAD)) // notes are quoted .filter(line => line.startsWith(QUOTE_LEAD)) // notes are quoted
.map(line => line.slice(QUOTE_LEAD.length)) // unquote the lines .map(line => line.slice(QUOTE_LEAD.length)); // unquote the lines
.join(' ') // join the note 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(); .trim();
} }
} }
@@ -532,6 +541,15 @@ function renderTrops (commit, excludeBranch) {
function renderDescription (commit) { function renderDescription (commit) {
let note = commit.note || commit.subject || ''; let note = commit.note || commit.subject || '';
note = note.trim(); 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) { if (note.length !== 0) {
note = note[0].toUpperCase() + note.substr(1); note = note[0].toUpperCase() + note.substr(1);

View File

@@ -1,10 +1,10 @@
param([string]$gomaDir=$PWD) param([string]$gomaDir=$PWD)
$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py" $cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py"
Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start" Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start"
$timedOut = $false; $waitTime = 0; $waitIncrement = 5; $timeout=120; $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) Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut)
if ($timedOut) { if ($timedOut) {
write-error 'Timed out waiting for goma to start'; exit 1; write-error 'Timed out waiting for goma to start'; exit 1;
} else { } else {
Write-Output "Successfully started goma!" Write-Output "Successfully started goma!"
} }

View File

@@ -744,9 +744,10 @@ void BaseWindow::AddBrowserView(v8::Local<v8::Value> value) {
gin::ConvertFromV8(isolate(), value, &browser_view)) { gin::ConvertFromV8(isolate(), value, &browser_view)) {
auto get_that_view = browser_views_.find(browser_view->ID()); auto get_that_view = browser_views_.find(browser_view->ID());
if (get_that_view == browser_views_.end()) { if (get_that_view == browser_views_.end()) {
window_->AddBrowserView(browser_view->view()); if (browser_view->web_contents()) {
if (browser_view->web_contents()) window_->AddBrowserView(browser_view->view());
browser_view->web_contents()->SetOwnerWindow(window_.get()); browser_view->web_contents()->SetOwnerWindow(window_.get());
}
browser_views_[browser_view->ID()].Reset(isolate(), value); browser_views_[browser_view->ID()].Reset(isolate(), value);
} }
} }
@@ -758,9 +759,10 @@ void BaseWindow::RemoveBrowserView(v8::Local<v8::Value> value) {
gin::ConvertFromV8(isolate(), value, &browser_view)) { gin::ConvertFromV8(isolate(), value, &browser_view)) {
auto get_that_view = browser_views_.find(browser_view->ID()); auto get_that_view = browser_views_.find(browser_view->ID());
if (get_that_view != browser_views_.end()) { if (get_that_view != browser_views_.end()) {
window_->RemoveBrowserView(browser_view->view()); if (browser_view->web_contents()) {
if (browser_view->web_contents()) window_->RemoveBrowserView(browser_view->view());
browser_view->web_contents()->SetOwnerWindow(nullptr); browser_view->web_contents()->SetOwnerWindow(nullptr);
}
(*get_that_view).second.Reset(isolate(), value); (*get_that_view).second.Reset(isolate(), value);
browser_views_.erase(get_that_view); browser_views_.erase(get_that_view);
} }
@@ -1055,9 +1057,10 @@ void BaseWindow::ResetBrowserViews() {
v8::Local<v8::Value>::New(isolate(), item.second), v8::Local<v8::Value>::New(isolate(), item.second),
&browser_view) && &browser_view) &&
!browser_view.IsEmpty()) { !browser_view.IsEmpty()) {
window_->RemoveBrowserView(browser_view->view()); if (browser_view->web_contents()) {
if (browser_view->web_contents())
browser_view->web_contents()->SetOwnerWindow(nullptr); browser_view->web_contents()->SetOwnerWindow(nullptr);
window_->RemoveBrowserView(browser_view->view());
}
} }
item.second.Reset(); item.second.Reset();

View File

@@ -86,18 +86,22 @@ const std::map<std::string, std::string>& GetGlobalCrashKeys() {
return GetGlobalCrashKeysMutable(); return GetGlobalCrashKeysMutable();
} }
base::FilePath GetClientIdPath() { bool GetClientIdPath(base::FilePath* path) {
base::FilePath path; if (base::PathService::Get(electron::DIR_CRASH_DUMPS, path)) {
base::PathService::Get(electron::DIR_CRASH_DUMPS, &path); *path = path->Append("client_id");
return path.Append("client_id"); return true;
}
return false;
} }
std::string ReadClientId() { std::string ReadClientId() {
base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowIO allow_io;
std::string client_id; std::string client_id;
// "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".length == 36 // "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".length == 36
if (!base::ReadFileToStringWithMaxSize(GetClientIdPath(), &client_id, 36) || base::FilePath client_id_path;
client_id.size() != 36) if (GetClientIdPath(&client_id_path) &&
(!base::ReadFileToStringWithMaxSize(client_id_path, &client_id, 36) ||
client_id.size() != 36))
return std::string(); return std::string();
return client_id; return client_id;
} }
@@ -105,7 +109,9 @@ std::string ReadClientId() {
void WriteClientId(const std::string& client_id) { void WriteClientId(const std::string& client_id) {
DCHECK_EQ(client_id.size(), 36u); DCHECK_EQ(client_id.size(), 36u);
base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowIO allow_io;
base::WriteFile(GetClientIdPath(), client_id); base::FilePath client_id_path;
if (GetClientIdPath(&client_id_path))
base::WriteFile(client_id_path, client_id);
} }
std::string GetClientId() { std::string GetClientId() {

View File

@@ -8,7 +8,6 @@
#include <wtsapi32.h> #include <wtsapi32.h>
#include "base/win/windows_types.h" #include "base/win/windows_types.h"
#include "base/win/windows_version.h"
#include "base/win/wrapped_window_proc.h" #include "base/win/wrapped_window_proc.h"
#include "ui/base/win/shell.h" #include "ui/base/win/shell.h"
#include "ui/gfx/win/hwnd_util.h" #include "ui/gfx/win/hwnd_util.h"
@@ -44,7 +43,12 @@ void PowerMonitor::InitPlatformSpecificMonitors() {
// For Windows 8 and later, a new "connected standy" mode has been added and // For Windows 8 and later, a new "connected standy" mode has been added and
// we must explicitly register for its notifications. // we must explicitly register for its notifications.
if (base::win::GetVersion() >= base::win::Version::WIN8) { auto RegisterSuspendResumeNotification =
reinterpret_cast<decltype(&::RegisterSuspendResumeNotification)>(
GetProcAddress(GetModuleHandle(L"user32.dll"),
"RegisterSuspendResumeNotification"));
if (RegisterSuspendResumeNotification) {
RegisterSuspendResumeNotification(static_cast<HANDLE>(window_), RegisterSuspendResumeNotification(static_cast<HANDLE>(window_),
DEVICE_NOTIFY_WINDOW_HANDLE); DEVICE_NOTIFY_WINDOW_HANDLE);
} }

View File

@@ -132,6 +132,7 @@
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "extensions/browser/script_executor.h" #include "extensions/browser/script_executor.h"
#include "extensions/browser/view_type_utils.h"
#include "shell/browser/extensions/electron_extension_web_contents_observer.h" #include "shell/browser/extensions/electron_extension_web_contents_observer.h"
#endif #endif
@@ -409,12 +410,45 @@ const void* kElectronApiWebContentsKey = &kElectronApiWebContentsKey;
} // namespace } // namespace
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
WebContents::Type GetTypeFromViewType(extensions::ViewType view_type) {
switch (view_type) {
case extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE:
return WebContents::Type::BACKGROUND_PAGE;
case extensions::VIEW_TYPE_APP_WINDOW:
case extensions::VIEW_TYPE_COMPONENT:
case extensions::VIEW_TYPE_EXTENSION_DIALOG:
case extensions::VIEW_TYPE_EXTENSION_POPUP:
case extensions::VIEW_TYPE_BACKGROUND_CONTENTS:
case extensions::VIEW_TYPE_EXTENSION_GUEST:
case extensions::VIEW_TYPE_TAB_CONTENTS:
case extensions::VIEW_TYPE_INVALID:
return WebContents::Type::REMOTE;
}
}
#endif
WebContents::WebContents(v8::Isolate* isolate, WebContents::WebContents(v8::Isolate* isolate,
content::WebContents* web_contents) content::WebContents* web_contents)
: content::WebContentsObserver(web_contents), : content::WebContentsObserver(web_contents),
type_(Type::REMOTE), type_(Type::REMOTE),
id_(GetAllWebContents().Add(this)), id_(GetAllWebContents().Add(this)),
weak_factory_(this) { weak_factory_(this) {
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
// WebContents created by extension host will have valid ViewType set.
extensions::ViewType view_type = extensions::GetViewType(web_contents);
if (view_type != extensions::VIEW_TYPE_INVALID) {
InitWithExtensionView(isolate, web_contents, view_type);
}
extensions::ElectronExtensionWebContentsObserver::CreateForWebContents(
web_contents);
script_executor_.reset(new extensions::ScriptExecutor(web_contents));
#endif
auto session = Session::CreateFrom(isolate, GetBrowserContext()); auto session = Session::CreateFrom(isolate, GetBrowserContext());
session_.Reset(isolate, session.ToV8()); session_.Reset(isolate, session.ToV8());
@@ -424,11 +458,7 @@ WebContents::WebContents(v8::Isolate* isolate,
web_contents->SetUserData(kElectronApiWebContentsKey, web_contents->SetUserData(kElectronApiWebContentsKey,
std::make_unique<UserDataLink>(GetWeakPtr())); std::make_unique<UserDataLink>(GetWeakPtr()));
InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate)); InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate));
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
extensions::ElectronExtensionWebContentsObserver::CreateForWebContents(
web_contents);
script_executor_.reset(new extensions::ScriptExecutor(web_contents));
#endif
registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser, registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser,
base::Unretained(this))); base::Unretained(this)));
receivers_.set_disconnect_handler(base::BindRepeating( receivers_.set_disconnect_handler(base::BindRepeating(
@@ -474,8 +504,8 @@ WebContents::WebContents(v8::Isolate* isolate,
// BrowserViews are not attached to a window initially so they should start // BrowserViews are not attached to a window initially so they should start
// off as hidden. This is also important for compositor recycling. See: // off as hidden. This is also important for compositor recycling. See:
// https://github.com/electron/electron/pull/21372 // https://github.com/electron/electron/pull/21372
bool initially_shown = type_ != Type::BROWSER_VIEW; initially_shown_ = type_ != Type::BROWSER_VIEW;
options.Get(options::kShow, &initially_shown); options.Get(options::kShow, &initially_shown_);
// Obtain the session. // Obtain the session.
std::string partition; std::string partition;
@@ -531,7 +561,7 @@ WebContents::WebContents(v8::Isolate* isolate,
#endif #endif
} else { } else {
content::WebContents::CreateParams params(session->browser_context()); content::WebContents::CreateParams params(session->browser_context());
params.initially_hidden = !initially_shown; params.initially_hidden = !initially_shown_;
web_contents = content::WebContents::Create(params); web_contents = content::WebContents::Create(params);
} }
@@ -636,13 +666,39 @@ void WebContents::InitWithSessionAndOptions(
std::make_unique<UserDataLink>(GetWeakPtr())); std::make_unique<UserDataLink>(GetWeakPtr()));
} }
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
void WebContents::InitWithExtensionView(v8::Isolate* isolate,
content::WebContents* web_contents,
extensions::ViewType view_type) {
// Must reassign type prior to calling `Init`.
type_ = GetTypeFromViewType(view_type);
if (GetType() == Type::REMOTE)
return;
// Allow toggling DevTools for background pages
Observe(web_contents);
InitWithWebContents(web_contents, GetBrowserContext(), IsGuest());
managed_web_contents()->GetView()->SetDelegate(this);
SecurityStateTabHelper::CreateForWebContents(web_contents);
}
#endif
WebContents::~WebContents() { WebContents::~WebContents() {
MarkDestroyed(); MarkDestroyed();
// The destroy() is called. // The destroy() is called.
if (managed_web_contents()) { if (managed_web_contents()) {
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
if (type_ == Type::BACKGROUND_PAGE) {
// Background pages are owned by extensions::ExtensionHost
managed_web_contents()->ReleaseWebContents();
}
#endif
managed_web_contents()->GetView()->SetDelegate(nullptr); managed_web_contents()->GetView()->SetDelegate(nullptr);
RenderViewDeleted(web_contents()->GetRenderViewHost()); if (web_contents()) {
RenderViewDeleted(web_contents()->GetRenderViewHost());
}
if (type_ == Type::BROWSER_WINDOW && owner_window()) { if (type_ == Type::BROWSER_WINDOW && owner_window()) {
// For BrowserWindow we should close the window and clean up everything // For BrowserWindow we should close the window and clean up everything
@@ -1379,6 +1435,7 @@ void WebContents::DevToolsOpened() {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::Locker locker(isolate); v8::Locker locker(isolate);
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
DCHECK(managed_web_contents());
auto handle = auto handle =
FromOrCreate(isolate, managed_web_contents()->GetDevToolsWebContents()); FromOrCreate(isolate, managed_web_contents()->GetDevToolsWebContents());
devtools_web_contents_.Reset(isolate, handle.ToV8()); devtools_web_contents_.Reset(isolate, handle.ToV8());
@@ -1720,7 +1777,8 @@ void WebContents::OpenDevTools(gin::Arguments* args) {
return; return;
std::string state; std::string state;
if (type_ == Type::WEB_VIEW || !owner_window()) { if (type_ == Type::WEB_VIEW || type_ == Type::BACKGROUND_PAGE ||
!owner_window()) {
state = "detach"; state = "detach";
} }
bool activate = true; bool activate = true;
@@ -1731,6 +1789,8 @@ void WebContents::OpenDevTools(gin::Arguments* args) {
options.Get("activate", &activate); options.Get("activate", &activate);
} }
} }
DCHECK(managed_web_contents());
managed_web_contents()->SetDockState(state); managed_web_contents()->SetDockState(state);
managed_web_contents()->ShowDevTools(activate); managed_web_contents()->ShowDevTools(activate);
} }
@@ -1739,6 +1799,7 @@ void WebContents::CloseDevTools() {
if (type_ == Type::REMOTE) if (type_ == Type::REMOTE)
return; return;
DCHECK(managed_web_contents());
managed_web_contents()->CloseDevTools(); managed_web_contents()->CloseDevTools();
} }
@@ -1746,6 +1807,7 @@ bool WebContents::IsDevToolsOpened() {
if (type_ == Type::REMOTE) if (type_ == Type::REMOTE)
return false; return false;
DCHECK(managed_web_contents());
return managed_web_contents()->IsDevToolsViewShowing(); return managed_web_contents()->IsDevToolsViewShowing();
} }
@@ -1753,6 +1815,7 @@ bool WebContents::IsDevToolsFocused() {
if (type_ == Type::REMOTE) if (type_ == Type::REMOTE)
return false; return false;
DCHECK(managed_web_contents());
return managed_web_contents()->GetView()->IsDevToolsViewFocused(); return managed_web_contents()->GetView()->IsDevToolsViewFocused();
} }
@@ -1761,6 +1824,7 @@ void WebContents::EnableDeviceEmulation(
if (type_ == Type::REMOTE) if (type_ == Type::REMOTE)
return; return;
DCHECK(web_contents());
auto* frame_host = web_contents()->GetMainFrame(); auto* frame_host = web_contents()->GetMainFrame();
if (frame_host) { if (frame_host) {
auto* widget_host_impl = auto* widget_host_impl =
@@ -1778,6 +1842,7 @@ void WebContents::DisableDeviceEmulation() {
if (type_ == Type::REMOTE) if (type_ == Type::REMOTE)
return; return;
DCHECK(web_contents());
auto* frame_host = web_contents()->GetMainFrame(); auto* frame_host = web_contents()->GetMainFrame();
if (frame_host) { if (frame_host) {
auto* widget_host_impl = auto* widget_host_impl =
@@ -1805,6 +1870,7 @@ void WebContents::InspectElement(int x, int y) {
if (!enable_devtools_) if (!enable_devtools_)
return; return;
DCHECK(managed_web_contents());
if (!managed_web_contents()->GetDevToolsWebContents()) if (!managed_web_contents()->GetDevToolsWebContents())
OpenDevTools(nullptr); OpenDevTools(nullptr);
managed_web_contents()->InspectElement(x, y); managed_web_contents()->InspectElement(x, y);
@@ -2731,6 +2797,10 @@ v8::Local<v8::Value> WebContents::Debugger(v8::Isolate* isolate) {
return v8::Local<v8::Value>::New(isolate, debugger_); return v8::Local<v8::Value>::New(isolate, debugger_);
} }
bool WebContents::WasInitiallyShown() {
return initially_shown_;
}
void WebContents::GrantOriginAccess(const GURL& url) { void WebContents::GrantOriginAccess(const GURL& url) {
content::ChildProcessSecurityPolicy::GetInstance()->GrantCommitOrigin( content::ChildProcessSecurityPolicy::GetInstance()->GrantCommitOrigin(
web_contents()->GetMainFrame()->GetProcess()->GetID(), web_contents()->GetMainFrame()->GetProcess()->GetID(),
@@ -2924,6 +2994,7 @@ v8::Local<v8::ObjectTemplate> WebContents::FillObjectTemplate(
.SetProperty("hostWebContents", &WebContents::HostWebContents) .SetProperty("hostWebContents", &WebContents::HostWebContents)
.SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents) .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents)
.SetProperty("debugger", &WebContents::Debugger) .SetProperty("debugger", &WebContents::Debugger)
.SetProperty("_initiallyShown", &WebContents::WasInitiallyShown)
.Build(); .Build();
} }

View File

@@ -48,6 +48,8 @@
#endif #endif
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "extensions/common/view_type.h"
namespace extensions { namespace extensions {
class ScriptExecutor; class ScriptExecutor;
} }
@@ -144,7 +146,7 @@ class WebContents : public gin::Wrappable<WebContents>,
public mojom::ElectronBrowser { public mojom::ElectronBrowser {
public: public:
enum class Type { enum class Type {
BACKGROUND_PAGE, // A DevTools extension background page. BACKGROUND_PAGE, // An extension background page.
BROWSER_WINDOW, // Used by BrowserWindow. BROWSER_WINDOW, // Used by BrowserWindow.
BROWSER_VIEW, // Used by BrowserView. BROWSER_VIEW, // Used by BrowserView.
REMOTE, // Thin wrap around an existing WebContents. REMOTE, // Thin wrap around an existing WebContents.
@@ -393,6 +395,7 @@ class WebContents : public gin::Wrappable<WebContents>,
content::WebContents* HostWebContents() const; content::WebContents* HostWebContents() const;
v8::Local<v8::Value> DevToolsWebContents(v8::Isolate* isolate); v8::Local<v8::Value> DevToolsWebContents(v8::Isolate* isolate);
v8::Local<v8::Value> Debugger(v8::Isolate* isolate); v8::Local<v8::Value> Debugger(v8::Isolate* isolate);
bool WasInitiallyShown();
WebContentsZoomController* GetZoomController() { return zoom_controller_; } WebContentsZoomController* GetZoomController() { return zoom_controller_; }
@@ -452,6 +455,12 @@ class WebContents : public gin::Wrappable<WebContents>,
gin::Handle<class Session> session, gin::Handle<class Session> session,
const gin_helper::Dictionary& options); const gin_helper::Dictionary& options);
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
void InitWithExtensionView(v8::Isolate* isolate,
content::WebContents* web_contents,
extensions::ViewType view_type);
#endif
// content::WebContentsDelegate: // content::WebContentsDelegate:
bool DidAddMessageToConsole(content::WebContents* source, bool DidAddMessageToConsole(content::WebContents* source,
blink::mojom::ConsoleMessageLevel level, blink::mojom::ConsoleMessageLevel level,
@@ -683,6 +692,8 @@ class WebContents : public gin::Wrappable<WebContents>,
// Observers of this WebContents. // Observers of this WebContents.
base::ObserverList<ExtendedWebContentsObserver> observers_; base::ObserverList<ExtendedWebContentsObserver> observers_;
bool initially_shown_ = true;
// The ID of the process of the currently committed RenderViewHost. // The ID of the process of the currently committed RenderViewHost.
// -1 means no speculative RVH has been committed yet. // -1 means no speculative RVH has been committed yet.
int currently_committed_process_id_ = -1; int currently_committed_process_id_ = -1;

View File

@@ -355,6 +355,7 @@ class Browser : public WindowListObserver {
#if defined(OS_MAC) #if defined(OS_MAC)
std::unique_ptr<ui::ScopedPasswordInputEnabler> password_input_enabler_; std::unique_ptr<ui::ScopedPasswordInputEnabler> password_input_enabler_;
base::Time last_dock_show_;
#endif #endif
#if defined(OS_LINUX) || defined(OS_WIN) #if defined(OS_LINUX) || defined(OS_WIN)

View File

@@ -383,6 +383,20 @@ std::string Browser::DockGetBadgeText() {
} }
void Browser::DockHide() { void Browser::DockHide() {
// Transforming application state from UIElement to Foreground is an
// asyncronous operation, and unfortunately there is currently no way to know
// when it is finished.
// So if we call DockHide => DockShow => DockHide => DockShow in a very short
// time, we would triger a bug of macOS that, there would be multiple dock
// icons of the app left in system.
// To work around this, we make sure DockHide does nothing if it is called
// immediately after DockShow. After some experiments, 1 second seems to be
// a proper interval.
if (!last_dock_show_.is_null() &&
base::Time::Now() - last_dock_show_ < base::TimeDelta::FromSeconds(1)) {
return;
}
for (auto* const& window : WindowList::GetWindows()) for (auto* const& window : WindowList::GetWindows())
[window->GetNativeWindow().GetNativeNSWindow() setCanHide:NO]; [window->GetNativeWindow().GetNativeNSWindow() setCanHide:NO];
@@ -398,6 +412,7 @@ bool Browser::DockIsVisible() {
} }
v8::Local<v8::Promise> Browser::DockShow(v8::Isolate* isolate) { v8::Local<v8::Promise> Browser::DockShow(v8::Isolate* isolate) {
last_dock_show_ = base::Time::Now();
gin_helper::Promise<void> promise(isolate); gin_helper::Promise<void> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle(); v8::Local<v8::Promise> handle = promise.GetHandle();

View File

@@ -1,65 +1,65 @@
// Copyright (c) 2019 GitHub, Inc. // Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be // Use of this source code is governed by the MIT license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "shell/browser/electron_autofill_driver.h" #include "shell/browser/electron_autofill_driver.h"
#include <memory> #include <memory>
#include <utility> #include <utility>
#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/render_widget_host_view.h"
#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/javascript_environment.h" #include "shell/browser/javascript_environment.h"
#include "shell/browser/native_window.h" #include "shell/browser/native_window.h"
namespace electron { namespace electron {
AutofillDriver::AutofillDriver( AutofillDriver::AutofillDriver(
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
mojom::ElectronAutofillDriverAssociatedRequest request) mojom::ElectronAutofillDriverAssociatedRequest request)
: render_frame_host_(render_frame_host), binding_(this) { : render_frame_host_(render_frame_host), binding_(this) {
autofill_popup_ = std::make_unique<AutofillPopup>(); autofill_popup_ = std::make_unique<AutofillPopup>();
binding_.Bind(std::move(request)); binding_.Bind(std::move(request));
} }
AutofillDriver::~AutofillDriver() = default; AutofillDriver::~AutofillDriver() = default;
void AutofillDriver::ShowAutofillPopup( void AutofillDriver::ShowAutofillPopup(
const gfx::RectF& bounds, const gfx::RectF& bounds,
const std::vector<base::string16>& values, const std::vector<base::string16>& values,
const std::vector<base::string16>& labels) { const std::vector<base::string16>& labels) {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
auto* web_contents = api::WebContents::From( auto* web_contents = api::WebContents::From(
content::WebContents::FromRenderFrameHost(render_frame_host_)); content::WebContents::FromRenderFrameHost(render_frame_host_));
if (!web_contents || !web_contents->owner_window()) if (!web_contents || !web_contents->owner_window())
return; return;
auto* embedder = web_contents->embedder(); auto* embedder = web_contents->embedder();
bool osr = bool osr =
web_contents->IsOffScreen() || (embedder && embedder->IsOffScreen()); web_contents->IsOffScreen() || (embedder && embedder->IsOffScreen());
gfx::RectF popup_bounds(bounds); gfx::RectF popup_bounds(bounds);
content::RenderFrameHost* embedder_frame_host = nullptr; content::RenderFrameHost* embedder_frame_host = nullptr;
if (embedder) { if (embedder) {
auto* embedder_view = embedder->web_contents()->GetMainFrame()->GetView(); auto* embedder_view = embedder->web_contents()->GetMainFrame()->GetView();
auto* view = web_contents->web_contents()->GetMainFrame()->GetView(); auto* view = web_contents->web_contents()->GetMainFrame()->GetView();
auto offset = view->GetViewBounds().origin() - auto offset = view->GetViewBounds().origin() -
embedder_view->GetViewBounds().origin(); embedder_view->GetViewBounds().origin();
popup_bounds.Offset(offset); popup_bounds.Offset(offset);
embedder_frame_host = embedder->web_contents()->GetMainFrame(); embedder_frame_host = embedder->web_contents()->GetMainFrame();
} }
autofill_popup_->CreateView(render_frame_host_, embedder_frame_host, osr, autofill_popup_->CreateView(render_frame_host_, embedder_frame_host, osr,
web_contents->owner_window()->content_view(), web_contents->owner_window()->content_view(),
popup_bounds); popup_bounds);
autofill_popup_->SetItems(values, labels); autofill_popup_->SetItems(values, labels);
} }
void AutofillDriver::HideAutofillPopup() { void AutofillDriver::HideAutofillPopup() {
if (autofill_popup_) if (autofill_popup_)
autofill_popup_->Hide(); autofill_popup_->Hide();
} }
} // namespace electron } // namespace electron

View File

@@ -1,44 +1,44 @@
// Copyright (c) 2019 GitHub, Inc. // Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be // Use of this source code is governed by the MIT license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_ #ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_
#define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_ #define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_
#include <memory> #include <memory>
#include <vector> #include <vector>
#if defined(TOOLKIT_VIEWS) #if defined(TOOLKIT_VIEWS)
#include "shell/browser/ui/autofill_popup.h" #include "shell/browser/ui/autofill_popup.h"
#endif #endif
#include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/associated_binding.h"
#include "shell/common/api/api.mojom.h" #include "shell/common/api/api.mojom.h"
namespace electron { namespace electron {
class AutofillDriver : public mojom::ElectronAutofillDriver { class AutofillDriver : public mojom::ElectronAutofillDriver {
public: public:
AutofillDriver(content::RenderFrameHost* render_frame_host, AutofillDriver(content::RenderFrameHost* render_frame_host,
mojom::ElectronAutofillDriverAssociatedRequest request); mojom::ElectronAutofillDriverAssociatedRequest request);
~AutofillDriver() override; ~AutofillDriver() override;
void ShowAutofillPopup(const gfx::RectF& bounds, void ShowAutofillPopup(const gfx::RectF& bounds,
const std::vector<base::string16>& values, const std::vector<base::string16>& values,
const std::vector<base::string16>& labels) override; const std::vector<base::string16>& labels) override;
void HideAutofillPopup() override; void HideAutofillPopup() override;
private: private:
content::RenderFrameHost* const render_frame_host_; content::RenderFrameHost* const render_frame_host_;
#if defined(TOOLKIT_VIEWS) #if defined(TOOLKIT_VIEWS)
std::unique_ptr<AutofillPopup> autofill_popup_; std::unique_ptr<AutofillPopup> autofill_popup_;
#endif #endif
mojo::AssociatedBinding<mojom::ElectronAutofillDriver> binding_; mojo::AssociatedBinding<mojom::ElectronAutofillDriver> binding_;
}; };
} // namespace electron } // namespace electron
#endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_ #endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_

View File

@@ -1,111 +1,111 @@
// Copyright (c) 2019 GitHub, Inc. // Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be // Use of this source code is governed by the MIT license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "shell/browser/electron_autofill_driver_factory.h" #include "shell/browser/electron_autofill_driver_factory.h"
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "shell/browser/electron_autofill_driver.h" #include "shell/browser/electron_autofill_driver.h"
namespace electron { namespace electron {
namespace { namespace {
std::unique_ptr<AutofillDriver> CreateDriver( std::unique_ptr<AutofillDriver> CreateDriver(
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
mojom::ElectronAutofillDriverAssociatedRequest request) { mojom::ElectronAutofillDriverAssociatedRequest request) {
return std::make_unique<AutofillDriver>(render_frame_host, return std::make_unique<AutofillDriver>(render_frame_host,
std::move(request)); std::move(request));
} }
} // namespace } // namespace
AutofillDriverFactory::~AutofillDriverFactory() = default; AutofillDriverFactory::~AutofillDriverFactory() = default;
// static // static
void AutofillDriverFactory::BindAutofillDriver( void AutofillDriverFactory::BindAutofillDriver(
mojom::ElectronAutofillDriverAssociatedRequest request, mojom::ElectronAutofillDriverAssociatedRequest request,
content::RenderFrameHost* render_frame_host) { content::RenderFrameHost* render_frame_host) {
content::WebContents* web_contents = content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host); content::WebContents::FromRenderFrameHost(render_frame_host);
if (!web_contents) if (!web_contents)
return; return;
AutofillDriverFactory* factory = AutofillDriverFactory* factory =
AutofillDriverFactory::FromWebContents(web_contents); AutofillDriverFactory::FromWebContents(web_contents);
if (!factory) if (!factory)
return; return;
AutofillDriver* driver = factory->DriverForFrame(render_frame_host); AutofillDriver* driver = factory->DriverForFrame(render_frame_host);
if (!driver) if (!driver)
factory->AddDriverForFrame( factory->AddDriverForFrame(
render_frame_host, render_frame_host,
base::BindOnce(CreateDriver, render_frame_host, std::move(request))); base::BindOnce(CreateDriver, render_frame_host, std::move(request)));
} }
AutofillDriverFactory::AutofillDriverFactory(content::WebContents* web_contents) AutofillDriverFactory::AutofillDriverFactory(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) { : content::WebContentsObserver(web_contents) {
const std::vector<content::RenderFrameHost*> frames = const std::vector<content::RenderFrameHost*> frames =
web_contents->GetAllFrames(); web_contents->GetAllFrames();
for (content::RenderFrameHost* frame : frames) { for (content::RenderFrameHost* frame : frames) {
if (frame->IsRenderFrameLive()) if (frame->IsRenderFrameLive())
RenderFrameCreated(frame); RenderFrameCreated(frame);
} }
} }
void AutofillDriverFactory::RenderFrameDeleted( void AutofillDriverFactory::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) { content::RenderFrameHost* render_frame_host) {
DeleteDriverForFrame(render_frame_host); DeleteDriverForFrame(render_frame_host);
} }
void AutofillDriverFactory::DidFinishNavigation( void AutofillDriverFactory::DidFinishNavigation(
content::NavigationHandle* navigation_handle) { content::NavigationHandle* navigation_handle) {
// For the purposes of this code, a navigation is not important if it has not // For the purposes of this code, a navigation is not important if it has not
// committed yet or if it's in a subframe. // committed yet or if it's in a subframe.
if (!navigation_handle->HasCommitted() || if (!navigation_handle->HasCommitted() ||
!navigation_handle->IsInMainFrame()) { !navigation_handle->IsInMainFrame()) {
return; return;
} }
CloseAllPopups(); CloseAllPopups();
} }
AutofillDriver* AutofillDriverFactory::DriverForFrame( AutofillDriver* AutofillDriverFactory::DriverForFrame(
content::RenderFrameHost* render_frame_host) { content::RenderFrameHost* render_frame_host) {
auto mapping = driver_map_.find(render_frame_host); auto mapping = driver_map_.find(render_frame_host);
return mapping == driver_map_.end() ? nullptr : mapping->second.get(); return mapping == driver_map_.end() ? nullptr : mapping->second.get();
} }
void AutofillDriverFactory::AddDriverForFrame( void AutofillDriverFactory::AddDriverForFrame(
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
CreationCallback factory_method) { CreationCallback factory_method) {
auto insertion_result = auto insertion_result =
driver_map_.insert(std::make_pair(render_frame_host, nullptr)); driver_map_.insert(std::make_pair(render_frame_host, nullptr));
// This can be called twice for the key representing the main frame. // This can be called twice for the key representing the main frame.
if (insertion_result.second) { if (insertion_result.second) {
insertion_result.first->second = std::move(factory_method).Run(); insertion_result.first->second = std::move(factory_method).Run();
} }
} }
void AutofillDriverFactory::DeleteDriverForFrame( void AutofillDriverFactory::DeleteDriverForFrame(
content::RenderFrameHost* render_frame_host) { content::RenderFrameHost* render_frame_host) {
driver_map_.erase(render_frame_host); driver_map_.erase(render_frame_host);
} }
void AutofillDriverFactory::CloseAllPopups() { void AutofillDriverFactory::CloseAllPopups() {
for (auto& it : driver_map_) { for (auto& it : driver_map_) {
it.second->HideAutofillPopup(); it.second->HideAutofillPopup();
} }
} }
WEB_CONTENTS_USER_DATA_KEY_IMPL(AutofillDriverFactory) WEB_CONTENTS_USER_DATA_KEY_IMPL(AutofillDriverFactory)
} // namespace electron } // namespace electron

View File

@@ -1,57 +1,57 @@
// Copyright (c) 2019 GitHub, Inc. // Copyright (c) 2019 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be // Use of this source code is governed by the MIT license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_ #ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_
#define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_ #define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h" #include "content/public/browser/web_contents_user_data.h"
#include "shell/common/api/api.mojom.h" #include "shell/common/api/api.mojom.h"
namespace electron { namespace electron {
class AutofillDriver; class AutofillDriver;
class AutofillDriverFactory class AutofillDriverFactory
: public content::WebContentsObserver, : public content::WebContentsObserver,
public content::WebContentsUserData<AutofillDriverFactory> { public content::WebContentsUserData<AutofillDriverFactory> {
public: public:
typedef base::OnceCallback<std::unique_ptr<AutofillDriver>()> typedef base::OnceCallback<std::unique_ptr<AutofillDriver>()>
CreationCallback; CreationCallback;
~AutofillDriverFactory() override; ~AutofillDriverFactory() override;
static void BindAutofillDriver( static void BindAutofillDriver(
mojom::ElectronAutofillDriverAssociatedRequest request, mojom::ElectronAutofillDriverAssociatedRequest request,
content::RenderFrameHost* render_frame_host); content::RenderFrameHost* render_frame_host);
// content::WebContentsObserver: // content::WebContentsObserver:
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
void DidFinishNavigation( void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override; content::NavigationHandle* navigation_handle) override;
AutofillDriver* DriverForFrame(content::RenderFrameHost* render_frame_host); AutofillDriver* DriverForFrame(content::RenderFrameHost* render_frame_host);
void AddDriverForFrame(content::RenderFrameHost* render_frame_host, void AddDriverForFrame(content::RenderFrameHost* render_frame_host,
CreationCallback factory_method); CreationCallback factory_method);
void DeleteDriverForFrame(content::RenderFrameHost* render_frame_host); void DeleteDriverForFrame(content::RenderFrameHost* render_frame_host);
void CloseAllPopups(); void CloseAllPopups();
WEB_CONTENTS_USER_DATA_KEY_DECL(); WEB_CONTENTS_USER_DATA_KEY_DECL();
private: private:
explicit AutofillDriverFactory(content::WebContents* web_contents); explicit AutofillDriverFactory(content::WebContents* web_contents);
friend class content::WebContentsUserData<AutofillDriverFactory>; friend class content::WebContentsUserData<AutofillDriverFactory>;
std::unordered_map<content::RenderFrameHost*, std::unique_ptr<AutofillDriver>> std::unordered_map<content::RenderFrameHost*, std::unique_ptr<AutofillDriver>>
driver_map_; driver_map_;
}; };
} // namespace electron } // namespace electron
#endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_ #endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_

View File

@@ -97,6 +97,7 @@
#include "shell/common/options_switches.h" #include "shell/common/options_switches.h"
#include "shell/common/platform_util.h" #include "shell/common/platform_util.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h" #include "third_party/blink/public/common/loader/url_loader_throttle.h"
#include "third_party/blink/public/mojom/badging/badging.mojom.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/native_theme/native_theme.h" #include "ui/native_theme/native_theme.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
@@ -1506,15 +1507,10 @@ void ElectronBrowserClient::OverrideURLLoaderFactoryParams(
const url::Origin& origin, const url::Origin& origin,
bool is_for_isolated_world, bool is_for_isolated_world,
network::mojom::URLLoaderFactoryParams* factory_params) { network::mojom::URLLoaderFactoryParams* factory_params) {
for (const auto& iter : process_preferences_) { // Bypass CORB when web security is disabled.
if (iter.second.browser_context != browser_context) auto it = process_preferences_.find(factory_params->process_id);
continue; if (it != process_preferences_.end() && !it->second.web_security) {
factory_params->is_corb_enabled = false;
if (!iter.second.web_security) {
// bypass CORB
factory_params->process_id = iter.first;
factory_params->is_corb_enabled = false;
}
} }
extensions::URLLoaderFactoryManager::OverrideURLLoaderFactoryParams( extensions::URLLoaderFactoryManager::OverrideURLLoaderFactoryParams(
@@ -1578,6 +1574,12 @@ void ElectronBrowserClient::BindHostReceiverForRenderer(
#endif #endif
} }
void BindBadgeManagerFrameReceiver(
content::RenderFrameHost* frame,
mojo::PendingReceiver<blink::mojom::BadgeService> receiver) {
LOG(WARNING) << "The Chromium Badging API is not available in Electron";
}
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
void BindMimeHandlerService( void BindMimeHandlerService(
content::RenderFrameHost* frame_host, content::RenderFrameHost* frame_host,
@@ -1614,6 +1616,8 @@ void ElectronBrowserClient::RegisterBrowserInterfaceBindersForFrame(
mojo::BinderMapWithContext<content::RenderFrameHost*>* map) { mojo::BinderMapWithContext<content::RenderFrameHost*>* map) {
map->Add<network_hints::mojom::NetworkHintsHandler>( map->Add<network_hints::mojom::NetworkHintsHandler>(
base::BindRepeating(&BindNetworkHintsHandler)); base::BindRepeating(&BindNetworkHintsHandler));
map->Add<blink::mojom::BadgeService>(
base::BindRepeating(&BindBadgeManagerFrameReceiver));
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
map->Add<extensions::mime_handler::MimeHandlerService>( map->Add<extensions::mime_handler::MimeHandlerService>(
base::BindRepeating(&BindMimeHandlerService)); base::BindRepeating(&BindMimeHandlerService));

View File

@@ -1,24 +1,24 @@
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>. // Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
// Use of this source code is governed by the MIT license that can be // Use of this source code is governed by the MIT license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "shell/browser/extensions/api/i18n/i18n_api.h" #include "shell/browser/extensions/api/i18n/i18n_api.h"
#include <string> #include <string>
#include <vector> #include <vector>
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "shell/common/extensions/api/i18n.h" #include "shell/common/extensions/api/i18n.h"
namespace GetAcceptLanguages = extensions::api::i18n::GetAcceptLanguages; namespace GetAcceptLanguages = extensions::api::i18n::GetAcceptLanguages;
namespace extensions { namespace extensions {
ExtensionFunction::ResponseAction I18nGetAcceptLanguagesFunction::Run() { ExtensionFunction::ResponseAction I18nGetAcceptLanguagesFunction::Run() {
auto locale = g_browser_process->GetApplicationLocale(); auto locale = g_browser_process->GetApplicationLocale();
std::vector<std::string> accept_languages = {locale}; std::vector<std::string> accept_languages = {locale};
return RespondNow( return RespondNow(
ArgumentList(GetAcceptLanguages::Results::Create(accept_languages))); ArgumentList(GetAcceptLanguages::Results::Create(accept_languages)));
} }
} // namespace extensions } // namespace extensions

View File

@@ -1,20 +1,20 @@
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>. // Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
// Use of this source code is governed by the MIT license that can be // Use of this source code is governed by the MIT license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_ #ifndef SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
#define SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_ #define SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
#include "extensions/browser/extension_function.h" #include "extensions/browser/extension_function.h"
namespace extensions { namespace extensions {
class I18nGetAcceptLanguagesFunction : public ExtensionFunction { class I18nGetAcceptLanguagesFunction : public ExtensionFunction {
~I18nGetAcceptLanguagesFunction() override {} ~I18nGetAcceptLanguagesFunction() override {}
ResponseAction Run() override; ResponseAction Run() override;
DECLARE_EXTENSION_FUNCTION("i18n.getAcceptLanguages", I18N_GETACCEPTLANGUAGES) DECLARE_EXTENSION_FUNCTION("i18n.getAcceptLanguages", I18N_GETACCEPTLANGUAGES)
}; };
} // namespace extensions } // namespace extensions
#endif // SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_ #endif // SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_

View File

@@ -0,0 +1,235 @@
// Copyright 2014 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.
// TODO(sentialx): emit relevant events in Electron's session?
#include "shell/browser/extensions/api/management/electron_management_api_delegate.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/macros.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "chrome/common/extensions/extension_metrics.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/web_application_info.h"
#include "chrome/common/webui_url_constants.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/api/management/management_api.h"
#include "extensions/browser/api/management/management_api_constants.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/api/management.h"
#include "extensions/common/extension.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
namespace {
class ManagementSetEnabledFunctionInstallPromptDelegate
: public extensions::InstallPromptDelegate {
public:
ManagementSetEnabledFunctionInstallPromptDelegate(
content::WebContents* web_contents,
content::BrowserContext* browser_context,
const extensions::Extension* extension,
const base::Callback<void(bool)>& callback) {
// TODO(sentialx): emit event
}
~ManagementSetEnabledFunctionInstallPromptDelegate() override {}
private:
base::WeakPtrFactory<ManagementSetEnabledFunctionInstallPromptDelegate>
weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(ManagementSetEnabledFunctionInstallPromptDelegate);
};
class ManagementUninstallFunctionUninstallDialogDelegate
: public extensions::UninstallDialogDelegate {
public:
ManagementUninstallFunctionUninstallDialogDelegate(
extensions::ManagementUninstallFunctionBase* function,
const extensions::Extension* target_extension,
bool show_programmatic_uninstall_ui) {
// TODO(sentialx): emit event
}
~ManagementUninstallFunctionUninstallDialogDelegate() override {}
private:
DISALLOW_COPY_AND_ASSIGN(ManagementUninstallFunctionUninstallDialogDelegate);
};
} // namespace
ElectronManagementAPIDelegate::ElectronManagementAPIDelegate() {}
ElectronManagementAPIDelegate::~ElectronManagementAPIDelegate() {}
void ElectronManagementAPIDelegate::LaunchAppFunctionDelegate(
const extensions::Extension* extension,
content::BrowserContext* context) const {
// TODO(sentialx): emit event
extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_EXTENSION_API,
extension->GetType());
}
GURL ElectronManagementAPIDelegate::GetFullLaunchURL(
const extensions::Extension* extension) const {
return extensions::AppLaunchInfo::GetFullLaunchURL(extension);
}
extensions::LaunchType ElectronManagementAPIDelegate::GetLaunchType(
const extensions::ExtensionPrefs* prefs,
const extensions::Extension* extension) const {
// TODO(sentialx)
return extensions::LAUNCH_TYPE_DEFAULT;
}
void ElectronManagementAPIDelegate::
GetPermissionWarningsByManifestFunctionDelegate(
extensions::ManagementGetPermissionWarningsByManifestFunction* function,
const std::string& manifest_str) const {
data_decoder::DataDecoder::ParseJsonIsolated(
manifest_str,
base::BindOnce(
&extensions::ManagementGetPermissionWarningsByManifestFunction::
OnParse,
function));
}
std::unique_ptr<extensions::InstallPromptDelegate>
ElectronManagementAPIDelegate::SetEnabledFunctionDelegate(
content::WebContents* web_contents,
content::BrowserContext* browser_context,
const extensions::Extension* extension,
const base::Callback<void(bool)>& callback) const {
return std::unique_ptr<ManagementSetEnabledFunctionInstallPromptDelegate>(
new ManagementSetEnabledFunctionInstallPromptDelegate(
web_contents, browser_context, extension, callback));
}
std::unique_ptr<extensions::UninstallDialogDelegate>
ElectronManagementAPIDelegate::UninstallFunctionDelegate(
extensions::ManagementUninstallFunctionBase* function,
const extensions::Extension* target_extension,
bool show_programmatic_uninstall_ui) const {
return std::unique_ptr<extensions::UninstallDialogDelegate>(
new ManagementUninstallFunctionUninstallDialogDelegate(
function, target_extension, show_programmatic_uninstall_ui));
}
bool ElectronManagementAPIDelegate::CreateAppShortcutFunctionDelegate(
extensions::ManagementCreateAppShortcutFunction* function,
const extensions::Extension* extension,
std::string* error) const {
return false; // TODO(sentialx): route event and return true
}
std::unique_ptr<extensions::AppForLinkDelegate>
ElectronManagementAPIDelegate::GenerateAppForLinkFunctionDelegate(
extensions::ManagementGenerateAppForLinkFunction* function,
content::BrowserContext* context,
const std::string& title,
const GURL& launch_url) const {
// TODO(sentialx)
return nullptr;
}
bool ElectronManagementAPIDelegate::CanContextInstallWebApps(
content::BrowserContext* context) const {
// TODO(sentialx)
return false;
}
void ElectronManagementAPIDelegate::InstallOrLaunchReplacementWebApp(
content::BrowserContext* context,
const GURL& web_app_url,
InstallOrLaunchWebAppCallback callback) const {
// TODO(sentialx)
}
bool ElectronManagementAPIDelegate::CanContextInstallAndroidApps(
content::BrowserContext* context) const {
return false;
}
void ElectronManagementAPIDelegate::CheckAndroidAppInstallStatus(
const std::string& package_name,
AndroidAppInstallStatusCallback callback) const {
std::move(callback).Run(false);
}
void ElectronManagementAPIDelegate::InstallReplacementAndroidApp(
const std::string& package_name,
InstallAndroidAppCallback callback) const {
std::move(callback).Run(false);
}
void ElectronManagementAPIDelegate::EnableExtension(
content::BrowserContext* context,
const std::string& extension_id) const {
// const extensions::Extension* extension =
// extensions::ExtensionRegistry::Get(context)->GetExtensionById(
// extension_id, extensions::ExtensionRegistry::EVERYTHING);
// TODO(sentialx): we don't have ExtensionService
// If the extension was disabled for a permissions increase, the Management
// API will have displayed a re-enable prompt to the user, so we know it's
// safe to grant permissions here.
// extensions::ExtensionSystem::Get(context)
// ->extension_service()
// ->GrantPermissionsAndEnableExtension(extension);
}
void ElectronManagementAPIDelegate::DisableExtension(
content::BrowserContext* context,
const extensions::Extension* source_extension,
const std::string& extension_id,
extensions::disable_reason::DisableReason disable_reason) const {
// TODO(sentialx): we don't have ExtensionService
// extensions::ExtensionSystem::Get(context)
// ->extension_service()
// ->DisableExtensionWithSource(source_extension, extension_id,
// disable_reason);
}
bool ElectronManagementAPIDelegate::UninstallExtension(
content::BrowserContext* context,
const std::string& transient_extension_id,
extensions::UninstallReason reason,
base::string16* error) const {
// TODO(sentialx): we don't have ExtensionService
// return extensions::ExtensionSystem::Get(context)
// ->extension_service()
// ->UninstallExtension(transient_extension_id, reason, error);
return false;
}
void ElectronManagementAPIDelegate::SetLaunchType(
content::BrowserContext* context,
const std::string& extension_id,
extensions::LaunchType launch_type) const {
// TODO(sentialx)
// extensions::SetLaunchType(context, extension_id, launch_type);
}
GURL ElectronManagementAPIDelegate::GetIconURL(
const extensions::Extension* extension,
int icon_size,
ExtensionIconSet::MatchType match,
bool grayscale) const {
GURL icon_url(base::StringPrintf("%s%s/%d/%d%s",
chrome::kChromeUIExtensionIconURL,
extension->id().c_str(), icon_size, match,
grayscale ? "?grayscale=true" : ""));
CHECK(icon_url.is_valid());
return icon_url;
}

View File

@@ -0,0 +1,86 @@
// Copyright 2014 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 SHELL_BROWSER_EXTENSIONS_API_MANAGEMENT_ELECTRON_MANAGEMENT_API_DELEGATE_H_
#define SHELL_BROWSER_EXTENSIONS_API_MANAGEMENT_ELECTRON_MANAGEMENT_API_DELEGATE_H_
#include <memory>
#include <string>
#include "base/task/cancelable_task_tracker.h"
#include "extensions/browser/api/management/management_api_delegate.h"
class ElectronManagementAPIDelegate : public extensions::ManagementAPIDelegate {
public:
ElectronManagementAPIDelegate();
~ElectronManagementAPIDelegate() override;
// ManagementAPIDelegate.
void LaunchAppFunctionDelegate(
const extensions::Extension* extension,
content::BrowserContext* context) const override;
GURL GetFullLaunchURL(const extensions::Extension* extension) const override;
extensions::LaunchType GetLaunchType(
const extensions::ExtensionPrefs* prefs,
const extensions::Extension* extension) const override;
void GetPermissionWarningsByManifestFunctionDelegate(
extensions::ManagementGetPermissionWarningsByManifestFunction* function,
const std::string& manifest_str) const override;
std::unique_ptr<extensions::InstallPromptDelegate> SetEnabledFunctionDelegate(
content::WebContents* web_contents,
content::BrowserContext* browser_context,
const extensions::Extension* extension,
const base::Callback<void(bool)>& callback) const override;
std::unique_ptr<extensions::UninstallDialogDelegate>
UninstallFunctionDelegate(
extensions::ManagementUninstallFunctionBase* function,
const extensions::Extension* target_extension,
bool show_programmatic_uninstall_ui) const override;
bool CreateAppShortcutFunctionDelegate(
extensions::ManagementCreateAppShortcutFunction* function,
const extensions::Extension* extension,
std::string* error) const override;
std::unique_ptr<extensions::AppForLinkDelegate>
GenerateAppForLinkFunctionDelegate(
extensions::ManagementGenerateAppForLinkFunction* function,
content::BrowserContext* context,
const std::string& title,
const GURL& launch_url) const override;
bool CanContextInstallWebApps(
content::BrowserContext* context) const override;
void InstallOrLaunchReplacementWebApp(
content::BrowserContext* context,
const GURL& web_app_url,
ManagementAPIDelegate::InstallOrLaunchWebAppCallback callback)
const override;
bool CanContextInstallAndroidApps(
content::BrowserContext* context) const override;
void CheckAndroidAppInstallStatus(
const std::string& package_name,
ManagementAPIDelegate::AndroidAppInstallStatusCallback callback)
const override;
void InstallReplacementAndroidApp(
const std::string& package_name,
ManagementAPIDelegate::InstallAndroidAppCallback callback) const override;
void EnableExtension(content::BrowserContext* context,
const std::string& extension_id) const override;
void DisableExtension(
content::BrowserContext* context,
const extensions::Extension* source_extension,
const std::string& extension_id,
extensions::disable_reason::DisableReason disable_reason) const override;
bool UninstallExtension(content::BrowserContext* context,
const std::string& transient_extension_id,
extensions::UninstallReason reason,
base::string16* error) const override;
void SetLaunchType(content::BrowserContext* context,
const std::string& extension_id,
extensions::LaunchType launch_type) const override;
GURL GetIconURL(const extensions::Extension* extension,
int icon_size,
ExtensionIconSet::MatchType match,
bool grayscale) const override;
};
#endif // SHELL_BROWSER_EXTENSIONS_API_MANAGEMENT_ELECTRON_MANAGEMENT_API_DELEGATE_H_

View File

@@ -25,6 +25,7 @@
#include "extensions/browser/api/app_runtime/app_runtime_api.h" #include "extensions/browser/api/app_runtime/app_runtime_api.h"
#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry.h"
#include "extensions/browser/info_map.h" #include "extensions/browser/info_map.h"
#include "extensions/browser/management_policy.h"
#include "extensions/browser/notification_types.h" #include "extensions/browser/notification_types.h"
#include "extensions/browser/null_app_sorting.h" #include "extensions/browser/null_app_sorting.h"
#include "extensions/browser/quota_service.h" #include "extensions/browser/quota_service.h"
@@ -91,6 +92,8 @@ void ElectronExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
if (!browser_context_->IsOffTheRecord()) if (!browser_context_->IsOffTheRecord())
LoadComponentExtensions(); LoadComponentExtensions();
management_policy_.reset(new ManagementPolicy);
} }
std::unique_ptr<base::DictionaryValue> ParseManifest( std::unique_ptr<base::DictionaryValue> ParseManifest(
@@ -130,7 +133,7 @@ RuntimeData* ElectronExtensionSystem::runtime_data() {
} }
ManagementPolicy* ElectronExtensionSystem::management_policy() { ManagementPolicy* ElectronExtensionSystem::management_policy() {
return nullptr; return management_policy_.get();
} }
ServiceWorkerManager* ElectronExtensionSystem::service_worker_manager() { ServiceWorkerManager* ElectronExtensionSystem::service_worker_manager() {

View File

@@ -104,6 +104,7 @@ class ElectronExtensionSystem : public ExtensionSystem {
std::unique_ptr<QuotaService> quota_service_; std::unique_ptr<QuotaService> quota_service_;
std::unique_ptr<SharedUserScriptManager> shared_user_script_manager_; std::unique_ptr<SharedUserScriptManager> shared_user_script_manager_;
std::unique_ptr<AppSorting> app_sorting_; std::unique_ptr<AppSorting> app_sorting_;
std::unique_ptr<ManagementPolicy> management_policy_;
std::unique_ptr<ElectronExtensionLoader> extension_loader_; std::unique_ptr<ElectronExtensionLoader> extension_loader_;

View File

@@ -9,6 +9,7 @@
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h" #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h"
#include "printing/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h"
#include "shell/browser/extensions/api/management/electron_management_api_delegate.h"
#include "shell/browser/extensions/electron_extension_web_contents_observer.h" #include "shell/browser/extensions/electron_extension_web_contents_observer.h"
#include "shell/browser/extensions/electron_messaging_delegate.h" #include "shell/browser/extensions/electron_messaging_delegate.h"
@@ -59,6 +60,11 @@ void ElectronExtensionsAPIClient::AttachWebContentsHelpers(
#endif #endif
} }
ManagementAPIDelegate*
ElectronExtensionsAPIClient::CreateManagementAPIDelegate() const {
return new ElectronManagementAPIDelegate;
}
std::unique_ptr<MimeHandlerViewGuestDelegate> std::unique_ptr<MimeHandlerViewGuestDelegate>
ElectronExtensionsAPIClient::CreateMimeHandlerViewGuestDelegate( ElectronExtensionsAPIClient::CreateMimeHandlerViewGuestDelegate(
MimeHandlerViewGuest* guest) const { MimeHandlerViewGuest* guest) const {

View File

@@ -25,6 +25,7 @@ class ElectronExtensionsAPIClient : public ExtensionsAPIClient {
std::unique_ptr<MimeHandlerViewGuestDelegate> std::unique_ptr<MimeHandlerViewGuestDelegate>
CreateMimeHandlerViewGuestDelegate( CreateMimeHandlerViewGuestDelegate(
MimeHandlerViewGuest* guest) const override; MimeHandlerViewGuest* guest) const override;
ManagementAPIDelegate* CreateManagementAPIDelegate() const override;
private: private:
std::unique_ptr<ElectronMessagingDelegate> messaging_delegate_; std::unique_ptr<ElectronMessagingDelegate> messaging_delegate_;

View File

@@ -32,8 +32,6 @@ namespace electron {
// An ExtensionsBrowserClient that supports a single content::BrowserContext // An ExtensionsBrowserClient that supports a single content::BrowserContext
// with no related incognito context. // with no related incognito context.
// Must be initialized via InitWithBrowserContext() once the BrowserContext is
// created.
class ElectronExtensionsBrowserClient class ElectronExtensionsBrowserClient
: public extensions::ExtensionsBrowserClient { : public extensions::ExtensionsBrowserClient {
public: public:
@@ -122,11 +120,6 @@ class ElectronExtensionsBrowserClient
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
const extensions::Extension* extension) const override; const extensions::Extension* extension) const override;
// |context| is the single BrowserContext used for IsValidContext().
// |pref_service| is used for GetPrefServiceForContext().
void InitWithBrowserContext(content::BrowserContext* context,
PrefService* pref_service);
// Sets the API client. // Sets the API client.
void SetAPIClientForTest(extensions::ExtensionsAPIClient* api_client); void SetAPIClientForTest(extensions::ExtensionsAPIClient* api_client);

View File

@@ -50,8 +50,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 11,0,0,20200826 FILEVERSION 11,0,0,6
PRODUCTVERSION 11,0,0,20200826 PRODUCTVERSION 11,0,0,6
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L

View File

@@ -90,7 +90,8 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
// Create array to keep file types and their name. // Create array to keep file types and their name.
for (const Filter& filter : filters) { for (const Filter& filter : filters) {
NSMutableSet* file_type_set = [NSMutableSet set]; NSMutableOrderedSet* file_type_set =
[NSMutableOrderedSet orderedSetWithCapacity:filters.size()];
[filter_names addObject:@(filter.first.c_str())]; [filter_names addObject:@(filter.first.c_str())];
for (std::string ext : filter.second) { for (std::string ext : filter.second) {
// macOS is incapable of understanding multiple file extensions, // macOS is incapable of understanding multiple file extensions,
@@ -104,7 +105,7 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
[file_type_set addObject:@(ext.c_str())]; [file_type_set addObject:@(ext.c_str())];
} }
[file_types_list addObject:[file_type_set allObjects]]; [file_types_list addObject:[file_type_set array]];
} }
// Passing empty array to setAllowedFileTypes will cause exception. // Passing empty array to setAllowedFileTypes will cause exception.

View File

@@ -4,12 +4,31 @@
#include "shell/browser/ui/views/win_frame_view.h" #include "shell/browser/ui/views/win_frame_view.h"
#include "base/win/windows_version.h"
#include "shell/browser/native_window_views.h" #include "shell/browser/native_window_views.h"
#include "ui/display/win/screen_win.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/views/win/hwnd_util.h" #include "ui/views/win/hwnd_util.h"
namespace electron { namespace electron {
namespace {
gfx::Insets GetGlassInsets() {
int frame_height =
display::win::ScreenWin::GetSystemMetricsInDIP(SM_CYSIZEFRAME) +
display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXPADDEDBORDER);
int frame_size =
base::win::GetVersion() < base::win::Version::WIN8
? display::win::ScreenWin::GetSystemMetricsInDIP(SM_CXSIZEFRAME)
: 0;
return gfx::Insets(frame_height, frame_size, frame_size, frame_size);
}
} // namespace
const char WinFrameView::kViewClassName[] = "WinFrameView"; const char WinFrameView::kViewClassName[] = "WinFrameView";
WinFrameView::WinFrameView() {} WinFrameView::WinFrameView() {}
@@ -23,6 +42,17 @@ gfx::Rect WinFrameView::GetWindowBoundsForClientBounds(
client_bounds); client_bounds);
} }
gfx::Rect WinFrameView::GetBoundsForClientView() const {
if (window_->IsMaximized() && !window_->has_frame()) {
gfx::Insets insets = GetGlassInsets();
gfx::Rect result(width(), height());
result.Inset(insets);
return result;
} else {
return bounds();
}
}
int WinFrameView::NonClientHitTest(const gfx::Point& point) { int WinFrameView::NonClientHitTest(const gfx::Point& point) {
if (window_->has_frame()) if (window_->has_frame())
return frame_->client_view()->NonClientHitTest(point); return frame_->client_view()->NonClientHitTest(point);

View File

@@ -16,6 +16,7 @@ class WinFrameView : public FramelessView {
~WinFrameView() override; ~WinFrameView() override;
// views::NonClientFrameView: // views::NonClientFrameView:
gfx::Rect GetBoundsForClientView() const override;
gfx::Rect GetWindowBoundsForClientBounds( gfx::Rect GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const override; const gfx::Rect& client_bounds) const override;
int NonClientHitTest(const gfx::Point& point) override; int NonClientHitTest(const gfx::Point& point) override;

View File

@@ -4,7 +4,12 @@
#include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h" #include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h"
#include "base/win/windows_version.h"
#include "shell/browser/ui/views/win_frame_view.h"
#include "ui/base/win/hwnd_metrics.h" #include "ui/base/win/hwnd_metrics.h"
#include "ui/base/win/shell.h"
#include "ui/display/win/screen_win.h"
#include "ui/views/win/hwnd_util.h"
namespace electron { namespace electron {
@@ -36,18 +41,49 @@ bool ElectronDesktopWindowTreeHostWin::HasNativeFrame() const {
// Since we never use chromium's titlebar implementation, we can just say // Since we never use chromium's titlebar implementation, we can just say
// that we use a native titlebar. This will disable the repaint locking when // that we use a native titlebar. This will disable the repaint locking when
// DWM composition is disabled. // DWM composition is disabled.
return true; return !ui::win::IsAeroGlassEnabled();
}
bool ElectronDesktopWindowTreeHostWin::GetDwmFrameInsetsInPixels(
gfx::Insets* insets) const {
if (IsMaximized() && !native_window_view_->has_frame()) {
HMONITOR monitor = ::MonitorFromWindow(
native_window_view_->GetAcceleratedWidget(), MONITOR_DEFAULTTONEAREST);
int frame_height = display::win::ScreenWin::GetSystemMetricsForMonitor(
monitor, SM_CYSIZEFRAME) +
display::win::ScreenWin::GetSystemMetricsForMonitor(
monitor, SM_CXPADDEDBORDER);
int frame_size = base::win::GetVersion() < base::win::Version::WIN8
? display::win::ScreenWin::GetSystemMetricsForMonitor(
monitor, SM_CXSIZEFRAME)
: 0;
insets->Set(frame_height, frame_size, frame_size, frame_size);
return true;
}
return false;
} }
bool ElectronDesktopWindowTreeHostWin::GetClientAreaInsets( bool ElectronDesktopWindowTreeHostWin::GetClientAreaInsets(
gfx::Insets* insets, gfx::Insets* insets,
HMONITOR monitor) const { HMONITOR monitor) const {
if (IsMaximized() && !native_window_view_->has_frame()) { if (IsMaximized() && !native_window_view_->has_frame()) {
// Windows automatically adds a standard width border to all sides when a if (base::win::GetVersion() < base::win::Version::WIN8) {
// window is maximized. // This tells Windows that most of the window is a client area, meaning
int frame_thickness = ui::GetFrameThickness(monitor) - 1; // Chrome will draw it. Windows still fills in the glass bits because of
*insets = gfx::Insets(frame_thickness, frame_thickness, frame_thickness, // the // DwmExtendFrameIntoClientArea call in |UpdateDWMFrame|.
frame_thickness); // Without this 1 pixel offset on the right and bottom:
// * windows paint in a more standard way, and
// * we get weird black bars at the top when maximized in multiple
// monitor configurations.
int border_thickness = 1;
insets->Set(0, 0, border_thickness, border_thickness);
} else {
const int frame_thickness = ui::GetFrameThickness(monitor);
// Reduce the Windows non-client border size because we extend the border
// into our client area in UpdateDWMFrame(). The top inset must be 0 or
// else Windows will draw a full native titlebar outside the client area.
insets->Set(0, frame_thickness, frame_thickness, frame_thickness);
}
return true; return true;
} }
return false; return false;

View File

@@ -27,6 +27,7 @@ class ElectronDesktopWindowTreeHostWin
LRESULT* result) override; LRESULT* result) override;
bool ShouldPaintAsActive() const override; bool ShouldPaintAsActive() const override;
bool HasNativeFrame() const override; bool HasNativeFrame() const override;
bool GetDwmFrameInsetsInPixels(gfx::Insets* insets) const override;
bool GetClientAreaInsets(gfx::Insets* insets, bool GetClientAreaInsets(gfx::Insets* insets,
HMONITOR monitor) const override; HMONITOR monitor) const override;

View File

@@ -5,5 +5,65 @@
"extension" "extension"
], ],
"location": "component" "location": "component"
} },
} "management": [
{
"channel": "stable",
"extension_types": [
"extension",
"legacy_packaged_app"
]
},
{
"channel": "stable",
"extension_types": [
"platform_app"
],
"whitelist": [
"AE27D69DBE571F4B1694F05C89B710C646792231", // Published ADT
// TODO(grv): clean up once Apps developer tool is published.
"5107DE9024C329EEA9C9A72D94C16723790C6422", // Apps Developer Tool.
"8C0B1873FFFB65E4D0F4D772879F7304CEF125C2", // Apps Editor old.
"FA0501B579070BB9CBD4FCAEC8CB0EDF22BA2F04", // Apps Editor published.
"EE17C698905F7F2E6DDC87C9C30F11E164C829F4", // Watchdog (Activity Log)
"90113DA9516526D24DAF156C629CC41C049E8882", // Watchdog Test Version
"4A4EA121622FCA3D78ED2AB534197F43D7189EE0", // Spark nightly build.
"9FDE6E7F06FCFA11D9A05041C7FF6D8AE662F5D1", // Spark release.
"50B4A905D522C06E27CA6D099E3E54BDA1F152C5", // Spark Beta channel.
"BA0C8BB92084C9741312D90D3EA882526853455F", // Spark dev channel.
"5F57A9AE8DFF5D6BB09DF8606270402612E871E5", // http://crbug.com/422624
"46578A13607D38F1DC8E280C4F499FB0A2F9565C", // http://crbug.com/819404
"898FB5A39687D210766B8998BA4530B99C9E6586", // http://crbug.com/819404
"82F30B65397BC3E4ADE627BBD857AB8A58210648", // http://crbug.com/819404
"C74B2AF138F9EDECD04D0965AB36CA66C8290466" // http://crbug.com/957772
]
},
{
"channel": "stable",
"extension_types": [
"hosted_app"
],
"whitelist": [
"B44D08FD98F1523ED5837D78D0A606EA9D6206E5" // Web Store
]
},
{
"channel": "stable",
"extension_types": [
"platform_app"
],
"session_types": [
"kiosk"
]
},
{
"channel": "stable",
"dependencies": [
"behavior:imprivata_login_screen_extension"
],
"extension_types": [
"login_screen_extension"
]
}
]
}

View File

@@ -38,6 +38,7 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = {
APIPermissionInfo::kFlagInternal}, APIPermissionInfo::kFlagInternal},
{APIPermission::kResourcesPrivate, "resourcesPrivate", {APIPermission::kResourcesPrivate, "resourcesPrivate",
APIPermissionInfo::kFlagCannotBeOptional}, APIPermissionInfo::kFlagCannotBeOptional},
{APIPermission::kManagement, "management"},
}; };
base::span<const APIPermissionInfo::InitInfo> GetPermissionInfos() { base::span<const APIPermissionInfo::InitInfo> GetPermissionInfos() {
return base::make_span(permissions_to_register); return base::make_span(permissions_to_register);

View File

@@ -400,9 +400,24 @@ node::Environment* NodeBindings::CreateEnvironment(
std::unique_ptr<const char*[]> c_argv = StringVectorToArgArray(args); std::unique_ptr<const char*[]> c_argv = StringVectorToArgArray(args);
isolate_data_ = isolate_data_ =
node::CreateIsolateData(context->GetIsolate(), uv_loop_, platform); node::CreateIsolateData(context->GetIsolate(), uv_loop_, platform);
node::Environment* env = node::CreateEnvironment(
isolate_data_, context, args.size(), c_argv.get(), 0, nullptr); node::Environment* env;
DCHECK(env); if (browser_env_ != BrowserEnvironment::BROWSER) {
v8::TryCatch try_catch(context->GetIsolate());
env = node::CreateEnvironment(isolate_data_, context, args.size(),
c_argv.get(), 0, nullptr);
DCHECK(env);
// This will only be caught when something has gone terrible wrong as all
// electron scripts are wrapped in a try {} catch {} in run-compiler.js
if (try_catch.HasCaught()) {
LOG(ERROR) << "Failed to initialize node environment in process: "
<< process_type;
}
} else {
env = node::CreateEnvironment(isolate_data_, context, args.size(),
c_argv.get(), 0, nullptr);
DCHECK(env);
}
// Clean up the global _noBrowserGlobals that we unironically injected into // Clean up the global _noBrowserGlobals that we unironically injected into
// the global scope // the global scope

View File

@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "shell/common/node_util.h" #include "shell/common/node_util.h"
#include "base/logging.h"
#include "shell/common/node_includes.h" #include "shell/common/node_includes.h"
#include "third_party/electron_node/src/node_native_module_env.h" #include "third_party/electron_node/src/node_native_module_env.h"
@@ -17,6 +18,7 @@ v8::MaybeLocal<v8::Value> CompileAndCall(
std::vector<v8::Local<v8::Value>>* arguments, std::vector<v8::Local<v8::Value>>* arguments,
node::Environment* optional_env) { node::Environment* optional_env) {
v8::Isolate* isolate = context->GetIsolate(); v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch try_catch(isolate);
v8::MaybeLocal<v8::Function> compiled = v8::MaybeLocal<v8::Function> compiled =
node::native_module::NativeModuleEnv::LookupAndCompile( node::native_module::NativeModuleEnv::LookupAndCompile(
context, id, parameters, optional_env); context, id, parameters, optional_env);
@@ -24,8 +26,14 @@ v8::MaybeLocal<v8::Value> CompileAndCall(
return v8::MaybeLocal<v8::Value>(); return v8::MaybeLocal<v8::Value>();
} }
v8::Local<v8::Function> fn = compiled.ToLocalChecked().As<v8::Function>(); v8::Local<v8::Function> fn = compiled.ToLocalChecked().As<v8::Function>();
return fn->Call(context, v8::Null(isolate), arguments->size(), v8::MaybeLocal<v8::Value> ret = fn->Call(
arguments->data()); context, v8::Null(isolate), arguments->size(), arguments->data());
// This will only be caught when something has gone terrible wrong as all
// electron scripts are wrapped in a try {} catch {} in run-compiler.js
if (try_catch.HasCaught()) {
LOG(ERROR) << "Failed to CompileAndCall electron script: " << id;
}
return ret;
} }
} // namespace util } // namespace util

View File

@@ -387,10 +387,14 @@ bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
if (!delete_sink) if (!delete_sink)
return false; return false;
BOOL pfAnyOperationsAborted;
// Processes the queued command DeleteItem. This will trigger // Processes the queued command DeleteItem. This will trigger
// the DeleteFileProgressSink to check for Recycle Bin. // the DeleteFileProgressSink to check for Recycle Bin.
return SUCCEEDED(pfo->DeleteItem(delete_item.Get(), delete_sink.Get())) && return SUCCEEDED(pfo->DeleteItem(delete_item.Get(), delete_sink.Get())) &&
SUCCEEDED(pfo->PerformOperations()); SUCCEEDED(pfo->PerformOperations()) &&
SUCCEEDED(pfo->GetAnyOperationsAborted(&pfAnyOperationsAborted)) &&
!pfAnyOperationsAborted;
} }
bool GetFolderPath(int key, base::FilePath* result) { bool GetFolderPath(int key, base::FilePath* result) {

View File

@@ -1437,6 +1437,16 @@ describe('app module', () => {
}); });
}); });
describe('dock.hide', () => {
it('should not throw', () => {
app.dock.hide();
expect(app.dock.isVisible()).to.equal(false);
});
});
// Note that dock.show tests should run after dock.hide tests, to work
// around a bug of macOS.
// See https://github.com/electron/electron/pull/25269 for more.
describe('dock.show', () => { describe('dock.show', () => {
it('should not throw', () => { it('should not throw', () => {
return app.dock.show().then(() => { return app.dock.show().then(() => {
@@ -1452,13 +1462,6 @@ describe('app module', () => {
await expect(app.dock.show()).to.eventually.be.fulfilled.equal(undefined); await expect(app.dock.show()).to.eventually.be.fulfilled.equal(undefined);
}); });
}); });
describe('dock.hide', () => {
it('should not throw', () => {
app.dock.hide();
expect(app.dock.isVisible()).to.equal(false);
});
});
}); });
describe('whenReady', () => { describe('whenReady', () => {

View File

@@ -245,7 +245,7 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
const crash = await waitForCrash(); const crash = await waitForCrash();
expect(crash.prod).to.equal('Electron'); expect(crash.prod).to.equal('Electron');
expect(crash._productName).to.equal('remote-control'); expect(crash._productName).to.equal('electron-test-remote-control');
expect(crash.process_type).to.equal('renderer'); expect(crash.process_type).to.equal('renderer');
expect(crash['electron.v8-fatal.location']).to.equal('v8::Context::New()'); expect(crash['electron.v8-fatal.location']).to.equal('v8::Context::New()');
expect(crash['electron.v8-fatal.message']).to.equal('Circular extension dependency'); expect(crash['electron.v8-fatal.message']).to.equal('Circular extension dependency');

View File

@@ -7,6 +7,8 @@ import * as fs from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
import { AddressInfo } from 'net'; import { AddressInfo } from 'net';
import { expect } from 'chai'; import { expect } from 'chai';
import { ifit } from './spec-helpers';
import { execSync } from 'child_process';
describe('shell module', () => { describe('shell module', () => {
describe('shell.openExternal()', () => { describe('shell.openExternal()', () => {
@@ -76,5 +78,38 @@ describe('shell module', () => {
const result = shell.moveItemToTrash(filename); const result = shell.moveItemToTrash(filename);
expect(result).to.be.false(); expect(result).to.be.false();
}); });
ifit(process.platform === 'darwin')('returns false when file has immutable flag', async () => {
const dir = await fs.mkdtemp(path.resolve(app.getPath('temp'), 'electron-shell-spec-'));
const tempPath = path.join(dir, 'locked-file');
await fs.writeFile(tempPath, 'delete me if you can');
// https://ss64.com/osx/chflags.html
execSync(`chflags uchg ${tempPath}`);
expect(shell.moveItemToTrash(tempPath)).to.be.false();
expect(await fs.pathExists(tempPath)).to.be.true();
execSync(`chflags nouchg ${tempPath}`);
expect(shell.moveItemToTrash(tempPath)).to.be.true();
expect(await fs.pathExists(tempPath)).to.be.false();
});
ifit(process.platform === 'win32')('returns false when path is in use', async () => {
const tempPath = await fs.mkdtemp(path.resolve(app.getPath('temp'), 'electron-shell-spec-'));
const cwd = process.cwd();
try {
// A process working directory is automatically locked on Windows.
// This is a workaround to avoid pulling in fs-extras flock method.
process.chdir(tempPath);
expect(shell.moveItemToTrash(tempPath)).to.be.false();
expect(await fs.pathExists(tempPath)).to.be.true();
} finally {
process.chdir(cwd);
}
expect(shell.moveItemToTrash(tempPath)).to.be.true();
expect(await fs.pathExists(tempPath)).to.be.false();
});
}); });
}); });

View File

@@ -245,6 +245,14 @@ describe('web security', () => {
<script src="${serverUrl}"></script>`); <script src="${serverUrl}"></script>`);
await p; await p;
}); });
it('does not crash when multiple WebContent are created with web security disabled', () => {
const options = { webPreferences: { webSecurity: false } };
const w1 = new BrowserWindow(options);
w1.loadURL(serverUrl);
const w2 = new BrowserWindow(options);
w2.loadURL(serverUrl);
});
}); });
describe('command line switches', () => { describe('command line switches', () => {

View File

@@ -37,6 +37,26 @@ describe('chrome extensions', () => {
}); });
}); });
it('does not crash when using chrome.management', async () => {
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
w.loadURL('about:blank');
await emittedOnce(w.webContents, 'dom-ready');
await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page'));
const args: any = await emittedOnce(app, 'web-contents-created');
const wc: Electron.WebContents = args[1];
await expect(wc.executeJavaScript(`
(() => {
return new Promise((resolve) => {
chrome.management.getSelf((info) => {
resolve(info);
});
})
})();
`)).to.eventually.have.property('id');
});
it('can open WebSQLDatabase in a background page', async () => { it('can open WebSQLDatabase in a background page', async () => {
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
@@ -297,16 +317,28 @@ describe('chrome extensions', () => {
const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise'); const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise');
expect(receivedMessage).to.deep.equal({ some: 'message' }); expect(receivedMessage).to.deep.equal({ some: 'message' });
}); });
});
it('has session in background page', async () => { it('has session in background page', async () => {
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page')); await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page'));
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } });
const promise = emittedOnce(app, 'web-contents-created'); const promise = emittedOnce(app, 'web-contents-created');
await w.loadURL('about:blank'); await w.loadURL('about:blank');
const [, bgPageContents] = await promise; const [, bgPageContents] = await promise;
expect(bgPageContents.session).to.not.equal(undefined); expect(bgPageContents.session).to.not.equal(undefined);
});
it('can open devtools of background page', async () => {
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page'));
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } });
const promise = emittedOnce(app, 'web-contents-created');
await w.loadURL('about:blank');
const [, bgPageContents] = await promise;
expect(bgPageContents.getType()).to.equal('backgroundPage');
bgPageContents.openDevTools();
bgPageContents.closeDevTools();
});
}); });
describe('devtools extensions', () => { describe('devtools extensions', () => {

View File

@@ -1,4 +1,4 @@
{ {
"name": "ipc-main-listeners", "name": "electron-test-ipc-main-listeners",
"main": "main.js" "main": "main.js"
} }

View File

@@ -1,4 +1,4 @@
{ {
"name": "net-log", "name": "electron-test-net-log",
"main": "main.js" "main": "main.js"
} }

View File

@@ -1,6 +1,6 @@
self.addEventListener('install', function (event) { self.addEventListener('install', function (event) {
console.log('log log'); console.log('log log');
console.info('info log'); console.info('info log');
console.warn('warn log'); console.warn('warn log');
console.error('error log'); console.error('error log');
}); });

View File

@@ -1,3 +1,3 @@
self.addEventListener('install', function (event) { self.addEventListener('install', function (event) {
console.log('Installed'); console.log('Installed');
}); });

View File

@@ -1,4 +1,4 @@
{ {
"name": "crash", "name": "electron-test-crash",
"main": "main.js" "main": "main.js"
} }

View File

@@ -1,4 +1,4 @@
{ {
"name": "remote-control", "name": "electron-test-remote-control",
"main": "main.js" "main": "main.js"
} }

View File

@@ -1,5 +1,5 @@
{ {
"name": "initial-app", "name": "electron-test-initial-app",
"version": "1.0.0", "version": "1.0.0",
"main": "./index.js" "main": "./index.js"
} }

View File

@@ -1,5 +1,5 @@
{ {
"name": "initial-app", "name": "electron-test-initial-app",
"version": "1.0.0", "version": "1.0.0",
"main": "./index.js" "main": "./index.js"
} }

View File

@@ -1,5 +1,5 @@
{ {
"name": "initial-app", "name": "electron-test-initial-app",
"version": "1.0.0", "version": "1.0.0",
"main": "./index.js" "main": "./index.js"
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"status":200,"url":"https://api.github.com/repos/electron/electron/issues/25216/comments?per_page=100","headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset","cache-control":"private, max-age=60, s-maxage=60","connection":"close","content-encoding":"gzip","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Wed, 02 Sep 2020 15:55:20 GMT","etag":"W/\"dc98adeb828ec1f60e0be31a73a31f30\"","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"GitHub.com","status":"200 OK","strict-transport-security":"max-age=31536000; includeSubdomains; preload","transfer-encoding":"chunked","vary":"Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With","x-accepted-oauth-scopes":"","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-media-type":"github.v3; format=json","x-github-request-id":"E876:6741:2819ABF:5CAD2C6:5F4FC068","x-oauth-scopes":"repo","x-ratelimit-limit":"5000","x-ratelimit-remaining":"4943","x-ratelimit-reset":"1599063118","x-xss-protection":"1; mode=block"},"data":[{"url":"https://api.github.com/repos/electron/electron/issues/comments/684017257","html_url":"https://github.com/electron/electron/pull/25216#issuecomment-684017257","issue_url":"https://api.github.com/repos/electron/electron/issues/25216","id":684017257,"node_id":"MDEyOklzc3VlQ29tbWVudDY4NDAxNzI1Nw==","user":{"login":"release-clerk[bot]","id":42386326,"node_id":"MDM6Qm90NDIzODYzMjY=","avatar_url":"https://avatars0.githubusercontent.com/in/16104?v=4","gravatar_id":"","url":"https://api.github.com/users/release-clerk%5Bbot%5D","html_url":"https://github.com/apps/release-clerk","followers_url":"https://api.github.com/users/release-clerk%5Bbot%5D/followers","following_url":"https://api.github.com/users/release-clerk%5Bbot%5D/following{/other_user}","gists_url":"https://api.github.com/users/release-clerk%5Bbot%5D/gists{/gist_id}","starred_url":"https://api.github.com/users/release-clerk%5Bbot%5D/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/release-clerk%5Bbot%5D/subscriptions","organizations_url":"https://api.github.com/users/release-clerk%5Bbot%5D/orgs","repos_url":"https://api.github.com/users/release-clerk%5Bbot%5D/repos","events_url":"https://api.github.com/users/release-clerk%5Bbot%5D/events{/privacy}","received_events_url":"https://api.github.com/users/release-clerk%5Bbot%5D/received_events","type":"Bot","site_admin":false},"created_at":"2020-08-31T20:22:50Z","updated_at":"2020-08-31T20:22:50Z","author_association":"NONE","body":"**Release Notes Persisted**\n\n> * Fixes the following issues for frameless when maximized on Windows:\r\n> * fix unreachable task bar when auto hidden with position top\r\n> * fix 1px extending to secondary monitor\r\n> * fix 1px overflowing into taskbar at certain resolutions\r\n> * fix white line on top of window under 4k resolutions","performed_via_github_app":null}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -106,6 +106,9 @@ describe('release notes', () => {
const newTropFix = new Commit('a6ff42c190cb5caf8f3e217748e49183a951491b', 'fix: workaround for hang when preventDefault-ing nativeWindowOpen (#22750)'); const newTropFix = new Commit('a6ff42c190cb5caf8f3e217748e49183a951491b', 'fix: workaround for hang when preventDefault-ing nativeWindowOpen (#22750)');
const oldTropFix = new Commit('8751f485c5a6c8c78990bfd55a4350700f81f8cd', 'fix: workaround for hang when preventDefault-ing nativeWindowOpen (#22749)'); const oldTropFix = new Commit('8751f485c5a6c8c78990bfd55a4350700f81f8cd', 'fix: workaround for hang when preventDefault-ing nativeWindowOpen (#22749)');
// a PR that has unusual note formatting
const sublist = new Commit('61dc1c88fd34a3e8fff80c80ed79d0455970e610', 'fix: client area inset calculation when maximized for framless windows (#25052) (#25216)');
before(() => { before(() => {
// location of relase-notes' octokit reply cache // location of relase-notes' octokit reply cache
const fixtureDir = path.resolve(__dirname, 'fixtures', 'release-notes'); const fixtureDir = path.resolve(__dirname, 'fixtures', 'release-notes');
@@ -151,6 +154,34 @@ describe('release notes', () => {
expect(results.feat[0].note).to.equal(realText); expect(results.feat[0].note).to.equal(realText);
}); });
describe('rendering', () => {
it('removes redundant bullet points', async function () {
const testCommit = sublist;
const version = 'v10.1.1';
gitFake.setBranch(newBranch, [...sharedHistory, testCommit]);
const results: any = await notes.get(oldBranch, newBranch, version);
const rendered: any = await notes.render(results);
expect(rendered).to.not.include('* *');
});
it('indents sublists', async function () {
const testCommit = sublist;
const version = 'v10.1.1';
gitFake.setBranch(newBranch, [...sharedHistory, testCommit]);
const results: any = await notes.get(oldBranch, newBranch, version);
const rendered: any = await notes.render(results);
expect(rendered).to.include([
'* Fixed the following issues for frameless when maximized on Windows:',
' * fix unreachable task bar when auto hidden with position top',
' * fix 1px extending to secondary monitor',
' * fix 1px overflowing into taskbar at certain resolutions',
' * fix white line on top of window under 4k resolutions. [#25216]'].join('\n'));
});
});
// test that when you feed in different semantic commit types, // test that when you feed in different semantic commit types,
// the parser returns them in the results' correct category // the parser returns them in the results' correct category
describe('semantic commit', () => { describe('semantic commit', () => {

View File

@@ -17,6 +17,14 @@ const features = process._linkedBinding('electron_common_features');
describe('chromium feature', () => { describe('chromium feature', () => {
const fixtures = path.resolve(__dirname, 'fixtures'); const fixtures = path.resolve(__dirname, 'fixtures');
describe('Badging API', () => {
it('does not crash', () => {
expect(() => {
navigator.setAppBadge(42);
}).to.not.throw();
});
});
describe('heap snapshot', () => { describe('heap snapshot', () => {
it('does not crash', function () { it('does not crash', function () {
process._linkedBinding('electron_common_v8_util').takeHeapSnapshot(); process._linkedBinding('electron_common_v8_util').takeHeapSnapshot();

View File

@@ -1,4 +1,4 @@
{ {
"name": "app-path", "name": "electron-test-app-path",
"main": "lib/index.js" "main": "lib/index.js"
} }

View File

@@ -1,4 +1,4 @@
{ {
"name": "command-line", "name": "electron-test-command-line",
"main": "main.js" "main": "main.js"
} }

View File

@@ -1,4 +1,4 @@
{ {
"name": "default-menu", "name": "electron-test-default-menu",
"main": "main.js" "main": "main.js"
} }

View File

@@ -1,5 +1,5 @@
{ {
"name": "locale-check", "name": "electron-test-locale-check",
"main": "main.js" "main": "main.js"
} }

View File

@@ -1,4 +1,4 @@
{ {
"name": "site-instance-overrides", "name": "electron-test-site-instance-overrides",
"main": "main.js" "main": "main.js"
} }

View File

@@ -1,4 +1,4 @@
{ {
"name": "window-all-closed", "name": "electron-test-window-all-closed",
"main": "main.js" "main": "main.js"
} }

View File

@@ -1,4 +1,4 @@
{ {
"name": "snapshot-items-available", "name": "electron-test-snapshot-items-available",
"main": "main.js" "main": "main.js"
} }

View File

@@ -56,6 +56,7 @@ declare namespace Electron {
getLastWebPreferences(): Electron.WebPreferences; getLastWebPreferences(): Electron.WebPreferences;
_getPreloadPaths(): string[]; _getPreloadPaths(): string[];
equal(other: WebContents): boolean; equal(other: WebContents): boolean;
_initiallyShown: boolean;
} }
interface WebPreferences { interface WebPreferences {