feat: make desktopCapturer main-process-only (#30720)
* feat: make desktopCapturer main-process-only * remove --enable-api-filtering-logging * remove test * merge lib/browser/api/desktop-capturer.ts with lib/browser/desktop-capturer.ts * remove desktop-capturer-get-sources event * fix specs * getSources needs to be async Co-authored-by: Milan Burda <milan.burda@gmail.com>
This commit is contained in:
parent
6db8d7918d
commit
4fd7c2adcd
17 changed files with 149 additions and 312 deletions
|
@ -500,16 +500,6 @@ gets emitted.
|
||||||
**Note:** Extra command line arguments might be added by Chromium,
|
**Note:** Extra command line arguments might be added by Chromium,
|
||||||
such as `--original-process-start-time`.
|
such as `--original-process-start-time`.
|
||||||
|
|
||||||
### Event: 'desktop-capturer-get-sources'
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
* `event` Event
|
|
||||||
* `webContents` [WebContents](web-contents.md)
|
|
||||||
|
|
||||||
Emitted when `desktopCapturer.getSources()` is called in the renderer process of `webContents`.
|
|
||||||
Calling `event.preventDefault()` will make it return empty sources.
|
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
The `app` object has the following methods:
|
The `app` object has the following methods:
|
||||||
|
|
|
@ -61,12 +61,6 @@ throttling in one window, you can take the hack of
|
||||||
|
|
||||||
Forces the maximum disk space to be used by the disk cache, in bytes.
|
Forces the maximum disk space to be used by the disk cache, in bytes.
|
||||||
|
|
||||||
### --enable-api-filtering-logging
|
|
||||||
|
|
||||||
Enables caller stack logging for the following APIs (filtering events):
|
|
||||||
|
|
||||||
* `desktopCapturer.getSources()` / `desktop-capturer-get-sources`
|
|
||||||
|
|
||||||
### --enable-logging[=file]
|
### --enable-logging[=file]
|
||||||
|
|
||||||
Prints Chromium's logging to stderr (or a log file).
|
Prints Chromium's logging to stderr (or a log file).
|
||||||
|
|
|
@ -3,40 +3,49 @@
|
||||||
> Access information about media sources that can be used to capture audio and
|
> Access information about media sources that can be used to capture audio and
|
||||||
> video from the desktop using the [`navigator.mediaDevices.getUserMedia`] API.
|
> video from the desktop using the [`navigator.mediaDevices.getUserMedia`] API.
|
||||||
|
|
||||||
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
|
Process: [Main](../glossary.md#main-process)
|
||||||
|
|
||||||
The following example shows how to capture video from a desktop window whose
|
The following example shows how to capture video from a desktop window whose
|
||||||
title is `Electron`:
|
title is `Electron`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// In the renderer process.
|
// In the main process.
|
||||||
const { desktopCapturer } = require('electron')
|
const { desktopCapturer } = require('electron')
|
||||||
|
|
||||||
desktopCapturer.getSources({ types: ['window', 'screen'] }).then(async sources => {
|
desktopCapturer.getSources({ types: ['window', 'screen'] }).then(async sources => {
|
||||||
for (const source of sources) {
|
for (const source of sources) {
|
||||||
if (source.name === 'Electron') {
|
if (source.name === 'Electron') {
|
||||||
try {
|
mainWindow.webContents.send('SET_SOURCE', source.id)
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({
|
|
||||||
audio: false,
|
|
||||||
video: {
|
|
||||||
mandatory: {
|
|
||||||
chromeMediaSource: 'desktop',
|
|
||||||
chromeMediaSourceId: source.id,
|
|
||||||
minWidth: 1280,
|
|
||||||
maxWidth: 1280,
|
|
||||||
minHeight: 720,
|
|
||||||
maxHeight: 720
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
handleStream(stream)
|
|
||||||
} catch (e) {
|
|
||||||
handleError(e)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// In the preload script.
|
||||||
|
const { ipcRenderer } = require('electron')
|
||||||
|
|
||||||
|
ipcRenderer.on('SET_SOURCE', async (event, sourceId) => {
|
||||||
|
try {
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
audio: false,
|
||||||
|
video: {
|
||||||
|
mandatory: {
|
||||||
|
chromeMediaSource: 'desktop',
|
||||||
|
chromeMediaSourceId: sourceId,
|
||||||
|
minWidth: 1280,
|
||||||
|
maxWidth: 1280,
|
||||||
|
minHeight: 720,
|
||||||
|
maxHeight: 720
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
handleStream(stream)
|
||||||
|
} catch (e) {
|
||||||
|
handleError(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function handleStream (stream) {
|
function handleStream (stream) {
|
||||||
const video = document.querySelector('video')
|
const video = document.querySelector('video')
|
||||||
|
|
|
@ -856,15 +856,6 @@ Returns:
|
||||||
|
|
||||||
Emitted when the renderer process sends a synchronous message via `ipcRenderer.sendSync()`.
|
Emitted when the renderer process sends a synchronous message via `ipcRenderer.sendSync()`.
|
||||||
|
|
||||||
#### Event: 'desktop-capturer-get-sources'
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
* `event` Event
|
|
||||||
|
|
||||||
Emitted when `desktopCapturer.getSources()` is called in the renderer process.
|
|
||||||
Calling `event.preventDefault()` will make it return empty sources.
|
|
||||||
|
|
||||||
#### Event: 'preferred-size-changed'
|
#### Event: 'preferred-size-changed'
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
|
@ -142,7 +142,6 @@ auto_filenames = {
|
||||||
"lib/common/web-view-methods.ts",
|
"lib/common/web-view-methods.ts",
|
||||||
"lib/renderer/api/context-bridge.ts",
|
"lib/renderer/api/context-bridge.ts",
|
||||||
"lib/renderer/api/crash-reporter.ts",
|
"lib/renderer/api/crash-reporter.ts",
|
||||||
"lib/renderer/api/desktop-capturer.ts",
|
|
||||||
"lib/renderer/api/ipc-renderer.ts",
|
"lib/renderer/api/ipc-renderer.ts",
|
||||||
"lib/renderer/api/web-frame.ts",
|
"lib/renderer/api/web-frame.ts",
|
||||||
"lib/renderer/inspector.ts",
|
"lib/renderer/inspector.ts",
|
||||||
|
@ -224,7 +223,6 @@ auto_filenames = {
|
||||||
"lib/browser/api/web-contents.ts",
|
"lib/browser/api/web-contents.ts",
|
||||||
"lib/browser/api/web-frame-main.ts",
|
"lib/browser/api/web-frame-main.ts",
|
||||||
"lib/browser/default-menu.ts",
|
"lib/browser/default-menu.ts",
|
||||||
"lib/browser/desktop-capturer.ts",
|
|
||||||
"lib/browser/devtools.ts",
|
"lib/browser/devtools.ts",
|
||||||
"lib/browser/guest-view-manager.ts",
|
"lib/browser/guest-view-manager.ts",
|
||||||
"lib/browser/guest-window-manager.ts",
|
"lib/browser/guest-window-manager.ts",
|
||||||
|
@ -271,7 +269,6 @@ auto_filenames = {
|
||||||
"lib/common/webpack-provider.ts",
|
"lib/common/webpack-provider.ts",
|
||||||
"lib/renderer/api/context-bridge.ts",
|
"lib/renderer/api/context-bridge.ts",
|
||||||
"lib/renderer/api/crash-reporter.ts",
|
"lib/renderer/api/crash-reporter.ts",
|
||||||
"lib/renderer/api/desktop-capturer.ts",
|
|
||||||
"lib/renderer/api/exports/electron.ts",
|
"lib/renderer/api/exports/electron.ts",
|
||||||
"lib/renderer/api/ipc-renderer.ts",
|
"lib/renderer/api/ipc-renderer.ts",
|
||||||
"lib/renderer/api/module-list.ts",
|
"lib/renderer/api/module-list.ts",
|
||||||
|
@ -309,7 +306,6 @@ auto_filenames = {
|
||||||
"lib/common/webpack-provider.ts",
|
"lib/common/webpack-provider.ts",
|
||||||
"lib/renderer/api/context-bridge.ts",
|
"lib/renderer/api/context-bridge.ts",
|
||||||
"lib/renderer/api/crash-reporter.ts",
|
"lib/renderer/api/crash-reporter.ts",
|
||||||
"lib/renderer/api/desktop-capturer.ts",
|
|
||||||
"lib/renderer/api/exports/electron.ts",
|
"lib/renderer/api/exports/electron.ts",
|
||||||
"lib/renderer/api/ipc-renderer.ts",
|
"lib/renderer/api/ipc-renderer.ts",
|
||||||
"lib/renderer/api/module-list.ts",
|
"lib/renderer/api/module-list.ts",
|
||||||
|
|
|
@ -1,5 +1,72 @@
|
||||||
import { getSourcesImpl } from '@electron/internal/browser/desktop-capturer';
|
const { createDesktopCapturer } = process._linkedBinding('electron_browser_desktop_capturer');
|
||||||
|
|
||||||
export async function getSources (options: Electron.SourcesOptions) {
|
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
|
||||||
return getSourcesImpl(null, options);
|
|
||||||
|
let currentlyRunning: {
|
||||||
|
options: ElectronInternal.GetSourcesOptions;
|
||||||
|
getSources: Promise<ElectronInternal.GetSourcesResult[]>;
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
|
// |options.types| can't be empty and must be an array
|
||||||
|
function isValid (options: Electron.SourcesOptions) {
|
||||||
|
const types = options ? options.types : undefined;
|
||||||
|
return Array.isArray(types);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSources (args: Electron.SourcesOptions) {
|
||||||
|
if (!isValid(args)) throw new Error('Invalid options');
|
||||||
|
|
||||||
|
const captureWindow = args.types.includes('window');
|
||||||
|
const captureScreen = args.types.includes('screen');
|
||||||
|
|
||||||
|
const { thumbnailSize = { width: 150, height: 150 } } = args;
|
||||||
|
const { fetchWindowIcons = false } = args;
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
captureWindow,
|
||||||
|
captureScreen,
|
||||||
|
thumbnailSize,
|
||||||
|
fetchWindowIcons
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const running of currentlyRunning) {
|
||||||
|
if (deepEqual(running.options, options)) {
|
||||||
|
// If a request is currently running for the same options
|
||||||
|
// return that promise
|
||||||
|
return running.getSources;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSources = new Promise<ElectronInternal.GetSourcesResult[]>((resolve, reject) => {
|
||||||
|
let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer();
|
||||||
|
|
||||||
|
const stopRunning = () => {
|
||||||
|
if (capturer) {
|
||||||
|
delete capturer._onerror;
|
||||||
|
delete capturer._onfinished;
|
||||||
|
capturer = null;
|
||||||
|
}
|
||||||
|
// Remove from currentlyRunning once we resolve or reject
|
||||||
|
currentlyRunning = currentlyRunning.filter(running => running.options !== options);
|
||||||
|
};
|
||||||
|
|
||||||
|
capturer._onerror = (error: string) => {
|
||||||
|
stopRunning();
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
|
||||||
|
capturer._onfinished = (sources: Electron.DesktopCapturerSource[]) => {
|
||||||
|
stopRunning();
|
||||||
|
resolve(sources);
|
||||||
|
};
|
||||||
|
|
||||||
|
capturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons);
|
||||||
|
});
|
||||||
|
|
||||||
|
currentlyRunning.push({
|
||||||
|
options,
|
||||||
|
getSources
|
||||||
|
});
|
||||||
|
|
||||||
|
return getSources;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
const { createDesktopCapturer } = process._linkedBinding('electron_browser_desktop_capturer');
|
|
||||||
|
|
||||||
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
|
|
||||||
|
|
||||||
let currentlyRunning: {
|
|
||||||
options: ElectronInternal.GetSourcesOptions;
|
|
||||||
getSources: Promise<ElectronInternal.GetSourcesResult[]>;
|
|
||||||
}[] = [];
|
|
||||||
|
|
||||||
// |options.types| can't be empty and must be an array
|
|
||||||
function isValid (options: Electron.SourcesOptions) {
|
|
||||||
const types = options ? options.types : undefined;
|
|
||||||
return Array.isArray(types);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getSourcesImpl = (sender: Electron.WebContents | null, args: Electron.SourcesOptions) => {
|
|
||||||
if (!isValid(args)) throw new Error('Invalid options');
|
|
||||||
|
|
||||||
const captureWindow = args.types.includes('window');
|
|
||||||
const captureScreen = args.types.includes('screen');
|
|
||||||
|
|
||||||
const { thumbnailSize = { width: 150, height: 150 } } = args;
|
|
||||||
const { fetchWindowIcons = false } = args;
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
captureWindow,
|
|
||||||
captureScreen,
|
|
||||||
thumbnailSize,
|
|
||||||
fetchWindowIcons
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const running of currentlyRunning) {
|
|
||||||
if (deepEqual(running.options, options)) {
|
|
||||||
// If a request is currently running for the same options
|
|
||||||
// return that promise
|
|
||||||
return running.getSources;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSources = new Promise<ElectronInternal.GetSourcesResult[]>((resolve, reject) => {
|
|
||||||
let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer();
|
|
||||||
|
|
||||||
const stopRunning = () => {
|
|
||||||
if (capturer) {
|
|
||||||
delete capturer._onerror;
|
|
||||||
delete capturer._onfinished;
|
|
||||||
capturer = null;
|
|
||||||
}
|
|
||||||
// Remove from currentlyRunning once we resolve or reject
|
|
||||||
currentlyRunning = currentlyRunning.filter(running => running.options !== options);
|
|
||||||
if (sender) {
|
|
||||||
sender.removeListener('destroyed', stopRunning);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
capturer._onerror = (error: string) => {
|
|
||||||
stopRunning();
|
|
||||||
reject(error);
|
|
||||||
};
|
|
||||||
|
|
||||||
capturer._onfinished = (sources: Electron.DesktopCapturerSource[]) => {
|
|
||||||
stopRunning();
|
|
||||||
resolve(sources);
|
|
||||||
};
|
|
||||||
|
|
||||||
capturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons);
|
|
||||||
|
|
||||||
// If the WebContents is destroyed before receiving result, just remove the
|
|
||||||
// reference to emit and the capturer itself so that it never dispatches
|
|
||||||
// back to the renderer
|
|
||||||
if (sender) {
|
|
||||||
sender.once('destroyed', stopRunning);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
currentlyRunning.push({
|
|
||||||
options,
|
|
||||||
getSources
|
|
||||||
});
|
|
||||||
|
|
||||||
return getSources;
|
|
||||||
};
|
|
|
@ -1,30 +1,9 @@
|
||||||
import { app } from 'electron/main';
|
|
||||||
import type { WebContents } from 'electron/main';
|
|
||||||
import { clipboard } from 'electron/common';
|
import { clipboard } from 'electron/common';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||||
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||||
|
|
||||||
import type * as desktopCapturerModule from '@electron/internal/browser/desktop-capturer';
|
|
||||||
|
|
||||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
|
||||||
|
|
||||||
const emitCustomEvent = function (contents: WebContents, eventName: string, ...args: any[]) {
|
|
||||||
const event = eventBinding.createWithSender(contents);
|
|
||||||
|
|
||||||
app.emit(eventName, event, contents, ...args);
|
|
||||||
contents.emit(eventName, event, ...args);
|
|
||||||
|
|
||||||
return event;
|
|
||||||
};
|
|
||||||
|
|
||||||
const logStack = function (contents: WebContents, code: string, stack: string) {
|
|
||||||
if (stack) {
|
|
||||||
console.warn(`WebContents (${contents.id}): ${code}`, stack);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Implements window.close()
|
// Implements window.close()
|
||||||
ipcMainInternal.on(IPC_MESSAGES.BROWSER_WINDOW_CLOSE, function (event) {
|
ipcMainInternal.on(IPC_MESSAGES.BROWSER_WINDOW_CLOSE, function (event) {
|
||||||
const window = event.sender.getOwnerBrowserWindow();
|
const window = event.sender.getOwnerBrowserWindow();
|
||||||
|
@ -58,22 +37,6 @@ ipcMainUtils.handleSync(IPC_MESSAGES.BROWSER_CLIPBOARD_SYNC, function (event, me
|
||||||
return (clipboard as any)[method](...args);
|
return (clipboard as any)[method](...args);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
|
||||||
const desktopCapturer = require('@electron/internal/browser/desktop-capturer') as typeof desktopCapturerModule;
|
|
||||||
|
|
||||||
ipcMainInternal.handle(IPC_MESSAGES.DESKTOP_CAPTURER_GET_SOURCES, async function (event, options: Electron.SourcesOptions, stack: string) {
|
|
||||||
logStack(event.sender, 'desktopCapturer.getSources()', stack);
|
|
||||||
const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources');
|
|
||||||
|
|
||||||
if (customEvent.defaultPrevented) {
|
|
||||||
console.error('Blocked desktopCapturer.getSources()');
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return await desktopCapturer.getSourcesImpl(event.sender, options);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const getPreloadScript = async function (preloadPath: string) {
|
const getPreloadScript = async function (preloadPath: string) {
|
||||||
let preloadSrc = null;
|
let preloadSrc = null;
|
||||||
let preloadError = null;
|
let preloadError = null;
|
||||||
|
|
|
@ -28,6 +28,4 @@ export const enum IPC_MESSAGES {
|
||||||
INSPECTOR_CONFIRM = 'INSPECTOR_CONFIRM',
|
INSPECTOR_CONFIRM = 'INSPECTOR_CONFIRM',
|
||||||
INSPECTOR_CONTEXT_MENU = 'INSPECTOR_CONTEXT_MENU',
|
INSPECTOR_CONTEXT_MENU = 'INSPECTOR_CONTEXT_MENU',
|
||||||
INSPECTOR_SELECT_FILE = 'INSPECTOR_SELECT_FILE',
|
INSPECTOR_SELECT_FILE = 'INSPECTOR_SELECT_FILE',
|
||||||
|
|
||||||
DESKTOP_CAPTURER_GET_SOURCES = 'DESKTOP_CAPTURER_GET_SOURCES',
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
|
||||||
import deprecate from '@electron/internal/common/api/deprecate';
|
|
||||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
|
||||||
|
|
||||||
const { hasSwitch } = process._linkedBinding('electron_common_command_line');
|
|
||||||
|
|
||||||
const enableStacks = hasSwitch('enable-api-filtering-logging');
|
|
||||||
|
|
||||||
function getCurrentStack () {
|
|
||||||
const target = {};
|
|
||||||
if (enableStacks) {
|
|
||||||
Error.captureStackTrace(target, getCurrentStack);
|
|
||||||
}
|
|
||||||
return (target as any).stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
let warned = process.noDeprecation;
|
|
||||||
export async function getSources (options: Electron.SourcesOptions) {
|
|
||||||
if (!warned) {
|
|
||||||
deprecate.log('The use of \'desktopCapturer.getSources\' in the renderer process is deprecated and will be removed. See https://www.electronjs.org/docs/breaking-changes#removed-desktopcapturergetsources-in-the-renderer for more details.');
|
|
||||||
warned = true;
|
|
||||||
}
|
|
||||||
return await ipcRendererInternal.invoke(IPC_MESSAGES.DESKTOP_CAPTURER_GET_SOURCES, options, getCurrentStack());
|
|
||||||
}
|
|
|
@ -5,10 +5,3 @@ export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
||||||
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
|
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
|
||||||
{ name: 'webFrame', loader: () => require('./web-frame') }
|
{ name: 'webFrame', loader: () => require('./web-frame') }
|
||||||
];
|
];
|
||||||
|
|
||||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
|
||||||
rendererModuleList.push({
|
|
||||||
name: 'desktopCapturer',
|
|
||||||
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,10 +26,3 @@ export const moduleList: ElectronInternal.ModuleEntry[] = [
|
||||||
private: true
|
private: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
|
||||||
moduleList.push({
|
|
||||||
name: 'desktopCapturer',
|
|
||||||
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -592,8 +592,7 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||||
switches::kStandardSchemes, switches::kEnableSandbox,
|
switches::kStandardSchemes, switches::kEnableSandbox,
|
||||||
switches::kSecureSchemes, switches::kBypassCSPSchemes,
|
switches::kSecureSchemes, switches::kBypassCSPSchemes,
|
||||||
switches::kCORSSchemes, switches::kFetchSchemes,
|
switches::kCORSSchemes, switches::kFetchSchemes,
|
||||||
switches::kServiceWorkerSchemes, switches::kEnableApiFilteringLogging,
|
switches::kServiceWorkerSchemes, switches::kStreamingSchemes};
|
||||||
switches::kStreamingSchemes};
|
|
||||||
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
|
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
|
||||||
kCommonSwitchNames,
|
kCommonSwitchNames,
|
||||||
base::size(kCommonSwitchNames));
|
base::size(kCommonSwitchNames));
|
||||||
|
|
|
@ -241,8 +241,6 @@ const char kAppUserModelId[] = "app-user-model-id";
|
||||||
// The application path
|
// The application path
|
||||||
const char kAppPath[] = "app-path";
|
const char kAppPath[] = "app-path";
|
||||||
|
|
||||||
const char kEnableApiFilteringLogging[] = "enable-api-filtering-logging";
|
|
||||||
|
|
||||||
// The command line switch versions of the options.
|
// The command line switch versions of the options.
|
||||||
const char kScrollBounce[] = "scroll-bounce";
|
const char kScrollBounce[] = "scroll-bounce";
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,6 @@ extern const char kCORSSchemes[];
|
||||||
extern const char kStreamingSchemes[];
|
extern const char kStreamingSchemes[];
|
||||||
extern const char kAppUserModelId[];
|
extern const char kAppUserModelId[];
|
||||||
extern const char kAppPath[];
|
extern const char kAppPath[];
|
||||||
extern const char kEnableApiFilteringLogging[];
|
|
||||||
|
|
||||||
extern const char kScrollBounce[];
|
extern const char kScrollBounce[];
|
||||||
extern const char kNodeIntegrationInWorker[];
|
extern const char kNodeIntegrationInWorker[];
|
||||||
|
|
|
@ -12,8 +12,6 @@ import { closeWindow, closeAllWindows } from './window-helpers';
|
||||||
import { ifdescribe, ifit } from './spec-helpers';
|
import { ifdescribe, ifit } from './spec-helpers';
|
||||||
import split = require('split')
|
import split = require('split')
|
||||||
|
|
||||||
const features = process._linkedBinding('electron_common_features');
|
|
||||||
|
|
||||||
const fixturesPath = path.resolve(__dirname, '../spec/fixtures');
|
const fixturesPath = path.resolve(__dirname, '../spec/fixtures');
|
||||||
|
|
||||||
describe('electron module', () => {
|
describe('electron module', () => {
|
||||||
|
@ -462,25 +460,6 @@ describe('app module', () => {
|
||||||
expect(webContents).to.equal(w.webContents);
|
expect(webContents).to.equal(w.webContents);
|
||||||
expect(details.reason).to.be.oneOf(['crashed', 'abnormal-exit']);
|
expect(details.reason).to.be.oneOf(['crashed', 'abnormal-exit']);
|
||||||
});
|
});
|
||||||
|
|
||||||
ifdescribe(features.isDesktopCapturerEnabled())('desktopCapturer module filtering', () => {
|
|
||||||
it('should emit desktop-capturer-get-sources event when desktopCapturer.getSources() is invoked', async () => {
|
|
||||||
w = new BrowserWindow({
|
|
||||||
show: false,
|
|
||||||
webPreferences: {
|
|
||||||
nodeIntegration: true,
|
|
||||||
contextIsolation: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await w.loadURL('about:blank');
|
|
||||||
|
|
||||||
const promise = emittedOnce(app, 'desktop-capturer-get-sources');
|
|
||||||
w.webContents.executeJavaScript('require(\'electron\').desktopCapturer.getSources({ types: [\'screen\'] })');
|
|
||||||
|
|
||||||
const [, webContents] = await promise;
|
|
||||||
expect(webContents).to.equal(w.webContents);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('app.badgeCount', () => {
|
describe('app.badgeCount', () => {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { screen, BrowserWindow, SourcesOptions } from 'electron/main';
|
import { screen, desktopCapturer, BrowserWindow } from 'electron/main';
|
||||||
import { desktopCapturer } from 'electron/common';
|
|
||||||
import { emittedOnce } from './events-helpers';
|
import { emittedOnce } from './events-helpers';
|
||||||
import { ifdescribe, ifit } from './spec-helpers';
|
import { ifdescribe, ifit } from './spec-helpers';
|
||||||
import { closeAllWindows } from './window-helpers';
|
import { closeAllWindows } from './window-helpers';
|
||||||
|
@ -23,76 +22,55 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||||
|
|
||||||
after(closeAllWindows);
|
after(closeAllWindows);
|
||||||
|
|
||||||
const getSources: typeof desktopCapturer.getSources = (options: SourcesOptions) => {
|
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
||||||
return w.webContents.executeJavaScript(`
|
ifit(process.platform !== 'linux')('should return a non-empty array of sources', async () => {
|
||||||
require('electron').desktopCapturer.getSources(${JSON.stringify(options)}).then(m => JSON.parse(JSON.stringify(m)))
|
const sources = await desktopCapturer.getSources({ types: ['window', 'screen'] });
|
||||||
`);
|
expect(sources).to.be.an('array').that.is.not.empty();
|
||||||
};
|
});
|
||||||
|
|
||||||
const generateSpecs = (description: string, getSources: typeof desktopCapturer.getSources) => {
|
it('throws an error for invalid options', async () => {
|
||||||
describe(description, () => {
|
const promise = desktopCapturer.getSources(['window', 'screen'] as any);
|
||||||
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
await expect(promise).to.be.eventually.rejectedWith(Error, 'Invalid options');
|
||||||
ifit(process.platform !== 'linux')('should return a non-empty array of sources', async () => {
|
});
|
||||||
const sources = await getSources({ types: ['window', 'screen'] });
|
|
||||||
expect(sources).to.be.an('array').that.is.not.empty();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws an error for invalid options', async () => {
|
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
||||||
const promise = getSources(['window', 'screen'] as any);
|
ifit(process.platform !== 'linux')('does not throw an error when called more than once (regression)', async () => {
|
||||||
await expect(promise).to.be.eventually.rejectedWith(Error, 'Invalid options');
|
const sources1 = await desktopCapturer.getSources({ types: ['window', 'screen'] });
|
||||||
});
|
expect(sources1).to.be.an('array').that.is.not.empty();
|
||||||
|
|
||||||
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
const sources2 = await desktopCapturer.getSources({ types: ['window', 'screen'] });
|
||||||
ifit(process.platform !== 'linux')('does not throw an error when called more than once (regression)', async () => {
|
expect(sources2).to.be.an('array').that.is.not.empty();
|
||||||
const sources1 = await getSources({ types: ['window', 'screen'] });
|
});
|
||||||
expect(sources1).to.be.an('array').that.is.not.empty();
|
|
||||||
|
|
||||||
const sources2 = await getSources({ types: ['window', 'screen'] });
|
ifit(process.platform !== 'linux')('responds to subsequent calls of different options', async () => {
|
||||||
expect(sources2).to.be.an('array').that.is.not.empty();
|
const promise1 = desktopCapturer.getSources({ types: ['window'] });
|
||||||
});
|
await expect(promise1).to.eventually.be.fulfilled();
|
||||||
|
|
||||||
ifit(process.platform !== 'linux')('responds to subsequent calls of different options', async () => {
|
const promise2 = desktopCapturer.getSources({ types: ['screen'] });
|
||||||
const promise1 = getSources({ types: ['window'] });
|
await expect(promise2).to.eventually.be.fulfilled();
|
||||||
await expect(promise1).to.eventually.be.fulfilled();
|
});
|
||||||
|
|
||||||
const promise2 = getSources({ types: ['screen'] });
|
// Linux doesn't return any window sources.
|
||||||
await expect(promise2).to.eventually.be.fulfilled();
|
ifit(process.platform !== 'linux')('returns an empty display_id for window sources on Windows and Mac', async () => {
|
||||||
});
|
const w = new BrowserWindow({ width: 200, height: 200 });
|
||||||
|
await w.loadURL('about:blank');
|
||||||
|
|
||||||
// Linux doesn't return any window sources.
|
const sources = await desktopCapturer.getSources({ types: ['window'] });
|
||||||
ifit(process.platform !== 'linux')('returns an empty display_id for window sources on Windows and Mac', async () => {
|
w.destroy();
|
||||||
const w = new BrowserWindow({ width: 200, height: 200 });
|
expect(sources).to.be.an('array').that.is.not.empty();
|
||||||
await w.loadURL('about:blank');
|
for (const { display_id: displayId } of sources) {
|
||||||
|
expect(displayId).to.be.a('string').and.be.empty();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const sources = await getSources({ types: ['window'] });
|
ifit(process.platform !== 'linux')('returns display_ids matching the Screen API on Windows and Mac', async () => {
|
||||||
w.destroy();
|
const displays = screen.getAllDisplays();
|
||||||
expect(sources).to.be.an('array').that.is.not.empty();
|
const sources = await desktopCapturer.getSources({ types: ['screen'] });
|
||||||
for (const { display_id: displayId } of sources) {
|
expect(sources).to.be.an('array').of.length(displays.length);
|
||||||
expect(displayId).to.be.a('string').and.be.empty();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ifit(process.platform !== 'linux')('returns display_ids matching the Screen API on Windows and Mac', async () => {
|
for (let i = 0; i < sources.length; i++) {
|
||||||
const displays = screen.getAllDisplays();
|
expect(sources[i].display_id).to.equal(displays[i].id.toString());
|
||||||
const sources = await getSources({ types: ['screen'] });
|
}
|
||||||
expect(sources).to.be.an('array').of.length(displays.length);
|
|
||||||
|
|
||||||
for (let i = 0; i < sources.length; i++) {
|
|
||||||
expect(sources[i].display_id).to.equal(displays[i].id.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
generateSpecs('in renderer process', getSources);
|
|
||||||
generateSpecs('in main process', desktopCapturer.getSources);
|
|
||||||
|
|
||||||
ifit(process.platform !== 'linux')('returns an empty source list if blocked by the main process', async () => {
|
|
||||||
w.webContents.once('desktop-capturer-get-sources', (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
const sources = await getSources({ types: ['screen'] });
|
|
||||||
expect(sources).to.be.empty();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('disabling thumbnail should return empty images', async () => {
|
it('disabling thumbnail should return empty images', async () => {
|
||||||
|
@ -101,14 +79,10 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||||
w2.show();
|
w2.show();
|
||||||
await wShown;
|
await wShown;
|
||||||
|
|
||||||
const isEmpties: boolean[] = await w.webContents.executeJavaScript(`
|
const isEmpties: boolean[] = (await desktopCapturer.getSources({
|
||||||
require('electron').desktopCapturer.getSources({
|
types: ['window', 'screen'],
|
||||||
types: ['window', 'screen'],
|
thumbnailSize: { width: 0, height: 0 }
|
||||||
thumbnailSize: { width: 0, height: 0 }
|
})).map(s => s.thumbnail.constructor.name === 'NativeImage' && s.thumbnail.isEmpty());
|
||||||
}).then((sources) => {
|
|
||||||
return sources.map(s => s.thumbnail.constructor.name === 'NativeImage' && s.thumbnail.isEmpty())
|
|
||||||
})
|
|
||||||
`);
|
|
||||||
|
|
||||||
w2.destroy();
|
w2.destroy();
|
||||||
expect(isEmpties).to.be.an('array').that.is.not.empty();
|
expect(isEmpties).to.be.an('array').that.is.not.empty();
|
||||||
|
@ -125,7 +99,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||||
await wFocused;
|
await wFocused;
|
||||||
|
|
||||||
const mediaSourceId = w.getMediaSourceId();
|
const mediaSourceId = w.getMediaSourceId();
|
||||||
const sources = await getSources({
|
const sources = await desktopCapturer.getSources({
|
||||||
types: ['window'],
|
types: ['window'],
|
||||||
thumbnailSize: { width: 0, height: 0 }
|
thumbnailSize: { width: 0, height: 0 }
|
||||||
});
|
});
|
||||||
|
@ -161,7 +135,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||||
const ids = mediaSourceId.split(':');
|
const ids = mediaSourceId.split(':');
|
||||||
expect(ids[1]).to.not.equal(ids[2]);
|
expect(ids[1]).to.not.equal(ids[2]);
|
||||||
|
|
||||||
const sources = await getSources({
|
const sources = await desktopCapturer.getSources({
|
||||||
types: ['window'],
|
types: ['window'],
|
||||||
thumbnailSize: { width: 0, height: 0 }
|
thumbnailSize: { width: 0, height: 0 }
|
||||||
});
|
});
|
||||||
|
@ -206,7 +180,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||||
|
|
||||||
// DesktopCapturer.getSources() returns sources sorted from foreground to
|
// DesktopCapturer.getSources() returns sources sorted from foreground to
|
||||||
// background, i.e. top to bottom.
|
// background, i.e. top to bottom.
|
||||||
let sources = await getSources({
|
let sources = await desktopCapturer.getSources({
|
||||||
types: ['window'],
|
types: ['window'],
|
||||||
thumbnailSize: { width: 0, height: 0 }
|
thumbnailSize: { width: 0, height: 0 }
|
||||||
});
|
});
|
||||||
|
@ -252,7 +226,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
sources = await getSources({
|
sources = await desktopCapturer.getSources({
|
||||||
types: ['window'],
|
types: ['window'],
|
||||||
thumbnailSize: { width: 0, height: 0 }
|
thumbnailSize: { width: 0, height: 0 }
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue