[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:
parent
42a0bc4759
commit
d4e70db999
|
@ -556,7 +556,6 @@ describe('main/windows/callsWidgetWindow', () => {
|
||||||
|
|
||||||
describe('handleGetDesktopSources', () => {
|
describe('handleGetDesktopSources', () => {
|
||||||
const callsWidgetWindow = new CallsWidgetWindow();
|
const callsWidgetWindow = new CallsWidgetWindow();
|
||||||
callsWidgetWindow.options = {callID: 'callID'};
|
|
||||||
callsWidgetWindow.win = {
|
callsWidgetWindow.win = {
|
||||||
webContents: {
|
webContents: {
|
||||||
send: jest.fn(),
|
send: jest.fn(),
|
||||||
|
@ -618,6 +617,7 @@ describe('main/windows/callsWidgetWindow', () => {
|
||||||
PermissionsManager.doPermissionRequest.mockReturnValue(Promise.resolve(true));
|
PermissionsManager.doPermissionRequest.mockReturnValue(Promise.resolve(true));
|
||||||
ViewManager.getViewByWebContentsId.mockImplementation((id) => [...views.values()].find((view) => view.webContentsId === id));
|
ViewManager.getViewByWebContentsId.mockImplementation((id) => [...views.values()].find((view) => view.webContentsId === id));
|
||||||
callsWidgetWindow.mainView = views.get('server-1_view-1');
|
callsWidgetWindow.mainView = views.get('server-1_view-1');
|
||||||
|
callsWidgetWindow.options = {callID: 'callID'};
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -625,6 +625,34 @@ describe('main/windows/callsWidgetWindow', () => {
|
||||||
callsWidgetWindow.missingScreensharePermissions = undefined;
|
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 () => {
|
it('should send sources back', async () => {
|
||||||
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([
|
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([]);
|
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(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(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
|
||||||
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledTimes(1);
|
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([
|
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([
|
||||||
{
|
{
|
||||||
id: 'screen0',
|
id: 'screen0',
|
||||||
|
@ -671,7 +712,7 @@ describe('main/windows/callsWidgetWindow', () => {
|
||||||
]);
|
]);
|
||||||
jest.spyOn(systemPreferences, 'getMediaAccessStatus').mockReturnValue('denied');
|
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(systemPreferences.getMediaAccessStatus).toHaveBeenCalledWith('screen');
|
||||||
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
|
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);
|
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 () => {
|
it('macos - no permissions', async () => {
|
||||||
const originalPlatform = process.platform;
|
const originalPlatform = process.platform;
|
||||||
Object.defineProperty(process, 'platform', {
|
Object.defineProperty(process, 'platform', {
|
||||||
|
@ -696,7 +758,7 @@ describe('main/windows/callsWidgetWindow', () => {
|
||||||
]);
|
]);
|
||||||
jest.spyOn(systemPreferences, 'getMediaAccessStatus').mockReturnValue('denied');
|
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(callsWidgetWindow.missingScreensharePermissions).toBe(true);
|
||||||
expect(resetScreensharePermissionsMacOS).toHaveBeenCalledTimes(1);
|
expect(resetScreensharePermissionsMacOS).toHaveBeenCalledTimes(1);
|
||||||
|
@ -704,7 +766,7 @@ describe('main/windows/callsWidgetWindow', () => {
|
||||||
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
|
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(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(resetScreensharePermissionsMacOS).toHaveBeenCalledTimes(2);
|
||||||
expect(openScreensharePermissionsSettingsMacOS).toHaveBeenCalledTimes(1);
|
expect(openScreensharePermissionsSettingsMacOS).toHaveBeenCalledTimes(1);
|
||||||
|
|
|
@ -378,15 +378,15 @@ export class CallsWidgetWindow {
|
||||||
private handleGetDesktopSources = async (event: IpcMainInvokeEvent, opts: Electron.SourcesOptions) => {
|
private handleGetDesktopSources = async (event: IpcMainInvokeEvent, opts: Electron.SourcesOptions) => {
|
||||||
log.debug('handleGetDesktopSources', opts);
|
log.debug('handleGetDesktopSources', opts);
|
||||||
|
|
||||||
if (event.sender.id !== this.mainView?.webContentsId) {
|
// For Calls we make an extra check to ensure the event is coming from the expected window (main view).
|
||||||
log.warn('handleGetDesktopSources', 'Blocked on wrong webContentsId');
|
// Otherwise we want to allow for other plugins to ask for screen sharing sources.
|
||||||
return [];
|
if (this.mainView && event.sender.id !== this.mainView.webContentsId) {
|
||||||
|
throw new Error('handleGetDesktopSources: blocked on wrong webContentsId');
|
||||||
}
|
}
|
||||||
|
|
||||||
const view = ViewManager.getViewByWebContentsId(event.sender.id);
|
const view = ViewManager.getViewByWebContentsId(event.sender.id);
|
||||||
if (!view) {
|
if (!view) {
|
||||||
log.error('handleGetDesktopSources: view not found');
|
throw new Error('handleGetDesktopSources: view not found');
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin' && systemPreferences.getMediaAccessStatus('screen') === 'denied') {
|
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})) {
|
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());
|
throw new Error('permissions denied');
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const screenPermissionsErrArgs = ['screen-permissions', this.callID];
|
const screenPermissionsErrArgs = ['screen-permissions', this.callID];
|
||||||
|
@ -425,10 +424,7 @@ export class CallsWidgetWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasScreenPermissions || !sources.length) {
|
if (!hasScreenPermissions || !sources.length) {
|
||||||
log.info('missing screen permissions');
|
throw new Error('handleGetDesktopSources: permissions denied');
|
||||||
view.sendToRenderer(CALLS_ERROR, ...screenPermissionsErrArgs);
|
|
||||||
this.win?.webContents.send(CALLS_ERROR, ...screenPermissionsErrArgs);
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = sources.map((source) => {
|
const message = sources.map((source) => {
|
||||||
|
@ -441,12 +437,14 @@ export class CallsWidgetWindow {
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}).catch((err) => {
|
}).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);
|
||||||
|
}
|
||||||
|
|
||||||
view.sendToRenderer(CALLS_ERROR, ...screenPermissionsErrArgs);
|
throw new Error(`handleGetDesktopSources: desktopCapturer.getSources failed: ${err}`);
|
||||||
this.win?.webContents.send(CALLS_ERROR, ...screenPermissionsErrArgs);
|
|
||||||
|
|
||||||
return [];
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue