Merge remote-tracking branch 'origin/master' into roller/chromium/master

This commit is contained in:
Jeremy Rose 2021-03-23 11:14:58 -07:00
commit 39e3576c48
68 changed files with 578 additions and 182 deletions

View file

@ -230,6 +230,18 @@ step-maybe-notify-slack-success: &step-maybe-notify-slack-success
fi fi
when: on_success when: on_success
step-maybe-cleanup-arm64-mac: &step-maybe-cleanup-arm64-mac
run:
name: Cleanup after testing
command: |
if [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then
killall Electron || echo "No Electron processes left running"
killall Safari || echo "No Safari processes left running"
rm -rf ~/Library/Application\ Support/Electron*
rm -rf ~/Library/Application\ Support/electron*
fi
when: always
step-checkout-electron: &step-checkout-electron step-checkout-electron: &step-checkout-electron
checkout: checkout:
path: src/electron path: src/electron
@ -1340,6 +1352,8 @@ steps-tests: &steps-tests
- *step-maybe-notify-slack-failure - *step-maybe-notify-slack-failure
- *step-maybe-cleanup-arm64-mac
steps-test-nan: &steps-test-nan steps-test-nan: &steps-test-nan
steps: steps:
- attach_workspace: - attach_workspace:

View file

@ -1 +1 @@
14.0.0-nightly.20210315 14.0.0-nightly.20210323

View file

@ -267,7 +267,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
be the absolute file path to the script. be the absolute file path to the script.
When node integration is turned off, the preload script can reintroduce When node integration is turned off, the preload script can reintroduce
Node global symbols back to the global scope. See example Node global symbols back to the global scope. See example
[here](process.md#event-loaded). [here](context-bridge.md#exposing-node-global-symbols).
* `sandbox` Boolean (optional) - If set, this will sandbox the renderer * `sandbox` Boolean (optional) - If set, this will sandbox the renderer
associated with the window, making it compatible with the Chromium associated with the window, making it compatible with the Chromium
OS-level sandbox and disabling the Node.js engine. This is not the same as OS-level sandbox and disabling the Node.js engine. This is not the same as
@ -1316,6 +1316,8 @@ The native type of the handle is `HWND` on Windows, `NSView*` on macOS, and
* `message` Integer * `message` Integer
* `callback` Function * `callback` Function
* `wParam` any - The `wParam` provided to the WndProc
* `lParam` any - The `lParam` provided to the WndProc
Hooks a windows message. The `callback` is called when Hooks a windows message. The `callback` is called when
the message is received in the WndProc. the message is received in the WndProc.

View file

@ -33,7 +33,7 @@ page you load in your renderer executes code in this world.
### Isolated World ### Isolated World
When `contextIsolation` is enabled in your `webPreferences`, your `preload` scripts run in an When `contextIsolation` is enabled in your `webPreferences` (this is the default behavior since Electron 12.0.0), your `preload` scripts run in an
"Isolated World". You can read more about context isolation and what it affects in the "Isolated World". You can read more about context isolation and what it affects in the
[security](../tutorial/security.md#3-enable-context-isolation-for-remote-content) docs. [security](../tutorial/security.md#3-enable-context-isolation-for-remote-content) docs.
@ -110,3 +110,22 @@ has been included below for completeness:
| `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped | | `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped |
If the type you care about is not in the above table, it is probably not supported. If the type you care about is not in the above table, it is probably not supported.
### Exposing Node Global Symbols
The `contextBridge` can be used by the preload script to give your renderer access to Node APIs.
The table of supported types described above also applies to Node APIs that you expose through `contextBridge`.
Please note that many Node APIs grant access to local system resources.
Be very cautious about which globals and APIs you expose to untrusted remote content.
```javascript
const { contextBridge } = require('electron')
const crypto = require('crypto')
contextBridge.exposeInMainWorld('nodeCrypto', {
sha256sum (data) {
const hash = crypto.createHash('sha256')
hash.update(data)
return hash.digest('hex')
}
})
```

View file

@ -77,7 +77,8 @@ The `crashReporter` module has the following methods:
### `crashReporter.start(options)` ### `crashReporter.start(options)`
* `options` Object * `options` Object
* `submitURL` String - URL that crash reports will be sent to as POST. * `submitURL` String (optional) - URL that crash reports will be sent to as
POST. Required unless `uploadToServer` is `false`.
* `productName` String (optional) - Defaults to `app.name`. * `productName` String (optional) - Defaults to `app.name`.
* `companyName` String (optional) _Deprecated_ - Deprecated alias for * `companyName` String (optional) _Deprecated_ - Deprecated alias for
`{ globalExtra: { _companyName: ... } }`. `{ globalExtra: { _companyName: ... } }`.

View file

@ -30,11 +30,13 @@ In sandboxed renderers the `process` object contains only a subset of the APIs:
* `arch` * `arch`
* `platform` * `platform`
* `sandboxed` * `sandboxed`
* `contextIsolated`
* `type` * `type`
* `version` * `version`
* `versions` * `versions`
* `mas` * `mas`
* `windowsStore` * `windowsStore`
* `contextId`
## Events ## Events
@ -43,19 +45,6 @@ In sandboxed renderers the `process` object contains only a subset of the APIs:
Emitted when Electron has loaded its internal initialization script and is Emitted when Electron has loaded its internal initialization script and is
beginning to load the web page or the main script. beginning to load the web page or the main script.
It can be used by the preload script to add removed Node global symbols back to
the global scope when node integration is turned off:
```javascript
// preload.js
const _setImmediate = setImmediate
const _clearImmediate = clearImmediate
process.once('loaded', () => {
global.setImmediate = _setImmediate
global.clearImmediate = _clearImmediate
})
```
## Properties ## Properties
### `process.defaultApp` _Readonly_ ### `process.defaultApp` _Readonly_
@ -93,6 +82,11 @@ A `String` representing the path to the resources directory.
A `Boolean`. When the renderer process is sandboxed, this property is `true`, A `Boolean`. When the renderer process is sandboxed, this property is `true`,
otherwise it is `undefined`. otherwise it is `undefined`.
### `process.contextIsolated` _Readonly_
A `Boolean` that indicates whether the current renderer context has `contextIsolation` enabled.
It is `undefined` in the main process.
### `process.throwDeprecation` ### `process.throwDeprecation`
A `Boolean` that controls whether or not deprecation warnings will be thrown as A `Boolean` that controls whether or not deprecation warnings will be thrown as
@ -133,6 +127,13 @@ A `String` representing Electron's version string.
A `Boolean`. If the app is running as a Windows Store app (appx), this property is `true`, A `Boolean`. If the app is running as a Windows Store app (appx), this property is `true`,
for otherwise it is `undefined`. for otherwise it is `undefined`.
### `process.contextId` _Readonly_
A `String` (optional) representing a globally unique ID of the current JavaScript context.
Each frame has its own JavaScript context. When contextIsolation is enabled, the isolated
world also has a separate JavaScript context.
This property is only available in the renderer process.
## Methods ## Methods
The `process` object has the following methods: The `process` object has the following methods:

View file

@ -33,8 +33,7 @@ contributing, and more. Please use the issue tracker for bugs only!
To submit a bug report: To submit a bug report:
When opening a new issue in the [`electron/electron` issue tracker](https://github.com/electron/electron/issues/new/choose), users When opening a new issue in the [`electron/electron` issue tracker](https://github.com/electron/electron/issues/new/choose), users
will be presented with [a template](https://github.com/electron/electron/blob/master/.github/ISSUE_TEMPLATE/Bug_report.md) will be presented with a template that should be filled in.
that should be filled in.
If you believe that you have found a bug in Electron, please fill out the template If you believe that you have found a bug in Electron, please fill out the template
to the best of your ability. to the best of your ability.

View file

@ -8,9 +8,9 @@
<body> <body>
<h1>Hello World!</h1> <h1>Hello World!</h1>
<p> <p>
We are using node <script>document.write(process.versions.node)</script>, We are using Node.js <span id="node-version"></span>,
Chrome <script>document.write(process.versions.chrome)</script>, Chromium <span id="chrome-version"></span>,
and Electron <script>document.write(process.versions.electron)</script>. and Electron <span id="electron-version"></span>.
</p> </p>
</body> </body>
</html> </html>

View file

@ -1,19 +1,27 @@
const { app, BrowserWindow } = require('electron') const { app, BrowserWindow } = require('electron')
const path = require('path')
function createWindow () { function createWindow () {
const win = new BrowserWindow({ const win = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true, preload: path.join(__dirname, 'preload.js')
contextIsolation: false
} }
}) })
win.loadFile('index.html') win.loadFile('index.html')
} }
app.whenReady().then(createWindow) app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
@ -21,8 +29,3 @@ app.on('window-all-closed', () => {
} }
}) })
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})

View file

@ -0,0 +1,11 @@
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})

View file

@ -32,6 +32,7 @@ From a development perspective, an Electron application is essentially a Node.js
my-electron-app/ my-electron-app/
├── package.json ├── package.json
├── main.js ├── main.js
├── preload.js
└── index.html └── index.html
``` ```
@ -55,45 +56,49 @@ The main script may look as follows:
```javascript fiddle='docs/fiddles/quick-start' ```javascript fiddle='docs/fiddles/quick-start'
const { app, BrowserWindow } = require('electron') const { app, BrowserWindow } = require('electron')
const path = require('path')
function createWindow () { function createWindow () {
const win = new BrowserWindow({ const win = new BrowserWindow({
width: 800, width: 800,
height: 600, height: 600,
webPreferences: { webPreferences: {
nodeIntegration: true preload: path.join(__dirname, 'preload.js')
} }
}) })
win.loadFile('index.html') win.loadFile('index.html')
} }
app.whenReady().then(createWindow) app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
``` ```
##### What is going on above? ##### What is going on above?
1. Line 1: First, you import the `app` and `BrowserWindow` modules of the `electron` package to be able to manage your application's lifecycle events, as well as create and control browser windows. 1. Line 1: First, you import the `app` and `BrowserWindow` modules of the `electron` package to be able to manage your application's lifecycle events, as well as create and control browser windows.
2. Line 3: After that, you define a function that creates a [new browser window](../api/browser-window.md#new-browserwindowoptions) with node integration enabled, loads `index.html` file into this window (line 12, we will discuss the file later). 2. Line 2: Second, you import the `path` package which provides utility functions for file paths.
3. Line 15: You create a new browser window by invoking the `createWindow` function once the Electron application [is initialized](../api/app.md#appwhenready). 3. Line 4: After that, you define a function that creates a [new browser window](../api/browser-window.md#new-browserwindowoptions) with a preload script, loads `index.html` file into this window (line 13, we will discuss the file later).
4. Line 17: You add a new listener that tries to quit the application when it no longer has any open windows. This listener is a no-op on macOS due to the operating system's [window management behavior](https://support.apple.com/en-ca/guide/mac-help/mchlp2469/mac). 4. Line 16: You create a new browser window by invoking the `createWindow` function once the Electron application [is initialized](../api/app.md#appwhenready).
5. Line 23: You add a new listener that creates a new browser window only if when the application has no visible windows after being activated. For example, after launching the application for the first time, or re-launching the already running application. 5. Line 18: You add a new listener that creates a new browser window only if when the application has no visible windows after being activated. For example, after launching the application for the first time, or re-launching the already running application.
6. Line 25: You add a new listener that tries to quit the application when it no longer has any open windows. This listener is a no-op on macOS due to the operating system's [window management behavior](https://support.apple.com/en-ca/guide/mac-help/mchlp2469/mac).
#### Create a web page #### Create a web page
This is the web page you want to display once the application is initialized. This web page represents the Renderer process. You can create multiple browser windows, where each window uses its own independent Renderer. Each window can optionally be granted with full access to Node.js API through the `nodeIntegration` preference. This is the web page you want to display once the application is initialized. This web page represents the Renderer process. You can create multiple browser windows, where each window uses its own independent Renderer. You can optionally grant access to additional Node.js APIs by exposing them from your preload script.
The `index.html` page looks as follows: The `index.html` page looks as follows:
@ -108,14 +113,38 @@ The `index.html` page looks as follows:
<body style="background: white;"> <body style="background: white;">
<h1>Hello World!</h1> <h1>Hello World!</h1>
<p> <p>
We are using node <script>document.write(process.versions.node)</script>, We are using Node.js <span id="node-version"></span>,
Chrome <script>document.write(process.versions.chrome)</script>, Chromium <span id="chrome-version"></span>,
and Electron <script>document.write(process.versions.electron)</script>. and Electron <span id="electron-version"></span>.
</p> </p>
</body> </body>
</html> </html>
``` ```
#### Define a preload script
Your preload script acts as a bridge between Node.js and your web page. It allows you to expose specific APIs and behaviors to your web page rather than insecurely exposing the entire Node.js API. In this example we will use the preload script to read version information from the `process` object and update the web page with that info.
```javascript fiddle='docs/fiddles/quick-start'
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
```
##### What's going on above?
1. On line 1: First you define an event listener that tells you when the web page has loaded
2. On line 2: Second you define a utility function used to set the text of the placeholders in the `index.html`
3. On line 7: Next you loop through the list of components whose version you want to display
4. On line 8: Finally, you call `replaceText` to look up the version placeholders in `index.html` and set their text value to the values from `process.versions`
#### Modify your package.json file #### Modify your package.json file
Your Electron application uses the `package.json` file as the main entry point (as any other Node.js application). The main script of your application is `main.js`, so modify the `package.json` file accordingly: Your Electron application uses the `package.json` file as the main entry point (as any other Node.js application). The main script of your application is `main.js`, so modify the `package.json` file accordingly:
@ -283,7 +312,7 @@ ipcRenderer.invoke('perform-action', ...args)
##### Node.js API ##### Node.js API
> NOTE: To access the Node.js API from the Renderer process, you need to set the `nodeIntegration` preference to `true`. > NOTE: To access the Node.js API from the Renderer process, you need to set the `nodeIntegration` preference to `true` and the `contextIsolation` preference to `false`. Please note that access to the Node.js API in any renderer that loads remote content is not recommended for [security reasons](../tutorial/security.md#2-do-not-enable-nodejs-integration-for-remote-content).
Electron exposes full access to Node.js API and its modules both in the Main and the Renderer processes. For example, you can read all the files from the root directory: Electron exposes full access to Node.js API and its modules both in the Main and the Renderer processes. For example, you can read all the files from the root directory:

View file

@ -86,12 +86,12 @@ const driver = new webdriver.Builder()
// The "9515" is the port opened by chrome driver. // The "9515" is the port opened by chrome driver.
.usingServer('http://localhost:9515') .usingServer('http://localhost:9515')
.withCapabilities({ .withCapabilities({
chromeOptions: { 'goog:chromeOptions': {
// Here is the path to your Electron binary. // Here is the path to your Electron binary.
binary: '/Path-to-Your-App.app/Contents/MacOS/Electron' binary: '/Path-to-Your-App.app/Contents/MacOS/Electron'
} }
}) })
.forBrowser('electron') .forBrowser('chrome') // note: use .forBrowser('electron') for selenium-webdriver <= 3.6.0
.build() .build()
driver.get('http://www.google.com') driver.get('http://www.google.com')

View file

@ -10,13 +10,13 @@ class CrashReporter {
extra = {}, extra = {},
globalExtra = {}, globalExtra = {},
ignoreSystemCrashHandler = false, ignoreSystemCrashHandler = false,
submitURL, submitURL = '',
uploadToServer = true, uploadToServer = true,
rateLimit = false, rateLimit = false,
compress = true compress = true
} = options || {}; } = options || {};
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start'); if (uploadToServer && !submitURL) throw new Error('submitURL must be specified when uploadToServer is true');
if (!compress && uploadToServer) { if (!compress && uploadToServer) {
deprecate.log('Sending uncompressed crash reports is deprecated and will be removed in a future version of Electron. Set { compress: true } to opt-in to the new behavior. Crash reports will be uploaded gzipped, which most crash reporting servers support.'); deprecate.log('Sending uncompressed crash reports is deprecated and will be removed in a future version of Electron. Set { compress: true } to opt-in to the new behavior. Crash reports will be uploaded gzipped, which most crash reporting servers support.');

View file

@ -94,6 +94,7 @@ const defaultPrintingSetting = {
pagesPerSheet: 1, pagesPerSheet: 1,
isFirstRequest: false, isFirstRequest: false,
previewUIID: 0, previewUIID: 0,
// True, if the document source is modifiable. e.g. HTML and not PDF.
previewModifiable: true, previewModifiable: true,
printToPDF: true, printToPDF: true,
deviceName: 'Save as PDF', deviceName: 'Save as PDF',

View file

@ -39,6 +39,10 @@ require('@electron/internal/common/init');
// The global variable will be used by ipc for event dispatching // The global variable will be used by ipc for event dispatching
const v8Util = process._linkedBinding('electron_common_v8_util'); const v8Util = process._linkedBinding('electron_common_v8_util');
// Expose process.contextId
const contextId = v8Util.getHiddenValue<string>(global, 'contextId');
Object.defineProperty(process, 'contextId', { enumerable: true, value: contextId });
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
const ipcRenderer = require('@electron/internal/renderer/api/ipc-renderer').default; const ipcRenderer = require('@electron/internal/renderer/api/ipc-renderer').default;

View file

@ -89,6 +89,10 @@ Object.defineProperty(preloadProcess, 'noDeprecation', {
} }
}); });
// Expose process.contextId
const contextId = v8Util.getHiddenValue<string>(global, 'contextId');
Object.defineProperty(preloadProcess, 'contextId', { enumerable: true, value: contextId });
process.on('loaded', () => (preloadProcess as events.EventEmitter).emit('loaded')); process.on('loaded', () => (preloadProcess as events.EventEmitter).emit('loaded'));
process.on('exit', () => (preloadProcess as events.EventEmitter).emit('exit')); process.on('exit', () => (preloadProcess as events.EventEmitter).emit('exit'));
(process as events.EventEmitter).on('document-start', () => (preloadProcess as events.EventEmitter).emit('document-start')); (process as events.EventEmitter).on('document-start', () => (preloadProcess as events.EventEmitter).emit('document-start'));

View file

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

View file

@ -0,0 +1,26 @@
enum RecordingMode { "record-until-full", "record-continuously", "record-as-much-as-possible", "trace-to-console" };
dictionary TraceConfig {
Recordingmode recording_mode;
unsigned long trace_buffer_size_in_kb;
unsigned long trace_buffer_size_in_events;
boolean enable_argument_filter;
sequence<DOMString> included_categories;
sequence<DOMString> excluded_categories;
sequence<unsigned short> included_process_ids;
sequence<DOMString> histogram_names;
object memory_dump_config;
};
dictionary TraceCategoriesAndOptions {
DOMString categoryFilter;
DOMString traceOptions;
};
interface ContentTracing {
Promise<sequence<DOMString>> getCategories();
Promise<void> startRecording(TraceConfig config);
Promise<void> startRecording(TraceCategoriesAndOptions categoriesAndOptions);
Promise<DOMString> stopRecording(optional DOMString resultFilePath);
Promise<TraceBufferUsage> getTraceBufferUsage();
};

View file

@ -1114,6 +1114,8 @@ int32_t BaseWindow::GetID() const {
} }
void BaseWindow::ResetBrowserViews() { void BaseWindow::ResetBrowserViews() {
v8::HandleScope scope(isolate());
for (auto& item : browser_views_) { for (auto& item : browser_views_) {
gin::Handle<BrowserView> browser_view; gin::Handle<BrowserView> browser_view;
if (gin::ConvertFromV8(isolate(), if (gin::ConvertFromV8(isolate(),

View file

@ -162,6 +162,9 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
v8::Locker locker(isolate); v8::Locker locker(isolate);
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
gin_helper::CallMethod(this, "_onerror", "Failed to get sources."); gin_helper::CallMethod(this, "_onerror", "Failed to get sources.");
Unpin();
return; return;
} }
@ -195,12 +198,19 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
v8::Locker locker(isolate); v8::Locker locker(isolate);
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
gin_helper::CallMethod(this, "_onfinished", captured_sources_); gin_helper::CallMethod(this, "_onfinished", captured_sources_);
Unpin();
} }
} }
// static // static
gin::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) { gin::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) {
return gin::CreateHandle(isolate, new DesktopCapturer(isolate)); auto handle = gin::CreateHandle(isolate, new DesktopCapturer(isolate));
// Keep reference alive until capturing has finished.
handle->Pin(isolate);
return handle;
} }
gin::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder( gin::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder(

View file

@ -13,12 +13,14 @@
#include "chrome/browser/media/webrtc/native_desktop_media_list.h" #include "chrome/browser/media/webrtc/native_desktop_media_list.h"
#include "gin/handle.h" #include "gin/handle.h"
#include "gin/wrappable.h" #include "gin/wrappable.h"
#include "shell/common/gin_helper/pinnable.h"
namespace electron { namespace electron {
namespace api { namespace api {
class DesktopCapturer : public gin::Wrappable<DesktopCapturer>, class DesktopCapturer : public gin::Wrappable<DesktopCapturer>,
public gin_helper::Pinnable<DesktopCapturer>,
public DesktopMediaListObserver { public DesktopMediaListObserver {
public: public:
struct Source { struct Source {

View file

@ -88,7 +88,7 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
} }
// If no preferred item is specified, try to show all of the menu items. // If no preferred item is specified, try to show all of the menu items.
if (!positioning_item) { if (!item) {
CGFloat windowBottom = CGRectGetMinY([view window].frame); CGFloat windowBottom = CGRectGetMinY([view window].frame);
CGFloat lowestMenuPoint = windowBottom + position.y - [menu size].height; CGFloat lowestMenuPoint = windowBottom + position.y - [menu size].height;
CGFloat screenBottom = CGRectGetMinY([view window].screen.frame); CGFloat screenBottom = CGRectGetMinY([view window].screen.frame);

View file

@ -10,6 +10,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "content/common/url_schemes.h"
#include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/child_process_security_policy.h"
#include "gin/object_template_builder.h" #include "gin/object_template_builder.h"
#include "shell/browser/browser.h" #include "shell/browser/browser.h"
@ -124,6 +125,13 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
} }
if (custom_scheme.options.allowServiceWorkers) { if (custom_scheme.options.allowServiceWorkers) {
service_worker_schemes.push_back(custom_scheme.scheme); service_worker_schemes.push_back(custom_scheme.scheme);
// There is no API to add service worker scheme, but there is an API to
// return const reference to the schemes vector.
// If in future the API is changed to return a copy instead of reference,
// the compilation will fail, and we should add a patch at that time.
auto& mutable_schemes = const_cast<std::vector<std::string>&>(
content::GetServiceWorkerSchemes());
mutable_schemes.push_back(custom_scheme.scheme);
} }
if (custom_scheme.options.stream) { if (custom_scheme.options.stream) {
g_streaming_schemes.push_back(custom_scheme.scheme); g_streaming_schemes.push_back(custom_scheme.scheme);

View file

@ -915,6 +915,7 @@ WebContents::~WebContents() {
return; return;
} }
inspectable_web_contents_->GetView()->SetDelegate(nullptr);
if (guest_delegate_) if (guest_delegate_)
guest_delegate_->WillDestroy(); guest_delegate_->WillDestroy();
@ -1761,6 +1762,7 @@ void WebContents::DevToolsOpened() {
v8::Locker locker(isolate); v8::Locker locker(isolate);
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
DCHECK(inspectable_web_contents_); DCHECK(inspectable_web_contents_);
DCHECK(inspectable_web_contents_->GetDevToolsWebContents());
auto handle = FromOrCreate( auto handle = FromOrCreate(
isolate, inspectable_web_contents_->GetDevToolsWebContents()); isolate, inspectable_web_contents_->GetDevToolsWebContents());
devtools_web_contents_.Reset(isolate, handle.ToV8()); devtools_web_contents_.Reset(isolate, handle.ToV8());

View file

@ -1363,22 +1363,26 @@ void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
int render_process_id, int render_process_id,
int render_frame_id, int render_frame_id,
NonNetworkURLLoaderFactoryMap* factories) { NonNetworkURLLoaderFactoryMap* factories) {
content::RenderFrameHost* frame_host = auto* render_process_host =
content::RenderFrameHost::FromID(render_process_id, render_frame_id); content::RenderProcessHost::FromID(render_process_id);
content::WebContents* web_contents = DCHECK(render_process_host);
content::WebContents::FromRenderFrameHost(frame_host); if (!render_process_host || !render_process_host->GetBrowserContext())
return;
ProtocolRegistry::FromBrowserContext(render_process_host->GetBrowserContext())
->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource,
factories);
if (web_contents) {
ProtocolRegistry::FromBrowserContext(web_contents->GetBrowserContext())
->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource,
factories);
}
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id, auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id,
render_frame_id); render_frame_id);
if (factory) if (factory)
factories->emplace(extensions::kExtensionScheme, std::move(factory)); factories->emplace(extensions::kExtensionScheme, std::move(factory));
content::RenderFrameHost* frame_host =
content::RenderFrameHost::FromID(render_process_id, render_frame_id);
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(frame_host);
if (!web_contents) if (!web_contents)
return; return;

View file

@ -317,8 +317,9 @@ void NativeBrowserViewMac::UpdateDraggableRegions(
const auto window_content_view_height = NSHeight(window_content_view.bounds); const auto window_content_view_height = NSHeight(window_content_view.bounds);
for (const auto& rect : drag_exclude_rects) { for (const auto& rect : drag_exclude_rects) {
const auto x = rect.x() + offset.x(); const auto x = rect.x() + offset.x();
const auto y = window_content_view_height - rect.bottom() + offset.y(); const auto y = window_content_view_height - (rect.bottom() + offset.y());
const auto exclude_rect = NSMakeRect(x, y, rect.width(), rect.height()); const auto exclude_rect = NSMakeRect(x, y, rect.width(), rect.height());
const auto drag_region_view_exclude_rect = const auto drag_region_view_exclude_rect =
[window_content_view convertRect:exclude_rect toView:drag_region_view]; [window_content_view convertRect:exclude_rect toView:drag_region_view];

View file

@ -206,6 +206,7 @@ class NativeWindow : public base::SupportsUserData,
virtual void SetTrafficLightPosition(base::Optional<gfx::Point> position) = 0; virtual void SetTrafficLightPosition(base::Optional<gfx::Point> position) = 0;
virtual base::Optional<gfx::Point> GetTrafficLightPosition() const = 0; virtual base::Optional<gfx::Point> GetTrafficLightPosition() const = 0;
virtual void RedrawTrafficLights() = 0; virtual void RedrawTrafficLights() = 0;
virtual void UpdateFrame() = 0;
#endif #endif
// Touchbar API // Touchbar API

View file

@ -14,6 +14,7 @@
#include "base/mac/scoped_nsobject.h" #include "base/mac/scoped_nsobject.h"
#include "shell/browser/native_window.h" #include "shell/browser/native_window.h"
#include "ui/display/display_observer.h"
#include "ui/native_theme/native_theme_observer.h" #include "ui/native_theme/native_theme_observer.h"
#include "ui/views/controls/native/native_view_host.h" #include "ui/views/controls/native/native_view_host.h"
@ -27,7 +28,9 @@ namespace electron {
class RootViewMac; class RootViewMac;
class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { class NativeWindowMac : public NativeWindow,
public ui::NativeThemeObserver,
public display::DisplayObserver {
public: public:
NativeWindowMac(const gin_helper::Dictionary& options, NativeWindow* parent); NativeWindowMac(const gin_helper::Dictionary& options, NativeWindow* parent);
~NativeWindowMac() override; ~NativeWindowMac() override;
@ -124,6 +127,7 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
void SetTrafficLightPosition(base::Optional<gfx::Point> position) override; void SetTrafficLightPosition(base::Optional<gfx::Point> position) override;
base::Optional<gfx::Point> GetTrafficLightPosition() const override; base::Optional<gfx::Point> GetTrafficLightPosition() const override;
void RedrawTrafficLights() override; void RedrawTrafficLights() override;
void UpdateFrame() override;
void SetTouchBar( void SetTouchBar(
std::vector<gin_helper::PersistentDictionary> items) override; std::vector<gin_helper::PersistentDictionary> items) override;
void RefreshTouchBarItem(const std::string& item_id) override; void RefreshTouchBarItem(const std::string& item_id) override;
@ -188,6 +192,10 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver {
// ui::NativeThemeObserver: // ui::NativeThemeObserver:
void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
// display::DisplayObserver:
void OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) override;
private: private:
// Add custom layers to the content view. // Add custom layers to the content view.
void AddContentViewLayers(); void AddContentViewLayers();

View file

@ -39,6 +39,7 @@
#include "shell/common/process_util.h" #include "shell/common/process_util.h"
#include "skia/ext/skia_utils_mac.h" #include "skia/ext/skia_utils_mac.h"
#include "third_party/webrtc/modules/desktop_capture/mac/window_list_utils.h" #include "third_party/webrtc/modules/desktop_capture/mac/window_list_utils.h"
#include "ui/display/screen.h"
#include "ui/gfx/skia_util.h" #include "ui/gfx/skia_util.h"
#include "ui/gl/gpu_switching_manager.h" #include "ui/gl/gpu_switching_manager.h"
#include "ui/views/background.h" #include "ui/views/background.h"
@ -258,6 +259,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
NativeWindow* parent) NativeWindow* parent)
: NativeWindow(options, parent), root_view_(new RootViewMac(this)) { : NativeWindow(options, parent), root_view_(new RootViewMac(this)) {
ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this); ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this);
display::Screen::GetScreen()->AddObserver(this);
int width = 800, height = 600; int width = 800, height = 600;
options.Get(options::kWidth, &width); options.Get(options::kWidth, &width);
@ -882,6 +884,17 @@ void NativeWindowMac::SetExcludedFromShownWindowsMenu(bool excluded) {
[window setExcludedFromWindowsMenu:excluded]; [window setExcludedFromWindowsMenu:excluded];
} }
void NativeWindowMac::OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) {
// We only want to force screen recalibration if we're in simpleFullscreen
// mode.
if (!is_simple_fullscreen_)
return;
base::PostTask(FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&NativeWindow::UpdateFrame, GetWeakPtr()));
}
void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) { void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
NSWindow* window = GetNativeWindow().GetNativeNSWindow(); NSWindow* window = GetNativeWindow().GetNativeNSWindow();
@ -1396,6 +1409,13 @@ void NativeWindowMac::RedrawTrafficLights() {
[buttons_view_ setNeedsDisplayForButtons]; [buttons_view_ setNeedsDisplayForButtons];
} }
// In simpleFullScreen mode, update the frame for new bounds.
void NativeWindowMac::UpdateFrame() {
NSWindow* window = GetNativeWindow().GetNativeNSWindow();
NSRect fullscreenFrame = [window.screen frame];
[window setFrame:fullscreenFrame display:YES animate:YES];
}
void NativeWindowMac::SetTouchBar( void NativeWindowMac::SetTouchBar(
std::vector<gin_helper::PersistentDictionary> items) { std::vector<gin_helper::PersistentDictionary> items) {
if (@available(macOS 10.12.2, *)) { if (@available(macOS 10.12.2, *)) {
@ -1551,6 +1571,7 @@ void NativeWindowMac::NotifyWindowWillLeaveFullScreen() {
void NativeWindowMac::Cleanup() { void NativeWindowMac::Cleanup() {
DCHECK(!IsClosed()); DCHECK(!IsClosed());
ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this);
display::Screen::GetScreen()->RemoveObserver(this);
[NSEvent removeMonitor:wheel_event_monitor_]; [NSEvent removeMonitor:wheel_event_monitor_];
} }

View file

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

View file

@ -201,12 +201,6 @@ class InspectableWebContents
void AddDevToolsExtensionsToClient(); void AddDevToolsExtensionsToClient();
#endif #endif
bool frontend_loaded_ = false;
scoped_refptr<content::DevToolsAgentHost> agent_host_;
std::unique_ptr<content::DevToolsFrontendHost> frontend_host_;
std::unique_ptr<DevToolsEmbedderMessageDispatcher>
embedder_message_dispatcher_;
DevToolsContentsResizingStrategy contents_resizing_strategy_; DevToolsContentsResizingStrategy contents_resizing_strategy_;
gfx::Rect devtools_bounds_; gfx::Rect devtools_bounds_;
bool can_dock_ = true; bool can_dock_ = true;
@ -228,6 +222,12 @@ class InspectableWebContents
bool is_guest_; bool is_guest_;
std::unique_ptr<InspectableWebContentsView> view_; std::unique_ptr<InspectableWebContentsView> view_;
bool frontend_loaded_ = false;
scoped_refptr<content::DevToolsAgentHost> agent_host_;
std::unique_ptr<content::DevToolsFrontendHost> frontend_host_;
std::unique_ptr<DevToolsEmbedderMessageDispatcher>
embedder_message_dispatcher_;
class NetworkResourceLoader; class NetworkResourceLoader;
std::set<std::unique_ptr<NetworkResourceLoader>, base::UniquePtrComparator> std::set<std::unique_ptr<NetworkResourceLoader>, base::UniquePtrComparator>
loaders_; loaders_;

View file

@ -363,21 +363,22 @@ void NodeBindings::Initialize() {
// Parse and set Node.js cli flags. // Parse and set Node.js cli flags.
SetNodeCliFlags(); SetNodeCliFlags();
// pass non-null program name to argv so it doesn't crash
// trying to index into a nullptr
int argc = 1;
int exec_argc = 0;
const char* prog_name = "electron";
const char** argv = &prog_name;
const char** exec_argv = nullptr;
std::unique_ptr<base::Environment> env(base::Environment::Create()); std::unique_ptr<base::Environment> env(base::Environment::Create());
SetNodeOptions(env.get()); SetNodeOptions(env.get());
// TODO(codebytere): this is going to be deprecated in the near future std::vector<std::string> argv = {"electron"};
// in favor of Init(std::vector<std::string>* argv, std::vector<std::string> exec_argv;
// std::vector<std::string>* exec_argv) std::vector<std::string> errors;
node::Init(&argc, argv, &exec_argc, &exec_argv);
int exit_code = node::InitializeNodeWithArgs(&argv, &exec_argv, &errors);
for (const std::string& error : errors) {
fprintf(stderr, "%s: %s\n", argv[0].c_str(), error.c_str());
}
if (exit_code != 0) {
exit(exit_code);
}
#if defined(OS_WIN) #if defined(OS_WIN)
// uv_init overrides error mode to suppress the default crash dialog, bring // uv_init overrides error mode to suppress the default crash dialog, bring
@ -533,15 +534,13 @@ void NodeBindings::LoadEnvironment(node::Environment* env) {
void NodeBindings::PrepareMessageLoop() { void NodeBindings::PrepareMessageLoop() {
#if !defined(OS_WIN) #if !defined(OS_WIN)
int handle = uv_backend_fd(uv_loop_); int handle = uv_backend_fd(uv_loop_);
#else
HANDLE handle = uv_loop_->iocp;
#endif
// If the backend fd hasn't changed, don't proceed. // If the backend fd hasn't changed, don't proceed.
if (handle == handle_) if (handle == handle_)
return; return;
handle_ = handle; handle_ = handle;
#endif
// Add dummy handle for libuv, otherwise libuv would quit when there is // Add dummy handle for libuv, otherwise libuv would quit when there is
// nothing to do. // nothing to do.

View file

@ -159,9 +159,7 @@ class NodeBindings {
// Isolate data used in creating the environment // Isolate data used in creating the environment
node::IsolateData* isolate_data_ = nullptr; node::IsolateData* isolate_data_ = nullptr;
#if defined(OS_WIN) #if !defined(OS_WIN)
HANDLE handle_;
#else
int handle_ = -1; int handle_ = -1;
#endif #endif

View file

@ -32,6 +32,7 @@
#include "base/win/windows_version.h" #include "base/win/windows_version.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "net/base/escape.h"
#include "shell/common/electron_paths.h" #include "shell/common/electron_paths.h"
#include "ui/base/win/shell.h" #include "ui/base/win/shell.h"
#include "url/gurl.h" #include "url/gurl.h"
@ -241,7 +242,8 @@ std::string OpenExternalOnWorkerThread(
// Quote the input scheme to be sure that the command does not have // Quote the input scheme to be sure that the command does not have
// parameters unexpected by the external program. This url should already // parameters unexpected by the external program. This url should already
// have been escaped. // have been escaped.
std::wstring escaped_url = L"\"" + base::UTF8ToWide(url.spec()) + L"\""; std::wstring escaped_url =
L"\"" + base::UTF8ToWide(net::EscapeExternalHandlerValue(url.spec())) +
std::wstring working_dir = options.working_dir.value(); std::wstring working_dir = options.working_dir.value();
if (reinterpret_cast<ULONG_PTR>( if (reinterpret_cast<ULONG_PTR>(

View file

@ -11,6 +11,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/feature_list.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame.h"
@ -25,6 +26,12 @@
#include "third_party/blink/public/web/web_element.h" #include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/public/web/web_local_frame.h"
namespace features {
const base::Feature kContextBridgeMutability{"ContextBridgeMutability",
base::FEATURE_DISABLED_BY_DEFAULT};
}
namespace electron { namespace electron {
namespace api { namespace api {
@ -554,6 +561,12 @@ void ExposeAPIInMainWorld(v8::Isolate* isolate,
if (maybe_proxy.IsEmpty()) if (maybe_proxy.IsEmpty())
return; return;
auto proxy = maybe_proxy.ToLocalChecked(); auto proxy = maybe_proxy.ToLocalChecked();
if (base::FeatureList::IsEnabled(features::kContextBridgeMutability)) {
global.Set(key, proxy);
return;
}
if (proxy->IsObject() && !proxy->IsTypedArray() && if (proxy->IsObject() && !proxy->IsTypedArray() &&
!DeepFreeze(v8::Local<v8::Object>::Cast(proxy), main_context)) !DeepFreeze(v8::Local<v8::Object>::Cast(proxy), main_context))
return; return;

View file

@ -146,9 +146,8 @@ void ElectronRendererClient::DidCreateScriptContext(
// Add Electron extended APIs. // Add Electron extended APIs.
electron_bindings_->BindTo(env->isolate(), env->process_object()); electron_bindings_->BindTo(env->isolate(), env->process_object());
AddRenderBindings(env->isolate(), env->process_object());
gin_helper::Dictionary process_dict(env->isolate(), env->process_object()); gin_helper::Dictionary process_dict(env->isolate(), env->process_object());
process_dict.SetReadOnly("isMainFrame", render_frame->IsMainFrame()); BindProcess(env->isolate(), &process_dict, render_frame);
// Load everything. // Load everything.
node_bindings_->LoadEnvironment(env); node_bindings_->LoadEnvironment(env);

View file

@ -131,7 +131,7 @@ ElectronSandboxedRendererClient::~ElectronSandboxedRendererClient() = default;
void ElectronSandboxedRendererClient::InitializeBindings( void ElectronSandboxedRendererClient::InitializeBindings(
v8::Local<v8::Object> binding, v8::Local<v8::Object> binding,
v8::Local<v8::Context> context, v8::Local<v8::Context> context,
bool is_main_frame) { content::RenderFrame* render_frame) {
auto* isolate = context->GetIsolate(); auto* isolate = context->GetIsolate();
gin_helper::Dictionary b(isolate, binding); gin_helper::Dictionary b(isolate, binding);
b.SetMethod("get", GetBinding); b.SetMethod("get", GetBinding);
@ -141,13 +141,13 @@ void ElectronSandboxedRendererClient::InitializeBindings(
b.Set("process", process); b.Set("process", process);
ElectronBindings::BindProcess(isolate, &process, metrics_.get()); ElectronBindings::BindProcess(isolate, &process, metrics_.get());
BindProcess(isolate, &process, render_frame);
process.SetMethod("uptime", Uptime); process.SetMethod("uptime", Uptime);
process.Set("argv", base::CommandLine::ForCurrentProcess()->argv()); process.Set("argv", base::CommandLine::ForCurrentProcess()->argv());
process.SetReadOnly("pid", base::GetCurrentProcId()); process.SetReadOnly("pid", base::GetCurrentProcId());
process.SetReadOnly("sandboxed", true); process.SetReadOnly("sandboxed", true);
process.SetReadOnly("type", "renderer"); process.SetReadOnly("type", "renderer");
process.SetReadOnly("isMainFrame", is_main_frame);
} }
void ElectronSandboxedRendererClient::RenderFrameCreated( void ElectronSandboxedRendererClient::RenderFrameCreated(
@ -218,8 +218,7 @@ void ElectronSandboxedRendererClient::DidCreateScriptContext(
// argument. // argument.
auto* isolate = context->GetIsolate(); auto* isolate = context->GetIsolate();
auto binding = v8::Object::New(isolate); auto binding = v8::Object::New(isolate);
InitializeBindings(binding, context, render_frame->IsMainFrame()); InitializeBindings(binding, context, render_frame);
AddRenderBindings(isolate, binding);
std::vector<v8::Local<v8::String>> sandbox_preload_bundle_params = { std::vector<v8::Local<v8::String>> sandbox_preload_bundle_params = {
node::FIXED_ONE_BYTE_STRING(isolate, "binding")}; node::FIXED_ONE_BYTE_STRING(isolate, "binding")};

View file

@ -21,7 +21,7 @@ class ElectronSandboxedRendererClient : public RendererClientBase {
void InitializeBindings(v8::Local<v8::Object> binding, void InitializeBindings(v8::Local<v8::Object> binding,
v8::Local<v8::Context> context, v8::Local<v8::Context> context,
bool is_main_frame); content::RenderFrame* render_frame);
// electron::RendererClientBase: // electron::RendererClientBase:
void DidCreateScriptContext(v8::Handle<v8::Context> context, void DidCreateScriptContext(v8::Handle<v8::Context> context,
content::RenderFrame* render_frame) override; content::RenderFrame* render_frame) override;

View file

@ -11,6 +11,7 @@
#if BUILDFLAG(ENABLE_EXTENSIONS) #if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
#include "extensions/renderer/guest_view/mime_handler_view/post_message_support.h"
#endif // BUILDFLAG(ENABLE_EXTENSIONS) #endif // BUILDFLAG(ENABLE_EXTENSIONS)
namespace electron { namespace electron {
@ -50,6 +51,20 @@ bool PrintRenderFrameHelperDelegate::IsPrintPreviewEnabled() {
bool PrintRenderFrameHelperDelegate::OverridePrint( bool PrintRenderFrameHelperDelegate::OverridePrint(
blink::WebLocalFrame* frame) { blink::WebLocalFrame* frame) {
#if BUILDFLAG(ENABLE_EXTENSIONS)
auto* post_message_support =
extensions::PostMessageSupport::FromWebLocalFrame(frame);
if (post_message_support) {
// This message is handled in chrome/browser/resources/pdf/pdf_viewer.js and
// instructs the PDF plugin to print. This is to make window.print() on a
// PDF plugin document correctly print the PDF. See
// https://crbug.com/448720.
base::DictionaryValue message;
message.SetString("type", "print");
post_message_support->PostMessageFromValue(message);
return true;
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
return false; return false;
} }

View file

@ -137,9 +137,13 @@ void RendererClientBase::DidCreateScriptContext(
global.SetHidden("contextId", context_id); global.SetHidden("contextId", context_id);
} }
void RendererClientBase::AddRenderBindings( void RendererClientBase::BindProcess(v8::Isolate* isolate,
v8::Isolate* isolate, gin_helper::Dictionary* process,
v8::Local<v8::Object> binding_object) {} content::RenderFrame* render_frame) {
process->SetReadOnly("isMainFrame", render_frame->IsMainFrame());
process->SetReadOnly("contextIsolated",
render_frame->GetBlinkPreferences().context_isolation);
}
void RendererClientBase::RenderThreadStarted() { void RendererClientBase::RenderThreadStarted() {
auto* command_line = base::CommandLine::ForCurrentProcess(); auto* command_line = base::CommandLine::ForCurrentProcess();

View file

@ -13,6 +13,7 @@
#include "content/public/renderer/content_renderer_client.h" #include "content/public/renderer/content_renderer_client.h"
#include "electron/buildflags/buildflags.h" #include "electron/buildflags/buildflags.h"
#include "printing/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h"
#include "shell/common/gin_helper/dictionary.h"
#include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/public/web/web_local_frame.h"
// In SHARED_INTERMEDIATE_DIR. // In SHARED_INTERMEDIATE_DIR.
#include "widevine_cdm_version.h" // NOLINT(build/include_directory) #include "widevine_cdm_version.h" // NOLINT(build/include_directory)
@ -92,8 +93,9 @@ class RendererClientBase : public content::ContentRendererClient
#endif #endif
protected: protected:
void AddRenderBindings(v8::Isolate* isolate, void BindProcess(v8::Isolate* isolate,
v8::Local<v8::Object> binding_object); gin_helper::Dictionary* process,
content::RenderFrame* render_frame);
// content::ContentRendererClient: // content::ContentRendererClient:
void RenderThreadStarted() override; void RenderThreadStarted() override;

View file

@ -143,7 +143,8 @@ describe('app module', () => {
}); });
}); });
describe('app.exit(exitCode)', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('app.exit(exitCode)', () => {
let appProcess: cp.ChildProcess | null = null; let appProcess: cp.ChildProcess | null = null;
afterEach(() => { afterEach(() => {
@ -209,7 +210,7 @@ describe('app module', () => {
}); });
}); });
// TODO(jeremy): figure out why these tests time out under ASan // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('app.requestSingleInstanceLock', () => { ifdescribe(!process.env.IS_ASAN)('app.requestSingleInstanceLock', () => {
it('prevents the second launch of app', async function () { it('prevents the second launch of app', async function () {
this.timeout(120000); this.timeout(120000);
@ -252,7 +253,8 @@ describe('app module', () => {
}); });
}); });
describe('app.relaunch', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('app.relaunch', () => {
let server: net.Server | null = null; let server: net.Server | null = null;
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'; const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch';
@ -852,7 +854,8 @@ describe('app module', () => {
}); });
}); });
describe('getAppPath', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('getAppPath', () => {
it('works for directories with package.json', async () => { it('works for directories with package.json', async () => {
const { appPath } = await runTestApp('app-path'); const { appPath } = await runTestApp('app-path');
expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path')); expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path'));
@ -1128,13 +1131,7 @@ describe('app module', () => {
}); });
}); });
describe('app launch through uri', () => { ifdescribe(process.platform === 'win32')('app launch through uri', () => {
before(function () {
if (process.platform !== 'win32') {
this.skip();
}
});
it('does not launch for argument following a URL', async () => { it('does not launch for argument following a URL', async () => {
const appPath = path.join(fixturesPath, 'api', 'quit-app'); const appPath = path.join(fixturesPath, 'api', 'quit-app');
// App should exit with non 123 code. // App should exit with non 123 code.
@ -1334,7 +1331,8 @@ describe('app module', () => {
}); });
}); });
describe('sandbox options', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('sandbox options', () => {
let appProcess: cp.ChildProcess = null as any; let appProcess: cp.ChildProcess = null as any;
let server: net.Server = null as any; let server: net.Server = null as any;
const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox'; const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox';
@ -1375,8 +1373,7 @@ describe('app module', () => {
}); });
describe('when app.enableSandbox() is called', () => { describe('when app.enableSandbox() is called', () => {
// TODO(jeremy): figure out why this times out under ASan it('adds --enable-sandbox to all renderer processes', done => {
ifit(!process.env.IS_ASAN)('adds --enable-sandbox to all renderer processes', done => {
const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app'); const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app');
appProcess = cp.spawn(process.execPath, [appPath, '--app-enable-sandbox']); appProcess = cp.spawn(process.execPath, [appPath, '--app-enable-sandbox']);
@ -1401,8 +1398,7 @@ describe('app module', () => {
}); });
describe('when the app is launched with --enable-sandbox', () => { describe('when the app is launched with --enable-sandbox', () => {
// TODO(jeremy): figure out why this times out under ASan it('adds --enable-sandbox to all renderer processes', done => {
ifit(!process.env.IS_ASAN)('adds --enable-sandbox to all renderer processes', done => {
const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app'); const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app');
appProcess = cp.spawn(process.execPath, [appPath, '--enable-sandbox']); appProcess = cp.spawn(process.execPath, [appPath, '--enable-sandbox']);
@ -1561,7 +1557,8 @@ describe('app module', () => {
}); });
}); });
describe('commandLine.hasSwitch (existing argv)', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('commandLine.hasSwitch (existing argv)', () => {
it('returns true when present', async () => { it('returns true when present', async () => {
const { hasSwitch } = await runTestApp('command-line', '--foobar'); const { hasSwitch } = await runTestApp('command-line', '--foobar');
expect(hasSwitch).to.equal(true); expect(hasSwitch).to.equal(true);
@ -1589,7 +1586,8 @@ describe('app module', () => {
}); });
}); });
describe('commandLine.getSwitchValue (existing argv)', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('commandLine.getSwitchValue (existing argv)', () => {
it('returns the value when present', async () => { it('returns the value when present', async () => {
const { getSwitchValue } = await runTestApp('command-line', '--foobar=test'); const { getSwitchValue } = await runTestApp('command-line', '--foobar=test');
expect(getSwitchValue).to.equal('test'); expect(getSwitchValue).to.equal('test');
@ -1616,7 +1614,8 @@ describe('app module', () => {
}); });
}); });
describe('default behavior', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('default behavior', () => {
describe('application menu', () => { describe('application menu', () => {
it('creates the default menu if the app does not set it', async () => { it('creates the default menu if the app does not set it', async () => {
const result = await runTestApp('default-menu'); const result = await runTestApp('default-menu');

View file

@ -2487,9 +2487,11 @@ describe('BrowserWindow module', () => {
expect(test.env).to.deep.equal(process.env); expect(test.env).to.deep.equal(process.env);
expect(test.execPath).to.equal(process.helperExecPath); expect(test.execPath).to.equal(process.helperExecPath);
expect(test.sandboxed).to.be.true('sandboxed'); expect(test.sandboxed).to.be.true('sandboxed');
expect(test.contextIsolated).to.be.false('contextIsolated');
expect(test.type).to.equal('renderer'); expect(test.type).to.equal('renderer');
expect(test.version).to.equal(process.version); expect(test.version).to.equal(process.version);
expect(test.versions).to.deep.equal(process.versions); expect(test.versions).to.deep.equal(process.versions);
expect(test.contextId).to.be.a('string');
if (process.platform === 'linux' && test.osSandbox) { if (process.platform === 'linux' && test.osSandbox) {
expect(test.creationTime).to.be.null('creation time'); expect(test.creationTime).to.be.null('creation time');
@ -4304,6 +4306,19 @@ describe('BrowserWindow module', () => {
const [, data] = await p; const [, data] = await p;
expect(data.pageContext.openedLocation).to.equal('about:blank'); expect(data.pageContext.openedLocation).to.equal('about:blank');
}); });
it('reports process.contextIsolated', async () => {
const iw = new BrowserWindow({
show: false,
webPreferences: {
contextIsolation: true,
preload: path.join(fixtures, 'api', 'isolated-process.js')
}
});
const p = emittedOnce(ipcMain, 'context-isolation');
iw.loadURL('about:blank');
const [, contextIsolation] = await p;
expect(contextIsolation).to.be.true('contextIsolation');
});
}); });
describe('reloading with allowRendererProcessReuse enabled', () => { describe('reloading with allowRendererProcessReuse enabled', () => {

View file

@ -5,6 +5,7 @@ import * as fs from 'fs-extra';
import * as http from 'http'; import * as http from 'http';
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import * as cp from 'child_process';
import { closeWindow } from './window-helpers'; import { closeWindow } from './window-helpers';
import { emittedOnce } from './events-helpers'; import { emittedOnce } from './events-helpers';
@ -1165,3 +1166,31 @@ describe('contextBridge', () => {
generateTests(true); generateTests(true);
generateTests(false); generateTests(false);
}); });
describe('ContextBridgeMutability', () => {
it('should not make properties unwriteable and read-only if ContextBridgeMutability is on', async () => {
const appPath = path.join(fixturesPath, 'context-bridge-mutability');
const appProcess = cp.spawn(process.execPath, ['--enable-logging', '--enable-features=ContextBridgeMutability', appPath]);
let output = '';
appProcess.stdout.on('data', data => { output += data; });
await emittedOnce(appProcess, 'exit');
expect(output).to.include('some-modified-text');
expect(output).to.include('obj-modified-prop');
expect(output).to.include('1,2,5,3,4');
});
it('should make properties unwriteable and read-only if ContextBridgeMutability is off', async () => {
const appPath = path.join(fixturesPath, 'context-bridge-mutability');
const appProcess = cp.spawn(process.execPath, ['--enable-logging', appPath]);
let output = '';
appProcess.stdout.on('data', data => { output += data; });
await emittedOnce(appProcess, 'exit');
expect(output).to.include('some-text');
expect(output).to.include('obj-prop');
expect(output).to.include('1,2,3,4');
});
});

View file

@ -361,7 +361,13 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
it('requires that the submitURL option be specified', () => { it('requires that the submitURL option be specified', () => {
expect(() => { expect(() => {
crashReporter.start({} as any); crashReporter.start({} as any);
}).to.throw('submitURL is a required option to crashReporter.start'); }).to.throw('submitURL must be specified when uploadToServer is true');
});
it('allows the submitURL option to be omitted when uploadToServer is false', () => {
expect(() => {
crashReporter.start({ uploadToServer: false } as any);
}).not.to.throw();
}); });
it('can be called twice', async () => { it('can be called twice', async () => {

View file

@ -1,4 +1,5 @@
import { expect } from 'chai'; import { expect } from 'chai';
import { v4 } from 'uuid';
import { protocol, webContents, WebContents, session, BrowserWindow, ipcMain } from 'electron/main'; import { protocol, webContents, WebContents, session, BrowserWindow, ipcMain } from 'electron/main';
import { AddressInfo } from 'net'; import { AddressInfo } from 'net';
import * as ChildProcess from 'child_process'; import * as ChildProcess from 'child_process';
@ -704,7 +705,7 @@ describe('protocol module', () => {
}); });
describe('protocol.registerSchemeAsPrivileged', () => { describe('protocol.registerSchemeAsPrivileged', () => {
// TODO(jeremy): figure out why this times out under ASan // Running child app under ASan might receive SIGKILL because of OOM.
ifit(!process.env.IS_ASAN)('does not crash on exit', async () => { ifit(!process.env.IS_ASAN)('does not crash on exit', async () => {
const appPath = path.join(__dirname, 'fixtures', 'api', 'custom-protocol-shutdown.js'); const appPath = path.join(__dirname, 'fixtures', 'api', 'custom-protocol-shutdown.js');
const appProcess = ChildProcess.spawn(process.execPath, ['--enable-logging', appPath]); const appProcess = ChildProcess.spawn(process.execPath, ['--enable-logging', appPath]);
@ -724,6 +725,36 @@ describe('protocol module', () => {
}); });
}); });
describe('protocol.registerSchemesAsPrivileged allowServiceWorkers', () => {
const { serviceWorkerScheme } = global as any;
protocol.registerStringProtocol(serviceWorkerScheme, (request, cb) => {
if (request.url.endsWith('.js')) {
cb({
mimeType: 'text/javascript',
charset: 'utf-8',
data: 'console.log("Loaded")'
});
} else {
cb({
mimeType: 'text/html',
charset: 'utf-8',
data: '<!DOCTYPE html>'
});
}
});
after(() => protocol.unregisterProtocol(serviceWorkerScheme));
it('should fail when registering invalid service worker', async () => {
await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`);
await expect(contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.notjs', {scope: './'})`)).to.be.rejected();
});
it('should be able to register service worker for custom scheme', async () => {
await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`);
await contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.js', {scope: './'})`);
});
});
describe.skip('protocol.registerSchemesAsPrivileged standard', () => { describe.skip('protocol.registerSchemesAsPrivileged standard', () => {
const standardScheme = (global as any).standardScheme; const standardScheme = (global as any).standardScheme;
const origin = `${standardScheme}://fake-host`; const origin = `${standardScheme}://fake-host`;

View file

@ -3,7 +3,6 @@ import { AddressInfo } from 'net';
import * as path from 'path'; import * as path from 'path';
import * as fs from 'fs'; import * as fs from 'fs';
import * as http from 'http'; import * as http from 'http';
import * as ChildProcess from 'child_process';
import { BrowserWindow, ipcMain, webContents, session, WebContents, app } from 'electron/main'; import { BrowserWindow, ipcMain, webContents, session, WebContents, app } from 'electron/main';
import { clipboard } from 'electron/common'; import { clipboard } from 'electron/common';
import { emittedOnce } from './events-helpers'; import { emittedOnce } from './events-helpers';
@ -1268,16 +1267,6 @@ describe('webContents module', () => {
}); });
}); });
describe('create()', () => {
it('does not crash on exit', async () => {
const appPath = path.join(fixturesPath, 'api', 'leak-exit-webcontents.js');
const electronPath = process.execPath;
const appProcess = ChildProcess.spawn(electronPath, [appPath]);
const [code] = await emittedOnce(appProcess, 'close');
expect(code).to.equal(0);
});
});
const crashPrefs = [ const crashPrefs = [
{ {
nodeIntegration: true nodeIntegration: true
@ -2016,13 +2005,6 @@ describe('webContents module', () => {
}); });
contents.loadURL('about:blank').then(() => contents.forcefullyCrashRenderer()); contents.loadURL('about:blank').then(() => contents.forcefullyCrashRenderer());
}); });
it('does not crash main process when quiting in it', async () => {
const appPath = path.join(mainFixturesPath, 'apps', 'quit', 'main.js');
const appProcess = ChildProcess.spawn(process.execPath, [appPath]);
const [code] = await emittedOnce(appProcess, 'close');
expect(code).to.equal(0);
});
}); });
it('emits a cancelable event before creating a child webcontents', async () => { it('emits a cancelable event before creating a child webcontents', async () => {

View file

@ -1,7 +1,3 @@
import { expect } from 'chai';
import * as ChildProcess from 'child_process';
import * as path from 'path';
import { emittedOnce } from './events-helpers';
import { closeWindow } from './window-helpers'; import { closeWindow } from './window-helpers';
import { BaseWindow, WebContentsView } from 'electron/main'; import { BaseWindow, WebContentsView } from 'electron/main';
@ -15,22 +11,6 @@ describe('WebContentsView', () => {
w.setContentView(new WebContentsView({})); w.setContentView(new WebContentsView({}));
}); });
describe('new WebContentsView()', () => {
it('does not crash on exit', async () => {
const appPath = path.join(__dirname, 'fixtures', 'api', 'leak-exit-webcontentsview.js');
const electronPath = process.execPath;
const appProcess = ChildProcess.spawn(electronPath, ['--enable-logging', appPath]);
let output = '';
appProcess.stdout.on('data', data => { output += data; });
appProcess.stderr.on('data', data => { output += data; });
const [code] = await emittedOnce(appProcess, 'exit');
if (code !== 0) {
console.log(code, output);
}
expect(code).to.equal(0);
});
});
function triggerGCByAllocation () { function triggerGCByAllocation () {
const arr = []; const arr = [];
for (let i = 0; i < 1000000; i++) { for (let i = 0; i < 1000000; i++) {

View file

@ -18,8 +18,6 @@ const features = process._linkedBinding('electron_common_features');
const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures'); const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures');
const isAsan = process.env.IS_ASAN;
describe('reporting api', () => { describe('reporting api', () => {
// TODO(nornagon): this started failing a lot on CI. Figure out why and fix // TODO(nornagon): this started failing a lot on CI. Figure out why and fix
// it. // it.
@ -298,7 +296,8 @@ describe('web security', () => {
}); });
}); });
describe('command line switches', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('command line switches', () => {
let appProcess: ChildProcess.ChildProcessWithoutNullStreams | undefined; let appProcess: ChildProcess.ChildProcessWithoutNullStreams | undefined;
afterEach(() => { afterEach(() => {
if (appProcess && !appProcess.killed) { if (appProcess && !appProcess.killed) {
@ -343,8 +342,7 @@ describe('command line switches', () => {
ifit(process.platform === 'linux')('should not change LC_ALL when --lang is not set', async () => testLocale('', lcAll, true)); ifit(process.platform === 'linux')('should not change LC_ALL when --lang is not set', async () => testLocale('', lcAll, true));
}); });
// TODO(nornagon): figure out why these tests fail under ASan. describe('--remote-debugging-pipe switch', () => {
ifdescribe(!isAsan)('--remote-debugging-pipe switch', () => {
it('should expose CDP via pipe', async () => { it('should expose CDP via pipe', async () => {
const electronPath = process.execPath; const electronPath = process.execPath;
appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-pipe'], { appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-pipe'], {
@ -386,8 +384,7 @@ describe('command line switches', () => {
}); });
}); });
// TODO(nornagon): figure out why these tests fail under ASan. describe('--remote-debugging-port switch', () => {
ifdescribe(!isAsan)('--remote-debugging-port switch', () => {
it('should display the discovery page', (done) => { it('should display the discovery page', (done) => {
const electronPath = process.execPath; const electronPath = process.execPath;
let output = ''; let output = '';

View file

@ -2,6 +2,7 @@ import { expect } from 'chai';
import * as cp from 'child_process'; import * as cp from 'child_process';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import { ifdescribe } from './spec-helpers';
const fixturePath = path.resolve(__dirname, 'fixtures', 'crash-cases'); const fixturePath = path.resolve(__dirname, 'fixtures', 'crash-cases');
@ -30,7 +31,8 @@ const runFixtureAndEnsureCleanExit = (args: string[]) => {
}); });
}; };
describe('crash cases', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('crash cases', () => {
afterEach(() => { afterEach(() => {
for (const child of children) { for (const child of children) {
child.kill(); child.kill();

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<body>
<script>
try {
window.str = 'some-modified-text';
window.obj.prop = 'obj-modified-prop';
window.arr.splice(2, 0, 5);
} catch (e) { }
console.log(window.str);
console.log(window.obj.prop);
console.log(window.arr);
</script>
</body>
</html>

View file

@ -0,0 +1,20 @@
const { app, BrowserWindow } = require('electron');
const path = require('path');
let win;
app.whenReady().then(function () {
win = new BrowserWindow({
webPreferences: {
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
});
win.loadFile('index.html');
win.webContents.on('console-message', (event, level, message) => {
console.log(message);
});
win.webContents.on('did-finish-load', () => app.quit());
});

View file

@ -0,0 +1,4 @@
{
"name": "context-bridge-mutability",
"main": "main.js"
}

View file

@ -0,0 +1,5 @@
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('str', 'some-text');
contextBridge.exposeInMainWorld('obj', { prop: 'obj-prop' });
contextBridge.exposeInMainWorld('arr', [1, 2, 3, 4]);

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
<script src="./renderer.js"></script>
</body>
</html>

View file

@ -0,0 +1,36 @@
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
async function createWindow () {
const mainWindow = new BrowserWindow({
show: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
});
await mainWindow.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
let count = 0;
ipcMain.handle('reload-successful', () => {
if (count === 2) {
app.quit();
} else {
count++;
return count;
}
});
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit();
});

View file

@ -0,0 +1,16 @@
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
ipcRenderer,
run: async () => {
const { promises: fs } = require('fs');
for (let i = 0; i < 10; i++) {
const list = await fs.readdir('.', { withFileTypes: true });
for (const file of list) {
if (file.isFile()) {
await fs.readFile(file.name, 'utf-8');
}
}
}
}
});

View file

@ -0,0 +1,8 @@
const count = localStorage.getItem('count');
const { run, ipcRenderer } = window.api;
run().then(async () => {
const count = await ipcRenderer.invoke('reload-successful');
if (count < 3) location.reload();
}).catch(console.log);

View file

@ -40,9 +40,11 @@
arch: process.arch, arch: process.arch,
platform: process.platform, platform: process.platform,
sandboxed: process.sandboxed, sandboxed: process.sandboxed,
contextIsolated: process.contextIsolated,
type: process.type, type: process.type,
version: process.version, version: process.version,
versions: process.versions versions: process.versions,
contextId: process.contextId
}; };
} }
} else if (location.href !== 'about:blank') { } else if (location.href !== 'about:blank') {

View file

@ -34,9 +34,11 @@ app.commandLine.appendSwitch('use-fake-device-for-media-stream');
global.standardScheme = 'app'; global.standardScheme = 'app';
global.zoomScheme = 'zoom'; global.zoomScheme = 'zoom';
global.serviceWorkerScheme = 'sw';
protocol.registerSchemesAsPrivileged([ protocol.registerSchemesAsPrivileged([
{ scheme: global.standardScheme, privileges: { standard: true, secure: true, stream: false } }, { scheme: global.standardScheme, privileges: { standard: true, secure: true, stream: false } },
{ scheme: global.zoomScheme, privileges: { standard: true, secure: true } }, { scheme: global.zoomScheme, privileges: { standard: true, secure: true } },
{ scheme: global.serviceWorkerScheme, privileges: { allowServiceWorkers: true, standard: true, secure: true } },
{ scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } },
{ scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } },
{ scheme: 'no-cors', privileges: { supportFetchAPI: true } }, { scheme: 'no-cors', privileges: { supportFetchAPI: true } },

View file

@ -7,6 +7,7 @@ import { ifdescribe, ifit } from './spec-helpers';
import { webContents, WebContents } from 'electron/main'; import { webContents, WebContents } from 'electron/main';
const features = process._linkedBinding('electron_common_features'); const features = process._linkedBinding('electron_common_features');
const mainFixturesPath = path.resolve(__dirname, 'fixtures');
describe('node feature', () => { describe('node feature', () => {
const fixtures = path.join(__dirname, '..', 'spec', 'fixtures'); const fixtures = path.join(__dirname, '..', 'spec', 'fixtures');
@ -22,6 +23,16 @@ describe('node feature', () => {
}); });
}); });
it('does not hang when using the fs module in the renderer process', async () => {
const appPath = path.join(mainFixturesPath, 'apps', 'libuv-hang', 'main.js');
const appProcess = childProcess.spawn(process.execPath, [appPath], {
cwd: path.join(mainFixturesPath, 'apps', 'libuv-hang'),
stdio: 'inherit'
});
const [code] = await emittedOnce(appProcess, 'close');
expect(code).to.equal(0);
});
describe('contexts', () => { describe('contexts', () => {
describe('setTimeout called under Chromium event loop in browser process', () => { describe('setTimeout called under Chromium event loop in browser process', () => {
it('Can be scheduled in time', (done) => { it('Can be scheduled in time', (done) => {
@ -123,11 +134,12 @@ describe('node feature', () => {
}); });
}); });
describe('Node.js cli flags', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(features.isRunAsNodeEnabled() && !process.env.IS_ASAN)('Node.js cli flags', () => {
let child: childProcess.ChildProcessWithoutNullStreams; let child: childProcess.ChildProcessWithoutNullStreams;
let exitPromise: Promise<any[]>; let exitPromise: Promise<any[]>;
ifit(features.isRunAsNodeEnabled())('Prohibits crypto-related flags in ELECTRON_RUN_AS_NODE mode', (done) => { it('Prohibits crypto-related flags in ELECTRON_RUN_AS_NODE mode', (done) => {
after(async () => { after(async () => {
const [code, signal] = await exitPromise; const [code, signal] = await exitPromise;
expect(signal).to.equal(null); expect(signal).to.equal(null);
@ -165,7 +177,8 @@ describe('node feature', () => {
}); });
}); });
ifdescribe(features.isRunAsNodeEnabled())('inspector', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifdescribe(features.isRunAsNodeEnabled() && !process.env.IS_ASAN)('inspector', () => {
let child: childProcess.ChildProcessWithoutNullStreams; let child: childProcess.ChildProcessWithoutNullStreams;
let exitPromise: Promise<any[]>; let exitPromise: Promise<any[]>;
@ -242,9 +255,8 @@ describe('node feature', () => {
} }
}); });
// IPC Electron child process not supported on Windows // IPC Electron child process not supported on Windows.
// TODO(jeremy): figure out why this times out under ASan ifit(process.platform !== 'win32')('does not crash when quitting with the inspector connected', function (done) {
ifit(process.platform !== 'win32' && !process.env.IS_ASAN)('does not crash when quitting with the inspector connected', function (done) {
child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'delay-exit'), '--inspect=0'], { child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'delay-exit'), '--inspect=0'], {
stdio: ['ipc'] stdio: ['ipc']
}) as childProcess.ChildProcessWithoutNullStreams; }) as childProcess.ChildProcessWithoutNullStreams;
@ -304,7 +316,8 @@ describe('node feature', () => {
}); });
}); });
it('Can find a module using a package.json main field', () => { // Running child app under ASan might receive SIGKILL because of OOM.
ifit(!process.env.IS_ASAN)('Can find a module using a package.json main field', () => {
const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')]); const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')]);
expect(result.status).to.equal(0); expect(result.status).to.equal(0);
}); });

View file

@ -9,7 +9,11 @@ import { ifit, ifdescribe, delay } from './spec-helpers';
const features = process._linkedBinding('electron_common_features'); const features = process._linkedBinding('electron_common_features');
const v8Util = process._linkedBinding('electron_common_v8_util'); const v8Util = process._linkedBinding('electron_common_v8_util');
ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function () {
// TODO(zcbenz): Spellchecker loads really slow on ASan, we should provide
// a small testing dictionary to make the tests load faster.
this.timeout((process.env.IS_ASAN ? 700 : 20) * 1000);
let w: BrowserWindow; let w: BrowserWindow;
async function rightClick () { async function rightClick () {
@ -28,7 +32,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => {
// to detect spellchecker is to keep checking with a busy loop. // to detect spellchecker is to keep checking with a busy loop.
async function rightClickUntil (fn: (params: Electron.ContextMenuParams) => boolean) { async function rightClickUntil (fn: (params: Electron.ContextMenuParams) => boolean) {
const now = Date.now(); const now = Date.now();
const timeout = 10 * 1000; const timeout = (process.env.IS_ASAN ? 600 : 10) * 1000;
let contextMenuParams = await rightClick(); let contextMenuParams = await rightClick();
while (!fn(contextMenuParams) && (Date.now() - now < timeout)) { while (!fn(contextMenuParams) && (Date.now() - now < timeout)) {
await delay(100); await delay(100);

View file

@ -3,6 +3,7 @@ import * as url from 'url';
import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main'; import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main';
import { closeAllWindows } from './window-helpers'; import { closeAllWindows } from './window-helpers';
import { emittedOnce, emittedUntil } from './events-helpers'; import { emittedOnce, emittedUntil } from './events-helpers';
import { ifdescribe } from './spec-helpers';
import { expect } from 'chai'; import { expect } from 'chai';
async function loadWebView (w: WebContents, attributes: Record<string, string>, openDevTools: boolean = false): Promise<void> { async function loadWebView (w: WebContents, attributes: Record<string, string>, openDevTools: boolean = false): Promise<void> {
@ -25,7 +26,8 @@ async function loadWebView (w: WebContents, attributes: Record<string, string>,
`); `);
} }
describe('<webview> tag', function () { // The render process of webview might receive SIGKILL because of OOM.
ifdescribe(!process.env.IS_ASAN)('<webview> tag', function () {
const fixtures = path.join(__dirname, '..', 'spec', 'fixtures'); const fixtures = path.join(__dirname, '..', 'spec', 'fixtures');
afterEach(closeAllWindows); afterEach(closeAllWindows);

View file

@ -115,4 +115,10 @@ describe('process module', () => {
expect(success).to.be.false(); expect(success).to.be.false();
}); });
}); });
describe('process.contextId', () => {
it('is a string', () => {
expect(process.contextId).to.be.a('string');
});
});
}); });

3
spec/fixtures/api/isolated-process.js vendored Normal file
View file

@ -0,0 +1,3 @@
const { ipcRenderer } = require('electron');
ipcRenderer.send('context-isolation', process.contextIsolated);