diff --git a/docs/api/desktop-capturer.md b/docs/api/desktop-capturer.md index 5465ee7313f..d4e51c123bc 100644 --- a/docs/api/desktop-capturer.md +++ b/docs/api/desktop-capturer.md @@ -12,8 +12,7 @@ title is `Electron`: // In the renderer process. const { desktopCapturer } = require('electron') -desktopCapturer.getSources({ types: ['window', 'screen'] }, async (error, sources) => { - if (error) throw error +desktopCapturer.getSources({ types: ['window', 'screen'] }).then(async sources => { for (const source of sources) { if (source.name === 'Electron') { try { @@ -99,3 +98,20 @@ objects, each `DesktopCapturerSource` represents a screen or an individual windo captured. [`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia + +**[Deprecated Soon](promisification.md)** + +### `desktopCapturer.getSources(options)` + +* `options` Object + * `types` String[] - An array of Strings that lists the types of desktop sources + to be captured, available types are `screen` and `window`. + * `thumbnailSize` [Size](structures/size.md) (optional) - The size that the media source thumbnail + should be scaled to. Default is `150` x `150`. + * `fetchWindowIcons` Boolean (optional) - Set to true to enable fetching window icons. The default + value is false. When false the appIcon property of the sources return null. Same if a source has + the type screen. + +Returns `Promise` - Resolves with an array of [`DesktopCapturerSource`](structures/desktop-capturer-source.md) objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured. + +[`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia diff --git a/docs/api/promisification.md b/docs/api/promisification.md index 55717d55a65..2ac78beb2f1 100644 --- a/docs/api/promisification.md +++ b/docs/api/promisification.md @@ -23,7 +23,6 @@ When a majority of affected functions are migrated, this flag will be enabled by - [cookies.remove(url, name, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#remove) - [cookies.flushStore(callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#flushStore) - [debugger.sendCommand(method[, commandParams, callback])](https://github.com/electron/electron/blob/master/docs/api/debugger.md#sendCommand) -- [desktopCapturer.getSources(options, callback)](https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md#getSources) - [dialog.showOpenDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showOpenDialog) - [dialog.showSaveDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showSaveDialog) - [dialog.showMessageBox([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showMessageBox) @@ -63,3 +62,4 @@ When a majority of affected functions are migrated, this flag will be enabled by - [app.getFileIcon(path[, options], callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#getFileIcon) - [shell.openExternal(url[, options, callback])](https://github.com/electron/electron/blob/master/docs/api/shell.md#openExternal) - [protocol.isProtocolHandled(scheme, callback)](https://github.com/electron/electron/blob/master/docs/api/protocol.md#isProtocolHandled) +- [desktopCapturer.getSources(options, callback)](https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md#getSources) \ No newline at end of file diff --git a/lib/renderer/api/desktop-capturer.js b/lib/renderer/api/desktop-capturer.js index da231d45722..d33a355c477 100644 --- a/lib/renderer/api/desktop-capturer.js +++ b/lib/renderer/api/desktop-capturer.js @@ -1,6 +1,6 @@ 'use strict' -const { nativeImage } = require('electron') +const { nativeImage, deprecate } = require('electron') const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') const includes = [].includes @@ -27,28 +27,33 @@ function mapSources (sources) { })) } -exports.getSources = function (options, callback) { - if (!isValid(options)) return callback(new Error('Invalid options')) - const captureWindow = includes.call(options.types, 'window') - const captureScreen = includes.call(options.types, 'screen') +const getSources = (options) => { + return new Promise((resolve, reject) => { + if (!isValid(options)) throw new Error('Invalid options') - if (options.thumbnailSize == null) { - options.thumbnailSize = { - width: 150, - height: 150 - } - } - if (options.fetchWindowIcons == null) { - options.fetchWindowIcons = false - } + const captureWindow = includes.call(options.types, 'window') + const captureScreen = includes.call(options.types, 'screen') - const id = incrementId() - ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, options.fetchWindowIcons, id) - return ipcRenderer.once(`ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`, (event, sources) => { - try { - callback(null, mapSources(sources)) - } catch (error) { - callback(error) + if (options.thumbnailSize == null) { + options.thumbnailSize = { + width: 150, + height: 150 + } } + if (options.fetchWindowIcons == null) { + options.fetchWindowIcons = false + } + + const id = incrementId() + ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, options.fetchWindowIcons, id) + return ipcRenderer.once(`ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`, (event, sources) => { + try { + resolve(mapSources(sources)) + } catch (error) { + reject(error) + } + }) }) } + +exports.getSources = deprecate.promisify(getSources, 1) diff --git a/spec/api-desktop-capturer-spec.js b/spec/api-desktop-capturer-spec.js index b0137c3b45e..9d888d646c8 100644 --- a/spec/api-desktop-capturer-spec.js +++ b/spec/api-desktop-capturer-spec.js @@ -1,11 +1,13 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') +const chaiAsPromised = require('chai-as-promised') const { desktopCapturer, ipcRenderer, remote } = require('electron') const { screen } = remote const features = process.atomBinding('features') const { expect } = chai chai.use(dirtyChai) +chai.use(chaiAsPromised) const isCI = remote.getGlobal('isCi') @@ -22,37 +24,43 @@ describe('desktopCapturer', () => { } }) - it('should return a non-empty array of sources', done => { - desktopCapturer.getSources({ - types: ['window', 'screen'] - }, (error, sources) => { - expect(error).to.be.null() + it('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() + }) + + // TODO(codebytere): remove when promisification is complete + it('should return a non-empty array of sources (callback)', (done) => { + desktopCapturer.getSources({ types: ['window', 'screen'] }, (err, sources) => { expect(sources).to.be.an('array').that.is.not.empty() + expect(err).to.be.null() done() }) }) - it('throws an error for invalid options', done => { - desktopCapturer.getSources(['window', 'screen'], error => { - expect(error.message).to.equal('Invalid options') - done() - }) + it('throws an error for invalid options', async () => { + const promise = desktopCapturer.getSources(['window', 'screen']) + expect(promise).to.be.eventually.rejectedWith(Error, 'Invalid options') }) - it('does not throw an error when called more than once (regression)', (done) => { - let callCount = 0 - const callback = (error, sources) => { - callCount++ - expect(error).to.be.null() - expect(sources).to.be.an('array').that.is.not.empty() - if (callCount === 2) done() - } + it('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() - desktopCapturer.getSources({ types: ['window', 'screen'] }, callback) - desktopCapturer.getSources({ types: ['window', 'screen'] }, callback) + const sources2 = await desktopCapturer.getSources({ types: ['window', 'screen'] }) + expect(sources2).to.be.an('array').that.is.not.empty() }) - it('responds to subsequent calls of different options', done => { + it('responds to subsequent calls of different options', async () => { + const promise1 = desktopCapturer.getSources({ types: ['window'] }) + expect(promise1).to.not.eventually.be.rejected() + + const promise2 = desktopCapturer.getSources({ types: ['screen'] }) + expect(promise2).to.not.eventually.be.rejected() + }) + + // TODO(codebytere): remove when promisification is complete + it('responds to subsequent calls of different options (callback)', (done) => { let callCount = 0 const callback = (error, sources) => { callCount++ @@ -64,49 +72,36 @@ describe('desktopCapturer', () => { desktopCapturer.getSources({ types: ['screen'] }, callback) }) - it('returns an empty display_id for window sources on Windows and Mac', done => { + it('returns an empty display_id for window sources on Windows and Mac', async () => { // Linux doesn't return any window sources. - if (process.platform !== 'win32' && process.platform !== 'darwin') { - return done() - } + if (process.platform !== 'win32' && process.platform !== 'darwin') return const { BrowserWindow } = remote const w = new BrowserWindow({ width: 200, height: 200 }) - desktopCapturer.getSources({ types: ['window'] }, (error, sources) => { - w.destroy() - expect(error).to.be.null() - 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() - } - done() - }) + 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() + } }) - it('returns display_ids matching the Screen API on Windows and Mac', done => { - if (process.platform !== 'win32' && process.platform !== 'darwin') { - return done() - } + it('returns display_ids matching the Screen API on Windows and Mac', async () => { + if (process.platform !== 'win32' && process.platform !== 'darwin') return const displays = screen.getAllDisplays() - desktopCapturer.getSources({ types: ['screen'] }, (error, sources) => { - expect(error).to.be.null() - expect(sources).to.be.an('array').of.length(displays.length) + const sources = await desktopCapturer.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()) - } - done() - }) + for (let i = 0; i < sources.length; i++) { + expect(sources[i].display_id).to.equal(displays[i].id.toString()) + } - it('returns empty sources when blocked', done => { + it('returns empty sources when blocked', async () => { ipcRenderer.send('handle-next-desktop-capturer-get-sources') - desktopCapturer.getSources({ types: ['screen'] }, (error, sources) => { - expect(error).to.be.null() - expect(sources).to.be.empty() - done() - }) + const sources = await desktopCapturer.getSources({ types: ['screen'] }) + expect(sources).to.be.empty() }) }) })