diff --git a/atom/browser/api/atom_api_desktop_capturer.h b/atom/browser/api/atom_api_desktop_capturer.h index 0cb22961c3f3c..962a1cc699a40 100644 --- a/atom/browser/api/atom_api_desktop_capturer.h +++ b/atom/browser/api/atom_api_desktop_capturer.h @@ -9,7 +9,7 @@ #include #include -#include "atom/browser/api/event_emitter.h" +#include "atom/browser/api/trackable_object.h" #include "chrome/browser/media/webrtc/desktop_media_list_observer.h" #include "chrome/browser/media/webrtc/native_desktop_media_list.h" #include "native_mate/handle.h" @@ -18,7 +18,7 @@ namespace atom { namespace api { -class DesktopCapturer : public mate::EventEmitter, +class DesktopCapturer : public mate::TrackableObject, public DesktopMediaListObserver { public: struct Source { diff --git a/docs/api/app.md b/docs/api/app.md index 9d5183e1883a5..5a6a1c45c5a71 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -401,6 +401,16 @@ 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. + ### Event: 'remote-require' Returns: diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 19573c20ee271..aa77ba8ba02c4 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -663,6 +663,15 @@ Returns: Emitted when the associated window logs a console message. Will not be emitted for windows with *offscreen rendering* enabled. +#### 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: 'remote-require' Returns: diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index 3067c2c7fb656..9acd6330c1bbf 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -377,6 +377,10 @@ WebContents.prototype._init = function () { }) }) + this.on('desktop-capturer-get-sources', (event, ...args) => { + app.emit('desktop-capturer-get-sources', event, this, ...args) + }) + this.on('remote-require', (event, ...args) => { app.emit('remote-require', event, this, ...args) }) diff --git a/lib/browser/desktop-capturer.js b/lib/browser/desktop-capturer.js index c2cdd73ef95cd..fa10803079d21 100644 --- a/lib/browser/desktop-capturer.js +++ b/lib/browser/desktop-capturer.js @@ -1,7 +1,9 @@ 'use strict' const ipcMain = require('@electron/internal/browser/ipc-main-internal') + const { desktopCapturer } = process.atomBinding('desktop_capturer') +const eventBinding = process.atomBinding('event') const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b) @@ -12,6 +14,14 @@ const electronSources = 'ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES' const capturerResult = (id) => `ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}` ipcMain.on(electronSources, (event, captureWindow, captureScreen, thumbnailSize, fetchWindowIcons, id) => { + const customEvent = eventBinding.createWithSender(event.sender) + event.sender.emit('desktop-capturer-get-sources', customEvent) + + if (customEvent.defaultPrevented) { + event.sender._sendInternal(capturerResult(id), []) + return + } + const request = { id, options: { diff --git a/lib/renderer/api/desktop-capturer.js b/lib/renderer/api/desktop-capturer.js index 74f9e3b3e2617..da231d45722e0 100644 --- a/lib/renderer/api/desktop-capturer.js +++ b/lib/renderer/api/desktop-capturer.js @@ -17,6 +17,16 @@ function isValid (options) { return Array.isArray(types) } +function mapSources (sources) { + return sources.map(source => ({ + id: source.id, + name: source.name, + thumbnail: nativeImage.createFromDataURL(source.thumbnail), + display_id: source.display_id, + appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null + })) +} + exports.getSources = function (options, callback) { if (!isValid(options)) return callback(new Error('Invalid options')) const captureWindow = includes.call(options.types, 'window') @@ -35,18 +45,10 @@ exports.getSources = function (options, callback) { 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) => { - callback(null, (() => { - const results = [] - sources.forEach(source => { - results.push({ - id: source.id, - name: source.name, - thumbnail: nativeImage.createFromDataURL(source.thumbnail), - display_id: source.display_id, - appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null - }) - }) - return results - })()) + try { + callback(null, mapSources(sources)) + } catch (error) { + callback(error) + } }) } diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 65821667677cb..af4e48740257f 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -370,6 +370,16 @@ describe('app module', () => { w = new BrowserWindow({ show: false }) }) + it('should emit desktop-capturer-get-sources event when desktopCapturer.getSources() is invoked', (done) => { + app.once('desktop-capturer-get-sources', (event, webContents) => { + expect(webContents).to.equal(w.webContents) + done() + }) + w = new BrowserWindow({ show: false }) + w.loadURL('about:blank') + w.webContents.executeJavaScript(`require('electron').desktopCapturer.getSources({ types: ['screen'] }, () => {})`) + }) + it('should emit remote-require event when remote.require() is invoked', (done) => { app.once('remote-require', (event, webContents, moduleName) => { expect(webContents).to.equal(w.webContents) diff --git a/spec/api-desktop-capturer-spec.js b/spec/api-desktop-capturer-spec.js index a74a2ac2aacdf..b0137c3b45ef4 100644 --- a/spec/api-desktop-capturer-spec.js +++ b/spec/api-desktop-capturer-spec.js @@ -1,6 +1,6 @@ const chai = require('chai') const dirtyChai = require('dirty-chai') -const { desktopCapturer, remote } = require('electron') +const { desktopCapturer, ipcRenderer, remote } = require('electron') const { screen } = remote const features = process.atomBinding('features') @@ -99,5 +99,14 @@ describe('desktopCapturer', () => { } done() }) + + it('returns empty sources when blocked', done => { + 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() + }) + }) }) }) diff --git a/spec/static/main.js b/spec/static/main.js index 55bac6bab6ab6..a9d8fc20488a1 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -233,6 +233,12 @@ app.on('ready', function () { }) }) +ipcMain.on('handle-next-desktop-capturer-get-sources', function (event) { + event.sender.once('desktop-capturer-get-sources', (event) => { + event.preventDefault() + }) +}) + ipcMain.on('handle-next-remote-require', function (event, modulesMap = {}) { event.sender.once('remote-require', (event, moduleName) => { event.preventDefault()