From 7f047675662f731d0fd8af4b66018a9bd8f6e966 Mon Sep 17 00:00:00 2001 From: Tudor-Ovidiu Avram Date: Wed, 9 Jun 2021 13:09:08 +0300 Subject: [PATCH] feat(api) expose event for mouse movements inside the iframe --- config.js | 5 ++ modules/API/API.js | 77 +++++++++++++++++++ modules/API/external/external_api.js | 3 + react/features/base/config/configWhitelist.js | 1 + .../conference/components/web/Conference.js | 65 +++++++++++++++- 5 files changed, 149 insertions(+), 2 deletions(-) diff --git a/config.js b/config.js index ef5734d3e..c0f5b49c1 100644 --- a/config.js +++ b/config.js @@ -792,6 +792,11 @@ var config = { websocketKeepAliveUrl */ + /** + * Default interval (milliseconds) for triggering mouseMoved iframe API event + */ + mouseMoveCallbackInterval: 1000, + /** Use this array to configure which notifications will be shown to the user The items correspond to the title or description key of that notification diff --git a/modules/API/API.js b/modules/API/API.js index f20a4ce04..031206e89 100644 --- a/modules/API/API.js +++ b/modules/API/API.js @@ -572,6 +572,44 @@ function toggleScreenSharing(enable) { } } +/** + * Removes sensitive data from a mouse event. + * + * @param {MouseEvent} event - The mouse event to sanitize. + * @returns {Object} + */ +function sanitizeMouseEvent(event: MouseEvent) { + const { + clientX, + clientY, + movementX, + movementY, + offsetX, + offsetY, + pageX, + pageY, + x, + y, + screenX, + screenY + } = event; + + return { + clientX, + clientY, + movementX, + movementY, + offsetX, + offsetY, + pageX, + pageY, + x, + y, + screenX, + screenY + }; +} + /** * Implements API class that communicates with external API class and provides * interface to access Jitsi Meet features by external applications that embed @@ -675,6 +713,45 @@ class API { }); } + /** + * Notify external application (if API is enabled) that the mouse has entered inside the iframe. + * + * @param {MouseEvent} event - The mousemove event. + * @returns {void} + */ + notifyMouseEnter(event: MouseEvent) { + this._sendEvent({ + name: 'mouse-enter', + event: sanitizeMouseEvent(event) + }); + } + + /** + * Notify external application (if API is enabled) that the mouse has entered inside the iframe. + * + * @param {MouseEvent} event - The mousemove event. + * @returns {void} + */ + notifyMouseLeave(event: MouseEvent) { + this._sendEvent({ + name: 'mouse-leave', + event: sanitizeMouseEvent(event) + }); + } + + /** + * Notify external application (if API is enabled) that the mouse has moved inside the iframe. + * + * @param {MouseEvent} event - The mousemove event. + * @returns {void} + */ + notifyMouseMove(event: MouseEvent) { + this._sendEvent({ + name: 'mouse-move', + event: sanitizeMouseEvent(event) + }); + } + /** * Notify external application that the video quality setting has changed. * diff --git a/modules/API/external/external_api.js b/modules/API/external/external_api.js index 66ddba519..04dc688ea 100644 --- a/modules/API/external/external_api.js +++ b/modules/API/external/external_api.js @@ -84,6 +84,9 @@ const events = { 'incoming-message': 'incomingMessage', 'log': 'log', 'mic-error': 'micError', + 'mouse-enter': 'mouseEnter', + 'mouse-leave': 'mouseLeave', + 'mouse-move': 'mouseMove', 'outgoing-message': 'outgoingMessage', 'participant-joined': 'participantJoined', 'participant-kicked-out': 'participantKickedOut', diff --git a/react/features/base/config/configWhitelist.js b/react/features/base/config/configWhitelist.js index 6536b2a5d..8ee54c41f 100644 --- a/react/features/base/config/configWhitelist.js +++ b/react/features/base/config/configWhitelist.js @@ -140,6 +140,7 @@ export default [ 'liveStreamingEnabled', 'localRecording', 'maxFullResolutionParticipants', + 'mouseMoveCallbackInterval', 'notifications', 'openSharedDocumentOnJoin', 'opusMaxAverageBitrate', diff --git a/react/features/conference/components/web/Conference.js b/react/features/conference/components/web/Conference.js index 958f3f977..42e08c2ac 100644 --- a/react/features/conference/components/web/Conference.js +++ b/react/features/conference/components/web/Conference.js @@ -85,6 +85,11 @@ type Props = AbstractProps & { */ _layoutClassName: string, + /** + * The config specified interval for triggering mouseMoved iframe api events + */ + _mouseMoveCallbackInterval: number, + /** * Name for this conference room. */ @@ -104,7 +109,11 @@ type Props = AbstractProps & { */ class Conference extends AbstractConference { _onFullScreenChange: Function; + _onMouseEnter: Function; + _onMouseLeave: Function; + _onMouseMove: Function; _onShowToolbar: Function; + _originalOnMouseMove: Function; _originalOnShowToolbar: Function; _setBackground: Function; @@ -117,9 +126,13 @@ class Conference extends AbstractConference { constructor(props) { super(props); + const { _mouseMoveCallbackInterval } = props; + // Throttle and bind this component's mousemove handler to prevent it // from firing too often. this._originalOnShowToolbar = this._onShowToolbar; + this._originalOnMouseMove = this._onMouseMove; + this._onShowToolbar = _.throttle( () => this._originalOnShowToolbar(), 100, @@ -128,6 +141,14 @@ class Conference extends AbstractConference { trailing: false }); + this._onMouseMove = _.throttle( + event => this._originalOnMouseMove(event), + _mouseMoveCallbackInterval, + { + leading: true, + trailing: false + }); + // Bind event handler so it is only bound once for every instance. this._onFullScreenChange = this._onFullScreenChange.bind(this); this._setBackground = this._setBackground.bind(this); @@ -192,7 +213,11 @@ class Conference extends AbstractConference { } = this.props; return ( -
+
{ this.props.dispatch(fullScreenChanged(APP.UI.isFullScreen())); } + /** + * Triggers iframe API mouseEnter event. + * + * @param {MouseEvent} event - The mouse event. + * @private + * @returns {void} + */ + _onMouseEnter(event) { + APP.API.notifyMouseEnter(event); + } + + /** + * Triggers iframe API mouseLeave event. + * + * @param {MouseEvent} event - The mouse event. + * @private + * @returns {void} + */ + _onMouseLeave(event) { + APP.API.notifyMouseLeave(event); + } + + /** + * Triggers iframe API mouseMove event. + * + * @param {MouseEvent} event - The mouse event. + * @private + * @returns {void} + */ + _onMouseMove(event) { + APP.API.notifyMouseMove(event); + } + /** * Displays the toolbar. * @@ -305,12 +363,15 @@ class Conference extends AbstractConference { * @returns {Props} */ function _mapStateToProps(state) { + const { backgroundAlpha, mouseMoveCallbackInterval } = state['features/base/config']; + return { ...abstractMapStateToProps(state), - _backgroundAlpha: state['features/base/config'].backgroundAlpha, + _backgroundAlpha: backgroundAlpha, _isLobbyScreenVisible: state['features/base/dialog']?.component === LobbyScreen, _isParticipantsPaneVisible: getParticipantsPaneOpen(state), _layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)], + _mouseMoveCallbackInterval: mouseMoveCallbackInterval, _roomName: getConferenceNameForTitle(state), _showPrejoin: isPrejoinPageVisible(state) };