From a92f2b7a06e3c2c24b836d934b23e3869f911c4d Mon Sep 17 00:00:00 2001 From: Claudio Costa Date: Wed, 1 Mar 2023 16:01:39 -0600 Subject: [PATCH] [MM-50995] Harden Calls events handling (#2571) * Simplify server switching logic in calls events * Harden calls events handling * Fix subpath --- src/main/preload/callsWidget.js | 2 +- src/main/windows/callsWidgetWindow.test.js | 124 +++++++++++++++++++-- src/main/windows/callsWidgetWindow.ts | 42 +++++-- src/main/windows/windowManager.test.js | 115 +++++++++++++++---- src/main/windows/windowManager.ts | 60 ++++++---- src/types/calls.ts | 4 +- 6 files changed, 285 insertions(+), 62 deletions(-) diff --git a/src/main/preload/callsWidget.js b/src/main/preload/callsWidget.js index e35f9f32..a7812144 100644 --- a/src/main/preload/callsWidget.js +++ b/src/main/preload/callsWidget.js @@ -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; } } diff --git a/src/main/windows/callsWidgetWindow.test.js b/src/main/windows/callsWidgetWindow.test.js index dc63f15b..d2bcc1c0 100644 --- a/src/main/windows/callsWidgetWindow.test.js +++ b/src/main/windows/callsWidgetWindow.test.js @@ -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); + }); }); }); diff --git a/src/main/windows/callsWidgetWindow.ts b/src/main/windows/callsWidgetWindow.ts index a41be645..48492ad1 100644 --- a/src/main/windows/callsWidgetWindow.ts +++ b/src/main/windows/callsWidgetWindow.ts @@ -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; + } } diff --git a/src/main/windows/windowManager.test.js b/src/main/windows/windowManager.test.js index 484fa246..18bc4859 100644 --- a/src/main/windows/windowManager.test.js +++ b/src/main/windows/windowManager.test.js @@ -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); + }); + }); }); diff --git a/src/main/windows/windowManager.ts b/src/main/windows/windowManager.ts index 7826f904..fea72e23 100644 --- a/src/main/windows/windowManager.ts +++ b/src/main/windows/windowManager.ts @@ -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); diff --git a/src/types/calls.ts b/src/types/calls.ts index 6619d7f6..4de7986a 100644 --- a/src/types/calls.ts +++ b/src/types/calls.ts @@ -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);