From 7127ec153a1422a078768ff6691d612e8878fd76 Mon Sep 17 00:00:00 2001 From: Claudio Costa Date: Thu, 16 Feb 2023 10:20:50 -0600 Subject: [PATCH] Send error to global widget if missing screen sharing permissions (#2554) --- src/common/communication.ts | 1 + src/main/preload/callsWidget.js | 11 ++++ src/main/windows/windowManager.test.js | 84 +++++++++++++++++++++++++- src/main/windows/windowManager.ts | 29 ++++++++- 4 files changed, 121 insertions(+), 4 deletions(-) diff --git a/src/common/communication.ts b/src/common/communication.ts index d918d62f..b247fd46 100644 --- a/src/common/communication.ts +++ b/src/common/communication.ts @@ -137,6 +137,7 @@ export const CALLS_WIDGET_SHARE_SCREEN = 'calls-widget-share-screen'; export const CALLS_WIDGET_CHANNEL_LINK_CLICK = 'calls-widget-channel-link-click'; export const CALLS_JOINED_CALL = 'calls-joined-call'; export const CALLS_POPOUT_FOCUS = 'calls-popout-focus'; +export const CALLS_ERROR = 'calls-error'; export const REQUEST_CLEAR_DOWNLOADS_DROPDOWN = 'request-clear-downloads-dropdown'; export const CLOSE_DOWNLOADS_DROPDOWN = 'close-downloads-dropdown'; diff --git a/src/main/preload/callsWidget.js b/src/main/preload/callsWidget.js index c9549f54..1bc37ed3 100644 --- a/src/main/preload/callsWidget.js +++ b/src/main/preload/callsWidget.js @@ -12,6 +12,7 @@ import { CALLS_WIDGET_RESIZE, CALLS_WIDGET_SHARE_SCREEN, CALLS_WIDGET_CHANNEL_LINK_CLICK, + CALLS_ERROR, DESKTOP_SOURCES_RESULT, DESKTOP_SOURCES_MODAL_REQUEST, DISPATCH_GET_DESKTOP_SOURCES, @@ -75,3 +76,13 @@ ipcRenderer.on(CALLS_WIDGET_SHARE_SCREEN, (event, message) => { window.location.origin, ); }); + +ipcRenderer.on(CALLS_ERROR, (event, message) => { + window.postMessage( + { + type: CALLS_ERROR, + message, + }, + window.location.origin, + ); +}); diff --git a/src/main/windows/windowManager.test.js b/src/main/windows/windowManager.test.js index 1e252fe4..01786fe6 100644 --- a/src/main/windows/windowManager.test.js +++ b/src/main/windows/windowManager.test.js @@ -4,7 +4,7 @@ /* eslint-disable max-lines */ 'use strict'; -import {app, systemPreferences} from 'electron'; +import {app, systemPreferences, desktopCapturer} from 'electron'; import Config from 'common/config'; import {getTabViewName, TAB_MESSAGING} from 'common/tabs/TabView'; @@ -39,6 +39,10 @@ jest.mock('electron', () => ({ }, systemPreferences: { getUserDefault: jest.fn(), + getMediaAccessStatus: jest.fn(() => 'granted'), + }, + desktopCapturer: { + getSources: jest.fn(), }, })); @@ -1042,6 +1046,84 @@ describe('main/windows/windowManager', () => { }); }); + describe('handleGetDesktopSources', () => { + const windowManager = new WindowManager(); + windowManager.viewManager = { + showByName: jest.fn(), + getCurrentView: jest.fn(), + }; + + beforeEach(() => { + windowManager.viewManager.views = new Map(); + windowManager.callsWidgetWindow = new CallsWidgetWindow(); + windowManager.callsWidgetWindow.win = { + webContents: { + send: jest.fn(), + }, + }; + }); + + afterEach(() => { + jest.resetAllMocks(); + Config.teams = []; + }); + + it('should send sources back', async () => { + jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([ + { + id: 'screen0', + thumbnail: { + toDataURL: jest.fn(), + }, + }, + { + id: 'window0', + thumbnail: { + toDataURL: jest.fn(), + }, + }, + ]); + + await windowManager.handleGetDesktopSources(null, 'widget', null); + + expect(windowManager.callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('desktop-sources-result', [ + { + id: 'screen0', + }, + { + id: 'window0', + }, + ]); + }); + + it('should send error with no sources', async () => { + jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([]); + await windowManager.handleGetDesktopSources(null, 'widget', null); + expect(windowManager.callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', { + err: 'screen-permissions', + }); + }); + + it('should send error with no permissions', async () => { + jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([ + { + id: 'screen0', + thumbnail: { + toDataURL: jest.fn(), + }, + }, + ]); + jest.spyOn(systemPreferences, 'getMediaAccessStatus').mockReturnValue('denied'); + + await windowManager.handleGetDesktopSources(null, 'widget', null); + + expect(systemPreferences.getMediaAccessStatus).toHaveBeenCalledWith('screen'); + expect(windowManager.callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', { + err: 'screen-permissions', + }); + }); + }); + describe('handleDesktopSourcesModalRequest', () => { const windowManager = new WindowManager(); windowManager.switchServer = jest.fn(); diff --git a/src/main/windows/windowManager.ts b/src/main/windows/windowManager.ts index 5ee032dd..cc1d0809 100644 --- a/src/main/windows/windowManager.ts +++ b/src/main/windows/windowManager.ts @@ -34,6 +34,7 @@ import { CALLS_LEAVE_CALL, DESKTOP_SOURCES_MODAL_REQUEST, CALLS_WIDGET_CHANNEL_LINK_CLICK, + CALLS_ERROR, } from 'common/communication'; import urlUtils from 'common/utils/url'; import {SECOND} from 'common/utils/constants'; @@ -822,16 +823,33 @@ export class WindowManager { return event.sender.id; } - handleGetDesktopSources = async (event: IpcMainEvent, viewName: string, opts: Electron.SourcesOptions) => { + handleGetDesktopSources = (event: IpcMainEvent, viewName: string, opts: Electron.SourcesOptions) => { log.debug('WindowManager.handleGetDesktopSources', {viewName, opts}); const globalWidget = viewName === 'widget' && this.callsWidgetWindow; const view = this.viewManager?.views.get(viewName); if (!view && !globalWidget) { - return; + return Promise.resolve(); } - desktopCapturer.getSources(opts).then((sources) => { + return desktopCapturer.getSources(opts).then((sources) => { + let hasScreenPermissions = true; + if (systemPreferences.getMediaAccessStatus) { + const screenPermissions = systemPreferences.getMediaAccessStatus('screen'); + log.debug('screenPermissions', screenPermissions); + if (screenPermissions === 'denied') { + log.info('no screen sharing permissions'); + hasScreenPermissions = false; + } + } + + if (!hasScreenPermissions || !sources?.length) { + this.callsWidgetWindow?.win.webContents.send(CALLS_ERROR, { + err: 'screen-permissions', + }); + return; + } + const message = sources.map((source) => { return { id: source.id, @@ -845,6 +863,11 @@ export class WindowManager { } else { this.callsWidgetWindow?.win.webContents.send(DESKTOP_SOURCES_RESULT, message); } + }).catch((err) => { + log.error('desktopCapturer.getSources failed', err); + this.callsWidgetWindow?.win.webContents.send(CALLS_ERROR, { + err: 'screen-permissions', + }); }); }