[MM-60227] Allow plugins to ask for desktop sources for screen sharing (#3135)

* Allow plugins to ask for desktop sources for screen sharing

* Remove unnecessary logging in case of throwing errors

* Remove more redundant logging
This commit is contained in:
Claudio Costa 2024-09-18 08:49:56 -06:00 committed by GitHub
parent 42a0bc4759
commit d4e70db999
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 83 additions and 23 deletions

View file

@ -556,7 +556,6 @@ describe('main/windows/callsWidgetWindow', () => {
describe('handleGetDesktopSources', () => {
const callsWidgetWindow = new CallsWidgetWindow();
callsWidgetWindow.options = {callID: 'callID'};
callsWidgetWindow.win = {
webContents: {
send: jest.fn(),
@ -618,6 +617,7 @@ describe('main/windows/callsWidgetWindow', () => {
PermissionsManager.doPermissionRequest.mockReturnValue(Promise.resolve(true));
ViewManager.getViewByWebContentsId.mockImplementation((id) => [...views.values()].find((view) => view.webContentsId === id));
callsWidgetWindow.mainView = views.get('server-1_view-1');
callsWidgetWindow.options = {callID: 'callID'};
});
afterEach(() => {
@ -625,6 +625,34 @@ describe('main/windows/callsWidgetWindow', () => {
callsWidgetWindow.missingScreensharePermissions = undefined;
});
it('should send sources back - uninitialized', async () => {
callsWidgetWindow.mainView = undefined;
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([
{
id: 'screen0',
thumbnail: {
toDataURL: jest.fn(),
},
},
{
id: 'window0',
thumbnail: {
toDataURL: jest.fn(),
},
},
]);
const sources = await callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null);
expect(sources).toEqual([
{
id: 'screen0',
},
{
id: 'window0',
},
]);
});
it('should send sources back', async () => {
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([
{
@ -652,15 +680,28 @@ describe('main/windows/callsWidgetWindow', () => {
]);
});
it('should send error with no sources', async () => {
it('should throw and send error with no sources', async () => {
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([]);
await callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null);
await expect(callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null)).rejects.toThrow('permissions denied');
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
expect(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledTimes(1);
});
it('should send error with no permissions', async () => {
it('should throw but not send calls error when uninitialized', async () => {
callsWidgetWindow.options = undefined;
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([]);
await expect(callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null)).rejects.toThrow('permissions denied');
expect(callsWidgetWindow.win.webContents.send).not.toHaveBeenCalled();
expect(views.get('server-1_view-1').sendToRenderer).not.toHaveBeenCalled();
});
it('should throw and send error with no permissions', async () => {
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([
{
id: 'screen0',
@ -671,7 +712,7 @@ describe('main/windows/callsWidgetWindow', () => {
]);
jest.spyOn(systemPreferences, 'getMediaAccessStatus').mockReturnValue('denied');
await callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null);
await expect(callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null)).rejects.toThrow('permissions denied');
expect(systemPreferences.getMediaAccessStatus).toHaveBeenCalledWith('screen');
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
@ -680,6 +721,27 @@ describe('main/windows/callsWidgetWindow', () => {
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledTimes(1);
});
it('should throw but not send error with no permissions when uninitialized', async () => {
callsWidgetWindow.options = undefined;
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([
{
id: 'screen0',
thumbnail: {
toDataURL: jest.fn(),
},
},
]);
jest.spyOn(systemPreferences, 'getMediaAccessStatus').mockReturnValue('denied');
await expect(callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null)).rejects.toThrow('permissions denied');
expect(systemPreferences.getMediaAccessStatus).toHaveBeenCalledWith('screen');
expect(callsWidgetWindow.win.webContents.send).not.toHaveBeenCalled();
expect(views.get('server-1_view-1').sendToRenderer).not.toHaveBeenCalled();
});
it('macos - no permissions', async () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
@ -696,7 +758,7 @@ describe('main/windows/callsWidgetWindow', () => {
]);
jest.spyOn(systemPreferences, 'getMediaAccessStatus').mockReturnValue('denied');
await callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null);
await expect(callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null)).rejects.toThrow('permissions denied');
expect(callsWidgetWindow.missingScreensharePermissions).toBe(true);
expect(resetScreensharePermissionsMacOS).toHaveBeenCalledTimes(1);
@ -704,7 +766,7 @@ describe('main/windows/callsWidgetWindow', () => {
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
expect(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
await callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null);
await expect(callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null)).rejects.toThrow('permissions denied');
expect(resetScreensharePermissionsMacOS).toHaveBeenCalledTimes(2);
expect(openScreensharePermissionsSettingsMacOS).toHaveBeenCalledTimes(1);

View file

@ -378,15 +378,15 @@ export class CallsWidgetWindow {
private handleGetDesktopSources = async (event: IpcMainInvokeEvent, opts: Electron.SourcesOptions) => {
log.debug('handleGetDesktopSources', opts);
if (event.sender.id !== this.mainView?.webContentsId) {
log.warn('handleGetDesktopSources', 'Blocked on wrong webContentsId');
return [];
// For Calls we make an extra check to ensure the event is coming from the expected window (main view).
// Otherwise we want to allow for other plugins to ask for screen sharing sources.
if (this.mainView && event.sender.id !== this.mainView.webContentsId) {
throw new Error('handleGetDesktopSources: blocked on wrong webContentsId');
}
const view = ViewManager.getViewByWebContentsId(event.sender.id);
if (!view) {
log.error('handleGetDesktopSources: view not found');
return [];
throw new Error('handleGetDesktopSources: view not found');
}
if (process.platform === 'darwin' && systemPreferences.getMediaAccessStatus('screen') === 'denied') {
@ -407,8 +407,7 @@ export class CallsWidgetWindow {
}
if (!await PermissionsManager.doPermissionRequest(view.webContentsId, 'screenShare', {requestingUrl: view.view.server.url.toString(), isMainFrame: false})) {
log.warn('screen share permissions disallowed', view.webContentsId, view.view.server.url.toString());
return [];
throw new Error('permissions denied');
}
const screenPermissionsErrArgs = ['screen-permissions', this.callID];
@ -425,10 +424,7 @@ export class CallsWidgetWindow {
}
if (!hasScreenPermissions || !sources.length) {
log.info('missing screen permissions');
view.sendToRenderer(CALLS_ERROR, ...screenPermissionsErrArgs);
this.win?.webContents.send(CALLS_ERROR, ...screenPermissionsErrArgs);
return [];
throw new Error('handleGetDesktopSources: permissions denied');
}
const message = sources.map((source) => {
@ -441,12 +437,14 @@ export class CallsWidgetWindow {
return message;
}).catch((err) => {
log.error('desktopCapturer.getSources failed', err);
// Only send calls error if this window has been initialized (i.e. we are in a call).
// The rest of the logic is shared so that other plugins can request screen sources.
if (this.callID) {
view.sendToRenderer(CALLS_ERROR, ...screenPermissionsErrArgs);
this.win?.webContents.send(CALLS_ERROR, ...screenPermissionsErrArgs);
}
return [];
throw new Error(`handleGetDesktopSources: desktopCapturer.getSources failed: ${err}`);
});
};