diff --git a/app/ui.js b/app/ui.js index 1202fe26..877a72e8 100644 --- a/app/ui.js +++ b/app/ui.js @@ -390,7 +390,7 @@ var UI = { * VISUAL * ------v------*/ - updateState: function(rfb, state, oldstate) { + updateState: function(event) { var msg; document.documentElement.classList.remove("noVNC_connecting"); @@ -398,7 +398,7 @@ var UI = { document.documentElement.classList.remove("noVNC_disconnecting"); document.documentElement.classList.remove("noVNC_reconnecting"); - switch (state) { + switch (event.detail.state) { case 'connecting': document.getElementById("noVNC_transition_text").textContent = _("Connecting..."); document.documentElement.classList.add("noVNC_connecting"); @@ -534,8 +534,8 @@ var UI = { document.getElementById('noVNC_status').classList.remove("noVNC_open"); }, - notification: function (rfb, msg, level) { - UI.showStatus(msg, level); + notification: function (e) { + UI.showStatus(e.detail.message, e.detail.level); }, activateControlbar: function(event) { @@ -966,9 +966,9 @@ var UI = { } }, - clipboardReceive: function(rfb, text) { - Log.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "..."); - document.getElementById('noVNC_clipboard_text').value = text; + clipboardReceive: function(e) { + Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0,40) + "..."); + document.getElementById('noVNC_clipboard_text').value = e.detail.text; Log.Debug("<< UI.clipboardReceive"); }, @@ -1040,15 +1040,15 @@ var UI = { { shared: UI.getSetting('shared'), repeaterID: UI.getSetting('repeaterID'), credentials: { password: password } }); - UI.rfb.onnotification = UI.notification; - UI.rfb.onupdatestate = UI.updateState; - UI.rfb.ondisconnected = UI.disconnectFinished; - UI.rfb.oncredentialsrequired = UI.credentials; - UI.rfb.oncapabilities = function () { UI.updatePowerButton(); UI.initialResize(); }; - UI.rfb.onclipboard = UI.clipboardReceive; - UI.rfb.onbell = UI.bell; - UI.rfb.onfbresize = UI.updateSessionSize; - UI.rfb.ondesktopname = UI.updateDesktopName; + UI.rfb.addEventListener("notification", UI.notification); + UI.rfb.addEventListener("updatestate", UI.updateState); + UI.rfb.addEventListener("disconnect", UI.disconnectFinished); + UI.rfb.addEventListener("credentialsrequired", UI.credentials); + UI.rfb.addEventListener("capabilities", function () { UI.updatePowerButton(); UI.initialResize(); }); + UI.rfb.addEventListener("clipboard", UI.clipboardReceive); + UI.rfb.addEventListener("bell", UI.bell); + UI.rfb.addEventListener("fbresize", UI.updateSessionSize); + UI.rfb.addEventListener("desktopname", UI.updateDesktopName); }, disconnect: function() { @@ -1072,9 +1072,9 @@ var UI = { UI.connect(null, UI.reconnect_password); }, - disconnectFinished: function (rfb, reason) { - if (typeof reason !== 'undefined') { - UI.showStatus(reason, 'error'); + disconnectFinished: function (e) { + if (typeof e.detail.reason !== 'undefined') { + UI.showStatus(e.detail.reason, 'error'); } else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) { document.getElementById("noVNC_transition_text").textContent = _("Reconnecting..."); document.documentElement.classList.add("noVNC_reconnecting"); @@ -1105,7 +1105,7 @@ var UI = { * PASSWORD * ------v------*/ - credentials: function(rfb, types) { + credentials: function(e) { // FIXME: handle more types document.getElementById('noVNC_password_dlg') .classList.add('noVNC_open'); @@ -1656,7 +1656,7 @@ var UI = { WebUtil.init_logging(UI.getSetting('logging')); }, - updateSessionSize: function(rfb, width, height) { + updateSessionSize: function(e) { UI.updateViewClip(); UI.updateScaling(); UI.fixScrollbars(); @@ -1674,13 +1674,13 @@ var UI = { screen.style.overflow = ""; }, - updateDesktopName: function(rfb, name) { - UI.desktopName = name; + updateDesktopName: function(e) { + UI.desktopName = e.detail.name; // Display the desktop name in the document title - document.title = name + " - noVNC"; + document.title = e.detail.name + " - noVNC"; }, - bell: function(rfb) { + bell: function(e) { if (WebUtil.getConfigVar('bell', 'on') === 'on') { var promise = document.getElementById('noVNC_bell').play(); // The standards disagree on the return value here diff --git a/core/rfb.js b/core/rfb.js index 55d20995..a3546b00 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -14,6 +14,7 @@ import * as Log from './util/logging.js'; import _ from './util/localization.js'; import { decodeUTF8 } from './util/strings.js'; import { browserSupportsCursorURIs, isTouchDevice } from './util/browsers.js'; +import EventTargetMixin from './util/eventtarget.js'; import Display from "./display.js"; import Keyboard from "./input/keyboard.js"; import Mouse from "./input/mouse.js"; @@ -24,6 +25,7 @@ import KeyTable from "./input/keysym.js"; import XtScancode from "./input/xtscancodes.js"; import Inflator from "./inflator.js"; import { encodings, encodingName } from "./encodings.js"; +import "./util/polyfill.js"; /*jslint white: false, browser: true */ /*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES, KeyTable, Inflator, XtScancode */ @@ -265,18 +267,6 @@ RFB.prototype = { get isClipped() { return this._display.isClipped; }, - // ===== EVENT HANDLERS ===== - - onupdatestate: function () {}, // onupdatestate(rfb, state, oldstate): connection state change - onnotification: function () {}, // onnotification(rfb, msg, level, options): notification for the UI - ondisconnected: function () {}, // ondisconnected(rfb, reason): disconnection finished - oncredentialsrequired: function () {}, // oncredentialsrequired(rfb, types): VNC credentials are required - onclipboard: function () {}, // onclipboard(rfb, text): RFB clipboard contents received - onbell: function () {}, // onbell(rfb): RFB Bell message received - onfbresize: function () {}, // onfbresize(rfb, width, height): frame buffer resized - ondesktopname: function () {}, // ondesktopname(rfb, name): desktop name received - oncapabilities: function () {}, // oncapabilities(rfb, caps): the supported capabilities has changed - // ===== PUBLIC METHODS ===== disconnect: function () { @@ -510,7 +500,8 @@ RFB.prototype = { // State change actions this._rfb_connection_state = state; - this.onupdatestate(this, state, oldstate); + var event = new CustomEvent("updatestate", { detail: { state: state } }); + this.dispatchEvent(event); var smsg = "New state '" + state + "', was '" + oldstate + "'."; Log.Debug(smsg); @@ -526,14 +517,16 @@ RFB.prototype = { switch (state) { case 'disconnected': - // Call ondisconnected callback after onupdatestate since + // Fire disconnected event after updatestate event since // we don't know if the UI only displays the latest message if (this._rfb_disconnect_reason !== "") { - this.ondisconnected(this, this._rfb_disconnect_reason); + event = new CustomEvent("disconnect", + { detail: { reason: this._rfb_disconnect_reason } }); } else { // No reason means clean disconnect - this.ondisconnected(this); + event = new CustomEvent("disconnect", { detail: {} }); } + this.dispatchEvent(event); break; case 'connecting': @@ -603,12 +596,16 @@ RFB.prototype = { return; } - this.onnotification(this, msg, level); + var event = new CustomEvent("notification", + { detail: { message: msg, level: level } }); + this.dispatchEvent(event); }, _setCapability: function (cap, val) { this._capabilities[cap] = val; - this.oncapabilities(this, this._capabilities); + var event = new CustomEvent("capabilities", + { detail: { capabilities: this._capabilities } }); + this.dispatchEvent(event); }, _handle_message: function () { @@ -818,7 +815,9 @@ RFB.prototype = { if (!this._rfb_credentials.username || !this._rfb_credentials.password || !this._rfb_credentials.target) { - this.oncredentialsrequired(this, ["username", "password", "target"]); + var event = new CustomEvent("credentialsrequired", + { detail: { types: ["username", "password", "target"] } }); + this.dispatchEvent(event); return false; } @@ -835,7 +834,9 @@ RFB.prototype = { if (this._sock.rQwait("auth challenge", 16)) { return false; } if (!this._rfb_credentials.password) { - this.oncredentialsrequired(this, ["password"]); + var event = new CustomEvent("credentialsrequired", + { detail: { types: ["password"] } }); + this.dispatchEvent(event); return false; } @@ -1072,7 +1073,9 @@ RFB.prototype = { } // we're past the point where we could backtrack, so it's safe to call this - this.ondesktopname(this, this._fb_name); + var event = new CustomEvent("desktopname", + { detail: { name: this._fb_name } }); + this.dispatchEvent(event); this._resize(width, height); @@ -1189,7 +1192,9 @@ RFB.prototype = { if (this._viewOnly) { return true; } - this.onclipboard(this, text); + var event = new CustomEvent("clipboard", + { detail: { text: text } }); + this.dispatchEvent(event); return true; }, @@ -1285,7 +1290,8 @@ RFB.prototype = { case 2: // Bell Log.Debug("Bell"); - this.onbell(this); + var event = new CustomEvent("bell", { detail: {} }); + this.dispatchEvent(event); return true; case 3: // ServerCutText @@ -1437,7 +1443,11 @@ RFB.prototype = { this._destBuff = new Uint8Array(this._fb_width * this._fb_height * 4); this._display.resize(this._fb_width, this._fb_height); - this.onfbresize(this, this._fb_width, this._fb_height); + + var event = new CustomEvent("fbresize", + { detail: { width: this._fb_width, + height: this._fb_height } }); + this.dispatchEvent(event); this._timing.fbu_rt_start = (new Date()).getTime(); this._updateContinuousUpdates(); @@ -1450,6 +1460,8 @@ RFB.prototype = { }, }; +Object.assign(RFB.prototype, EventTargetMixin); + // Class Methods RFB.messages = { keyEvent: function (sock, keysym, down) { diff --git a/core/util/eventtarget.js b/core/util/eventtarget.js new file mode 100644 index 00000000..61bc7a1c --- /dev/null +++ b/core/util/eventtarget.js @@ -0,0 +1,40 @@ +/* + * noVNC: HTML5 VNC client + * Copyright 2017 Pierre Ossman for Cendio AB + * Licensed under MPL 2.0 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +var EventTargetMixin = { + _listeners: null, + + addEventListener: function(type, callback) { + if (!this._listeners) { + this._listeners = new Map(); + } + if (!this._listeners.has(type)) { + this._listeners.set(type, new Set()); + } + this._listeners.get(type).add(callback); + }, + + removeEventListener: function(type, callback) { + if (!this._listeners || !this._listeners.has(type)) { + return; + } + this._listeners.get(type).delete(callback); + }, + + dispatchEvent: function(event) { + if (!this._listeners || !this._listeners.has(event.type)) { + return true; + } + this._listeners.get(event.type).forEach(function (callback) { + callback.call(this, event); + }, this); + return !event.defaultPrevented; + }, +}; + +export default EventTargetMixin; diff --git a/core/util/polyfill.js b/core/util/polyfill.js new file mode 100644 index 00000000..8c600e6f --- /dev/null +++ b/core/util/polyfill.js @@ -0,0 +1,54 @@ +/* + * noVNC: HTML5 VNC client + * Copyright 2017 Pierre Ossman for noVNC + * Licensed under MPL 2.0 or any later version (see LICENSE.txt) + */ + +/* Polyfills to provide new APIs in old browsers */ + +/* Object.assign() (taken from MDN) */ +if (typeof Object.assign != 'function') { + // Must be writable: true, enumerable: false, configurable: true + Object.defineProperty(Object, "assign", { + value: function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }, + writable: true, + configurable: true + }); +} + +/* CustomEvent constructor (taken from MDN) */ +(function () { + function CustomEvent ( event, params ) { + params = params || { bubbles: false, cancelable: false, detail: undefined }; + var evt = document.createEvent( 'CustomEvent' ); + evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); + return evt; + } + + CustomEvent.prototype = window.Event.prototype; + + if (typeof window.CustomEvent !== "function") { + window.CustomEvent = CustomEvent; + } +})(); diff --git a/docs/API.md b/docs/API.md index 519d3bfe..983d7357 100644 --- a/docs/API.md +++ b/docs/API.md @@ -56,39 +56,41 @@ protocol stream. | `power` | `boolean` | Machine power control is available | `resize` | `boolean` | The framebuffer can be resized -### Event handlers +### Events -[`RFB.onupdatestate()`](#rfbonupdatestate) - - An event handler called when the connection state of the `RFB` - object changes. +[`updatestate`](#updatestate) + - The `updatestate` event is fired when the connection state of the + `RFB` object changes. -[`RFB.onnotification()`](#rfbonnotification) - - An event handler called when the `RFB` usage has a message to - display to the user. +[`notification`](#notification) + - The `notification` event is fired when the `RFB` usage has a + message to display to the user. -[`RFB.ondisconnected()`](#rfbondisconnected) - - An event handler called when the `RFB` object disconnects. +[`disconnect`](#disconnected) + - The `disconnect` event is fired when the `RFB` object disconnects. -[`RFB.oncredentialsrequired()`](#rfboncredentialsrequired) - - An event hander called when more credentials must be given to - continue. +[`credentialsrequired`](#credentialsrequired) + - The `credentialsrequired` event is fired when more credentials must + be given to continue. -[`RFB.onclipboard()`](#rfbonclipboard) - - An event handler called when clipboard data is received from the - server. +[`clipboard`](#clipboard) + - The `clipboard` event is fired when clipboard data is received from + the server. -[`RFB.onbell()`](#rfbonbell) - - An event handler called when a audible bell request is received +[`bell`](#bell) + - The `bell` event is fired when a audible bell request is received from the server. -[`RFB.onfbresize()`](#rfbonfbresize) - - An event handler called when the framebuffer size is changed. +[`fbresize`](#fbresize) + - The `fbresize` event is fired when the framebuffer size is changed. -[`RFB.ondesktopname()`](#rfbondesktopname) - - An event handler called when the remote desktop name changes. +[`desktopname`](#desktopname) + - The `desktopname` event is fired when the remote desktop name + changes. -[`RFB.oncapabilities()`](#rfboncapabilities) - - An event handler called when `RFB.capabilities` is updated. +[`capabilities`](#capabilities) + - The `capabilities` event is fired when `RFB.capabilities` is + updated. ### Methods @@ -96,9 +98,8 @@ protocol stream. - Disconnect from the server. [`RFB.sendCredentials()`](#rfbsendcredentials) - - Send credentials to server. Should be called after - [`oncredentialsrequired`](#rfboncredentialsrequired) has been - called. + - Send credentials to server. Should be called after the + [`credentialsrequired`](#credentialsrequired) event has fired. [`RFB.sendKey()`](#rfbsendKey) - Send a key event. @@ -175,10 +176,13 @@ connection to a specified VNC server. - A `DOMString` specifying the ID to provide to any VNC repeater encountered. -#### RFB.onupdatestate +#### updatestate -The `onupdatestate` event handler is fired after the noVNC connection -state changes. Here is a list of the states that are reported: +The `updatestate` event is fired after the noVNC connection state +changes. The `detail` property is an `Object` containg the property +`state` with the new connection state. + +Here is a list of the states that are reported: | connection state | description | ----------------- | ------------ @@ -191,91 +195,66 @@ Note that a `RFB` objects can not transition from the disconnected state in any way, a new instance of the object has to be created for new connections. -##### Syntax +#### notification - RFB.onupdatestate = function(rfb, state) { ... } +The `notification` event is fired when the `RFB` object wants a message +displayed to the user. The `detail` property is an `Object` containing +the following properties: -#### RFB.onnotification +| Property | Type | Description +| --------- | ----------- | ----------- +| `message` | `DOMString` | The message to display +| `level` | `DOMString` | The severity of the message -The `onnotification` event handler is fired when the `RFB` object wants -a message displayed to the user. **`msg`** is a `DOMString` specifying -the actual message, and **`level`** is a `DOMString` indicating the -severity of the message. The following levels are currently defined: +The following levels are currently defined: - `"normal"` - `"warn"` - `"error"` -##### Syntax +#### disconnect - RFB.onnotification = function(rfb, msg, level) { ... } +The `disconnect` event is fired when the connection has been +terminated. The `detail` property is an `Object` the optionally +contains the property `reason`. `reason` is a `DOMString` specifying +the reason in the event of an unexpected termination. `reason` will be +omitted for a clean termination. -#### RFB.ondisconnected +#### credentialsrequired -The `ondisconnected` event handler is fired when the connection has -been terminated. **`reason`** is `undefined` for a clean termination -and a `DOMString` specifying the reason in the event of an unexpected -termination. +The `credentialsrequired` event is fired when the server requests more +credentials than were specified to [`RFB()`](#rfb-1). The `detail` +property is an `Object` containing the property `types` which is an +`Array` of `DOMString` listing the credentials that are required. -##### Syntax +#### clipboard - RFB.ondisconnected = function(rfb, reason) { ... } +The `clipboard` event is fired when the server has sent clipboard data. +The `detail` property is an `Object` containing the property `text` +which is a `DOMString` with the clipboard data. -#### RFB.oncredentialsrequired +#### bell -The `oncredentialsrequired` event handler is fired when the server -requests more credentials than were specified to [`RFB()`](#rfb-1). The -**`types`** argument is a list of all the credentials that are -required. +The `bell` event is fired when the server has requested an audible +bell. -##### Syntax +#### fbresize - RFB.oncredentialsrequired = function(rfb, types) { ... } +The `fbresize` event is fired when the framebuffer has changed +dimensions. The `detail` property is an `Object` with the properties +`width` and `height` specifying the new dimensions. -#### RFB.onclipboard +#### desktopname -The `onclipboard` event handler is fired when the server has sent -clipboard data. +The `desktopname` event is fired when the name of the remote desktop +changes. The `detail` property is an `Object` with the property `name` +which is a `DOMString` specifying the new name. -##### Syntax +#### capabilities - RFB.onclipboard = function(rfb, text) { ... } - -#### RFB.onbell - -The `onbell` event handler is fired when the server has requested an -audible bell. - -##### Syntax - - RFB.onbell = function(rfb) { ... } - -#### RFB.onfbresize - -The `onfbresize` event handler is fired when the framebuffer has -changed dimensions. - -##### Syntax - - RFB.onfbresize = function(rfb, width, height) { ... } - -#### RFB.ondesktopname - -The `ondesktopname` event handler is fired when the name of the remote -desktop changes. - -##### Syntax - - RFB.ondesktopname = function(rfb, name) { ... } - -#### RFB.oncapabilities - -The `oncapabilities` event handler is fired whenever an entry is added -or removed from `RFB.capabilities`. - -##### Syntax - - RFB.oncapabilities = function(rfb, capabilites) { ... } +The `capabilities` event is fired whenever an entry is added or removed +from `RFB.capabilities`. The `detail` property is an `Object` with the +property `capabilities` containing the new value of `RFB.capabilities`. #### RFB.disconnect() @@ -289,7 +268,7 @@ connected server. #### RFB.sendCredentials() The `RFB.sendCredentials()` method is used to provide the missing -credentials after `RFB.oncredentialsrequired` has been fired. +credentials after a `credentialsrequired` event has been fired. ##### Syntax @@ -405,7 +384,7 @@ the framebuffer. The capability `resize` must be set for this method to have any effect. Note that this is merely a request and the server may deny it. -[`RFB.onfbresize`](#rfbonfbresize) will be called when the framebuffer +The [`fbresize`](#fbresize) event will be fired when the framebuffer actually changes dimensions. ##### Syntax diff --git a/tests/test.rfb.js b/tests/test.rfb.js index c218ae07..4dbe1840 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -68,10 +68,11 @@ describe('Remote Frame Buffer Protocol Client', function() { describe('#RFB', function () { it('should set the current state to "connecting"', function () { var client = new RFB(document.createElement('canvas'), 'wss://host:8675'); - client.onupdatestate = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("updatestate", spy); this.clock.tick(); - expect(client.onupdatestate).to.have.been.calledOnce; - expect(client.onupdatestate).to.have.been.calledWith(client, 'connecting'); + expect(spy).to.have.been.calledOnce; + expect(spy.args[0][0].detail.state).to.equal('connecting'); }); it('should actually connect to the websocket', function () { @@ -90,11 +91,12 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should set the current state to "disconnecting"', function () { - client.onupdatestate = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("updatestate", spy); client.disconnect(); - expect(client.onupdatestate).to.have.been.calledTwice; - expect(client.onupdatestate).to.have.been.calledWith(client, 'disconnecting'); - expect(client.onupdatestate).to.have.been.calledWith(client, 'disconnected'); + expect(spy).to.have.been.calledTwice; + expect(spy.args[0][0].detail.state).to.equal('disconnecting'); + expect(spy.args[1][0].detail.state).to.equal('disconnected'); }); it('should unregister error event handler', function () { @@ -319,10 +321,10 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should call the updateState callback', function () { - client.onupdatestate = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("updatestate", spy); client._updateConnectionState('disconnecting'); - var spy = client.onupdatestate; - expect(spy.args[0][1]).to.equal('disconnecting'); + expect(spy.args[0][0].detail.state).to.equal('disconnecting'); }); it('should set the rfb_connection_state', function () { @@ -338,19 +340,19 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should ignore state changes to the same state', function () { - client.onupdatestate = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("updatestate", spy); client._rfb_connection_state = 'connecting'; client._updateConnectionState('connecting'); - var spy = client.onupdatestate; expect(spy).to.not.have.been.called; }); it('should ignore illegal state changes', function () { - client.onupdatestate = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("updatestate", spy); client._rfb_connection_state = 'connected'; client._updateConnectionState('disconnected'); expect(client._rfb_connection_state).to.not.equal('disconnected'); - var spy = client.onupdatestate; expect(spy).to.not.have.been.called; }); }); @@ -389,13 +391,12 @@ describe('Remote Frame Buffer Protocol Client', function() { it('should result in disconnect callback with message when reason given', function () { client._rfb_connection_state = 'connected'; - client.ondisconnected = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("disconnect", spy); client._fail('a reason'); - var spy = client.ondisconnected; this.clock.tick(2000); expect(spy).to.have.been.calledOnce; - expect(spy.args[0].length).to.equal(2); - expect(spy.args[0][1]).to.equal('a reason'); + expect(spy.args[0][0].detail.reason).to.equal('a reason'); }); }); @@ -405,18 +406,18 @@ describe('Remote Frame Buffer Protocol Client', function() { beforeEach(function () { client = make_rfb(); }); it('should call the notification callback', function () { - client.onnotification = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("notification", spy); client._notification('notify!', 'warn'); - var spy = client.onnotification; expect(spy).to.have.been.calledOnce; - expect(spy.args[0][1]).to.equal('notify!'); - expect(spy.args[0][2]).to.equal('warn'); + expect(spy.args[0][0].detail.message).to.equal('notify!'); + expect(spy.args[0][0].detail.level).to.equal('warn'); }); it('should not call the notification callback when level is invalid', function () { - client.onnotification = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("notification", spy); client._notification('notify!', 'invalid'); - var spy = client.onnotification; expect(spy).to.not.have.been.called; }); }); @@ -459,40 +460,40 @@ describe('Remote Frame Buffer Protocol Client', function() { beforeEach(function () { client = make_rfb(); }); it('should call the disconnect callback if the state is "disconnected"', function () { - client.ondisconnected = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("disconnect", spy); client._rfb_connection_state = 'disconnecting'; client._rfb_disconnect_reason = "error"; client._updateConnectionState('disconnected'); - var spy = client.ondisconnected; expect(spy).to.have.been.calledOnce; - expect(spy.args[0][1]).to.equal("error"); + expect(spy.args[0][0].detail.reason).to.equal("error"); }); it('should not call the disconnect callback if the state is not "disconnected"', function () { - client.ondisconnected = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("disconnect", spy); client._sock._websocket.close = function () {}; // explicitly don't call onclose client._updateConnectionState('disconnecting'); - var spy = client.ondisconnected; expect(spy).to.not.have.been.called; }); it('should call the disconnect callback without msg when no reason given', function () { - client.ondisconnected = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("disconnect", spy); client._rfb_connection_state = 'disconnecting'; client._rfb_disconnect_reason = ""; client._updateConnectionState('disconnected'); - var spy = client.ondisconnected; expect(spy).to.have.been.calledOnce; expect(spy.args[0].length).to.equal(1); }); it('should call the updateState callback before the disconnect callback', function () { - client.ondisconnected = sinon.spy(); - client.onupdatestate = sinon.spy(); + var updateStateSpy = sinon.spy(); + var disconnectSpy = sinon.spy(); + client.addEventListener("disconnect", disconnectSpy); + client.addEventListener("updatestate", updateStateSpy); client._rfb_connection_state = 'disconnecting'; client._updateConnectionState('disconnected'); - var updateStateSpy = client.onupdatestate; - var disconnectSpy = client.ondisconnected; expect(updateStateSpy.calledBefore(disconnectSpy)).to.be.true; }); }); @@ -715,18 +716,18 @@ describe('Remote Frame Buffer Protocol Client', function() { client._rfb_version = 3.8; }); - it('should call the onCredentialsRequired callback if missing a password', function () { - client.oncredentialsrequired = sinon.spy(); + it('should fire the credentialsrequired event if missing a password', function () { + var spy = sinon.spy(); + client.addEventListener("credentialsrequired", spy); send_security(2, client); var challenge = []; for (var i = 0; i < 16; i++) { challenge[i] = i; } client._sock._websocket._receive_data(new Uint8Array(challenge)); - var spy = client.oncredentialsrequired; expect(client._rfb_credentials).to.be.empty; expect(spy).to.have.been.calledOnce; - expect(spy.args[0][1]).to.have.members(["password"]); + expect(spy.args[0][0].detail.types).to.have.members(["password"]); }); it('should encrypt the password with DES and then send it back', function () { @@ -769,26 +770,26 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce; }); - it('should call the onCredentialsRequired callback if all credentials are missing', function() { - client.oncredentialsrequired = sinon.spy(); + it('should fire the credentialsrequired event if all credentials are missing', function() { + var spy = sinon.spy(); + client.addEventListener("credentialsrequired", spy); client._rfb_credentials = {}; send_security(22, client); - var spy = client.oncredentialsrequired; expect(client._rfb_credentials).to.be.empty; expect(spy).to.have.been.calledOnce; - expect(spy.args[0][1]).to.have.members(["username", "password", "target"]); + expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]); }); - it('should call the onCredentialsRequired callback if some credentials are missing', function() { - client.oncredentialsrequired = sinon.spy(); + it('should fire the credentialsrequired event if some credentials are missing', function() { + var spy = sinon.spy(); + client.addEventListener("credentialsrequired", spy); client._rfb_credentials = { username: 'user', target: 'target' }; send_security(22, client); - var spy = client.oncredentialsrequired; expect(spy).to.have.been.calledOnce; - expect(spy.args[0][1]).to.have.members(["username", "password", "target"]); + expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]); }); it('should send user and target separately', function () { @@ -998,13 +999,13 @@ describe('Remote Frame Buffer Protocol Client', function() { // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them it('should set the framebuffer name and call the callback', function () { - client.ondesktopname = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("desktopname", spy); send_server_init({ name: 'some name' }, client); - var spy = client.ondesktopname; expect(client._fb_name).to.equal('some name'); expect(spy).to.have.been.calledOnce; - expect(spy.args[0][1]).to.equal('some name'); + expect(spy.args[0][0].detail.name).to.equal('some name'); }); it('should handle the extended init message of the tight encoding', function () { @@ -1027,16 +1028,16 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should call the resize callback and resize the display', function () { - client.onfbresize = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("fbresize", spy); sinon.spy(client._display, 'resize'); send_server_init({ width: 27, height: 32 }, client); - var spy = client.onfbresize; expect(client._display.resize).to.have.been.calledOnce; expect(client._display.resize).to.have.been.calledWith(27, 32); expect(spy).to.have.been.calledOnce; - expect(spy.args[0][1]).to.equal(27); - expect(spy.args[0][2]).to.equal(32); + expect(spy.args[0][0].detail.width).to.equal(27); + expect(spy.args[0][0].detail.height).to.equal(32); }); it('should grab the mouse and keyboard', function () { @@ -1436,13 +1437,14 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should handle the DesktopSize pseduo-encoding', function () { - client.onfbresize = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("fbresize", spy); sinon.spy(client._display, 'resize'); send_fbu_msg([{ x: 0, y: 0, width: 20, height: 50, encoding: -223 }], [[]], client); - var spy = client.onfbresize; expect(spy).to.have.been.calledOnce; - expect(spy).to.have.been.calledWith(sinon.match.any, 20, 50); + expect(spy.args[0][0].detail.width).to.equal(20); + expect(spy.args[0][0].detail.height).to.equal(50); expect(client._fb_width).to.equal(20); expect(client._fb_height).to.equal(50); @@ -1452,6 +1454,8 @@ describe('Remote Frame Buffer Protocol Client', function() { }); describe('the ExtendedDesktopSize pseudo-encoding handler', function () { + var resizeSpy; + beforeEach(function () { client._supportsSetDesktopSize = false; // a really small frame @@ -1459,7 +1463,8 @@ describe('Remote Frame Buffer Protocol Client', function() { client._fb_height = 4; client._display.resize(4, 4); sinon.spy(client._display, 'resize'); - client.onfbresize = sinon.spy(); + resizeSpy = sinon.spy(); + client.addEventListener("fbresize", resizeSpy); }); function make_screen_data (nr_of_screens) { @@ -1479,7 +1484,8 @@ describe('Remote Frame Buffer Protocol Client', function() { } it('should call callback when resize is supported', function () { - client.oncapabilities = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("capabilities", spy); expect(client._supportsSetDesktopSize).to.be.false; expect(client.capabilities.resize).to.be.false; @@ -1492,8 +1498,8 @@ describe('Remote Frame Buffer Protocol Client', function() { make_screen_data(1), client); expect(client._supportsSetDesktopSize).to.be.true; - expect(client.oncapabilities).to.have.been.calledOnce; - expect(client.oncapabilities.args[0][1].resize).to.be.true; + expect(spy).to.have.been.calledOnce; + expect(spy.args[0][0].detail.capabilities.resize).to.be.true; expect(client.capabilities.resize).to.be.true; }), @@ -1511,9 +1517,9 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._display.resize).to.have.been.calledOnce; expect(client._display.resize).to.have.been.calledWith(20, 50); - var spy = client.onfbresize; - expect(spy).to.have.been.calledOnce; - expect(spy).to.have.been.calledWith(sinon.match.any, 20, 50); + expect(resizeSpy).to.have.been.calledOnce; + expect(resizeSpy.args[0][0].detail.width).to.equal(20); + expect(resizeSpy.args[0][0].detail.height).to.equal(50); }); it('should handle a resize requested by another client', function () { @@ -1530,9 +1536,9 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._display.resize).to.have.been.calledOnce; expect(client._display.resize).to.have.been.calledWith(20, 50); - var spy = client.onfbresize; - expect(spy).to.have.been.calledOnce; - expect(spy).to.have.been.calledWith(sinon.match.any, 20, 50); + expect(resizeSpy).to.have.been.calledOnce; + expect(resizeSpy.args[0][0].detail.width).to.equal(20); + expect(resizeSpy.args[0][0].detail.height).to.equal(50); }); it('should be able to recieve requests which contain data for multiple screens', function () { @@ -1549,9 +1555,9 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._display.resize).to.have.been.calledOnce; expect(client._display.resize).to.have.been.calledWith(60, 50); - var spy = client.onfbresize; - expect(spy).to.have.been.calledOnce; - expect(spy).to.have.been.calledWith(sinon.match.any, 60, 50); + expect(resizeSpy).to.have.been.calledOnce; + expect(resizeSpy.args[0][0].detail.width).to.equal(60); + expect(resizeSpy.args[0][0].detail.height).to.equal(50); }); it('should not handle a failed request', function () { @@ -1567,8 +1573,7 @@ describe('Remote Frame Buffer Protocol Client', function() { expect(client._display.resize).to.not.have.been.called; - var spy = client.onfbresize; - expect(spy).to.not.have.been.called; + expect(resizeSpy).to.not.have.been.called; }); }); @@ -1585,19 +1590,20 @@ describe('Remote Frame Buffer Protocol Client', function() { describe('XVP Message Handling', function () { it('should send a notification on XVP_FAIL', function () { - client.onnotification = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("notification", spy); client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 0])); - var spy = client.onnotification; expect(spy).to.have.been.calledOnce; - expect(spy.args[0][1]).to.equal('XVP Operation Failed'); + expect(spy.args[0][0].detail.message).to.equal('XVP Operation Failed'); }); it('should set the XVP version and fire the callback with the version on XVP_INIT', function () { - client.oncapabilities = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("capabilities", spy); client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 1])); expect(client._rfb_xvp_ver).to.equal(10); - expect(client.oncapabilities).to.have.been.calledOnce; - expect(client.oncapabilities.args[0][1].power).to.be.true; + expect(spy).to.have.been.calledOnce; + expect(spy.args[0][0].detail.capabilities.power).to.be.true; expect(client.capabilities.power).to.be.true; }); @@ -1613,18 +1619,19 @@ describe('Remote Frame Buffer Protocol Client', function() { var data = [3, 0, 0, 0]; push32(data, expected_str.length); for (var i = 0; i < expected_str.length; i++) { data.push(expected_str.charCodeAt(i)); } - client.onclipboard = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("clipboard", spy); client._sock._websocket._receive_data(new Uint8Array(data)); - var spy = client.onclipboard; expect(spy).to.have.been.calledOnce; - expect(spy.args[0][1]).to.equal(expected_str); + expect(spy.args[0][0].detail.text).to.equal(expected_str); }); it('should fire the bell callback on Bell', function () { - client.onbell = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("bell", spy); client._sock._websocket._receive_data(new Uint8Array([2])); - expect(client.onbell).to.have.been.calledOnce; + expect(spy).to.have.been.calledOnce; }); it('should respond correctly to ServerFence', function () { @@ -1850,9 +1857,10 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should process all normal messages directly', function () { - client.onbell = sinon.spy(); + var spy = sinon.spy(); + client.addEventListener("bell", spy); client._sock._websocket._receive_data(new Uint8Array([0x02, 0x02])); - expect(client.onbell).to.have.been.calledTwice; + expect(spy).to.have.been.calledTwice; }); // open events diff --git a/vnc_lite.html b/vnc_lite.html index 1a952f1b..6575b463 100644 --- a/vnc_lite.html +++ b/vnc_lite.html @@ -98,10 +98,10 @@ UIresize(); doneInitialResize = true; } - function updateDesktopName(rfb, name) { - desktopName = name; + function updateDesktopName(e) { + desktopName = e.detail.name; } - function credentials(rfb, types) { + function credentials(e) { var html; var form = document.createElement('form'); @@ -148,9 +148,9 @@ document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_" + level); document.getElementById('noVNC_status').textContent = text; } - function updateState(rfb, state, oldstate) { + function updateState(e) { var cad = document.getElementById('sendCtrlAltDelButton'); - switch (state) { + switch (e.detail.state) { case 'connecting': status("Connecting", "normal"); break; @@ -172,11 +172,11 @@ status("Disconnected", "normal"); break; default: - status(state, "warn"); + status(e.detail.state, "warn"); break; } - if (state === 'connected') { + if (e.detail.state === 'connected') { cad.disabled = false; } else { cad.disabled = true; @@ -184,13 +184,13 @@ } } - function disconnected(rfb, reason) { - if (typeof(reason) !== 'undefined') { - status(reason, "error"); + function disconnect(e) { + if (typeof(e.detail.reason) !== 'undefined') { + status(e.detail.reason, "error"); } } - function notification(rfb, msg, level) { - status(msg, level); + function notification(e) { + status(e.detail.message, e.detail.level); } window.onresize = function () { @@ -275,12 +275,12 @@ shared: WebUtil.getConfigVar('shared', true), credentials: { password: password } }); rfb.viewOnly = WebUtil.getConfigVar('view_only', false); - rfb.onnotification = notification; - rfb.onupdatestate = updateState; - rfb.ondisconnected = disconnected; - rfb.oncapabilities = function () { updatePowerButtons(); initialResize(); }; - rfb.oncredentialsrequired = credentials; - rfb.ondesktopname = updateDesktopName; + rfb.addEventListener("notification", notification); + rfb.addEventListener("updatestate", updateState); + rfb.addEventListener("disconnect", disconnect); + rfb.addEventListener("capabilities", function () { updatePowerButtons(); initialResize(); }); + rfb.addEventListener("credentialsrequired", credentials); + rfb.addEventListener("desktopname", updateDesktopName); })();