[MM-50995] Harden Calls events handling (#2571)

* Simplify server switching logic in calls events

* Harden calls events handling

* Fix subpath
This commit is contained in:
Claudio Costa 2023-03-01 16:01:39 -06:00 committed by streamer45
parent ed5e92a09c
commit a92f2b7a06
No known key found for this signature in database
GPG key ID: C31222BC9269BBBC
6 changed files with 285 additions and 62 deletions

View file

@ -49,7 +49,7 @@ window.addEventListener('message', ({origin, data = {}} = {}) => {
case CALLS_POPOUT_FOCUS:
case CALLS_ERROR:
case CALLS_LEAVE_CALL: {
ipcRenderer.send(type, message);
ipcRenderer.send(type, 'widget', message);
break;
}
}

View file

@ -30,13 +30,19 @@ jest.mock('../views/webContentEvents', () => ({
generateNewWindowListener: jest.fn(),
}));
jest.mock('common/utils/url', () => {
const originalModule = jest.requireActual('common/utils/url');
return {
...originalModule,
...originalModule.default,
};
});
describe('main/windows/callsWidgetWindow', () => {
describe('create CallsWidgetWindow', () => {
const widgetConfig = {
callID: 'test-call-id',
siteURL: 'http://localhost:8065',
title: '',
serverName: 'test-server-name',
channelURL: '/team/channel_id',
};
@ -48,8 +54,18 @@ describe('main/windows/callsWidgetWindow', () => {
view: {
webContents: {
send: jest.fn(),
id: 'mainViewID',
},
},
serverInfo: {
server: {
name: 'test-server-name',
url: new URL('http://localhost:8065'),
},
},
getWebContents: () => ({
id: 'mainViewID',
}),
};
const baseWindow = new EventEmitter();
@ -170,6 +186,11 @@ describe('main/windows/callsWidgetWindow', () => {
});
it('resize', () => {
baseWindow.webContents = {
...baseWindow.webContents,
id: 'windowID',
};
baseWindow.show = jest.fn(() => {
baseWindow.emit('show');
});
@ -202,7 +223,24 @@ describe('main/windows/callsWidgetWindow', () => {
height: MINIMUM_CALLS_WIDGET_HEIGHT,
});
widgetWindow.onResize(null, {
widgetWindow.onResize({
sender: {
id: 'badID',
},
},
'widget',
{
element: 'calls-widget',
width: 300,
height: 100,
});
expect(baseWindow.webContents.getZoomFactor).not.toHaveBeenCalled();
widgetWindow.onResize({
sender: baseWindow.webContents,
},
'widget',
{
element: 'calls-widget',
width: 300,
height: 100,
@ -217,6 +255,11 @@ describe('main/windows/callsWidgetWindow', () => {
});
it('zoom', () => {
baseWindow.webContents = {
...baseWindow.webContents,
id: 'windowID',
};
baseWindow.show = jest.fn(() => {
baseWindow.emit('show');
});
@ -244,7 +287,9 @@ describe('main/windows/callsWidgetWindow', () => {
expect(baseWindow.webContents.getZoomFactor).toHaveBeenCalledTimes(0);
baseWindow.webContents.getZoomFactor = jest.fn(() => 2.0);
widgetWindow.onResize(null, {
widgetWindow.onResize({
sender: baseWindow.webContents,
}, 'widget', {
element: 'calls-widget',
width: 300,
height: 100,
@ -258,7 +303,10 @@ describe('main/windows/callsWidgetWindow', () => {
});
baseWindow.webContents.getZoomFactor = jest.fn(() => 0.5);
widgetWindow.onResize(null, {
widgetWindow.onResize({
sender: baseWindow.webContents,
}, 'widget', {
element: 'calls-widget',
width: 300,
height: 100,
@ -290,11 +338,29 @@ describe('main/windows/callsWidgetWindow', () => {
it('getWidgetURL', () => {
const config = {
...widgetConfig,
siteURL: 'http://localhost:8065/subpath',
title: 'call test title #/&',
};
const widgetWindow = new CallsWidgetWindow(mainWindow, mainView, config);
const expected = `${config.siteURL}/plugins/${CALLS_PLUGIN_ID}/standalone/widget.html?call_id=${config.callID}&title=call+test+title+%23%2F%26`;
const expected = `${mainView.serverInfo.server.url}plugins/${CALLS_PLUGIN_ID}/standalone/widget.html?call_id=${config.callID}&title=call+test+title+%23%2F%26`;
expect(widgetWindow.getWidgetURL()).toBe(expected);
});
it('getWidgetURL - under subpath', () => {
const config = {
...widgetConfig,
title: 'call test title #/&',
};
const view = {
serverInfo: {
server: {
url: new URL('http://localhost:8065/subpath'),
},
},
};
const widgetWindow = new CallsWidgetWindow(mainWindow, view, config);
const expected = `${view.serverInfo.server.url}/plugins/${CALLS_PLUGIN_ID}/standalone/widget.html?call_id=${config.callID}&title=call+test+title+%23%2F%26`;
expect(widgetWindow.getWidgetURL()).toBe(expected);
});
@ -302,6 +368,7 @@ describe('main/windows/callsWidgetWindow', () => {
baseWindow.webContents = {
...baseWindow.webContents,
send: jest.fn(),
id: 'windowID',
};
const widgetWindow = new CallsWidgetWindow(mainWindow, mainView, widgetConfig);
@ -309,7 +376,15 @@ describe('main/windows/callsWidgetWindow', () => {
sourceID: 'test-source-id',
withAudio: false,
};
widgetWindow.onShareScreen(null, '', message);
widgetWindow.onShareScreen({
sender: {id: 'badID'},
}, '', message);
expect(widgetWindow.win.webContents.send).not.toHaveBeenCalled();
widgetWindow.onShareScreen({
sender: baseWindow.webContents,
}, '', message);
expect(widgetWindow.win.webContents.send).toHaveBeenCalledWith(CALLS_WIDGET_SHARE_SCREEN, message);
});
@ -317,13 +392,22 @@ describe('main/windows/callsWidgetWindow', () => {
baseWindow.webContents = {
...baseWindow.webContents,
send: jest.fn(),
id: 'windowID',
};
const widgetWindow = new CallsWidgetWindow(mainWindow, mainView, widgetConfig);
const message = {
callID: 'test-call-id',
};
widgetWindow.onJoinedCall(null, message);
widgetWindow.onJoinedCall({
sender: {id: 'badID'},
}, message);
expect(widgetWindow.mainView.view.webContents.send).not.toHaveBeenCalled();
widgetWindow.onJoinedCall({
sender: baseWindow.webContents,
}, 'widget', message);
expect(widgetWindow.mainView.view.webContents.send).toHaveBeenCalledWith(CALLS_JOINED_CALL, message);
});
@ -402,5 +486,27 @@ describe('main/windows/callsWidgetWindow', () => {
const widgetWindow = new CallsWidgetWindow(mainWindow, mainView, widgetConfig);
expect(widgetWindow.getMainView()).toEqual(mainView);
});
it('isAllowedEvent', () => {
baseWindow.webContents = {
...baseWindow.webContents,
id: 'windowID',
};
const widgetWindow = new CallsWidgetWindow(mainWindow, mainView, widgetConfig);
expect(widgetWindow.isAllowedEvent({
sender: {
id: 'senderID',
},
})).toEqual(false);
expect(widgetWindow.isAllowedEvent({
sender: widgetWindow.mainView.view.webContents,
})).toEqual(true);
expect(widgetWindow.isAllowedEvent({
sender: baseWindow.webContents,
})).toEqual(true);
});
});
});

View file

@ -1,7 +1,6 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import url from 'url';
import {EventEmitter} from 'events';
import {BrowserWindow, Rectangle, ipcMain, IpcMainEvent} from 'electron';
import log from 'electron-log';
@ -23,7 +22,7 @@ import {
CALLS_PLUGIN_ID,
} from 'common/utils/constants';
import Utils from 'common/utils/util';
import urlUtils from 'common/utils/url';
import urlUtils, {getFormattedPathName} from 'common/utils/url';
import {
CALLS_JOINED_CALL,
CALLS_POPOUT_FOCUS,
@ -92,7 +91,7 @@ export default class CallsWidgetWindow extends EventEmitter {
}
public getServerName() {
return this.config.serverName;
return this.mainView.serverInfo.server.name;
}
public getChannelURL() {
@ -121,8 +120,9 @@ export default class CallsWidgetWindow extends EventEmitter {
}
private getWidgetURL() {
const u = new url.URL(this.config.siteURL);
u.pathname += `/plugins/${CALLS_PLUGIN_ID}/standalone/widget.html`;
const u = urlUtils.parseURL(this.mainView.serverInfo.server.url.toString()) as URL;
u.pathname = getFormattedPathName(u.pathname);
u.pathname += `plugins/${CALLS_PLUGIN_ID}/standalone/widget.html`;
u.searchParams.append('call_id', this.config.callID);
if (this.config.title) {
u.searchParams.append('title', this.config.title);
@ -131,9 +131,14 @@ export default class CallsWidgetWindow extends EventEmitter {
return u.toString();
}
private onResize = (event: IpcMainEvent, msg: CallsWidgetResizeMessage) => {
private onResize = (ev: IpcMainEvent, _: string, msg: CallsWidgetResizeMessage) => {
log.debug('CallsWidgetWindow.onResize', msg);
if (!this.isAllowedEvent(ev)) {
log.warn('CallsWidgetWindow.onResize', 'Disallowed calls event');
return;
}
const zoomFactor = this.win.webContents.getZoomFactor();
const currBounds = this.win.getBounds();
const newBounds = {
@ -146,11 +151,25 @@ export default class CallsWidgetWindow extends EventEmitter {
this.setBounds(newBounds);
}
private onShareScreen = (ev: IpcMainEvent, viewName: string, message: CallsWidgetShareScreenMessage) => {
private onShareScreen = (ev: IpcMainEvent, _: string, message: CallsWidgetShareScreenMessage) => {
log.debug('CallsWidgetWindow.onShareScreen');
if (!this.isAllowedEvent(ev)) {
log.warn('Disallowed calls event');
return;
}
this.win.webContents.send(CALLS_WIDGET_SHARE_SCREEN, message);
}
private onJoinedCall = (ev: IpcMainEvent, message: CallsJoinedCallMessage) => {
private onJoinedCall = (ev: IpcMainEvent, _: string, message: CallsJoinedCallMessage) => {
log.debug('CallsWidgetWindow.onJoinedCall');
if (!this.isAllowedEvent(ev)) {
log.warn('CallsWidgetWindow.onJoinedCall', 'Disallowed calls event');
return;
}
this.mainView.view.webContents.send(CALLS_JOINED_CALL, message);
}
@ -229,5 +248,12 @@ export default class CallsWidgetWindow extends EventEmitter {
public getMainView() {
return this.mainView;
}
public isAllowedEvent(event: IpcMainEvent) {
// Only allow events coming from either the widget window or the
// original Mattermost view that initiated it.
return event.sender.id === this.getWebContentsId() ||
event.sender.id === this.getMainView().getWebContents().id;
}
}

View file

@ -1018,8 +1018,8 @@ describe('main/windows/windowManager', () => {
const view = {
name: 'server-1_tab-messaging',
serverInfo: {
remoteInfo: {
siteURL: 'http://server-1.com',
server: {
url: new URL('http://server-1.com'),
},
},
};
@ -1032,7 +1032,7 @@ describe('main/windows/windowManager', () => {
it('should create calls widget window', () => {
expect(windowManager.callsWidgetWindow).toBeUndefined();
windowManager.createCallsWidgetWindow(null, 'server-1_tab-messaging', {callID: 'test'});
windowManager.createCallsWidgetWindow('server-1_tab-messaging', {callID: 'test'});
expect(windowManager.callsWidgetWindow).toBeDefined();
});
@ -1040,7 +1040,7 @@ describe('main/windows/windowManager', () => {
const widgetWindow = windowManager.callsWidgetWindow;
expect(widgetWindow).toBeDefined();
widgetWindow.getCallID = jest.fn(() => 'test');
windowManager.createCallsWidgetWindow(null, 'server-1_tab-messaging', {callID: 'test'});
windowManager.createCallsWidgetWindow('server-1_tab-messaging', {callID: 'test'});
expect(windowManager.callsWidgetWindow).toEqual(widgetWindow);
});
@ -1048,7 +1048,7 @@ describe('main/windows/windowManager', () => {
const widgetWindow = windowManager.callsWidgetWindow;
expect(widgetWindow).toBeDefined();
widgetWindow.getCallID = jest.fn(() => 'test');
windowManager.createCallsWidgetWindow(null, 'server-1_tab-messaging', {callID: 'test2'});
windowManager.createCallsWidgetWindow('server-1_tab-messaging', {callID: 'test2'});
expect(windowManager.callsWidgetWindow).not.toEqual(widgetWindow);
});
});
@ -1061,12 +1061,18 @@ describe('main/windows/windowManager', () => {
};
beforeEach(() => {
CallsWidgetWindow.mockImplementation(() => {
return {
isAllowedEvent: jest.fn().mockReturnValue(true),
win: {
webContents: {
send: jest.fn(),
},
},
};
});
windowManager.callsWidgetWindow = new CallsWidgetWindow();
windowManager.callsWidgetWindow.win = {
webContents: {
send: jest.fn(),
},
};
Config.teams = [
{
@ -1140,7 +1146,7 @@ describe('main/windows/windowManager', () => {
},
]);
await windowManager.handleGetDesktopSources(null, 'server-1_tab-1', null);
await windowManager.handleGetDesktopSources('server-1_tab-1', null);
expect(windowManager.viewManager.views.get('server-1_tab-1').view.webContents.send).toHaveBeenCalledWith('desktop-sources-result', [
{
@ -1154,7 +1160,7 @@ describe('main/windows/windowManager', () => {
it('should send error with no sources', async () => {
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([]);
await windowManager.handleGetDesktopSources(null, 'server-2_tab-1', null);
await windowManager.handleGetDesktopSources('server-2_tab-1', null);
expect(windowManager.callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', {
err: 'screen-permissions',
});
@ -1175,7 +1181,7 @@ describe('main/windows/windowManager', () => {
]);
jest.spyOn(systemPreferences, 'getMediaAccessStatus').mockReturnValue('denied');
await windowManager.handleGetDesktopSources(null, 'server-1_tab-1', null);
await windowManager.handleGetDesktopSources('server-1_tab-1', null);
expect(systemPreferences.getMediaAccessStatus).toHaveBeenCalledWith('screen');
expect(windowManager.callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', {
@ -1204,7 +1210,7 @@ describe('main/windows/windowManager', () => {
]);
jest.spyOn(systemPreferences, 'getMediaAccessStatus').mockReturnValue('denied');
await windowManager.handleGetDesktopSources(null, 'server-1_tab-1', null);
await windowManager.handleGetDesktopSources('server-1_tab-1', null);
expect(windowManager.missingScreensharePermissions).toBe(true);
expect(resetScreensharePermissionsMacOS).toHaveBeenCalledTimes(1);
@ -1216,7 +1222,7 @@ describe('main/windows/windowManager', () => {
err: 'screen-permissions',
});
await windowManager.handleGetDesktopSources(null, 'server-1_tab-1', null);
await windowManager.handleGetDesktopSources('server-1_tab-1', null);
expect(resetScreensharePermissionsMacOS).toHaveBeenCalledTimes(2);
expect(openScreensharePermissionsSettingsMacOS).toHaveBeenCalledTimes(1);
@ -1239,6 +1245,13 @@ describe('main/windows/windowManager', () => {
CallsWidgetWindow.mockImplementation(() => {
return {
getServerName: () => 'server-1',
getMainView: jest.fn().mockReturnValue({
view: {
webContents: {
send: jest.fn(),
},
},
}),
};
});
@ -1310,6 +1323,14 @@ describe('main/windows/windowManager', () => {
CallsWidgetWindow.mockImplementation(() => {
return {
getServerName: () => 'server-2',
getMainView: jest.fn().mockReturnValue({
view: {
webContents: {
send: jest.fn(),
},
},
}),
getChannelURL: jest.fn(),
};
});
@ -1398,7 +1419,7 @@ describe('main/windows/windowManager', () => {
it('should focus view and propagate error to main view', () => {
windowManager.callsWidgetWindow = new CallsWidgetWindow();
windowManager.handleCallsError(null, {err: 'client-error'});
windowManager.handleCallsError('', {err: 'client-error'});
expect(windowManager.switchServer).toHaveBeenCalledWith('server-2');
expect(windowManager.mainWindow.focus).toHaveBeenCalled();
expect(windowManager.callsWidgetWindow.getMainView().view.webContents.send).toHaveBeenCalledWith('calls-error', {err: 'client-error'});
@ -1407,6 +1428,7 @@ describe('main/windows/windowManager', () => {
describe('handleCallsLinkClick', () => {
const windowManager = new WindowManager();
windowManager.switchServer = jest.fn();
const view1 = {
view: {
webContents: {
@ -1418,12 +1440,26 @@ describe('main/windows/windowManager', () => {
views: new Map([
['server-1_tab-messaging', view1],
]),
getCurrentView: jest.fn(),
};
beforeEach(() => {
CallsWidgetWindow.mockImplementation(() => {
return {
getServerName: () => 'server-1',
getMainView: jest.fn().mockReturnValue(view1),
};
});
});
afterEach(() => {
jest.resetAllMocks();
Config.teams = [];
});
it('should pass through the click link to browser history push', () => {
windowManager.viewManager.getCurrentView.mockReturnValue(view1);
windowManager.handleCallsLinkClick(null, {link: '/other/subpath'});
windowManager.callsWidgetWindow = new CallsWidgetWindow();
windowManager.handleCallsLinkClick('', {link: '/other/subpath'});
expect(windowManager.switchServer).toHaveBeenCalledWith('server-1');
expect(view1.view.webContents.send).toBeCalledWith('browser-history-push', '/other/subpath');
});
});
@ -1432,8 +1468,8 @@ describe('main/windows/windowManager', () => {
const view = {
name: 'server-1_tab-messaging',
serverInfo: {
remoteInfo: {
siteURL: 'http://server-1.com',
server: {
url: new URL('http://server-1.com'),
},
},
};
@ -1454,8 +1490,43 @@ describe('main/windows/windowManager', () => {
};
});
windowManager.createCallsWidgetWindow(null, 'server-1_tab-messaging', {callID: 'test'});
windowManager.createCallsWidgetWindow('server-1_tab-messaging', 'http://localhost:8065', {callID: 'test'});
expect(windowManager.getServerURLFromWebContentsId('callsID')).toBe(windowManager.callsWidgetWindow.getURL());
});
});
describe('genCallsEventHandler', () => {
const windowManager = new WindowManager();
const handler = jest.fn();
it('should call handler if callsWidgetWindow is not defined', () => {
windowManager.genCallsEventHandler(handler)();
expect(handler).toHaveBeenCalledTimes(1);
});
it('should not call handler if source is not allowed', () => {
CallsWidgetWindow.mockImplementation(() => {
return {
isAllowedEvent: jest.fn().mockReturnValue(false),
};
});
windowManager.callsWidgetWindow = new CallsWidgetWindow();
windowManager.genCallsEventHandler(handler)();
expect(handler).not.toHaveBeenCalled();
});
it('should call handler if source is allowed', () => {
CallsWidgetWindow.mockImplementation(() => {
return {
isAllowedEvent: jest.fn().mockReturnValue(true),
};
});
windowManager.callsWidgetWindow = new CallsWidgetWindow();
windowManager.genCallsEventHandler(handler)();
expect(handler).toHaveBeenCalledTimes(1);
});
});
});

View file

@ -11,6 +11,7 @@ import {
CallsJoinCallMessage,
CallsErrorMessage,
CallsLinkClickMessage,
CallsEventHandler,
} from 'types/calls';
import {
@ -97,15 +98,17 @@ export class WindowManager {
ipcMain.on(APP_LOGGED_OUT, this.handleAppLoggedOut);
ipcMain.handle(GET_VIEW_NAME, this.handleGetViewName);
ipcMain.handle(GET_VIEW_WEBCONTENTS_ID, this.handleGetWebContentsId);
ipcMain.on(DISPATCH_GET_DESKTOP_SOURCES, this.handleGetDesktopSources);
ipcMain.on(RELOAD_CURRENT_VIEW, this.handleReloadCurrentView);
ipcMain.on(VIEW_FINISHED_RESIZING, this.handleViewFinishedResizing);
ipcMain.on(CALLS_JOIN_CALL, this.createCallsWidgetWindow);
ipcMain.on(CALLS_LEAVE_CALL, () => this.callsWidgetWindow?.close());
ipcMain.on(DESKTOP_SOURCES_MODAL_REQUEST, this.handleDesktopSourcesModalRequest);
ipcMain.on(CALLS_WIDGET_CHANNEL_LINK_CLICK, this.handleCallsWidgetChannelLinkClick);
ipcMain.on(CALLS_ERROR, this.handleCallsError);
ipcMain.on(CALLS_LINK_CLICK, this.handleCallsLinkClick);
// Calls handlers
ipcMain.on(DISPATCH_GET_DESKTOP_SOURCES, this.genCallsEventHandler(this.handleGetDesktopSources));
ipcMain.on(DESKTOP_SOURCES_MODAL_REQUEST, this.genCallsEventHandler(this.handleDesktopSourcesModalRequest));
ipcMain.on(CALLS_JOIN_CALL, this.genCallsEventHandler(this.createCallsWidgetWindow));
ipcMain.on(CALLS_LEAVE_CALL, this.genCallsEventHandler(this.handleCallsLeave));
ipcMain.on(CALLS_WIDGET_CHANNEL_LINK_CLICK, this.genCallsEventHandler(this.handleCallsWidgetChannelLinkClick));
ipcMain.on(CALLS_ERROR, this.genCallsEventHandler(this.handleCallsError));
ipcMain.on(CALLS_LINK_CLICK, this.genCallsEventHandler(this.handleCallsLinkClick));
}
handleUpdateConfig = () => {
@ -114,7 +117,17 @@ export class WindowManager {
}
}
createCallsWidgetWindow = (event: IpcMainEvent, viewName: string, msg: CallsJoinCallMessage) => {
genCallsEventHandler = (handler: CallsEventHandler) => {
return (event: IpcMainEvent, viewName: string, msg?: any) => {
if (this.callsWidgetWindow && !this.callsWidgetWindow.isAllowedEvent(event)) {
log.warn('WindowManager.genCallsEventHandler', 'Disallowed calls event');
return;
}
handler(viewName, msg);
};
}
createCallsWidgetWindow = (viewName: string, msg: CallsJoinCallMessage) => {
log.debug('WindowManager.createCallsWidgetWindow');
if (this.callsWidgetWindow) {
// trying to join again the call we are already in should not be allowed.
@ -130,10 +143,8 @@ export class WindowManager {
}
this.callsWidgetWindow = new CallsWidgetWindow(this.mainWindow!, currentView, {
siteURL: currentView.serverInfo.remoteInfo.siteURL!,
callID: msg.callID,
title: msg.title,
serverName: this.currentServerName!,
channelURL: msg.channelURL,
});
@ -146,23 +157,23 @@ export class WindowManager {
if (this.callsWidgetWindow) {
this.switchServer(this.callsWidgetWindow.getServerName());
this.mainWindow?.focus();
const currentView = this.viewManager?.getCurrentView();
currentView?.view.webContents.send(DESKTOP_SOURCES_MODAL_REQUEST);
this.callsWidgetWindow.getMainView().view.webContents.send(DESKTOP_SOURCES_MODAL_REQUEST);
}
}
handleCallsWidgetChannelLinkClick = () => {
log.debug('WindowManager.handleCallsWidgetChannelLinkClick');
if (this.callsWidgetWindow) {
this.switchServer(this.callsWidgetWindow.getServerName());
this.mainWindow?.focus();
const currentView = this.viewManager?.getCurrentView();
currentView?.view.webContents.send(BROWSER_HISTORY_PUSH, this.callsWidgetWindow.getChannelURL());
this.callsWidgetWindow.getMainView().view.webContents.send(BROWSER_HISTORY_PUSH, this.callsWidgetWindow.getChannelURL());
}
}
handleCallsError = (event: IpcMainEvent, msg: CallsErrorMessage) => {
handleCallsError = (_: string, msg: CallsErrorMessage) => {
log.debug('WindowManager.handleCallsError', msg);
if (this.callsWidgetWindow) {
this.switchServer(this.callsWidgetWindow.getServerName());
this.mainWindow?.focus();
@ -170,11 +181,20 @@ export class WindowManager {
}
}
handleCallsLinkClick = (_: IpcMainEvent, msg: CallsLinkClickMessage) => {
handleCallsLinkClick = (_: string, msg: CallsLinkClickMessage) => {
log.debug('WindowManager.handleCallsLinkClick with linkURL', msg.link);
this.mainWindow?.focus();
const currentView = this.viewManager?.getCurrentView();
currentView?.view.webContents.send(BROWSER_HISTORY_PUSH, msg.link);
if (this.callsWidgetWindow) {
this.switchServer(this.callsWidgetWindow.getServerName());
this.mainWindow?.focus();
this.callsWidgetWindow.getMainView().view.webContents.send(BROWSER_HISTORY_PUSH, msg.link);
}
}
handleCallsLeave = () => {
log.debug('WindowManager.handleCallsLeave');
this.callsWidgetWindow?.close();
}
showSettingsWindow = () => {
@ -850,7 +870,7 @@ export class WindowManager {
return event.sender.id;
}
handleGetDesktopSources = async (event: IpcMainEvent, viewName: string, opts: Electron.SourcesOptions) => {
handleGetDesktopSources = async (viewName: string, opts: Electron.SourcesOptions) => {
log.debug('WindowManager.handleGetDesktopSources', {viewName, opts});
const view = this.viewManager?.views.get(viewName);

View file

@ -1,10 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export type CallsWidgetWindowConfig = {
siteURL: string;
callID: string;
title: string;
serverName: string;
channelURL: string;
}
@ -38,3 +36,5 @@ export type CallsErrorMessage = {
export type CallsLinkClickMessage = {
link: string | URL;
}
export type CallsEventHandler = ((viewName: string, msg: any) => void) | ((viewName: string, opts: Electron.SourcesOptions) => Promise<void>);