mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
26 Commits
v11.4.5
...
v12.0.0-ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
733d56e908 | ||
|
|
43485b8705 | ||
|
|
bda6378685 | ||
|
|
1b6534b326 | ||
|
|
e9e7eee25e | ||
|
|
7e698df8f3 | ||
|
|
e0611d0946 | ||
|
|
184e72fafd | ||
|
|
3745b76da8 | ||
|
|
bf7dbff858 | ||
|
|
7c10f86c6e | ||
|
|
9803e4d526 | ||
|
|
068b464e13 | ||
|
|
a09694ae85 | ||
|
|
e06a1c2746 | ||
|
|
c5320b3951 | ||
|
|
6cc960f214 | ||
|
|
a4b6fce907 | ||
|
|
8f727b3569 | ||
|
|
95073decd3 | ||
|
|
e8ef1ef252 | ||
|
|
075502477e | ||
|
|
97755bbd85 | ||
|
|
bab69ae4d2 | ||
|
|
443540fd13 | ||
|
|
5e1950ceff |
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -2,3 +2,13 @@
|
||||
# files to be checked out with LF endings even if core.autocrlf is true.
|
||||
*.patch text eol=lf
|
||||
patches/**/.patches merge=union
|
||||
|
||||
# Source code and markdown files should always use LF as line ending.
|
||||
*.cc text eol=lf
|
||||
*.mm text eol=lf
|
||||
*.h text eol=lf
|
||||
*.js text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.py text eol=lf
|
||||
*.ps1 text eol=lf
|
||||
*.md text eol=lf
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -15,7 +15,6 @@ Contributors guide: https://github.com/electron/electron/blob/master/CONTRIBUTIN
|
||||
- [ ] relevant documentation is changed or added
|
||||
- [ ] PR title follows semantic [commit guidelines](https://github.com/electron/electron/blob/master/docs/development/pull-requests.md#commit-message-guidelines)
|
||||
- [ ] [PR release notes](https://github.com/electron/clerk/blob/master/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/master/README.md#examples).
|
||||
- [ ] This is **NOT A BREAKING CHANGE**. Breaking changes may not be merged to master until 11-x-y is branched.
|
||||
|
||||
#### Release Notes
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
11.0.0-nightly.20200826
|
||||
12.0.0-nightly.20200903
|
||||
@@ -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
|
||||
> tab". Since Electron has no such concept, passing `-1` as a tab ID is not
|
||||
> supported and will raise an error.
|
||||
|
||||
### `chrome.management`
|
||||
|
||||
The following methods of `chrome.management` are supported:
|
||||
|
||||
- `chrome.management.getAll`
|
||||
- `chrome.management.get`
|
||||
- `chrome.management.getSelf`
|
||||
- `chrome.management.getPermissionWarningsById`
|
||||
- `chrome.management.getPermissionWarningsByManifest`
|
||||
- `chrome.management.onEnabled`
|
||||
- `chrome.management.onDisabled`
|
||||
|
||||
@@ -8,19 +8,19 @@ Process: [Main](../glossary.md#main-process)
|
||||
|
||||
The `powerMonitor` module emits the following events:
|
||||
|
||||
### Event: 'suspend'
|
||||
### Event: 'suspend' _macOS_ _Windows_
|
||||
|
||||
Emitted when the system is suspending.
|
||||
|
||||
### Event: 'resume'
|
||||
### Event: 'resume' _macOS_ _Windows_
|
||||
|
||||
Emitted when system is resuming.
|
||||
|
||||
### Event: 'on-ac' _Windows_
|
||||
### Event: 'on-ac' _macOS_ _Windows_
|
||||
|
||||
Emitted when the system changes to AC power.
|
||||
|
||||
### Event: 'on-battery' _Windows_
|
||||
### Event: 'on-battery' _macOS_ _Windows_
|
||||
|
||||
Emitted when system changes to battery power.
|
||||
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
|
||||
Process: [Renderer](../glossary.md#renderer-process)
|
||||
|
||||
> ⚠️ WARNING ⚠️
|
||||
> The `remote` module is [deprecated](https://github.com/electron/electron/issues/21408).
|
||||
> Instead of `remote`, use [`ipcRenderer`](ipc-renderer.md) and
|
||||
> [`ipcMain`](ipc-main.md).
|
||||
>
|
||||
> Read more about why the `remote` module is deprecated [here](https://medium.com/@nornagon/electrons-remote-module-considered-harmful-70d69500f31).
|
||||
>
|
||||
> If you still want to use `remote` despite the performance and security
|
||||
> concerns, see [@electron/remote](https://github.com/electron/remote).
|
||||
|
||||
The `remote` module provides a simple way to do inter-process communication
|
||||
(IPC) between the renderer process (web page) and the main process.
|
||||
|
||||
|
||||
@@ -45,15 +45,27 @@ Returns `Promise<void>`
|
||||
|
||||
Open the given external protocol URL in the desktop's default manner. (For example, mailto: URLs in the user's default mail agent).
|
||||
|
||||
### `shell.moveItemToTrash(fullPath[, deleteOnFail])`
|
||||
### `shell.moveItemToTrash(fullPath[, deleteOnFail])` _Deprecated_
|
||||
|
||||
* `fullPath` String
|
||||
* `deleteOnFail` Boolean (optional) - Whether or not to unilaterally remove the item if the Trash is disabled or unsupported on the volume. _macOS_
|
||||
|
||||
Returns `Boolean` - Whether the item was successfully moved to the trash or otherwise deleted.
|
||||
|
||||
> NOTE: This method is deprecated. Use `shell.trashItem` instead.
|
||||
|
||||
Move the given file to trash and returns a boolean status for the operation.
|
||||
|
||||
### `shell.trashItem(path)`
|
||||
|
||||
* `path` String - path to the item to be moved to the trash.
|
||||
|
||||
Returns `Promise<void>` - Resolves when the operation has been completed.
|
||||
Rejects if there was an error while deleting the requested item.
|
||||
|
||||
This moves a path to the OS-specific trash location (Trash on macOS, Recycle
|
||||
Bin on Windows, and a desktop-environment-specific location on Linux).
|
||||
|
||||
### `shell.beep()`
|
||||
|
||||
Play the beep sound.
|
||||
|
||||
@@ -1117,6 +1117,10 @@ increment above or below represents zooming 20% larger or smaller to default
|
||||
limits of 300% and 50% of original size, respectively. The formula for this is
|
||||
`scale := 1.2 ^ level`.
|
||||
|
||||
> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the
|
||||
> zoom level for a specific domain propagates across all instances of windows with
|
||||
> the same domain. Differentiating the window URLs will make zoom work per-window.
|
||||
|
||||
#### `contents.getZoomLevel()`
|
||||
|
||||
Returns `Number` - the current zoom level.
|
||||
@@ -1296,9 +1300,9 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
|
||||
* `pagesPerSheet` Number (optional) - The number of pages to print per page sheet.
|
||||
* `collate` Boolean (optional) - Whether the web page should be collated.
|
||||
* `copies` Number (optional) - The number of copies of the web page to print.
|
||||
* `pageRanges` Record<string, number> (optional) - The page range to print.
|
||||
* `from` Number - the start page.
|
||||
* `to` Number - the end page.
|
||||
* `pageRanges` Object[] (optional) - The page range to print. On macOS, only one range is honored.
|
||||
* `from` Number - Index of the first page to print (0-based).
|
||||
* `to` Number - Index of the last page to print (inclusive) (0-based).
|
||||
* `duplexMode` String (optional) - Set the duplex mode of the printed web page. Can be `simplex`, `shortEdge`, or `longEdge`.
|
||||
* `dpi` Record<string, number> (optional)
|
||||
* `horizontal` Number (optional) - The horizontal dpi.
|
||||
@@ -1324,10 +1328,10 @@ Example usage:
|
||||
const options = {
|
||||
silent: true,
|
||||
deviceName: 'My-Printer',
|
||||
pageRanges: {
|
||||
pageRanges: [{
|
||||
from: 0,
|
||||
to: 1
|
||||
}
|
||||
}]
|
||||
}
|
||||
win.webContents.print(options, (success, errorType) => {
|
||||
if (!success) console.log(errorType)
|
||||
@@ -1345,8 +1349,8 @@ win.webContents.print(options, (success, errorType) => {
|
||||
default margin, 1 for no margin, and 2 for minimum margin.
|
||||
* `scaleFactor` Number (optional) - The scale factor of the web page. Can range from 0 to 100.
|
||||
* `pageRanges` Record<string, number> (optional) - The page range to print.
|
||||
* `from` Number - zero-based index of the first page to print.
|
||||
* `to` Number - zero-based index of the last page to print (inclusive).
|
||||
* `from` Number - Index of the first page to print (0-based).
|
||||
* `to` Number - Index of the last page to print (inclusive) (0-based).
|
||||
* `pageSize` String | Size (optional) - Specify page size of the generated PDF. Can be `A3`,
|
||||
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height` and `width` in microns.
|
||||
* `printBackground` Boolean (optional) - Whether to print CSS backgrounds.
|
||||
|
||||
@@ -41,6 +41,10 @@ Changes the zoom level to the specified level. The original size is 0 and each
|
||||
increment above or below represents zooming 20% larger or smaller to default
|
||||
limits of 300% and 50% of original size, respectively.
|
||||
|
||||
> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the
|
||||
> zoom level for a specific domain propagates across all instances of windows with
|
||||
> the same domain. Differentiating the window URLs will make zoom work per-window.
|
||||
|
||||
### `webFrame.getZoomLevel()`
|
||||
|
||||
Returns `Number` - The current zoom level.
|
||||
|
||||
@@ -560,9 +560,9 @@ Stops any `findInPage` request for the `webview` with the provided `action`.
|
||||
* `pagesPerSheet` Number (optional) - The number of pages to print per page sheet.
|
||||
* `collate` Boolean (optional) - Whether the web page should be collated.
|
||||
* `copies` Number (optional) - The number of copies of the web page to print.
|
||||
* `pageRanges` Record<string, number> (optional) - The page range to print.
|
||||
* `from` Number - zero-based index of the first page to print.
|
||||
* `to` Number - zero-based index of the last page to print (inclusive).
|
||||
* `pageRanges` Object[] (optional) - The page range to print.
|
||||
* `from` Number - Index of the first page to print (0-based).
|
||||
* `to` Number - Index of the last page to print (inclusive) (0-based).
|
||||
* `duplexMode` String (optional) - Set the duplex mode of the printed web page. Can be `simplex`, `shortEdge`, or `longEdge`.
|
||||
* `dpi` Record<string, number> (optional)
|
||||
* `horizontal` Number (optional) - The horizontal dpi.
|
||||
@@ -587,9 +587,9 @@ Prints `webview`'s web page. Same as `webContents.print([options])`.
|
||||
default margin, 1 for no margin, and 2 for minimum margin.
|
||||
and `width` in microns.
|
||||
* `scaleFactor` Number (optional) - The scale factor of the web page. Can range from 0 to 100.
|
||||
* `pageRanges` Record<string, number> (optional) - The page range to print.
|
||||
* `from` Number - the first page to print.
|
||||
* `to` Number - the last page to print (inclusive).
|
||||
* `pageRanges` Record<string, number> (optional) - The page range to print. On macOS, only the first range is honored.
|
||||
* `from` Number - Index of the first page to print (0-based).
|
||||
* `to` Number - Index of the last page to print (inclusive) (0-based).
|
||||
* `pageSize` String | Size (optional) - Specify page size of the generated PDF. Can be `A3`,
|
||||
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height`
|
||||
* `printBackground` Boolean (optional) - Whether to print CSS backgrounds.
|
||||
@@ -648,6 +648,10 @@ increment above or below represents zooming 20% larger or smaller to default
|
||||
limits of 300% and 50% of original size, respectively. The formula for this is
|
||||
`scale := 1.2 ^ level`.
|
||||
|
||||
> **NOTE**: The zoom policy at the Chromium level is same-origin, meaning that the
|
||||
> zoom level for a specific domain propagates across all instances of windows with
|
||||
> the same domain. Differentiating the window URLs will make zoom work per-window.
|
||||
|
||||
### `<webview>.getZoomFactor()`
|
||||
|
||||
Returns `Number` - the current zoom factor.
|
||||
|
||||
@@ -1,95 +1,95 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('open-error-dialog', event => {
|
||||
dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
|
||||
})
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('open-error-dialog', event => {
|
||||
dialog.showErrorBox('An Error Message', 'Demonstrating an error message.')
|
||||
})
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
const errorBtn = document.getElementById('error-dialog')
|
||||
|
||||
errorBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-error-dialog')
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
const errorBtn = document.getElementById('error-dialog')
|
||||
|
||||
errorBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-error-dialog')
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,70 +1,70 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-information-dialog', event => {
|
||||
const options = {
|
||||
type: 'info',
|
||||
title: 'Information',
|
||||
message: "This is an information dialog. Isn't it nice?",
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
dialog.showMessageBox(options, index => {
|
||||
event.sender.send('information-dialog-selection', index)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const informationBtn = document.getElementById('information-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
informationBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-information-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('information-dialog-selection', (event, index) => {
|
||||
let message = 'You selected '
|
||||
if (index === 0) message += 'yes.'
|
||||
else message += 'no.'
|
||||
document.getElementById('info-selection').innerHTML = message
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const informationBtn = document.getElementById('information-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
informationBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-information-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('information-dialog-selection', (event, index) => {
|
||||
let message = 'You selected '
|
||||
if (index === 0) message += 'yes.'
|
||||
else message += 'no.'
|
||||
document.getElementById('info-selection').innerHTML = message
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,70 +1,70 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on('open-file-dialog', event => {
|
||||
dialog.showOpenDialog(
|
||||
{
|
||||
properties: ['openFile', 'openDirectory']
|
||||
},
|
||||
files => {
|
||||
if (files) {
|
||||
event.sender.send('selected-directory', files)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const selectDirBtn = document.getElementById('select-directory')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
selectDirBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-file-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('selected-directory', (event, path) => {
|
||||
document.getElementById('selected-file').innerHTML = `You selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const selectDirBtn = document.getElementById('select-directory')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
selectDirBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('open-file-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('selected-directory', (event, path) => {
|
||||
document.getElementById('selected-file').innerHTML = `You selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,66 +1,66 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('save-dialog', event => {
|
||||
const options = {
|
||||
title: 'Save an Image',
|
||||
filters: [{ name: 'Images', extensions: ['jpg', 'png', 'gif'] }]
|
||||
}
|
||||
dialog.showSaveDialog(options, filename => {
|
||||
event.sender.send('saved-file', filename)
|
||||
})
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const saveBtn = document.getElementById('save-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
saveBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('save-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('saved-file', (event, path) => {
|
||||
if (!path) path = 'No path'
|
||||
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const saveBtn = document.getElementById('save-dialog')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
saveBtn.addEventListener('click', event => {
|
||||
ipcRenderer.send('save-dialog')
|
||||
})
|
||||
|
||||
ipcRenderer.on('saved-file', (event, path) => {
|
||||
if (!path) path = 'No path'
|
||||
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
const { shell } = require('electron')
|
||||
const os = require('os')
|
||||
|
||||
const exLinksBtn = document.getElementById('open-ex-links')
|
||||
const fileManagerBtn = document.getElementById('open-file-manager')
|
||||
|
||||
fileManagerBtn.addEventListener('click', (event) => {
|
||||
shell.showItemInFolder(os.homedir())
|
||||
})
|
||||
|
||||
exLinksBtn.addEventListener('click', (event) => {
|
||||
shell.openExternal('https://electronjs.org')
|
||||
const { shell } = require('electron')
|
||||
const os = require('os')
|
||||
|
||||
const exLinksBtn = document.getElementById('open-ex-links')
|
||||
const fileManagerBtn = document.getElementById('open-file-manager')
|
||||
|
||||
fileManagerBtn.addEventListener('click', (event) => {
|
||||
shell.showItemInFolder(os.homedir())
|
||||
})
|
||||
|
||||
exLinksBtn.addEventListener('click', (event) => {
|
||||
shell.openExternal('https://electronjs.org')
|
||||
})
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
const basicNotification = {
|
||||
title: 'Basic Notification',
|
||||
body: 'Short message part'
|
||||
}
|
||||
|
||||
const notification = {
|
||||
title: 'Notification with image',
|
||||
body: 'Short message plus a custom image',
|
||||
icon: 'https://via.placeholder.com/150'
|
||||
}
|
||||
|
||||
const basicNotificationButton = document.getElementById('basic-noti')
|
||||
const notificationButton = document.getElementById('advanced-noti')
|
||||
|
||||
notificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(notification.title, notification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
|
||||
basicNotificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(basicNotification.title, basicNotification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
const basicNotification = {
|
||||
title: 'Basic Notification',
|
||||
body: 'Short message part'
|
||||
}
|
||||
|
||||
const notification = {
|
||||
title: 'Notification with image',
|
||||
body: 'Short message plus a custom image',
|
||||
icon: 'https://via.placeholder.com/150'
|
||||
}
|
||||
|
||||
const basicNotificationButton = document.getElementById('basic-noti')
|
||||
const notificationButton = document.getElementById('advanced-noti')
|
||||
|
||||
notificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(notification.title, notification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
|
||||
basicNotificationButton.addEventListener('click', () => {
|
||||
const myNotification = new window.Notification(basicNotification.title, basicNotification)
|
||||
|
||||
myNotification.onclick = () => {
|
||||
console.log('Notification clicked')
|
||||
}
|
||||
})
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,35 +1,35 @@
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const trayBtn = document.getElementById('put-in-tray')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let trayOn = false
|
||||
|
||||
trayBtn.addEventListener('click', function (event) {
|
||||
if (trayOn) {
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
ipcRenderer.send('remove-tray')
|
||||
} else {
|
||||
trayOn = true
|
||||
const message = 'Click demo again to remove.'
|
||||
document.getElementById('tray-countdown').innerHTML = message
|
||||
ipcRenderer.send('put-in-tray')
|
||||
}
|
||||
})
|
||||
// Tray removed from context menu on icon
|
||||
ipcRenderer.on('tray-removed', function () {
|
||||
ipcRenderer.send('remove-tray')
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
const { ipcRenderer, shell } = require('electron')
|
||||
|
||||
const trayBtn = document.getElementById('put-in-tray')
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let trayOn = false
|
||||
|
||||
trayBtn.addEventListener('click', function (event) {
|
||||
if (trayOn) {
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
ipcRenderer.send('remove-tray')
|
||||
} else {
|
||||
trayOn = true
|
||||
const message = 'Click demo again to remove.'
|
||||
document.getElementById('tray-countdown').innerHTML = message
|
||||
ipcRenderer.send('put-in-tray')
|
||||
}
|
||||
})
|
||||
// Tray removed from context menu on icon
|
||||
ipcRenderer.on('tray-removed', function () {
|
||||
ipcRenderer.send('remove-tray')
|
||||
trayOn = false
|
||||
document.getElementById('tray-countdown').innerHTML = ''
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const framelessWindowBtn = document.getElementById('frameless-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
framelessWindowBtn.addEventListener('click', (event) => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
let win = new BrowserWindow({ frame: false })
|
||||
|
||||
win.on('close', () => { win = null })
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const framelessWindowBtn = document.getElementById('frameless-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
framelessWindowBtn.addEventListener('click', (event) => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
let win = new BrowserWindow({ frame: false })
|
||||
|
||||
win.on('close', () => { win = null })
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const manageWindowBtn = document.getElementById('manage-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let win
|
||||
|
||||
manageWindowBtn.addEventListener('click', (event) => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
win = new BrowserWindow({ width: 400, height: 275 })
|
||||
|
||||
win.on('resize', updateReply)
|
||||
win.on('move', updateReply)
|
||||
win.on('close', () => { win = null })
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
|
||||
function updateReply () {
|
||||
const manageWindowReply = document.getElementById('manage-window-reply')
|
||||
const message = `Size: ${win.getSize()} Position: ${win.getPosition()}`
|
||||
manageWindowReply.innerText = message
|
||||
}
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const manageWindowBtn = document.getElementById('manage-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let win
|
||||
|
||||
manageWindowBtn.addEventListener('click', (event) => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
win = new BrowserWindow({ width: 400, height: 275 })
|
||||
|
||||
win.on('resize', updateReply)
|
||||
win.on('move', updateReply)
|
||||
win.on('close', () => { win = null })
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
|
||||
function updateReply () {
|
||||
const manageWindowReply = document.getElementById('manage-window-reply')
|
||||
const message = `Size: ${win.getSize()} Position: ${win.getPosition()}`
|
||||
manageWindowReply.innerText = message
|
||||
}
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
mainWindow.on('closed', function () {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', function () {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it is common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const listenToWindowBtn = document.getElementById('listen-to-window')
|
||||
const focusModalBtn = document.getElementById('focus-on-modal-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let win
|
||||
|
||||
listenToWindowBtn.addEventListener('click', () => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
win = new BrowserWindow({ width: 600, height: 400 })
|
||||
|
||||
const hideFocusBtn = () => {
|
||||
focusModalBtn.classList.add('disappear')
|
||||
focusModalBtn.classList.remove('smooth-appear')
|
||||
focusModalBtn.removeEventListener('click', clickHandler)
|
||||
}
|
||||
|
||||
const showFocusBtn = (btn) => {
|
||||
if (!win) return
|
||||
focusModalBtn.classList.add('smooth-appear')
|
||||
focusModalBtn.classList.remove('disappear')
|
||||
focusModalBtn.addEventListener('click', clickHandler)
|
||||
}
|
||||
|
||||
win.on('focus', hideFocusBtn)
|
||||
win.on('blur', showFocusBtn)
|
||||
win.on('close', () => {
|
||||
hideFocusBtn()
|
||||
win = null
|
||||
})
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
|
||||
const clickHandler = () => { win.focus() }
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
const { BrowserWindow } = require('electron').remote
|
||||
const shell = require('electron').shell
|
||||
|
||||
const listenToWindowBtn = document.getElementById('listen-to-window')
|
||||
const focusModalBtn = document.getElementById('focus-on-modal-window')
|
||||
|
||||
const links = document.querySelectorAll('a[href]')
|
||||
|
||||
let win
|
||||
|
||||
listenToWindowBtn.addEventListener('click', () => {
|
||||
const modalPath = 'https://electronjs.org'
|
||||
win = new BrowserWindow({ width: 600, height: 400 })
|
||||
|
||||
const hideFocusBtn = () => {
|
||||
focusModalBtn.classList.add('disappear')
|
||||
focusModalBtn.classList.remove('smooth-appear')
|
||||
focusModalBtn.removeEventListener('click', clickHandler)
|
||||
}
|
||||
|
||||
const showFocusBtn = (btn) => {
|
||||
if (!win) return
|
||||
focusModalBtn.classList.add('smooth-appear')
|
||||
focusModalBtn.classList.remove('disappear')
|
||||
focusModalBtn.addEventListener('click', clickHandler)
|
||||
}
|
||||
|
||||
win.on('focus', hideFocusBtn)
|
||||
win.on('blur', showFocusBtn)
|
||||
win.on('close', () => {
|
||||
hideFocusBtn()
|
||||
win = null
|
||||
})
|
||||
win.loadURL(modalPath)
|
||||
win.show()
|
||||
|
||||
const clickHandler = () => { win.focus() }
|
||||
})
|
||||
|
||||
Array.prototype.forEach.call(links, (link) => {
|
||||
const url = link.getAttribute('href')
|
||||
if (url.indexOf('http') === 0) {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -565,7 +565,9 @@ filenames = {
|
||||
"shell/common/node_util.h",
|
||||
"shell/common/options_switches.cc",
|
||||
"shell/common/options_switches.h",
|
||||
"shell/common/platform_util.cc",
|
||||
"shell/common/platform_util.h",
|
||||
"shell/common/platform_util_internal.h",
|
||||
"shell/common/platform_util_linux.cc",
|
||||
"shell/common/platform_util_mac.mm",
|
||||
"shell/common/platform_util_win.cc",
|
||||
@@ -625,6 +627,8 @@ filenames = {
|
||||
"shell/browser/extensions/api/resources_private/resources_private_api.h",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc",
|
||||
"shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.cc",
|
||||
"shell/browser/extensions/api/management/electron_management_api_delegate.h",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.cc",
|
||||
"shell/browser/extensions/api/tabs/tabs_api.h",
|
||||
"shell/browser/extensions/api/streams_private/streams_private_api.cc",
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
export default process._linkedBinding('electron_common_shell');
|
||||
import { deprecate } from 'electron/main';
|
||||
|
||||
const shell = process._linkedBinding('electron_common_shell');
|
||||
|
||||
shell.moveItemToTrash = deprecate.renameFunction(shell.moveItemToTrash, 'shell.trashItem');
|
||||
|
||||
export default shell;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "11.0.0-nightly.20200826",
|
||||
"version": "12.0.0-nightly.20200903",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -100,3 +100,4 @@ remove_some_deps_that_do_not_work_on_arm64.patch
|
||||
fix_check_issecureeventinputenabled_in_constructor_before_setting.patch
|
||||
skip_atk_toolchain_check.patch
|
||||
worker_feat_add_hook_to_notify_script_ready.patch
|
||||
fix_properly_honor_printing_page_ranges.patch
|
||||
|
||||
129
patches/chromium/fix_properly_honor_printing_page_ranges.patch
Normal file
129
patches/chromium/fix_properly_honor_printing_page_ranges.patch
Normal file
@@ -0,0 +1,129 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Thu, 20 Aug 2020 10:55:48 -0700
|
||||
Subject: fix: properly honor printing page ranges
|
||||
|
||||
The print ranges in Chromium's print job settings were not being properly
|
||||
plumbed through to PMPrintSettings on mcOS. This fixes that by setting
|
||||
them should they exist.
|
||||
|
||||
This will be upstreamed.
|
||||
|
||||
diff --git a/printing/printing_context_mac.h b/printing/printing_context_mac.h
|
||||
index 06cdc0e1a4a50e29a148c97c964c30a939e800da..5db5cdb61be0beee4506313dcde46c499e011383 100644
|
||||
--- a/printing/printing_context_mac.h
|
||||
+++ b/printing/printing_context_mac.h
|
||||
@@ -81,6 +81,10 @@ class PRINTING_EXPORT PrintingContextMac : public PrintingContext {
|
||||
// Returns true if the orientation was set.
|
||||
bool SetOrientationIsLandscape(bool landscape);
|
||||
|
||||
+ // Set the page range in native print info object.
|
||||
+ // Returns true if the range was set.
|
||||
+ bool SetPrintRangeInPrintSettings(const PageRanges& ranges);
|
||||
+
|
||||
// Sets duplex mode in PMPrintSettings.
|
||||
// Returns true if duplex mode is set.
|
||||
bool SetDuplexModeInPrintSettings(mojom::DuplexMode mode);
|
||||
diff --git a/printing/printing_context_mac.mm b/printing/printing_context_mac.mm
|
||||
index 56bcfbe625537c7b1bc2a999c261836bb15896ba..f916e78de99b87e0dae4f5f87329886b658ccd93 100644
|
||||
--- a/printing/printing_context_mac.mm
|
||||
+++ b/printing/printing_context_mac.mm
|
||||
@@ -188,7 +188,8 @@ PMPaper MatchPaper(CFArrayRef paper_list,
|
||||
!SetCopiesInPrintSettings(settings_->copies()) ||
|
||||
!SetCollateInPrintSettings(settings_->collate()) ||
|
||||
!SetDuplexModeInPrintSettings(settings_->duplex_mode()) ||
|
||||
- !SetOutputColor(settings_->color())) {
|
||||
+ !SetOutputColor(settings_->color()) ||
|
||||
+ !SetPrintRangeInPrintSettings(settings_->ranges()) ) {
|
||||
return OnError();
|
||||
}
|
||||
}
|
||||
@@ -341,6 +342,22 @@ PMPaper MatchPaper(CFArrayRef paper_list,
|
||||
return PMSetCopies(print_settings, copies, false) == noErr;
|
||||
}
|
||||
|
||||
+bool PrintingContextMac::SetPrintRangeInPrintSettings(const PageRanges& ranges) {
|
||||
+ // Default is already NSPrintAllPages - we can safely bail.
|
||||
+ if (ranges.empty())
|
||||
+ return true;
|
||||
+
|
||||
+ auto* print_settings =
|
||||
+ static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
|
||||
+
|
||||
+ // macOS does not allow multiple ranges, so pluck the first.
|
||||
+ auto range = ranges.front();
|
||||
+ bool set_first_page = PMSetFirstPage(print_settings, range.from + 1, false) == noErr;
|
||||
+ bool set_last_page = PMSetLastPage(print_settings, range.to + 1, false) == noErr;
|
||||
+
|
||||
+ return set_first_page && set_last_page;
|
||||
+}
|
||||
+
|
||||
bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
|
||||
PMPrintSettings print_settings =
|
||||
static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
|
||||
diff --git a/printing/printing_context_system_dialog_win.cc b/printing/printing_context_system_dialog_win.cc
|
||||
index d3c8677f30d72efc49b28f293260c74c7b8d8b4e..f6e66aaa58ab1881d64dcbb320ae8b5ac7631b28 100644
|
||||
--- a/printing/printing_context_system_dialog_win.cc
|
||||
+++ b/printing/printing_context_system_dialog_win.cc
|
||||
@@ -52,14 +52,28 @@ void PrintingContextSystemDialogWin::AskUserForSettings(
|
||||
PRINTPAGERANGE ranges[32];
|
||||
dialog_options.nStartPage = START_PAGE_GENERAL;
|
||||
if (max_pages) {
|
||||
- // Default initialize to print all the pages.
|
||||
memset(ranges, 0, sizeof(ranges));
|
||||
- ranges[0].nFromPage = 1;
|
||||
- ranges[0].nToPage = max_pages;
|
||||
- dialog_options.nPageRanges = 1;
|
||||
- dialog_options.nMaxPageRanges = base::size(ranges);
|
||||
+
|
||||
+ auto page_ranges = settings_->ranges();
|
||||
+ if (!page_ranges.empty()) {
|
||||
+ for (size_t i = 0; i < page_ranges.size(); i++) {
|
||||
+ auto range = page_ranges[i];
|
||||
+ ranges[i].nFromPage = range.from + 1;
|
||||
+ ranges[i].nToPage = range.to + 1;
|
||||
+ }
|
||||
+ dialog_options.nPageRanges = page_ranges.size();
|
||||
+
|
||||
+ // Ensure the Pages radio button is selected.
|
||||
+ dialog_options.Flags |= PD_PAGENUMS;
|
||||
+ } else {
|
||||
+ ranges[0].nFromPage = 1;
|
||||
+ ranges[0].nToPage = max_pages;
|
||||
+ dialog_options.nPageRanges = 1;
|
||||
+ }
|
||||
+
|
||||
dialog_options.nMinPage = 1;
|
||||
dialog_options.nMaxPage = max_pages;
|
||||
+ dialog_options.nMaxPageRanges = base::size(ranges);
|
||||
dialog_options.lpPageRanges = ranges;
|
||||
} else {
|
||||
// No need to bother, we don't know how many pages are available.
|
||||
diff --git a/ui/gtk/printing/print_dialog_gtk.cc b/ui/gtk/printing/print_dialog_gtk.cc
|
||||
index 8a78a609295a1d90208bdc7e73d7a850c319f361..2f5bc01bedf84126d8fb1597a0b813a2b7230279 100644
|
||||
--- a/ui/gtk/printing/print_dialog_gtk.cc
|
||||
+++ b/ui/gtk/printing/print_dialog_gtk.cc
|
||||
@@ -239,6 +239,23 @@ void PrintDialogGtk::UpdateSettings(
|
||||
|
||||
gtk_print_settings_set_n_copies(gtk_settings_, settings->copies());
|
||||
gtk_print_settings_set_collate(gtk_settings_, settings->collate());
|
||||
+
|
||||
+ auto print_ranges = settings->ranges();
|
||||
+ if (!print_ranges.empty()) {
|
||||
+ // Tell the system that we only intend to print a subset of pages.
|
||||
+ gtk_print_settings_set_print_pages(gtk_settings_, GTK_PRINT_PAGES_RANGES);
|
||||
+
|
||||
+ GtkPageRange* ranges;
|
||||
+ ranges = g_new(GtkPageRange, print_ranges.size());
|
||||
+ for (size_t i = 0; i < print_ranges.size(); i++) {
|
||||
+ auto range = print_ranges[i];
|
||||
+ ranges[i].start = range.from;
|
||||
+ ranges[i].end = range.to;
|
||||
+ }
|
||||
+
|
||||
+ gtk_print_settings_set_page_ranges(gtk_settings_, ranges, 1);
|
||||
+ g_free(ranges);
|
||||
+ }
|
||||
|
||||
#if defined(USE_CUPS)
|
||||
// Set advanced settings first so they can be overridden by user applied
|
||||
@@ -43,3 +43,4 @@ fix_allow_preventing_setpromiserejectcallback.patch
|
||||
lib_use_non-symbols_in_isurlinstance_check.patch
|
||||
feat_add_implementation_of_v8_platform_postjob.patch
|
||||
fix_enable_tls_renegotiation.patch
|
||||
n-api_src_provide_asynchronous_cleanup_hooks.patch
|
||||
|
||||
250
patches/node/n-api_src_provide_asynchronous_cleanup_hooks.patch
Normal file
250
patches/node/n-api_src_provide_asynchronous_cleanup_hooks.patch
Normal file
@@ -0,0 +1,250 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Tue, 25 Aug 2020 19:34:12 -0700
|
||||
Subject: n-api,src: provide asynchronous cleanup hooks
|
||||
|
||||
Backports https://github.com/nodejs/node/pull/34572 and
|
||||
https://github.com/nodejs/node/pull/34819.
|
||||
|
||||
Sometimes addons need to perform cleanup actions, for example
|
||||
closing libuv handles or waiting for requests to finish, that
|
||||
cannot be performed synchronously.
|
||||
|
||||
Add C++ API and N-API functions that allow providing such
|
||||
asynchronous cleanup hooks.
|
||||
|
||||
This patch can be removed when Electron upgrades to a version of Node.js
|
||||
which contains the above referenced commit(s).
|
||||
|
||||
diff --git a/src/api/hooks.cc b/src/api/hooks.cc
|
||||
index 037bdda6f41c825dd112b0cc9fca0ebde47c6163..3b16c0350d8a8494202144407664af41d338fe04 100644
|
||||
--- a/src/api/hooks.cc
|
||||
+++ b/src/api/hooks.cc
|
||||
@@ -73,8 +73,35 @@ int EmitExit(Environment* env) {
|
||||
.ToChecked();
|
||||
}
|
||||
|
||||
+typedef void (*CleanupHook)(void* arg);
|
||||
+typedef void (*AsyncCleanupHook)(void* arg, void(*)(void*), void*);
|
||||
+
|
||||
+struct AsyncCleanupHookInfo final {
|
||||
+ Environment* env;
|
||||
+ AsyncCleanupHook fun;
|
||||
+ void* arg;
|
||||
+ bool started = false;
|
||||
+ // Use a self-reference to make sure the storage is kept alive while the
|
||||
+ // cleanup hook is registered but not yet finished.
|
||||
+ std::shared_ptr<AsyncCleanupHookInfo> self;
|
||||
+};
|
||||
+
|
||||
+// Opaque type that is basically an alias for `shared_ptr<AsyncCleanupHookInfo>`
|
||||
+// (but not publicly so for easier ABI/API changes). In particular,
|
||||
+// std::shared_ptr does not generally maintain a consistent ABI even on a
|
||||
+// specific platform.
|
||||
+struct ACHHandle final {
|
||||
+ std::shared_ptr<AsyncCleanupHookInfo> info;
|
||||
+};
|
||||
+// This is implemented as an operator on a struct because otherwise you can't
|
||||
+// default-initialize AsyncCleanupHookHandle, because in C++ for a
|
||||
+// std::unique_ptr to be default-initializable the deleter type also needs
|
||||
+// to be default-initializable; in particular, function types don't satisfy
|
||||
+// this.
|
||||
+void DeleteACHHandle::operator ()(ACHHandle* handle) const { delete handle; }
|
||||
+
|
||||
void AddEnvironmentCleanupHook(Isolate* isolate,
|
||||
- void (*fun)(void* arg),
|
||||
+ CleanupHook fun,
|
||||
void* arg) {
|
||||
Environment* env = Environment::GetCurrent(isolate);
|
||||
CHECK_NOT_NULL(env);
|
||||
@@ -82,13 +109,50 @@ void AddEnvironmentCleanupHook(Isolate* isolate,
|
||||
}
|
||||
|
||||
void RemoveEnvironmentCleanupHook(Isolate* isolate,
|
||||
- void (*fun)(void* arg),
|
||||
+ CleanupHook fun,
|
||||
void* arg) {
|
||||
Environment* env = Environment::GetCurrent(isolate);
|
||||
CHECK_NOT_NULL(env);
|
||||
env->RemoveCleanupHook(fun, arg);
|
||||
}
|
||||
|
||||
+static void FinishAsyncCleanupHook(void* arg) {
|
||||
+ AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
|
||||
+ std::shared_ptr<AsyncCleanupHookInfo> keep_alive = info->self;
|
||||
+
|
||||
+ info->env->DecreaseWaitingRequestCounter();
|
||||
+ info->self.reset();
|
||||
+}
|
||||
+
|
||||
+static void RunAsyncCleanupHook(void* arg) {
|
||||
+ AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
|
||||
+ info->env->IncreaseWaitingRequestCounter();
|
||||
+ info->started = true;
|
||||
+ info->fun(info->arg, FinishAsyncCleanupHook, info);
|
||||
+}
|
||||
+
|
||||
+AsyncCleanupHookHandle AddEnvironmentCleanupHook(
|
||||
+ Isolate* isolate,
|
||||
+ AsyncCleanupHook fun,
|
||||
+ void* arg) {
|
||||
+ Environment* env = Environment::GetCurrent(isolate);
|
||||
+ CHECK_NOT_NULL(env);
|
||||
+ auto info = std::make_shared<AsyncCleanupHookInfo>();
|
||||
+ info->env = env;
|
||||
+ info->fun = fun;
|
||||
+ info->arg = arg;
|
||||
+ info->self = info;
|
||||
+ env->AddCleanupHook(RunAsyncCleanupHook, info.get());
|
||||
+ return AsyncCleanupHookHandle(new ACHHandle { info });
|
||||
+}
|
||||
+
|
||||
+void RemoveEnvironmentCleanupHook(
|
||||
+ AsyncCleanupHookHandle handle) {
|
||||
+ if (handle->info->started) return;
|
||||
+ handle->info->self.reset();
|
||||
+ handle->info->env->RemoveCleanupHook(RunAsyncCleanupHook, handle->info.get());
|
||||
+}
|
||||
+
|
||||
async_id AsyncHooksGetExecutionAsyncId(Isolate* isolate) {
|
||||
Environment* env = Environment::GetCurrent(isolate);
|
||||
if (env == nullptr) return -1;
|
||||
diff --git a/src/node.h b/src/node.h
|
||||
index 60ecc3bd3499c23b297bc11e7f052aede20520c2..4c4e55e338d7b42c36818a45f6b41170c495adde 100644
|
||||
--- a/src/node.h
|
||||
+++ b/src/node.h
|
||||
@@ -739,6 +739,20 @@ NODE_EXTERN void RemoveEnvironmentCleanupHook(v8::Isolate* isolate,
|
||||
void (*fun)(void* arg),
|
||||
void* arg);
|
||||
|
||||
+/* These are async equivalents of the above. After the cleanup hook is invoked,
|
||||
+ * `cb(cbarg)` *must* be called, and attempting to remove the cleanup hook will
|
||||
+ * have no effect. */
|
||||
+struct ACHHandle;
|
||||
+struct NODE_EXTERN DeleteACHHandle { void operator()(ACHHandle*) const; };
|
||||
+typedef std::unique_ptr<ACHHandle, DeleteACHHandle> AsyncCleanupHookHandle;
|
||||
+
|
||||
+NODE_EXTERN AsyncCleanupHookHandle AddEnvironmentCleanupHook(
|
||||
+ v8::Isolate* isolate,
|
||||
+ void (*fun)(void* arg, void (*cb)(void*), void* cbarg),
|
||||
+ void* arg);
|
||||
+
|
||||
+NODE_EXTERN void RemoveEnvironmentCleanupHook(AsyncCleanupHookHandle holder);
|
||||
+
|
||||
/* Returns the id of the current execution context. If the return value is
|
||||
* zero then no execution has been set. This will happen if the user handles
|
||||
* I/O from native code. */
|
||||
diff --git a/src/node_api.cc b/src/node_api.cc
|
||||
index fe24eca1b8e2d81fbafd0a1e2da38d957fbaa1c1..66168bd2c28ce6481516e63734616f487e3ec3e1 100644
|
||||
--- a/src/node_api.cc
|
||||
+++ b/src/node_api.cc
|
||||
@@ -507,6 +507,70 @@ napi_status napi_remove_env_cleanup_hook(napi_env env,
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
+struct napi_async_cleanup_hook_handle__ {
|
||||
+ napi_async_cleanup_hook_handle__(napi_env env,
|
||||
+ napi_async_cleanup_hook user_hook,
|
||||
+ void* user_data):
|
||||
+ env_(env),
|
||||
+ user_hook_(user_hook),
|
||||
+ user_data_(user_data) {
|
||||
+ handle_ = node::AddEnvironmentCleanupHook(env->isolate, Hook, this);
|
||||
+ env->Ref();
|
||||
+ }
|
||||
+
|
||||
+ ~napi_async_cleanup_hook_handle__() {
|
||||
+ node::RemoveEnvironmentCleanupHook(std::move(handle_));
|
||||
+ if (done_cb_ != nullptr)
|
||||
+ done_cb_(done_data_);
|
||||
+
|
||||
+ // Release the `env` handle asynchronously since it would be surprising if
|
||||
+ // a call to a N-API function would destroy `env` synchronously.
|
||||
+ static_cast<node_napi_env>(env_)->node_env()
|
||||
+ ->SetImmediate([env = env_](node::Environment*) { env->Unref(); });
|
||||
+ }
|
||||
+
|
||||
+ static void Hook(void* data, void (*done_cb)(void*), void* done_data) {
|
||||
+ auto handle = static_cast<napi_async_cleanup_hook_handle__*>(data);
|
||||
+ handle->done_cb_ = done_cb;
|
||||
+ handle->done_data_ = done_data;
|
||||
+ handle->user_hook_(handle, handle->user_data_);
|
||||
+ }
|
||||
+
|
||||
+ node::AsyncCleanupHookHandle handle_;
|
||||
+ napi_env env_ = nullptr;
|
||||
+ napi_async_cleanup_hook user_hook_ = nullptr;
|
||||
+ void* user_data_ = nullptr;
|
||||
+ void (*done_cb_)(void*) = nullptr;
|
||||
+ void* done_data_ = nullptr;
|
||||
+};
|
||||
+
|
||||
+napi_status napi_add_async_cleanup_hook(
|
||||
+ napi_env env,
|
||||
+ napi_async_cleanup_hook hook,
|
||||
+ void* arg,
|
||||
+ napi_async_cleanup_hook_handle* remove_handle) {
|
||||
+ CHECK_ENV(env);
|
||||
+ CHECK_ARG(env, hook);
|
||||
+
|
||||
+ napi_async_cleanup_hook_handle__* handle =
|
||||
+ new napi_async_cleanup_hook_handle__(env, hook, arg);
|
||||
+
|
||||
+ if (remove_handle != nullptr)
|
||||
+ *remove_handle = handle;
|
||||
+
|
||||
+ return napi_clear_last_error(env);
|
||||
+}
|
||||
+
|
||||
+napi_status napi_remove_async_cleanup_hook(
|
||||
+ napi_async_cleanup_hook_handle remove_handle) {
|
||||
+
|
||||
+ if (remove_handle == nullptr)
|
||||
+ return napi_invalid_arg;
|
||||
+
|
||||
+ delete remove_handle;
|
||||
+ return napi_ok;
|
||||
+}
|
||||
+
|
||||
napi_status napi_fatal_exception(napi_env env, napi_value err) {
|
||||
NAPI_PREAMBLE(env);
|
||||
CHECK_ARG(env, err);
|
||||
diff --git a/src/node_api.h b/src/node_api.h
|
||||
index 2f1b45572d8130f27492eb6188c1aa611f2e01a3..577a1dcd94987202819e7a36a2d9674f13d13614 100644
|
||||
--- a/src/node_api.h
|
||||
+++ b/src/node_api.h
|
||||
@@ -250,6 +250,19 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func);
|
||||
|
||||
#endif // NAPI_VERSION >= 4
|
||||
|
||||
+#ifdef NAPI_EXPERIMENTAL
|
||||
+
|
||||
+NAPI_EXTERN napi_status napi_add_async_cleanup_hook(
|
||||
+ napi_env env,
|
||||
+ napi_async_cleanup_hook hook,
|
||||
+ void* arg,
|
||||
+ napi_async_cleanup_hook_handle* remove_handle);
|
||||
+
|
||||
+NAPI_EXTERN napi_status napi_remove_async_cleanup_hook(
|
||||
+ napi_async_cleanup_hook_handle remove_handle);
|
||||
+
|
||||
+#endif // NAPI_EXPERIMENTAL
|
||||
+
|
||||
EXTERN_C_END
|
||||
|
||||
#endif // SRC_NODE_API_H_
|
||||
diff --git a/src/node_api_types.h b/src/node_api_types.h
|
||||
index 1c9a2b8aa21889c0d29fb02b234ae9698d122c2c..0e400e9676df5ba09d350fe7a2a70a1dc9e4d3d6 100644
|
||||
--- a/src/node_api_types.h
|
||||
+++ b/src/node_api_types.h
|
||||
@@ -41,4 +41,10 @@ typedef struct {
|
||||
const char* release;
|
||||
} napi_node_version;
|
||||
|
||||
+#ifdef NAPI_EXPERIMENTAL
|
||||
+typedef struct napi_async_cleanup_hook_handle__* napi_async_cleanup_hook_handle;
|
||||
+typedef void (*napi_async_cleanup_hook)(napi_async_cleanup_hook_handle handle,
|
||||
+ void* data);
|
||||
+#endif // NAPI_EXPERIMENTAL
|
||||
+
|
||||
#endif // SRC_NODE_API_TYPES_H_
|
||||
@@ -91,7 +91,7 @@ async function createRelease (branchToTarget, isBeta) {
|
||||
if (newVersion.indexOf('nightly') > 0) {
|
||||
releaseBody = 'Note: This is a nightly release. Please file new issues ' +
|
||||
'for any bugs you find in it.\n \n This release is published to npm ' +
|
||||
'under the nightly tag and can be installed via `npm install electron@nightly`, ' +
|
||||
'under the electron-nightly package and can be installed via `npm install electron-nightly`, ' +
|
||||
`or \`npm install electron-nightly@${newVersion.substr(1)}\`.\n \n ${releaseNotes.text}`;
|
||||
} else {
|
||||
releaseBody = 'Note: This is a beta release. Please file new issues ' +
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
param([string]$gomaDir=$PWD)
|
||||
$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py"
|
||||
Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start"
|
||||
$timedOut = $false; $waitTime = 0; $waitIncrement = 5; $timeout=120;
|
||||
Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut)
|
||||
if ($timedOut) {
|
||||
write-error 'Timed out waiting for goma to start'; exit 1;
|
||||
} else {
|
||||
Write-Output "Successfully started goma!"
|
||||
}
|
||||
param([string]$gomaDir=$PWD)
|
||||
$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py"
|
||||
Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start"
|
||||
$timedOut = $false; $waitTime = 0; $waitIncrement = 5; $timeout=120;
|
||||
Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut)
|
||||
if ($timedOut) {
|
||||
write-error 'Timed out waiting for goma to start'; exit 1;
|
||||
} else {
|
||||
Write-Output "Successfully started goma!"
|
||||
}
|
||||
|
||||
@@ -744,9 +744,10 @@ void BaseWindow::AddBrowserView(v8::Local<v8::Value> value) {
|
||||
gin::ConvertFromV8(isolate(), value, &browser_view)) {
|
||||
auto get_that_view = browser_views_.find(browser_view->ID());
|
||||
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_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)) {
|
||||
auto get_that_view = browser_views_.find(browser_view->ID());
|
||||
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);
|
||||
}
|
||||
(*get_that_view).second.Reset(isolate(), value);
|
||||
browser_views_.erase(get_that_view);
|
||||
}
|
||||
@@ -1055,9 +1057,10 @@ void BaseWindow::ResetBrowserViews() {
|
||||
v8::Local<v8::Value>::New(isolate(), item.second),
|
||||
&browser_view) &&
|
||||
!browser_view.IsEmpty()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
item.second.Reset();
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <wtsapi32.h>
|
||||
|
||||
#include "base/win/windows_types.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "base/win/wrapped_window_proc.h"
|
||||
#include "ui/base/win/shell.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
|
||||
// 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_),
|
||||
DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
}
|
||||
|
||||
@@ -2069,8 +2069,9 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
int from, to;
|
||||
if (range.Get("from", &from) && range.Get("to", &to)) {
|
||||
base::Value range(base::Value::Type::DICTIONARY);
|
||||
range.SetIntKey(printing::kSettingPageRangeFrom, from);
|
||||
range.SetIntKey(printing::kSettingPageRangeTo, to);
|
||||
// Chromium uses 1-based page ranges, so increment each by 1.
|
||||
range.SetIntKey(printing::kSettingPageRangeFrom, from + 1);
|
||||
range.SetIntKey(printing::kSettingPageRangeTo, to + 1);
|
||||
page_range_list.Append(std::move(range));
|
||||
} else {
|
||||
continue;
|
||||
|
||||
@@ -355,6 +355,7 @@ class Browser : public WindowListObserver {
|
||||
|
||||
#if defined(OS_MAC)
|
||||
std::unique_ptr<ui::ScopedPasswordInputEnabler> password_input_enabler_;
|
||||
base::Time last_dock_show_;
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_WIN)
|
||||
|
||||
@@ -383,6 +383,20 @@ std::string Browser::DockGetBadgeText() {
|
||||
}
|
||||
|
||||
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())
|
||||
[window->GetNativeWindow().GetNativeNSWindow() setCanHide:NO];
|
||||
|
||||
@@ -398,6 +412,7 @@ bool Browser::DockIsVisible() {
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> Browser::DockShow(v8::Isolate* isolate) {
|
||||
last_dock_show_ = base::Time::Now();
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/electron_autofill_driver.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
AutofillDriver::AutofillDriver(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request)
|
||||
: render_frame_host_(render_frame_host), binding_(this) {
|
||||
autofill_popup_ = std::make_unique<AutofillPopup>();
|
||||
binding_.Bind(std::move(request));
|
||||
}
|
||||
|
||||
AutofillDriver::~AutofillDriver() = default;
|
||||
|
||||
void AutofillDriver::ShowAutofillPopup(
|
||||
const gfx::RectF& bounds,
|
||||
const std::vector<base::string16>& values,
|
||||
const std::vector<base::string16>& labels) {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto* web_contents = api::WebContents::From(
|
||||
content::WebContents::FromRenderFrameHost(render_frame_host_));
|
||||
if (!web_contents || !web_contents->owner_window())
|
||||
return;
|
||||
|
||||
auto* embedder = web_contents->embedder();
|
||||
|
||||
bool osr =
|
||||
web_contents->IsOffScreen() || (embedder && embedder->IsOffScreen());
|
||||
gfx::RectF popup_bounds(bounds);
|
||||
content::RenderFrameHost* embedder_frame_host = nullptr;
|
||||
if (embedder) {
|
||||
auto* embedder_view = embedder->web_contents()->GetMainFrame()->GetView();
|
||||
auto* view = web_contents->web_contents()->GetMainFrame()->GetView();
|
||||
auto offset = view->GetViewBounds().origin() -
|
||||
embedder_view->GetViewBounds().origin();
|
||||
popup_bounds.Offset(offset);
|
||||
embedder_frame_host = embedder->web_contents()->GetMainFrame();
|
||||
}
|
||||
|
||||
autofill_popup_->CreateView(render_frame_host_, embedder_frame_host, osr,
|
||||
web_contents->owner_window()->content_view(),
|
||||
popup_bounds);
|
||||
autofill_popup_->SetItems(values, labels);
|
||||
}
|
||||
|
||||
void AutofillDriver::HideAutofillPopup() {
|
||||
if (autofill_popup_)
|
||||
autofill_popup_->Hide();
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/electron_autofill_driver.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
AutofillDriver::AutofillDriver(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request)
|
||||
: render_frame_host_(render_frame_host), binding_(this) {
|
||||
autofill_popup_ = std::make_unique<AutofillPopup>();
|
||||
binding_.Bind(std::move(request));
|
||||
}
|
||||
|
||||
AutofillDriver::~AutofillDriver() = default;
|
||||
|
||||
void AutofillDriver::ShowAutofillPopup(
|
||||
const gfx::RectF& bounds,
|
||||
const std::vector<base::string16>& values,
|
||||
const std::vector<base::string16>& labels) {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto* web_contents = api::WebContents::From(
|
||||
content::WebContents::FromRenderFrameHost(render_frame_host_));
|
||||
if (!web_contents || !web_contents->owner_window())
|
||||
return;
|
||||
|
||||
auto* embedder = web_contents->embedder();
|
||||
|
||||
bool osr =
|
||||
web_contents->IsOffScreen() || (embedder && embedder->IsOffScreen());
|
||||
gfx::RectF popup_bounds(bounds);
|
||||
content::RenderFrameHost* embedder_frame_host = nullptr;
|
||||
if (embedder) {
|
||||
auto* embedder_view = embedder->web_contents()->GetMainFrame()->GetView();
|
||||
auto* view = web_contents->web_contents()->GetMainFrame()->GetView();
|
||||
auto offset = view->GetViewBounds().origin() -
|
||||
embedder_view->GetViewBounds().origin();
|
||||
popup_bounds.Offset(offset);
|
||||
embedder_frame_host = embedder->web_contents()->GetMainFrame();
|
||||
}
|
||||
|
||||
autofill_popup_->CreateView(render_frame_host_, embedder_frame_host, osr,
|
||||
web_contents->owner_window()->content_view(),
|
||||
popup_bounds);
|
||||
autofill_popup_->SetItems(values, labels);
|
||||
}
|
||||
|
||||
void AutofillDriver::HideAutofillPopup() {
|
||||
if (autofill_popup_)
|
||||
autofill_popup_->Hide();
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_
|
||||
#define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#if defined(TOOLKIT_VIEWS)
|
||||
#include "shell/browser/ui/autofill_popup.h"
|
||||
#endif
|
||||
|
||||
#include "mojo/public/cpp/bindings/associated_binding.h"
|
||||
#include "shell/common/api/api.mojom.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class AutofillDriver : public mojom::ElectronAutofillDriver {
|
||||
public:
|
||||
AutofillDriver(content::RenderFrameHost* render_frame_host,
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request);
|
||||
|
||||
~AutofillDriver() override;
|
||||
|
||||
void ShowAutofillPopup(const gfx::RectF& bounds,
|
||||
const std::vector<base::string16>& values,
|
||||
const std::vector<base::string16>& labels) override;
|
||||
void HideAutofillPopup() override;
|
||||
|
||||
private:
|
||||
content::RenderFrameHost* const render_frame_host_;
|
||||
|
||||
#if defined(TOOLKIT_VIEWS)
|
||||
std::unique_ptr<AutofillPopup> autofill_popup_;
|
||||
#endif
|
||||
|
||||
mojo::AssociatedBinding<mojom::ElectronAutofillDriver> binding_;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_
|
||||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_
|
||||
#define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#if defined(TOOLKIT_VIEWS)
|
||||
#include "shell/browser/ui/autofill_popup.h"
|
||||
#endif
|
||||
|
||||
#include "mojo/public/cpp/bindings/associated_binding.h"
|
||||
#include "shell/common/api/api.mojom.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class AutofillDriver : public mojom::ElectronAutofillDriver {
|
||||
public:
|
||||
AutofillDriver(content::RenderFrameHost* render_frame_host,
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request);
|
||||
|
||||
~AutofillDriver() override;
|
||||
|
||||
void ShowAutofillPopup(const gfx::RectF& bounds,
|
||||
const std::vector<base::string16>& values,
|
||||
const std::vector<base::string16>& labels) override;
|
||||
void HideAutofillPopup() override;
|
||||
|
||||
private:
|
||||
content::RenderFrameHost* const render_frame_host_;
|
||||
|
||||
#if defined(TOOLKIT_VIEWS)
|
||||
std::unique_ptr<AutofillPopup> autofill_popup_;
|
||||
#endif
|
||||
|
||||
mojo::AssociatedBinding<mojom::ElectronAutofillDriver> binding_;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_
|
||||
|
||||
@@ -1,111 +1,111 @@
|
||||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/electron_autofill_driver_factory.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/callback.h"
|
||||
#include "content/public/browser/navigation_handle.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "shell/browser/electron_autofill_driver.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<AutofillDriver> CreateDriver(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request) {
|
||||
return std::make_unique<AutofillDriver>(render_frame_host,
|
||||
std::move(request));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AutofillDriverFactory::~AutofillDriverFactory() = default;
|
||||
|
||||
// static
|
||||
void AutofillDriverFactory::BindAutofillDriver(
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request,
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
content::WebContents* web_contents =
|
||||
content::WebContents::FromRenderFrameHost(render_frame_host);
|
||||
if (!web_contents)
|
||||
return;
|
||||
|
||||
AutofillDriverFactory* factory =
|
||||
AutofillDriverFactory::FromWebContents(web_contents);
|
||||
if (!factory)
|
||||
return;
|
||||
|
||||
AutofillDriver* driver = factory->DriverForFrame(render_frame_host);
|
||||
if (!driver)
|
||||
factory->AddDriverForFrame(
|
||||
render_frame_host,
|
||||
base::BindOnce(CreateDriver, render_frame_host, std::move(request)));
|
||||
}
|
||||
|
||||
AutofillDriverFactory::AutofillDriverFactory(content::WebContents* web_contents)
|
||||
: content::WebContentsObserver(web_contents) {
|
||||
const std::vector<content::RenderFrameHost*> frames =
|
||||
web_contents->GetAllFrames();
|
||||
for (content::RenderFrameHost* frame : frames) {
|
||||
if (frame->IsRenderFrameLive())
|
||||
RenderFrameCreated(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::RenderFrameDeleted(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
DeleteDriverForFrame(render_frame_host);
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::DidFinishNavigation(
|
||||
content::NavigationHandle* navigation_handle) {
|
||||
// For the purposes of this code, a navigation is not important if it has not
|
||||
// committed yet or if it's in a subframe.
|
||||
if (!navigation_handle->HasCommitted() ||
|
||||
!navigation_handle->IsInMainFrame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CloseAllPopups();
|
||||
}
|
||||
|
||||
AutofillDriver* AutofillDriverFactory::DriverForFrame(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
auto mapping = driver_map_.find(render_frame_host);
|
||||
return mapping == driver_map_.end() ? nullptr : mapping->second.get();
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::AddDriverForFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
CreationCallback factory_method) {
|
||||
auto insertion_result =
|
||||
driver_map_.insert(std::make_pair(render_frame_host, nullptr));
|
||||
// This can be called twice for the key representing the main frame.
|
||||
if (insertion_result.second) {
|
||||
insertion_result.first->second = std::move(factory_method).Run();
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::DeleteDriverForFrame(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
driver_map_.erase(render_frame_host);
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::CloseAllPopups() {
|
||||
for (auto& it : driver_map_) {
|
||||
it.second->HideAutofillPopup();
|
||||
}
|
||||
}
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_IMPL(AutofillDriverFactory)
|
||||
|
||||
} // namespace electron
|
||||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/electron_autofill_driver_factory.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/callback.h"
|
||||
#include "content/public/browser/navigation_handle.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "shell/browser/electron_autofill_driver.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<AutofillDriver> CreateDriver(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request) {
|
||||
return std::make_unique<AutofillDriver>(render_frame_host,
|
||||
std::move(request));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AutofillDriverFactory::~AutofillDriverFactory() = default;
|
||||
|
||||
// static
|
||||
void AutofillDriverFactory::BindAutofillDriver(
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request,
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
content::WebContents* web_contents =
|
||||
content::WebContents::FromRenderFrameHost(render_frame_host);
|
||||
if (!web_contents)
|
||||
return;
|
||||
|
||||
AutofillDriverFactory* factory =
|
||||
AutofillDriverFactory::FromWebContents(web_contents);
|
||||
if (!factory)
|
||||
return;
|
||||
|
||||
AutofillDriver* driver = factory->DriverForFrame(render_frame_host);
|
||||
if (!driver)
|
||||
factory->AddDriverForFrame(
|
||||
render_frame_host,
|
||||
base::BindOnce(CreateDriver, render_frame_host, std::move(request)));
|
||||
}
|
||||
|
||||
AutofillDriverFactory::AutofillDriverFactory(content::WebContents* web_contents)
|
||||
: content::WebContentsObserver(web_contents) {
|
||||
const std::vector<content::RenderFrameHost*> frames =
|
||||
web_contents->GetAllFrames();
|
||||
for (content::RenderFrameHost* frame : frames) {
|
||||
if (frame->IsRenderFrameLive())
|
||||
RenderFrameCreated(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::RenderFrameDeleted(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
DeleteDriverForFrame(render_frame_host);
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::DidFinishNavigation(
|
||||
content::NavigationHandle* navigation_handle) {
|
||||
// For the purposes of this code, a navigation is not important if it has not
|
||||
// committed yet or if it's in a subframe.
|
||||
if (!navigation_handle->HasCommitted() ||
|
||||
!navigation_handle->IsInMainFrame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CloseAllPopups();
|
||||
}
|
||||
|
||||
AutofillDriver* AutofillDriverFactory::DriverForFrame(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
auto mapping = driver_map_.find(render_frame_host);
|
||||
return mapping == driver_map_.end() ? nullptr : mapping->second.get();
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::AddDriverForFrame(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
CreationCallback factory_method) {
|
||||
auto insertion_result =
|
||||
driver_map_.insert(std::make_pair(render_frame_host, nullptr));
|
||||
// This can be called twice for the key representing the main frame.
|
||||
if (insertion_result.second) {
|
||||
insertion_result.first->second = std::move(factory_method).Run();
|
||||
}
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::DeleteDriverForFrame(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
driver_map_.erase(render_frame_host);
|
||||
}
|
||||
|
||||
void AutofillDriverFactory::CloseAllPopups() {
|
||||
for (auto& it : driver_map_) {
|
||||
it.second->HideAutofillPopup();
|
||||
}
|
||||
}
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_IMPL(AutofillDriverFactory)
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_
|
||||
#define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "content/public/browser/web_contents_user_data.h"
|
||||
#include "shell/common/api/api.mojom.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class AutofillDriver;
|
||||
|
||||
class AutofillDriverFactory
|
||||
: public content::WebContentsObserver,
|
||||
public content::WebContentsUserData<AutofillDriverFactory> {
|
||||
public:
|
||||
typedef base::OnceCallback<std::unique_ptr<AutofillDriver>()>
|
||||
CreationCallback;
|
||||
|
||||
~AutofillDriverFactory() override;
|
||||
|
||||
static void BindAutofillDriver(
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request,
|
||||
content::RenderFrameHost* render_frame_host);
|
||||
|
||||
// content::WebContentsObserver:
|
||||
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
|
||||
void DidFinishNavigation(
|
||||
content::NavigationHandle* navigation_handle) override;
|
||||
|
||||
AutofillDriver* DriverForFrame(content::RenderFrameHost* render_frame_host);
|
||||
void AddDriverForFrame(content::RenderFrameHost* render_frame_host,
|
||||
CreationCallback factory_method);
|
||||
void DeleteDriverForFrame(content::RenderFrameHost* render_frame_host);
|
||||
|
||||
void CloseAllPopups();
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_DECL();
|
||||
|
||||
private:
|
||||
explicit AutofillDriverFactory(content::WebContents* web_contents);
|
||||
friend class content::WebContentsUserData<AutofillDriverFactory>;
|
||||
|
||||
std::unordered_map<content::RenderFrameHost*, std::unique_ptr<AutofillDriver>>
|
||||
driver_map_;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_
|
||||
// Copyright (c) 2019 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_
|
||||
#define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "content/public/browser/web_contents_user_data.h"
|
||||
#include "shell/common/api/api.mojom.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class AutofillDriver;
|
||||
|
||||
class AutofillDriverFactory
|
||||
: public content::WebContentsObserver,
|
||||
public content::WebContentsUserData<AutofillDriverFactory> {
|
||||
public:
|
||||
typedef base::OnceCallback<std::unique_ptr<AutofillDriver>()>
|
||||
CreationCallback;
|
||||
|
||||
~AutofillDriverFactory() override;
|
||||
|
||||
static void BindAutofillDriver(
|
||||
mojom::ElectronAutofillDriverAssociatedRequest request,
|
||||
content::RenderFrameHost* render_frame_host);
|
||||
|
||||
// content::WebContentsObserver:
|
||||
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
|
||||
void DidFinishNavigation(
|
||||
content::NavigationHandle* navigation_handle) override;
|
||||
|
||||
AutofillDriver* DriverForFrame(content::RenderFrameHost* render_frame_host);
|
||||
void AddDriverForFrame(content::RenderFrameHost* render_frame_host,
|
||||
CreationCallback factory_method);
|
||||
void DeleteDriverForFrame(content::RenderFrameHost* render_frame_host);
|
||||
|
||||
void CloseAllPopups();
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_DECL();
|
||||
|
||||
private:
|
||||
explicit AutofillDriverFactory(content::WebContents* web_contents);
|
||||
friend class content::WebContentsUserData<AutofillDriverFactory>;
|
||||
|
||||
std::unordered_map<content::RenderFrameHost*, std::unique_ptr<AutofillDriver>>
|
||||
driver_map_;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_
|
||||
|
||||
@@ -1506,15 +1506,10 @@ void ElectronBrowserClient::OverrideURLLoaderFactoryParams(
|
||||
const url::Origin& origin,
|
||||
bool is_for_isolated_world,
|
||||
network::mojom::URLLoaderFactoryParams* factory_params) {
|
||||
for (const auto& iter : process_preferences_) {
|
||||
if (iter.second.browser_context != browser_context)
|
||||
continue;
|
||||
|
||||
if (!iter.second.web_security) {
|
||||
// bypass CORB
|
||||
factory_params->process_id = iter.first;
|
||||
factory_params->is_corb_enabled = false;
|
||||
}
|
||||
// Bypass CORB when web security is disabled.
|
||||
auto it = process_preferences_.find(factory_params->process_id);
|
||||
if (it != process_preferences_.end() && !it->second.web_security) {
|
||||
factory_params->is_corb_enabled = false;
|
||||
}
|
||||
|
||||
extensions::URLLoaderFactoryManager::OverrideURLLoaderFactoryParams(
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/extensions/api/i18n/i18n_api.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "shell/common/extensions/api/i18n.h"
|
||||
|
||||
namespace GetAcceptLanguages = extensions::api::i18n::GetAcceptLanguages;
|
||||
|
||||
namespace extensions {
|
||||
|
||||
ExtensionFunction::ResponseAction I18nGetAcceptLanguagesFunction::Run() {
|
||||
auto locale = g_browser_process->GetApplicationLocale();
|
||||
std::vector<std::string> accept_languages = {locale};
|
||||
return RespondNow(
|
||||
ArgumentList(GetAcceptLanguages::Results::Create(accept_languages)));
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/extensions/api/i18n/i18n_api.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "shell/common/extensions/api/i18n.h"
|
||||
|
||||
namespace GetAcceptLanguages = extensions::api::i18n::GetAcceptLanguages;
|
||||
|
||||
namespace extensions {
|
||||
|
||||
ExtensionFunction::ResponseAction I18nGetAcceptLanguagesFunction::Run() {
|
||||
auto locale = g_browser_process->GetApplicationLocale();
|
||||
std::vector<std::string> accept_languages = {locale};
|
||||
return RespondNow(
|
||||
ArgumentList(GetAcceptLanguages::Results::Create(accept_languages)));
|
||||
}
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
|
||||
#define SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
|
||||
|
||||
#include "extensions/browser/extension_function.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
class I18nGetAcceptLanguagesFunction : public ExtensionFunction {
|
||||
~I18nGetAcceptLanguagesFunction() override {}
|
||||
ResponseAction Run() override;
|
||||
DECLARE_EXTENSION_FUNCTION("i18n.getAcceptLanguages", I18N_GETACCEPTLANGUAGES)
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
|
||||
// Copyright (c) 2020 Samuel Maddock <sam@samuelmaddock.com>.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
|
||||
#define SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
|
||||
|
||||
#include "extensions/browser/extension_function.h"
|
||||
|
||||
namespace extensions {
|
||||
|
||||
class I18nGetAcceptLanguagesFunction : public ExtensionFunction {
|
||||
~I18nGetAcceptLanguagesFunction() override {}
|
||||
ResponseAction Run() override;
|
||||
DECLARE_EXTENSION_FUNCTION("i18n.getAcceptLanguages", I18N_GETACCEPTLANGUAGES)
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // SHELL_BROWSER_EXTENSIONS_API_I18N_I18N_API_H_
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "extensions/browser/api/app_runtime/app_runtime_api.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/info_map.h"
|
||||
#include "extensions/browser/management_policy.h"
|
||||
#include "extensions/browser/notification_types.h"
|
||||
#include "extensions/browser/null_app_sorting.h"
|
||||
#include "extensions/browser/quota_service.h"
|
||||
@@ -91,6 +92,8 @@ void ElectronExtensionSystem::InitForRegularProfile(bool extensions_enabled) {
|
||||
|
||||
if (!browser_context_->IsOffTheRecord())
|
||||
LoadComponentExtensions();
|
||||
|
||||
management_policy_.reset(new ManagementPolicy);
|
||||
}
|
||||
|
||||
std::unique_ptr<base::DictionaryValue> ParseManifest(
|
||||
@@ -130,7 +133,7 @@ RuntimeData* ElectronExtensionSystem::runtime_data() {
|
||||
}
|
||||
|
||||
ManagementPolicy* ElectronExtensionSystem::management_policy() {
|
||||
return nullptr;
|
||||
return management_policy_.get();
|
||||
}
|
||||
|
||||
ServiceWorkerManager* ElectronExtensionSystem::service_worker_manager() {
|
||||
|
||||
@@ -104,6 +104,7 @@ class ElectronExtensionSystem : public ExtensionSystem {
|
||||
std::unique_ptr<QuotaService> quota_service_;
|
||||
std::unique_ptr<SharedUserScriptManager> shared_user_script_manager_;
|
||||
std::unique_ptr<AppSorting> app_sorting_;
|
||||
std::unique_ptr<ManagementPolicy> management_policy_;
|
||||
|
||||
std::unique_ptr<ElectronExtensionLoader> extension_loader_;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.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_messaging_delegate.h"
|
||||
|
||||
@@ -59,6 +60,11 @@ void ElectronExtensionsAPIClient::AttachWebContentsHelpers(
|
||||
#endif
|
||||
}
|
||||
|
||||
ManagementAPIDelegate*
|
||||
ElectronExtensionsAPIClient::CreateManagementAPIDelegate() const {
|
||||
return new ElectronManagementAPIDelegate;
|
||||
}
|
||||
|
||||
std::unique_ptr<MimeHandlerViewGuestDelegate>
|
||||
ElectronExtensionsAPIClient::CreateMimeHandlerViewGuestDelegate(
|
||||
MimeHandlerViewGuest* guest) const {
|
||||
|
||||
@@ -25,6 +25,7 @@ class ElectronExtensionsAPIClient : public ExtensionsAPIClient {
|
||||
std::unique_ptr<MimeHandlerViewGuestDelegate>
|
||||
CreateMimeHandlerViewGuestDelegate(
|
||||
MimeHandlerViewGuest* guest) const override;
|
||||
ManagementAPIDelegate* CreateManagementAPIDelegate() const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<ElectronMessagingDelegate> messaging_delegate_;
|
||||
|
||||
@@ -43,10 +43,7 @@
|
||||
// https://developer.apple.com/documentation/foundation/nsusernotificationactivationtype?language=objc
|
||||
if (notif.activationType ==
|
||||
NSUserNotificationActivationTypeContentsClicked) {
|
||||
// If a notification with a reply button is clicked and the user has not
|
||||
// yet replied, we do not want to destroy the notification.
|
||||
bool should_destroy = ![notif hasReplyButton];
|
||||
notification->NotificationClicked(should_destroy);
|
||||
notification->NotificationClicked();
|
||||
} else if (notif.activationType ==
|
||||
NSUserNotificationActivationTypeActionButtonClicked) {
|
||||
notification->NotificationActivated();
|
||||
|
||||
@@ -21,12 +21,10 @@ Notification::~Notification() {
|
||||
delegate()->NotificationDestroyed();
|
||||
}
|
||||
|
||||
void Notification::NotificationClicked(bool should_destroy) {
|
||||
void Notification::NotificationClicked() {
|
||||
if (delegate())
|
||||
delegate()->NotificationClick();
|
||||
|
||||
if (should_destroy)
|
||||
Destroy();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Notification::NotificationDismissed() {
|
||||
|
||||
@@ -54,7 +54,7 @@ class Notification {
|
||||
virtual void Dismiss() = 0;
|
||||
|
||||
// Should be called by derived classes.
|
||||
void NotificationClicked(bool should_destroy = true);
|
||||
void NotificationClicked();
|
||||
void NotificationDismissed();
|
||||
void NotificationFailed();
|
||||
|
||||
|
||||
@@ -476,7 +476,7 @@ IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||
IInspectable* args) {
|
||||
base::PostTask(
|
||||
FROM_HERE, {content::BrowserThread::UI},
|
||||
base::BindOnce(&Notification::NotificationClicked, notification_, true));
|
||||
base::BindOnce(&Notification::NotificationClicked, notification_));
|
||||
if (IsDebuggingNotifications())
|
||||
LOG(INFO) << "Notification clicked";
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 11,0,0,20200826
|
||||
PRODUCTVERSION 11,0,0,20200826
|
||||
FILEVERSION 12,0,0,20200903
|
||||
PRODUCTVERSION 12,0,0,20200903
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "11.0.0"
|
||||
VALUE "FileVersion", "12.0.0"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "11.0.0"
|
||||
VALUE "ProductVersion", "12.0.0"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -90,7 +90,8 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
||||
|
||||
// Create array to keep file types and their name.
|
||||
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())];
|
||||
for (std::string ext : filter.second) {
|
||||
// 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_types_list addObject:[file_type_set allObjects]];
|
||||
[file_types_list addObject:[file_type_set array]];
|
||||
}
|
||||
|
||||
// Passing empty array to setAllowedFileTypes will cause exception.
|
||||
|
||||
@@ -4,12 +4,31 @@
|
||||
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
|
||||
#include "base/win/windows_version.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/win/hwnd_util.h"
|
||||
|
||||
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";
|
||||
|
||||
WinFrameView::WinFrameView() {}
|
||||
@@ -23,6 +42,17 @@ gfx::Rect WinFrameView::GetWindowBoundsForClientBounds(
|
||||
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) {
|
||||
if (window_->has_frame())
|
||||
return frame_->client_view()->NonClientHitTest(point);
|
||||
|
||||
@@ -16,6 +16,7 @@ class WinFrameView : public FramelessView {
|
||||
~WinFrameView() override;
|
||||
|
||||
// views::NonClientFrameView:
|
||||
gfx::Rect GetBoundsForClientView() const override;
|
||||
gfx::Rect GetWindowBoundsForClientBounds(
|
||||
const gfx::Rect& client_bounds) const override;
|
||||
int NonClientHitTest(const gfx::Point& point) override;
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
|
||||
#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/shell.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
#include "ui/views/win/hwnd_util.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -36,18 +41,49 @@ bool ElectronDesktopWindowTreeHostWin::HasNativeFrame() const {
|
||||
// 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
|
||||
// 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(
|
||||
gfx::Insets* insets,
|
||||
HMONITOR monitor) const {
|
||||
if (IsMaximized() && !native_window_view_->has_frame()) {
|
||||
// Windows automatically adds a standard width border to all sides when a
|
||||
// window is maximized.
|
||||
int frame_thickness = ui::GetFrameThickness(monitor) - 1;
|
||||
*insets = gfx::Insets(frame_thickness, frame_thickness, frame_thickness,
|
||||
frame_thickness);
|
||||
if (base::win::GetVersion() < base::win::Version::WIN8) {
|
||||
// This tells Windows that most of the window is a client area, meaning
|
||||
// Chrome will draw it. Windows still fills in the glass bits because of
|
||||
// the // DwmExtendFrameIntoClientArea call in |UpdateDWMFrame|.
|
||||
// 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 false;
|
||||
|
||||
@@ -27,6 +27,7 @@ class ElectronDesktopWindowTreeHostWin
|
||||
LRESULT* result) override;
|
||||
bool ShouldPaintAsActive() const override;
|
||||
bool HasNativeFrame() const override;
|
||||
bool GetDwmFrameInsetsInPixels(gfx::Insets* insets) const override;
|
||||
bool GetClientAreaInsets(gfx::Insets* insets,
|
||||
HMONITOR monitor) const override;
|
||||
|
||||
|
||||
@@ -94,6 +94,25 @@ bool MoveItemToTrash(gin::Arguments* args) {
|
||||
return platform_util::MoveItemToTrash(full_path, delete_on_fail);
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> TrashItem(v8::Isolate* isolate,
|
||||
const base::FilePath& path) {
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
platform_util::TrashItem(
|
||||
path, base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, bool success,
|
||||
const std::string& error) {
|
||||
if (success) {
|
||||
promise.Resolve();
|
||||
} else {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
}
|
||||
},
|
||||
std::move(promise)));
|
||||
return handle;
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
bool WriteShortcutLink(const base::FilePath& shortcut_path,
|
||||
gin_helper::Arguments* args) {
|
||||
@@ -158,6 +177,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
dict.SetMethod("openPath", &OpenPath);
|
||||
dict.SetMethod("openExternal", &OpenExternal);
|
||||
dict.SetMethod("moveItemToTrash", &MoveItemToTrash);
|
||||
dict.SetMethod("trashItem", &TrashItem);
|
||||
dict.SetMethod("beep", &platform_util::Beep);
|
||||
#if defined(OS_WIN)
|
||||
dict.SetMethod("writeShortcutLink", &WriteShortcutLink);
|
||||
|
||||
@@ -5,5 +5,65 @@
|
||||
"extension"
|
||||
],
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -38,6 +38,7 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = {
|
||||
APIPermissionInfo::kFlagInternal},
|
||||
{APIPermission::kResourcesPrivate, "resourcesPrivate",
|
||||
APIPermissionInfo::kFlagCannotBeOptional},
|
||||
{APIPermission::kManagement, "management"},
|
||||
};
|
||||
base::span<const APIPermissionInfo::InitInfo> GetPermissionInfos() {
|
||||
return base::make_span(permissions_to_register);
|
||||
|
||||
37
shell/common/platform_util.cc
Normal file
37
shell/common/platform_util.cc
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2020 Slack Technologies, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/common/platform_util.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "shell/common/platform_util_internal.h"
|
||||
|
||||
namespace platform_util {
|
||||
|
||||
void TrashItemOnBlockingThread(
|
||||
const base::FilePath& full_path,
|
||||
base::OnceCallback<void(bool, const std::string&)> callback) {
|
||||
std::string error;
|
||||
bool success = internal::PlatformTrashItem(full_path, &error);
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE, base::BindOnce(std::move(callback), success, error));
|
||||
}
|
||||
|
||||
void TrashItem(const base::FilePath& full_path,
|
||||
base::OnceCallback<void(bool, const std::string&)> callback) {
|
||||
// XXX: is continue_on_shutdown right?
|
||||
base::ThreadPool::PostTask(FROM_HERE,
|
||||
{base::MayBlock(), base::WithBaseSyncPrimitives(),
|
||||
base::TaskPriority::USER_BLOCKING,
|
||||
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
|
||||
base::BindOnce(&TrashItemOnBlockingThread,
|
||||
full_path, std::move(callback)));
|
||||
}
|
||||
|
||||
} // namespace platform_util
|
||||
@@ -40,9 +40,13 @@ void OpenExternal(const GURL& url,
|
||||
const OpenExternalOptions& options,
|
||||
OpenCallback callback);
|
||||
|
||||
// Move a file to trash.
|
||||
// Move a file to trash. (Deprecated.)
|
||||
bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail);
|
||||
|
||||
// Move a file to trash, asynchronously.
|
||||
void TrashItem(const base::FilePath& full_path,
|
||||
base::OnceCallback<void(bool, const std::string&)> callback);
|
||||
|
||||
void Beep();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
26
shell/common/platform_util_internal.h
Normal file
26
shell/common/platform_util_internal.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2020 Slack Technologies, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SHELL_COMMON_PLATFORM_UTIL_INTERNAL_H_
|
||||
#define SHELL_COMMON_PLATFORM_UTIL_INTERNAL_H_
|
||||
|
||||
#include "shell/common/platform_util.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace base {
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
namespace platform_util {
|
||||
namespace internal {
|
||||
|
||||
// Called by platform_util.cc on to invoke platform specific logic to move
|
||||
// |path| to trash using a suitable handler.
|
||||
bool PlatformTrashItem(const base::FilePath& path, std::string* error);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace platform_util
|
||||
|
||||
#endif // SHELL_COMMON_PLATFORM_UTIL_INTERNAL_H_
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "dbus/bus.h"
|
||||
#include "dbus/message.h"
|
||||
#include "dbus/object_proxy.h"
|
||||
#include "shell/common/platform_util_internal.h"
|
||||
#include "ui/gtk/gtk_util.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
@@ -220,6 +221,19 @@ bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) {
|
||||
return XDGUtil(argv, base::FilePath(), true, platform_util::OpenCallback());
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
bool PlatformTrashItem(const base::FilePath& full_path, std::string* error) {
|
||||
if (!MoveItemToTrash(full_path, false)) {
|
||||
// TODO(nornagon): at least include the exit code?
|
||||
*error = "Failed to move item to trash";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void Beep() {
|
||||
// echo '\a' > /dev/console
|
||||
FILE* fp = fopen("/dev/console", "a");
|
||||
|
||||
@@ -117,10 +117,13 @@ void OpenExternal(const GURL& url,
|
||||
});
|
||||
}
|
||||
|
||||
bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) {
|
||||
bool MoveItemToTrashWithError(const base::FilePath& full_path,
|
||||
bool delete_on_fail,
|
||||
std::string* error) {
|
||||
NSString* path_string = base::SysUTF8ToNSString(full_path.value());
|
||||
if (!path_string) {
|
||||
LOG(WARNING) << "Invalid file path " << full_path.value();
|
||||
*error = "Invalid file path: " + full_path.value();
|
||||
LOG(WARNING) << *error;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -142,14 +145,27 @@ bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) {
|
||||
}
|
||||
|
||||
if (!did_trash) {
|
||||
*error = base::SysNSStringToUTF8([err localizedDescription]);
|
||||
LOG(WARNING) << "NSWorkspace failed to move file " << full_path.value()
|
||||
<< " to trash: "
|
||||
<< base::SysNSStringToUTF8([err localizedDescription]);
|
||||
<< " to trash: " << *error;
|
||||
}
|
||||
|
||||
return did_trash;
|
||||
}
|
||||
|
||||
bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
|
||||
std::string error; // ignored
|
||||
return MoveItemToTrashWithError(path, delete_on_fail, &error);
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
bool PlatformTrashItem(const base::FilePath& full_path, std::string* error) {
|
||||
return MoveItemToTrashWithError(full_path, false, error);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void Beep() {
|
||||
NSBeep();
|
||||
}
|
||||
|
||||
@@ -347,15 +347,15 @@ void OpenExternal(const GURL& url,
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
|
||||
base::win::ScopedCOMInitializer com_initializer;
|
||||
if (!com_initializer.Succeeded())
|
||||
return false;
|
||||
|
||||
bool MoveItemToTrashWithError(const base::FilePath& path,
|
||||
bool delete_on_fail,
|
||||
std::string* error) {
|
||||
Microsoft::WRL::ComPtr<IFileOperation> pfo;
|
||||
if (FAILED(::CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL,
|
||||
IID_PPV_ARGS(&pfo))))
|
||||
IID_PPV_ARGS(&pfo)))) {
|
||||
*error = "Failed to create FileOperation instance";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Elevation prompt enabled for UAC protected files. This overrides the
|
||||
// SILENT, NO_UI and NOERRORUI flags.
|
||||
@@ -365,34 +365,77 @@ bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
|
||||
// ALLOWUNDO in favor of ADDUNDORECORD.
|
||||
if (FAILED(pfo->SetOperationFlags(
|
||||
FOF_NO_UI | FOFX_ADDUNDORECORD | FOF_NOERRORUI | FOF_SILENT |
|
||||
FOFX_SHOWELEVATIONPROMPT | FOFX_RECYCLEONDELETE)))
|
||||
FOFX_SHOWELEVATIONPROMPT | FOFX_RECYCLEONDELETE))) {
|
||||
*error = "Failed to set operation flags";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// For Windows 7 and Vista, RecycleOnDelete is the default behavior.
|
||||
if (FAILED(pfo->SetOperationFlags(FOF_NO_UI | FOF_ALLOWUNDO |
|
||||
FOF_NOERRORUI | FOF_SILENT |
|
||||
FOFX_SHOWELEVATIONPROMPT)))
|
||||
FOFX_SHOWELEVATIONPROMPT))) {
|
||||
*error = "Failed to set operation flags";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create an IShellItem from the supplied source path.
|
||||
Microsoft::WRL::ComPtr<IShellItem> delete_item;
|
||||
if (FAILED(SHCreateItemFromParsingName(
|
||||
path.value().c_str(), NULL,
|
||||
IID_PPV_ARGS(delete_item.GetAddressOf()))))
|
||||
IID_PPV_ARGS(delete_item.GetAddressOf())))) {
|
||||
*error = "Failed to parse path";
|
||||
return false;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IFileOperationProgressSink> delete_sink(
|
||||
new DeleteFileProgressSink);
|
||||
if (!delete_sink)
|
||||
if (!delete_sink) {
|
||||
*error = "Failed to create delete sink";
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL pfAnyOperationsAborted;
|
||||
|
||||
// Processes the queued command DeleteItem. This will trigger
|
||||
// the DeleteFileProgressSink to check for Recycle Bin.
|
||||
return SUCCEEDED(pfo->DeleteItem(delete_item.Get(), delete_sink.Get())) &&
|
||||
SUCCEEDED(pfo->PerformOperations());
|
||||
if (!SUCCEEDED(pfo->DeleteItem(delete_item.Get(), delete_sink.Get()))) {
|
||||
*error = "Failed to enqueue DeleteItem command";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(pfo->PerformOperations())) {
|
||||
*error = "Failed to perform delete operation";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(pfo->GetAnyOperationsAborted(&pfAnyOperationsAborted))) {
|
||||
*error = "Failed to check operation status";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pfAnyOperationsAborted) {
|
||||
*error = "Operation was aborted";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoveItemToTrash(const base::FilePath& path, bool delete_on_fail) {
|
||||
std::string error; // ignored
|
||||
base::win::ScopedCOMInitializer com_initializer;
|
||||
return MoveItemToTrashWithError(path, delete_on_fail, &error);
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
bool PlatformTrashItem(const base::FilePath& full_path, std::string* error) {
|
||||
return MoveItemToTrashWithError(full_path, false, error);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
bool GetFolderPath(int key, base::FilePath* result) {
|
||||
wchar_t system_buffer[MAX_PATH];
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
it('should not throw', () => {
|
||||
return app.dock.show().then(() => {
|
||||
@@ -1452,13 +1462,6 @@ describe('app module', () => {
|
||||
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', () => {
|
||||
|
||||
@@ -245,7 +245,7 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
|
||||
|
||||
const crash = await waitForCrash();
|
||||
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['electron.v8-fatal.location']).to.equal('v8::Context::New()');
|
||||
expect(crash['electron.v8-fatal.message']).to.equal('Circular extension dependency');
|
||||
|
||||
@@ -7,6 +7,8 @@ import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { AddressInfo } from 'net';
|
||||
import { expect } from 'chai';
|
||||
import { ifit } from './spec-helpers';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
describe('shell module', () => {
|
||||
describe('shell.openExternal()', () => {
|
||||
@@ -76,5 +78,53 @@ describe('shell module', () => {
|
||||
const result = shell.moveItemToTrash(filename);
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
describe('shell.trashItem()', () => {
|
||||
it('moves an item to the trash', async () => {
|
||||
const dir = await fs.mkdtemp(path.resolve(app.getPath('temp'), 'electron-shell-spec-'));
|
||||
const filename = path.join(dir, 'temp-to-be-deleted');
|
||||
await fs.writeFile(filename, 'dummy-contents');
|
||||
await shell.trashItem(filename);
|
||||
expect(fs.existsSync(filename)).to.be.false();
|
||||
});
|
||||
|
||||
it('throws when called with a nonexistent path', async () => {
|
||||
const filename = path.join(app.getPath('temp'), 'does-not-exist');
|
||||
await expect(shell.trashItem(filename)).to.eventually.be.rejected();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -245,6 +245,14 @@ describe('web security', () => {
|
||||
<script src="${serverUrl}"></script>`);
|
||||
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', () => {
|
||||
|
||||
@@ -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 () => {
|
||||
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "ipc-main-listeners",
|
||||
"name": "electron-test-ipc-main-listeners",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "net-log",
|
||||
"name": "electron-test-net-log",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
self.addEventListener('install', function (event) {
|
||||
console.log('log log');
|
||||
console.info('info log');
|
||||
console.warn('warn log');
|
||||
console.error('error log');
|
||||
});
|
||||
self.addEventListener('install', function (event) {
|
||||
console.log('log log');
|
||||
console.info('info log');
|
||||
console.warn('warn log');
|
||||
console.error('error log');
|
||||
});
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
self.addEventListener('install', function (event) {
|
||||
console.log('Installed');
|
||||
});
|
||||
self.addEventListener('install', function (event) {
|
||||
console.log('Installed');
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "crash",
|
||||
"name": "electron-test-crash",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "remote-control",
|
||||
"name": "electron-test-remote-control",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "initial-app",
|
||||
"name": "electron-test-initial-app",
|
||||
"version": "1.0.0",
|
||||
"main": "./index.js"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "initial-app",
|
||||
"name": "electron-test-initial-app",
|
||||
"version": "1.0.0",
|
||||
"main": "./index.js"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "initial-app",
|
||||
"name": "electron-test-initial-app",
|
||||
"version": "1.0.0",
|
||||
"main": "./index.js"
|
||||
}
|
||||
2
spec/fixtures/api/app-path/package.json
vendored
2
spec/fixtures/api/app-path/package.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "app-path",
|
||||
"name": "electron-test-app-path",
|
||||
"main": "lib/index.js"
|
||||
}
|
||||
|
||||
2
spec/fixtures/api/command-line/package.json
vendored
2
spec/fixtures/api/command-line/package.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "command-line",
|
||||
"name": "electron-test-command-line",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
2
spec/fixtures/api/default-menu/package.json
vendored
2
spec/fixtures/api/default-menu/package.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "default-menu",
|
||||
"name": "electron-test-default-menu",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
2
spec/fixtures/api/locale-check/package.json
vendored
2
spec/fixtures/api/locale-check/package.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "locale-check",
|
||||
"name": "electron-test-locale-check",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "site-instance-overrides",
|
||||
"name": "electron-test-site-instance-overrides",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "window-all-closed",
|
||||
"name": "electron-test-window-all-closed",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "snapshot-items-available",
|
||||
"name": "electron-test-snapshot-items-available",
|
||||
"main": "main.js"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user