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,
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
### --enable-api-filtering-logging
|
||||
|
||||
Enables caller stack logging for the following APIs (filtering events):
|
||||
|
||||
* `desktopCapturer.getSources()` / `desktop-capturer-get-sources`
|
||||
|
||||
### --enable-logging[=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
|
||||
> 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
|
||||
title is `Electron`:
|
||||
|
||||
```javascript
|
||||
// In the renderer process.
|
||||
// In the main process.
|
||||
const { desktopCapturer } = require('electron')
|
||||
|
||||
desktopCapturer.getSources({ types: ['window', 'screen'] }).then(async sources => {
|
||||
for (const source of sources) {
|
||||
if (source.name === 'Electron') {
|
||||
try {
|
||||
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)
|
||||
}
|
||||
mainWindow.webContents.send('SET_SOURCE', source.id)
|
||||
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) {
|
||||
const video = document.querySelector('video')
|
||||
|
|
|
@ -856,15 +856,6 @@ Returns:
|
|||
|
||||
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'
|
||||
|
||||
Returns:
|
||||
|
|
|
@ -142,7 +142,6 @@ auto_filenames = {
|
|||
"lib/common/web-view-methods.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
|
@ -224,7 +223,6 @@ auto_filenames = {
|
|||
"lib/browser/api/web-contents.ts",
|
||||
"lib/browser/api/web-frame-main.ts",
|
||||
"lib/browser/default-menu.ts",
|
||||
"lib/browser/desktop-capturer.ts",
|
||||
"lib/browser/devtools.ts",
|
||||
"lib/browser/guest-view-manager.ts",
|
||||
"lib/browser/guest-window-manager.ts",
|
||||
|
@ -271,7 +269,6 @@ auto_filenames = {
|
|||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
|
@ -309,7 +306,6 @@ auto_filenames = {
|
|||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.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) {
|
||||
return getSourcesImpl(null, options);
|
||||
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 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 * as fs from 'fs';
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
||||
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()
|
||||
ipcMainInternal.on(IPC_MESSAGES.BROWSER_WINDOW_CLOSE, function (event) {
|
||||
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);
|
||||
});
|
||||
|
||||
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) {
|
||||
let preloadSrc = null;
|
||||
let preloadError = null;
|
||||
|
|
|
@ -28,6 +28,4 @@ export const enum IPC_MESSAGES {
|
|||
INSPECTOR_CONFIRM = 'INSPECTOR_CONFIRM',
|
||||
INSPECTOR_CONTEXT_MENU = 'INSPECTOR_CONTEXT_MENU',
|
||||
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: '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
|
||||
}
|
||||
];
|
||||
|
||||
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::kSecureSchemes, switches::kBypassCSPSchemes,
|
||||
switches::kCORSSchemes, switches::kFetchSchemes,
|
||||
switches::kServiceWorkerSchemes, switches::kEnableApiFilteringLogging,
|
||||
switches::kStreamingSchemes};
|
||||
switches::kServiceWorkerSchemes, switches::kStreamingSchemes};
|
||||
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
|
||||
kCommonSwitchNames,
|
||||
base::size(kCommonSwitchNames));
|
||||
|
|
|
@ -241,8 +241,6 @@ const char kAppUserModelId[] = "app-user-model-id";
|
|||
// The application path
|
||||
const char kAppPath[] = "app-path";
|
||||
|
||||
const char kEnableApiFilteringLogging[] = "enable-api-filtering-logging";
|
||||
|
||||
// The command line switch versions of the options.
|
||||
const char kScrollBounce[] = "scroll-bounce";
|
||||
|
||||
|
|
|
@ -118,7 +118,6 @@ extern const char kCORSSchemes[];
|
|||
extern const char kStreamingSchemes[];
|
||||
extern const char kAppUserModelId[];
|
||||
extern const char kAppPath[];
|
||||
extern const char kEnableApiFilteringLogging[];
|
||||
|
||||
extern const char kScrollBounce[];
|
||||
extern const char kNodeIntegrationInWorker[];
|
||||
|
|
|
@ -12,8 +12,6 @@ import { closeWindow, closeAllWindows } from './window-helpers';
|
|||
import { ifdescribe, ifit } from './spec-helpers';
|
||||
import split = require('split')
|
||||
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, '../spec/fixtures');
|
||||
|
||||
describe('electron module', () => {
|
||||
|
@ -462,25 +460,6 @@ describe('app module', () => {
|
|||
expect(webContents).to.equal(w.webContents);
|
||||
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', () => {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { expect } from 'chai';
|
||||
import { screen, BrowserWindow, SourcesOptions } from 'electron/main';
|
||||
import { desktopCapturer } from 'electron/common';
|
||||
import { screen, desktopCapturer, BrowserWindow } from 'electron/main';
|
||||
import { emittedOnce } from './events-helpers';
|
||||
import { ifdescribe, ifit } from './spec-helpers';
|
||||
import { closeAllWindows } from './window-helpers';
|
||||
|
@ -23,76 +22,55 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
|||
|
||||
after(closeAllWindows);
|
||||
|
||||
const getSources: typeof desktopCapturer.getSources = (options: SourcesOptions) => {
|
||||
return w.webContents.executeJavaScript(`
|
||||
require('electron').desktopCapturer.getSources(${JSON.stringify(options)}).then(m => JSON.parse(JSON.stringify(m)))
|
||||
`);
|
||||
};
|
||||
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
||||
ifit(process.platform !== 'linux')('should return a non-empty array of sources', async () => {
|
||||
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) => {
|
||||
describe(description, () => {
|
||||
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
||||
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 () => {
|
||||
const promise = desktopCapturer.getSources(['window', 'screen'] as any);
|
||||
await expect(promise).to.be.eventually.rejectedWith(Error, 'Invalid options');
|
||||
});
|
||||
|
||||
it('throws an error for invalid options', async () => {
|
||||
const promise = getSources(['window', 'screen'] as any);
|
||||
await expect(promise).to.be.eventually.rejectedWith(Error, 'Invalid options');
|
||||
});
|
||||
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
||||
ifit(process.platform !== 'linux')('does not throw an error when called more than once (regression)', async () => {
|
||||
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.
|
||||
ifit(process.platform !== 'linux')('does not throw an error when called more than once (regression)', async () => {
|
||||
const sources1 = await getSources({ types: ['window', 'screen'] });
|
||||
expect(sources1).to.be.an('array').that.is.not.empty();
|
||||
const sources2 = await desktopCapturer.getSources({ types: ['window', 'screen'] });
|
||||
expect(sources2).to.be.an('array').that.is.not.empty();
|
||||
});
|
||||
|
||||
const sources2 = await getSources({ types: ['window', 'screen'] });
|
||||
expect(sources2).to.be.an('array').that.is.not.empty();
|
||||
});
|
||||
ifit(process.platform !== 'linux')('responds to subsequent calls of different options', async () => {
|
||||
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 promise1 = getSources({ types: ['window'] });
|
||||
await expect(promise1).to.eventually.be.fulfilled();
|
||||
const promise2 = desktopCapturer.getSources({ types: ['screen'] });
|
||||
await expect(promise2).to.eventually.be.fulfilled();
|
||||
});
|
||||
|
||||
const promise2 = getSources({ types: ['screen'] });
|
||||
await expect(promise2).to.eventually.be.fulfilled();
|
||||
});
|
||||
// Linux doesn't return any window sources.
|
||||
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.
|
||||
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');
|
||||
const sources = await desktopCapturer.getSources({ types: ['window'] });
|
||||
w.destroy();
|
||||
expect(sources).to.be.an('array').that.is.not.empty();
|
||||
for (const { display_id: displayId } of sources) {
|
||||
expect(displayId).to.be.a('string').and.be.empty();
|
||||
}
|
||||
});
|
||||
|
||||
const sources = await getSources({ types: ['window'] });
|
||||
w.destroy();
|
||||
expect(sources).to.be.an('array').that.is.not.empty();
|
||||
for (const { display_id: displayId } of sources) {
|
||||
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 () => {
|
||||
const displays = screen.getAllDisplays();
|
||||
const sources = await desktopCapturer.getSources({ types: ['screen'] });
|
||||
expect(sources).to.be.an('array').of.length(displays.length);
|
||||
|
||||
ifit(process.platform !== 'linux')('returns display_ids matching the Screen API on Windows and Mac', async () => {
|
||||
const displays = screen.getAllDisplays();
|
||||
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();
|
||||
for (let i = 0; i < sources.length; i++) {
|
||||
expect(sources[i].display_id).to.equal(displays[i].id.toString());
|
||||
}
|
||||
});
|
||||
|
||||
it('disabling thumbnail should return empty images', async () => {
|
||||
|
@ -101,14 +79,10 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
|||
w2.show();
|
||||
await wShown;
|
||||
|
||||
const isEmpties: boolean[] = await w.webContents.executeJavaScript(`
|
||||
require('electron').desktopCapturer.getSources({
|
||||
types: ['window', 'screen'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
}).then((sources) => {
|
||||
return sources.map(s => s.thumbnail.constructor.name === 'NativeImage' && s.thumbnail.isEmpty())
|
||||
})
|
||||
`);
|
||||
const isEmpties: boolean[] = (await desktopCapturer.getSources({
|
||||
types: ['window', 'screen'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
})).map(s => s.thumbnail.constructor.name === 'NativeImage' && s.thumbnail.isEmpty());
|
||||
|
||||
w2.destroy();
|
||||
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;
|
||||
|
||||
const mediaSourceId = w.getMediaSourceId();
|
||||
const sources = await getSources({
|
||||
const sources = await desktopCapturer.getSources({
|
||||
types: ['window'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
});
|
||||
|
@ -161,7 +135,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
|||
const ids = mediaSourceId.split(':');
|
||||
expect(ids[1]).to.not.equal(ids[2]);
|
||||
|
||||
const sources = await getSources({
|
||||
const sources = await desktopCapturer.getSources({
|
||||
types: ['window'],
|
||||
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
|
||||
// background, i.e. top to bottom.
|
||||
let sources = await getSources({
|
||||
let sources = await desktopCapturer.getSources({
|
||||
types: ['window'],
|
||||
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'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue