From a7127fee73a5fb8941f3076eef9eab4eaaabc998 Mon Sep 17 00:00:00 2001 From: Samuel Mannehed Date: Mon, 29 Aug 2016 14:59:28 +0200 Subject: [PATCH] Don't abuse state change function for messages This doesn't even work anymore since we fixed it to ignore changes to the current state. Add a separate callback for notifications instead. --- app/ui.js | 5 +++++ core/rfb.js | 35 +++++++++++++++++++++++++++++++++-- tests/test.rfb.js | 31 +++++++++++++++++++++++++++---- vnc_auto.html | 4 ++++ 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/app/ui.js b/app/ui.js index 601abc2..f65e198 100644 --- a/app/ui.js +++ b/app/ui.js @@ -338,6 +338,7 @@ var UI; initRFB: function() { try { UI.rfb = new RFB({'target': document.getElementById('noVNC_canvas'), + 'onNotification': UI.notification, 'onUpdateState': UI.updateState, 'onPasswordRequired': UI.passwordRequired, 'onXvpInit': UI.updateXvpButton, @@ -486,6 +487,10 @@ var UI; document.getElementById('noVNC_status').classList.remove("noVNC_open"); }, + notification: function (rfb, msg, level, options) { + UI.showStatus(msg, level); + }, + activateControlbar: function(event) { clearTimeout(UI.idleControlbarTimeout); // We manipulate the anchor instead of the actual control diff --git a/core/rfb.js b/core/rfb.js index f441dab..8a5d817 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -158,6 +158,7 @@ // Callback functions 'onUpdateState': function () { }, // onUpdateState(rfb, state, oldstate, statusMsg): state update/change + 'onNotification': function () { }, // onNotification(rfb, msg, level, options): notification for UI 'onPasswordRequired': function () { }, // onPasswordRequired(rfb, msg): VNC password is required 'onClipboard': function () { }, // onClipboard(rfb, text): RFB clipboard contents received 'onBell': function () { }, // onBell(rfb): RFB Bell message received @@ -535,6 +536,33 @@ return false; }, + /* + * Send a notification to the UI. Valid levels are: + * 'normal'|'warn'|'error' + * + * NOTE: Options could be added in the future. + * NOTE: If this function is called multiple times, remember that the + * interface could be only showing the latest notification. + */ + _notification: function(msg, level, options) { + switch (level) { + case 'normal': + case 'warn': + case 'error': + Util.Debug("Notification[" + level + "]:" + msg); + break; + default: + Util.Error("Invalid notification level: " + level); + return; + } + + if (options) { + this._onNotification(this, msg, level, options); + } else { + this._onNotification(this, msg, level); + } + }, + _handle_message: function () { if (this._sock.rQlen() === 0) { Util.Warn("handle_message called on an empty receive queue"); @@ -1130,7 +1158,8 @@ switch (xvp_msg) { case 0: // XVP_FAIL - this._updateState(this._rfb_state, "Operation Failed"); + Util.Error("Operation Failed"); + this._notification("XVP Operation Failed", 'error'); break; case 1: // XVP_INIT this._rfb_xvp_ver = xvp_ver; @@ -1322,6 +1351,7 @@ // Callback functions ['onUpdateState', 'rw', 'func'], // onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change + ['onNotification', 'rw', 'func'], // onNotification(rfb, msg, level, options): notification for the UI ['onPasswordRequired', 'rw', 'func'], // onPasswordRequired(rfb, msg): VNC password is required ['onClipboard', 'rw', 'func'], // onClipboard(rfb, text): RFB clipboard contents received ['onBell', 'rw', 'func'], // onBell(rfb): RFB Bell message received @@ -2275,7 +2305,8 @@ msg = "Unknown reason"; break; } - Util.Info("Server did not accept the resize request: " + msg); + this._notification("Server did not accept the resize request: " + + msg, 'normal'); return true; } diff --git a/tests/test.rfb.js b/tests/test.rfb.js index e1a11f0..d6f1a1f 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -338,6 +338,28 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._disconnTimer).to.be.null; }); }); + + describe('#_notification', function () { + var client; + beforeEach(function () { client = make_rfb(); }); + + it('should call the notification callback', function () { + client.set_onNotification(sinon.spy()); + client._notification('notify!', 'warn'); + var spy = client.get_onNotification(); + expect(spy).to.have.been.calledOnce; + expect(spy.args[0][1]).to.equal('notify!'); + expect(spy.args[0][2]).to.equal('warn'); + }); + + it('should not call the notification callback when level is invalid', function () { + client.set_onNotification(sinon.spy()); + client._notification('notify!', 'invalid'); + var spy = client.get_onNotification(); + expect(spy).to.not.have.been.called; + }); + }); + }); describe('Page States', function () { @@ -1730,11 +1752,12 @@ describe('Remote Frame Buffer Protocol Client', function() { client._fb_height = 32; }); - it('should call updateState with a message on XVP_FAIL, but keep the same state', function () { - client._updateState = sinon.spy(); + it('should send a notification on XVP_FAIL', function () { + client.set_onNotification(sinon.spy()); client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 0])); - expect(client._updateState).to.have.been.calledOnce; - expect(client._updateState).to.have.been.calledWith('normal', 'Operation Failed'); + var spy = client.get_onNotification(); + expect(spy).to.have.been.calledOnce; + expect(spy.args[0][1]).to.equal('XVP Operation Failed'); }); it('should set the XVP version and fire the callback with the version on XVP_INIT', function () { diff --git a/vnc_auto.html b/vnc_auto.html index 1d9cd9e..4362080 100644 --- a/vnc_auto.html +++ b/vnc_auto.html @@ -161,6 +161,9 @@ s.innerHTML = msg; } } + function notification(rfb, msg, level, options) { + $D('noVNC_status').innerHTML = msg; + } window.onresize = function () { // When the window has been resized, wait until the size remains @@ -236,6 +239,7 @@ 'local_cursor': WebUtil.getConfigVar('cursor', true), 'shared': WebUtil.getConfigVar('shared', true), 'view_only': WebUtil.getConfigVar('view_only', false), + 'onNotification: notification, 'onUpdateState': updateState, 'onXvpInit': xvpInit, 'onPasswordRequired': passwordRequired, -- 2.39.5