1 /* jshint expr: true */
2 var assert
= chai
.assert
;
3 var expect
= chai
.expect
;
5 import RFB
from '../core/rfb.js';
6 import Websock
from '../core/websock.js';
7 import { encodings
} from '../core/encodings.js';
9 import FakeWebSocket
from './fake.websocket.js';
10 import sinon
from '../vendor/sinon.js';
12 function make_rfb (extra_opts
) {
17 extra_opts
.target
= extra_opts
.target
|| document
.createElement('canvas');
18 return new RFB(extra_opts
);
21 var push8 = function (arr
, num
) {
26 var push16 = function (arr
, num
) {
28 arr
.push((num
>> 8) & 0xFF,
32 var push32 = function (arr
, num
) {
34 arr
.push((num
>> 24) & 0xFF,
40 describe('Remote Frame Buffer Protocol Client', function() {
42 before(FakeWebSocket
.replace
);
43 after(FakeWebSocket
.restore
);
46 this.clock
= sinon
.useFakeTimers();
47 // Use a single set of buffers instead of reallocating to
49 var sock
= new Websock();
50 var _sQ
= new Uint8Array(sock
._sQbufferSize
);
51 var rQ
= new Uint8Array(sock
._rQbufferSize
);
53 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
54 Websock
.prototype._allocate_buffers = function () {
62 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
66 describe('Public API Basic Behavior', function () {
68 beforeEach(function () {
72 describe('#connect', function () {
73 beforeEach(function () { client
._updateConnectionState
= sinon
.spy(); });
75 it('should set the current state to "connecting"', function () {
76 client
.connect('host', 8675);
77 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
78 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('connecting');
81 it('should not try to connect if we are missing a host', function () {
82 client
._fail
= sinon
.spy();
83 client
._rfb_connection_state
= '';
84 client
.connect(undefined, 8675);
85 expect(client
._fail
).to
.have
.been
.calledOnce
;
86 expect(client
._updateConnectionState
).to
.not
.have
.been
.called
;
87 expect(client
._rfb_connection_state
).to
.equal('');
91 describe('#disconnect', function () {
92 beforeEach(function () { client
._updateConnectionState
= sinon
.spy(); });
94 it('should set the current state to "disconnecting"', function () {
96 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
97 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('disconnecting');
100 it('should unregister error event handler', function () {
101 sinon
.spy(client
._sock
, 'off');
103 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
106 it('should unregister message event handler', function () {
107 sinon
.spy(client
._sock
, 'off');
109 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
112 it('should unregister open event handler', function () {
113 sinon
.spy(client
._sock
, 'off');
115 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
119 describe('#sendPassword', function () {
120 beforeEach(function () { this.clock
= sinon
.useFakeTimers(); });
121 afterEach(function () { this.clock
.restore(); });
123 it('should set the rfb password properly"', function () {
124 client
.sendPassword('pass');
125 expect(client
._rfb_password
).to
.equal('pass');
128 it('should call init_msg "soon"', function () {
129 client
._init_msg
= sinon
.spy();
130 client
.sendPassword('pass');
132 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
136 describe('#sendCtrlAlDel', function () {
137 beforeEach(function () {
138 client
._sock
= new Websock();
139 client
._sock
.open('ws://', 'binary');
140 client
._sock
._websocket
._open();
141 sinon
.spy(client
._sock
, 'flush');
142 client
._rfb_connection_state
= 'connected';
143 client
._view_only
= false;
146 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
147 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
148 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
149 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
150 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
151 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
152 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
153 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
155 client
.sendCtrlAltDel();
156 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
159 it('should not send the keys if we are not in a normal state', function () {
160 client
._rfb_connection_state
= "broken";
161 client
.sendCtrlAltDel();
162 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
165 it('should not send the keys if we are set as view_only', function () {
166 client
._view_only
= true;
167 client
.sendCtrlAltDel();
168 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
172 describe('#sendKey', function () {
173 beforeEach(function () {
174 client
._sock
= new Websock();
175 client
._sock
.open('ws://', 'binary');
176 client
._sock
._websocket
._open();
177 sinon
.spy(client
._sock
, 'flush');
178 client
._rfb_connection_state
= 'connected';
179 client
._view_only
= false;
182 it('should send a single key with the given code and state (down = true)', function () {
183 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
184 RFB
.messages
.keyEvent(expected
, 123, 1);
185 client
.sendKey(123, 'Key123', true);
186 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
189 it('should send both a down and up event if the state is not specified', function () {
190 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
191 RFB
.messages
.keyEvent(expected
, 123, 1);
192 RFB
.messages
.keyEvent(expected
, 123, 0);
193 client
.sendKey(123, 'Key123');
194 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
197 it('should not send the key if we are not in a normal state', function () {
198 client
._rfb_connection_state
= "broken";
199 client
.sendKey(123, 'Key123');
200 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
203 it('should not send the key if we are set as view_only', function () {
204 client
._view_only
= true;
205 client
.sendKey(123, 'Key123');
206 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
209 it('should send QEMU extended events if supported', function () {
210 client
._qemuExtKeyEventSupported
= true;
211 var expected
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
212 RFB
.messages
.QEMUExtendedKeyEvent(expected
, 0x20, true, 0x0039);
213 client
.sendKey(0x20, 'Space', true);
214 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
217 it('should not send QEMU extended events if unknown key code', function () {
218 client
._qemuExtKeyEventSupported
= true;
219 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
220 RFB
.messages
.keyEvent(expected
, 123, 1);
221 client
.sendKey(123, 'FooBar', true);
222 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
226 describe('#clipboardPasteFrom', function () {
227 beforeEach(function () {
228 client
._sock
= new Websock();
229 client
._sock
.open('ws://', 'binary');
230 client
._sock
._websocket
._open();
231 sinon
.spy(client
._sock
, 'flush');
232 client
._rfb_connection_state
= 'connected';
233 client
._view_only
= false;
236 it('should send the given text in a paste event', function () {
237 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0, flush: function () {}};
238 RFB
.messages
.clientCutText(expected
, 'abc');
239 client
.clipboardPasteFrom('abc');
240 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
243 it('should not send the text if we are not in a normal state', function () {
244 client
._rfb_connection_state
= "broken";
245 client
.clipboardPasteFrom('abc');
246 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
250 describe("#requestDesktopSize", function () {
251 beforeEach(function() {
252 client
._sock
= new Websock();
253 client
._sock
.open('ws://', 'binary');
254 client
._sock
._websocket
._open();
255 sinon
.spy(client
._sock
, 'flush');
256 client
._rfb_connection_state
= 'connected';
257 client
._view_only
= false;
258 client
._supportsSetDesktopSize
= true;
261 it('should send the request with the given width and height', function () {
262 var expected
= [251];
263 push8(expected
, 0); // padding
264 push16(expected
, 1); // width
265 push16(expected
, 2); // height
266 push8(expected
, 1); // number-of-screens
267 push8(expected
, 0); // padding before screen array
268 push32(expected
, 0); // id
269 push16(expected
, 0); // x-position
270 push16(expected
, 0); // y-position
271 push16(expected
, 1); // width
272 push16(expected
, 2); // height
273 push32(expected
, 0); // flags
275 client
.requestDesktopSize(1, 2);
276 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
279 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
280 client
._supportsSetDesktopSize
= false;
281 client
.requestDesktopSize(1,2);
282 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
285 it('should not send the request if we are not in a normal state', function () {
286 client
._rfb_connection_state
= "broken";
287 client
.requestDesktopSize(1,2);
288 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
292 describe("XVP operations", function () {
293 beforeEach(function () {
294 client
._sock
= new Websock();
295 client
._sock
.open('ws://', 'binary');
296 client
._sock
._websocket
._open();
297 sinon
.spy(client
._sock
, 'flush');
298 client
._rfb_connection_state
= 'connected';
299 client
._view_only
= false;
300 client
._rfb_xvp_ver
= 1;
303 it('should send the shutdown signal on #xvpShutdown', function () {
304 client
.xvpShutdown();
305 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
308 it('should send the reboot signal on #xvpReboot', function () {
310 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
313 it('should send the reset signal on #xvpReset', function () {
315 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
318 it('should support sending arbitrary XVP operations via #xvpOp', function () {
320 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x07]));
323 it('should not send XVP operations with higher versions than we support', function () {
324 expect(client
.xvpOp(2, 7)).to
.be
.false;
325 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
330 describe('Misc Internals', function () {
331 describe('#_updateConnectionState', function () {
333 beforeEach(function () {
334 this.clock
= sinon
.useFakeTimers();
338 afterEach(function () {
339 this.clock
.restore();
342 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
343 var spy
= sinon
.spy();
344 client
._disconnTimer
= setTimeout(spy
, 50);
345 client
._updateConnectionState('connecting');
347 expect(spy
).to
.not
.have
.been
.called
;
348 expect(client
._disconnTimer
).to
.be
.null;
351 it('should call the updateState callback', function () {
352 client
.set_onUpdateState(sinon
.spy());
353 client
._updateConnectionState('connecting');
354 var spy
= client
.get_onUpdateState();
355 expect(spy
).to
.have
.been
.calledOnce
;
356 expect(spy
.args
[0][1]).to
.equal('connecting');
359 it('should set the rfb_connection_state', function () {
360 client
._rfb_connection_state
= 'disconnecting';
361 client
._updateConnectionState('disconnected');
362 expect(client
._rfb_connection_state
).to
.equal('disconnected');
365 it('should not change the state when we are disconnected', function () {
366 client
._rfb_connection_state
= 'disconnected';
367 client
._updateConnectionState('connecting');
368 expect(client
._rfb_connection_state
).to
.not
.equal('connecting');
371 it('should ignore state changes to the same state', function () {
372 client
.set_onUpdateState(sinon
.spy());
373 client
._rfb_connection_state
= 'connecting';
374 client
._updateConnectionState('connecting');
375 var spy
= client
.get_onUpdateState();
376 expect(spy
).to
.not
.have
.been
.called
;
379 it('should ignore illegal state changes', function () {
380 client
.set_onUpdateState(sinon
.spy());
381 client
._rfb_connection_state
= 'connected';
382 client
._updateConnectionState('disconnected');
383 expect(client
._rfb_connection_state
).to
.not
.equal('disconnected');
384 var spy
= client
.get_onUpdateState();
385 expect(spy
).to
.not
.have
.been
.called
;
389 describe('#_fail', function () {
391 beforeEach(function () {
392 this.clock
= sinon
.useFakeTimers();
394 client
.connect('host', 8675);
397 afterEach(function () {
398 this.clock
.restore();
401 it('should close the WebSocket connection', function () {
402 sinon
.spy(client
._sock
, 'close');
404 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
407 it('should transition to disconnected', function () {
408 sinon
.spy(client
, '_updateConnectionState');
410 this.clock
.tick(2000);
411 expect(client
._updateConnectionState
).to
.have
.been
.called
;
412 expect(client
._rfb_connection_state
).to
.equal('disconnected');
415 it('should set disconnect_reason', function () {
416 client
._rfb_connection_state
= 'connected';
417 client
._fail('a reason');
418 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
421 it('should not include details in disconnect_reason', function () {
422 client
._rfb_connection_state
= 'connected';
423 client
._fail('a reason', 'details');
424 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
427 it('should result in disconnect callback with message when reason given', function () {
428 client
._rfb_connection_state
= 'connected';
429 client
.set_onDisconnected(sinon
.spy());
430 client
._fail('a reason');
431 var spy
= client
.get_onDisconnected();
432 this.clock
.tick(2000);
433 expect(spy
).to
.have
.been
.calledOnce
;
434 expect(spy
.args
[0].length
).to
.equal(2);
435 expect(spy
.args
[0][1]).to
.equal('a reason');
440 describe('#_notification', function () {
442 beforeEach(function () { client
= make_rfb(); });
444 it('should call the notification callback', function () {
445 client
.set_onNotification(sinon
.spy());
446 client
._notification('notify!', 'warn');
447 var spy
= client
.get_onNotification();
448 expect(spy
).to
.have
.been
.calledOnce
;
449 expect(spy
.args
[0][1]).to
.equal('notify!');
450 expect(spy
.args
[0][2]).to
.equal('warn');
453 it('should not call the notification callback when level is invalid', function () {
454 client
.set_onNotification(sinon
.spy());
455 client
._notification('notify!', 'invalid');
456 var spy
= client
.get_onNotification();
457 expect(spy
).to
.not
.have
.been
.called
;
462 describe('Connection States', function () {
463 describe('connecting', function () {
465 beforeEach(function () { client
= make_rfb(); });
467 it('should reset the variable states', function () {
468 sinon
.spy(client
, '_init_vars');
469 client
._updateConnectionState('connecting');
470 expect(client
._init_vars
).to
.have
.been
.calledOnce
;
473 it('should actually connect to the websocket', function () {
474 sinon
.spy(client
._sock
, 'open');
475 client
._updateConnectionState('connecting');
476 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
479 it('should use wss:// to connect if encryption is enabled', function () {
480 sinon
.spy(client
._sock
, 'open');
481 client
.set_encrypt(true);
482 client
._updateConnectionState('connecting');
483 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
486 it('should use ws:// to connect if encryption is not enabled', function () {
487 sinon
.spy(client
._sock
, 'open');
488 client
.set_encrypt(true);
489 client
._updateConnectionState('connecting');
490 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
493 it('should use a uri with the host, port, and path specified to connect', function () {
494 sinon
.spy(client
._sock
, 'open');
495 client
.set_encrypt(false);
496 client
._rfb_host
= 'HOST';
497 client
._rfb_port
= 8675;
498 client
._rfb_path
= 'PATH';
499 client
._updateConnectionState('connecting');
500 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
503 it('should not include a port in the uri if not specified in connect', function () {
504 sinon
.spy(client
._sock
, 'open');
505 client
.set_encrypt(true);
506 client
.connect('HOST', undefined)
507 expect(client
._sock
.open
).to
.have
.been
.calledWith('wss://HOST/');
511 describe('disconnecting', function () {
513 beforeEach(function () {
514 this.clock
= sinon
.useFakeTimers();
516 client
.connect('host', 8675);
519 afterEach(function () {
520 this.clock
.restore();
523 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
524 sinon
.spy(client
, '_updateConnectionState');
525 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
526 client
._updateConnectionState('disconnecting');
527 this.clock
.tick(client
.get_disconnectTimeout() * 1000);
528 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
529 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
530 expect(client
._rfb_connection_state
).to
.equal("disconnected");
533 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
534 client
._updateConnectionState('disconnecting');
535 this.clock
.tick(client
.get_disconnectTimeout() * 500);
536 client
._sock
._websocket
.close();
537 this.clock
.tick(client
.get_disconnectTimeout() * 500 + 1);
538 expect(client
._rfb_connection_state
).to
.equal('disconnected');
541 it('should close the WebSocket connection', function () {
542 sinon
.spy(client
._sock
, 'close');
543 client
._updateConnectionState('disconnecting');
544 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
548 describe('disconnected', function () {
550 beforeEach(function () { client
= make_rfb(); });
552 it('should call the disconnect callback if the state is "disconnected"', function () {
553 client
.set_onDisconnected(sinon
.spy());
554 client
._rfb_connection_state
= 'disconnecting';
555 client
._rfb_disconnect_reason
= "error";
556 client
._updateConnectionState('disconnected');
557 var spy
= client
.get_onDisconnected();
558 expect(spy
).to
.have
.been
.calledOnce
;
559 expect(spy
.args
[0][1]).to
.equal("error");
562 it('should not call the disconnect callback if the state is not "disconnected"', function () {
563 client
.set_onDisconnected(sinon
.spy());
564 client
._updateConnectionState('disconnecting');
565 var spy
= client
.get_onDisconnected();
566 expect(spy
).to
.not
.have
.been
.called
;
569 it('should call the disconnect callback without msg when no reason given', function () {
570 client
.set_onDisconnected(sinon
.spy());
571 client
._rfb_connection_state
= 'disconnecting';
572 client
._rfb_disconnect_reason
= "";
573 client
._updateConnectionState('disconnected');
574 var spy
= client
.get_onDisconnected();
575 expect(spy
).to
.have
.been
.calledOnce
;
576 expect(spy
.args
[0].length
).to
.equal(1);
579 it('should call the updateState callback before the disconnect callback', function () {
580 client
.set_onDisconnected(sinon
.spy());
581 client
.set_onUpdateState(sinon
.spy());
582 client
._rfb_connection_state
= 'disconnecting';
583 client
._updateConnectionState('disconnected');
584 var updateStateSpy
= client
.get_onUpdateState();
585 var disconnectSpy
= client
.get_onDisconnected();
586 expect(updateStateSpy
.calledBefore(disconnectSpy
)).to
.be
.true;
590 // NB(directxman12): Connected does *nothing* in updateConnectionState
593 describe('Protocol Initialization States', function () {
594 describe('ProtocolVersion', function () {
595 beforeEach(function () {
596 this.clock
= sinon
.useFakeTimers();
599 afterEach(function () {
600 this.clock
.restore();
603 function send_ver (ver
, client
) {
604 var arr
= new Uint8Array(12);
605 for (var i
= 0; i
< ver
.length
; i
++) {
606 arr
[i
+4] = ver
.charCodeAt(i
);
608 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
610 client
._sock
._websocket
._receive_data(arr
);
613 describe('version parsing', function () {
615 beforeEach(function () {
617 client
.connect('host', 8675);
618 client
._sock
._websocket
._open();
621 it('should interpret version 000.000 as a repeater', function () {
622 client
._repeaterID
= '\x01\x02\x03\x04\x05';
623 send_ver('000.000', client
);
624 expect(client
._rfb_version
).to
.equal(0);
626 var sent_data
= client
._sock
._websocket
._get_sent_data();
627 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
630 it('should interpret version 003.003 as version 3.3', function () {
631 send_ver('003.003', client
);
632 expect(client
._rfb_version
).to
.equal(3.3);
635 it('should interpret version 003.006 as version 3.3', function () {
636 send_ver('003.006', client
);
637 expect(client
._rfb_version
).to
.equal(3.3);
640 it('should interpret version 003.889 as version 3.3', function () {
641 send_ver('003.889', client
);
642 expect(client
._rfb_version
).to
.equal(3.3);
645 it('should interpret version 003.007 as version 3.7', function () {
646 send_ver('003.007', client
);
647 expect(client
._rfb_version
).to
.equal(3.7);
650 it('should interpret version 003.008 as version 3.8', function () {
651 send_ver('003.008', client
);
652 expect(client
._rfb_version
).to
.equal(3.8);
655 it('should interpret version 004.000 as version 3.8', function () {
656 send_ver('004.000', client
);
657 expect(client
._rfb_version
).to
.equal(3.8);
660 it('should interpret version 004.001 as version 3.8', function () {
661 send_ver('004.001', client
);
662 expect(client
._rfb_version
).to
.equal(3.8);
665 it('should interpret version 005.000 as version 3.8', function () {
666 send_ver('005.000', client
);
667 expect(client
._rfb_version
).to
.equal(3.8);
670 it('should fail on an invalid version', function () {
671 sinon
.spy(client
, "_fail");
672 send_ver('002.000', client
);
673 expect(client
._fail
).to
.have
.been
.calledOnce
;
678 beforeEach(function () {
680 client
.connect('host', 8675);
681 client
._sock
._websocket
._open();
684 it('should handle two step repeater negotiation', function () {
685 client
._repeaterID
= '\x01\x02\x03\x04\x05';
687 send_ver('000.000', client
);
688 expect(client
._rfb_version
).to
.equal(0);
689 var sent_data
= client
._sock
._websocket
._get_sent_data();
690 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
691 expect(sent_data
).to
.have
.length(250);
693 send_ver('003.008', client
);
694 expect(client
._rfb_version
).to
.equal(3.8);
697 it('should send back the interpreted version', function () {
698 send_ver('004.000', client
);
700 var expected_str
= 'RFB 003.008\n';
702 for (var i
= 0; i
< expected_str
.length
; i
++) {
703 expected
[i
] = expected_str
.charCodeAt(i
);
706 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
709 it('should transition to the Security state on successful negotiation', function () {
710 send_ver('003.008', client
);
711 expect(client
._rfb_init_state
).to
.equal('Security');
715 describe('Security', function () {
718 beforeEach(function () {
720 client
.connect('host', 8675);
721 client
._sock
._websocket
._open();
722 client
._rfb_init_state
= 'Security';
725 it('should simply receive the auth scheme when for versions < 3.7', function () {
726 client
._rfb_version
= 3.6;
727 var auth_scheme_raw
= [1, 2, 3, 4];
728 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
729 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
730 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
731 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
734 it('should prefer no authentication is possible', function () {
735 client
._rfb_version
= 3.7;
736 var auth_schemes
= [2, 1, 3];
737 client
._sock
._websocket
._receive_data(auth_schemes
);
738 expect(client
._rfb_auth_scheme
).to
.equal(1);
739 expect(client
._sock
).to
.have
.sent(new Uint8Array([1, 1]));
742 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
743 client
._rfb_version
= 3.7;
744 var auth_schemes
= [2, 22, 16];
745 client
._sock
._websocket
._receive_data(auth_schemes
);
746 expect(client
._rfb_auth_scheme
).to
.equal(22);
747 expect(client
._sock
).to
.have
.sent(new Uint8Array([22]));
750 it('should fail if there are no supported schemes for versions >= 3.7', function () {
751 sinon
.spy(client
, "_fail");
752 client
._rfb_version
= 3.7;
753 var auth_schemes
= [1, 32];
754 client
._sock
._websocket
._receive_data(auth_schemes
);
755 expect(client
._fail
).to
.have
.been
.calledOnce
;
758 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
759 client
._rfb_version
= 3.7;
760 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
761 sinon
.spy(client
, '_fail');
762 client
._sock
._websocket
._receive_data(failure_data
);
764 expect(client
._fail
).to
.have
.been
.calledOnce
;
765 expect(client
._fail
).to
.have
.been
.calledWith(
766 'Error while negotiating with server','Security failure: whoops');
769 it('should transition to the Authentication state and continue on successful negotiation', function () {
770 client
._rfb_version
= 3.7;
771 var auth_schemes
= [1, 1];
772 client
._negotiate_authentication
= sinon
.spy();
773 client
._sock
._websocket
._receive_data(auth_schemes
);
774 expect(client
._rfb_init_state
).to
.equal('Authentication');
775 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
779 describe('Authentication', function () {
782 beforeEach(function () {
784 client
.connect('host', 8675);
785 client
._sock
._websocket
._open();
786 client
._rfb_init_state
= 'Security';
789 function send_security(type
, cl
) {
790 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
793 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
794 client
._rfb_version
= 3.6;
795 var err_msg
= "Whoopsies";
796 var data
= [0, 0, 0, 0];
797 var err_len
= err_msg
.length
;
798 push32(data
, err_len
);
799 for (var i
= 0; i
< err_len
; i
++) {
800 data
.push(err_msg
.charCodeAt(i
));
803 sinon
.spy(client
, '_fail');
804 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
805 expect(client
._fail
).to
.have
.been
.calledWith(
806 'Authentication failure', 'Whoopsies');
809 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
810 client
._rfb_version
= 3.8;
811 send_security(1, client
);
812 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
815 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
816 client
._rfb_version
= 3.7;
817 send_security(1, client
);
818 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
821 it('should fail on an unknown auth scheme', function () {
822 sinon
.spy(client
, "_fail");
823 client
._rfb_version
= 3.8;
824 send_security(57, client
);
825 expect(client
._fail
).to
.have
.been
.calledOnce
;
828 describe('VNC Authentication (type 2) Handler', function () {
831 beforeEach(function () {
833 client
.connect('host', 8675);
834 client
._sock
._websocket
._open();
835 client
._rfb_init_state
= 'Security';
836 client
._rfb_version
= 3.8;
839 it('should call the passwordRequired callback if missing a password', function () {
840 client
.set_onPasswordRequired(sinon
.spy());
841 send_security(2, client
);
843 var spy
= client
.get_onPasswordRequired();
844 expect(client
._rfb_password
.length
).to
.equal(0);
845 expect(spy
).to
.have
.been
.calledOnce
;
848 it('should encrypt the password with DES and then send it back', function () {
849 client
._rfb_password
= 'passwd';
850 send_security(2, client
);
851 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
854 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
855 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
857 var des_pass
= RFB
.genDES('passwd', challenge
);
858 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
861 it('should transition to SecurityResult immediately after sending the password', function () {
862 client
._rfb_password
= 'passwd';
863 send_security(2, client
);
866 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
867 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
869 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
873 describe('XVP Authentication (type 22) Handler', function () {
876 beforeEach(function () {
878 client
.connect('host', 8675);
879 client
._sock
._websocket
._open();
880 client
._rfb_init_state
= 'Security';
881 client
._rfb_version
= 3.8;
884 it('should fall through to standard VNC authentication upon completion', function () {
885 client
.set_xvp_password_sep('#');
886 client
._rfb_password
= 'user#target#password';
887 client
._negotiate_std_vnc_auth
= sinon
.spy();
888 send_security(22, client
);
889 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
892 it('should call the passwordRequired callback if the password is missing', function() {
893 client
.set_onPasswordRequired(sinon
.spy());
894 client
._rfb_password
= '';
895 send_security(22, client
);
897 var spy
= client
.get_onPasswordRequired();
898 expect(client
._rfb_password
.length
).to
.equal(0);
899 expect(spy
).to
.have
.been
.calledOnce
;
902 it('should call the passwordRequired callback if the password is improperly formatted', function() {
903 client
.set_onPasswordRequired(sinon
.spy());
904 client
._rfb_password
= 'user@target';
905 send_security(22, client
);
907 var spy
= client
.get_onPasswordRequired();
908 expect(spy
).to
.have
.been
.calledOnce
;
911 it('should split the password, send the first two parts, and pass on the last part', function () {
912 client
.set_xvp_password_sep('#');
913 client
._rfb_password
= 'user#target#password';
914 client
._negotiate_std_vnc_auth
= sinon
.spy();
916 send_security(22, client
);
918 expect(client
._rfb_password
).to
.equal('password');
920 var expected
= [22, 4, 6]; // auth selection, len user, len target
921 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
923 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
927 describe('TightVNC Authentication (type 16) Handler', function () {
930 beforeEach(function () {
932 client
.connect('host', 8675);
933 client
._sock
._websocket
._open();
934 client
._rfb_init_state
= 'Security';
935 client
._rfb_version
= 3.8;
936 send_security(16, client
);
937 client
._sock
._websocket
._get_sent_data(); // skip the security reply
940 function send_num_str_pairs(pairs
, client
) {
941 var pairs_len
= pairs
.length
;
943 push32(data
, pairs_len
);
945 for (var i
= 0; i
< pairs_len
; i
++) {
946 push32(data
, pairs
[i
][0]);
948 for (j
= 0; j
< 4; j
++) {
949 data
.push(pairs
[i
][1].charCodeAt(j
));
951 for (j
= 0; j
< 8; j
++) {
952 data
.push(pairs
[i
][2].charCodeAt(j
));
956 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
959 it('should skip tunnel negotiation if no tunnels are requested', function () {
960 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
961 expect(client
._rfb_tightvnc
).to
.be
.true;
964 it('should fail if no supported tunnels are listed', function () {
965 sinon
.spy(client
, "_fail");
966 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
967 expect(client
._fail
).to
.have
.been
.calledOnce
;
970 it('should choose the notunnel tunnel type', function () {
971 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
972 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
975 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
976 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
977 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
978 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
979 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
980 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
983 /*it('should attempt to use VNC auth over no auth when possible', function () {
984 client._rfb_tightvnc = true;
985 client._negotiate_std_vnc_auth = sinon.spy();
986 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
987 expect(client._sock).to.have.sent([0, 0, 0, 1]);
988 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
989 expect(client._rfb_auth_scheme).to.equal(2);
990 });*/ // while this would make sense, the original code doesn't actually do this
992 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
993 client
._rfb_tightvnc
= true;
994 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
995 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
996 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
999 it('should accept VNC authentication and transition to that', function () {
1000 client
._rfb_tightvnc
= true;
1001 client
._negotiate_std_vnc_auth
= sinon
.spy();
1002 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
1003 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
1004 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
1005 expect(client
._rfb_auth_scheme
).to
.equal(2);
1008 it('should fail if there are no supported auth types', function () {
1009 sinon
.spy(client
, "_fail");
1010 client
._rfb_tightvnc
= true;
1011 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
1012 expect(client
._fail
).to
.have
.been
.calledOnce
;
1017 describe('SecurityResult', function () {
1020 beforeEach(function () {
1021 client
= make_rfb();
1022 client
.connect('host', 8675);
1023 client
._sock
._websocket
._open();
1024 client
._rfb_init_state
= 'SecurityResult';
1027 it('should fall through to ServerInitialisation on a response code of 0', function () {
1028 client
._updateConnectionState
= sinon
.spy();
1029 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1030 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1033 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
1034 client
._rfb_version
= 3.8;
1035 sinon
.spy(client
, '_fail');
1036 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
1037 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
1038 expect(client
._fail
).to
.have
.been
.calledWith(
1039 'Authentication failure', 'whoops');
1042 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
1043 sinon
.spy(client
, '_fail');
1044 client
._rfb_version
= 3.7;
1045 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
1046 expect(client
._fail
).to
.have
.been
.calledWith('Authentication failure');
1050 describe('ClientInitialisation', function () {
1053 beforeEach(function () {
1054 client
= make_rfb();
1055 client
.connect('host', 8675);
1056 client
._sock
._websocket
._open();
1057 client
._rfb_init_state
= 'SecurityResult';
1060 it('should transition to the ServerInitialisation state', function () {
1061 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1062 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1065 it('should send 1 if we are in shared mode', function () {
1066 client
.set_shared(true);
1067 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1068 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1071 it('should send 0 if we are not in shared mode', function () {
1072 client
.set_shared(false);
1073 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1074 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1078 describe('ServerInitialisation', function () {
1081 beforeEach(function () {
1082 client
= make_rfb();
1083 client
.connect('host', 8675);
1084 client
._sock
._websocket
._open();
1085 client
._rfb_init_state
= 'ServerInitialisation';
1088 function send_server_init(opts
, client
) {
1089 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1090 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1091 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1092 for (var opt
in opts
) {
1093 full_opts
[opt
] = opts
[opt
];
1097 push16(data
, full_opts
.width
);
1098 push16(data
, full_opts
.height
);
1100 data
.push(full_opts
.bpp
);
1101 data
.push(full_opts
.depth
);
1102 data
.push(full_opts
.big_endian
);
1103 data
.push(full_opts
.true_color
);
1105 push16(data
, full_opts
.red_max
);
1106 push16(data
, full_opts
.green_max
);
1107 push16(data
, full_opts
.blue_max
);
1108 push8(data
, full_opts
.red_shift
);
1109 push8(data
, full_opts
.green_shift
);
1110 push8(data
, full_opts
.blue_shift
);
1117 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1120 push32(name_data
, full_opts
.name
.length
);
1121 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1122 name_data
.push(full_opts
.name
.charCodeAt(i
));
1124 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1127 it('should set the framebuffer width and height', function () {
1128 send_server_init({ width
: 32, height
: 84 }, client
);
1129 expect(client
._fb_width
).to
.equal(32);
1130 expect(client
._fb_height
).to
.equal(84);
1133 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1135 it('should set the framebuffer name and call the callback', function () {
1136 client
.set_onDesktopName(sinon
.spy());
1137 send_server_init({ name
: 'some name' }, client
);
1139 var spy
= client
.get_onDesktopName();
1140 expect(client
._fb_name
).to
.equal('some name');
1141 expect(spy
).to
.have
.been
.calledOnce
;
1142 expect(spy
.args
[0][1]).to
.equal('some name');
1145 it('should handle the extended init message of the tight encoding', function () {
1146 // NB(sross): we don't actually do anything with it, so just test that we can
1147 // read it w/o throwing an error
1148 client
._rfb_tightvnc
= true;
1149 send_server_init({}, client
);
1151 var tight_data
= [];
1152 push16(tight_data
, 1);
1153 push16(tight_data
, 2);
1154 push16(tight_data
, 3);
1155 push16(tight_data
, 0);
1156 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1159 client
._sock
._websocket
._receive_data(tight_data
);
1161 expect(client
._rfb_connection_state
).to
.equal('connected');
1164 it('should call the resize callback and resize the display', function () {
1165 client
.set_onFBResize(sinon
.spy());
1166 sinon
.spy(client
._display
, 'resize');
1167 send_server_init({ width
: 27, height
: 32 }, client
);
1169 var spy
= client
.get_onFBResize();
1170 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1171 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1172 expect(spy
).to
.have
.been
.calledOnce
;
1173 expect(spy
.args
[0][1]).to
.equal(27);
1174 expect(spy
.args
[0][2]).to
.equal(32);
1177 it('should grab the mouse and keyboard', function () {
1178 sinon
.spy(client
._keyboard
, 'grab');
1179 sinon
.spy(client
._mouse
, 'grab');
1180 send_server_init({}, client
);
1181 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1182 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1185 describe('Initial Update Request', function () {
1186 beforeEach(function () {
1187 sinon
.spy(RFB
.messages
, "pixelFormat");
1188 sinon
.spy(RFB
.messages
, "clientEncodings");
1189 sinon
.spy(RFB
.messages
, "fbUpdateRequest");
1192 afterEach(function () {
1193 RFB
.messages
.pixelFormat
.restore();
1194 RFB
.messages
.clientEncodings
.restore();
1195 RFB
.messages
.fbUpdateRequest
.restore();
1198 // TODO(directxman12): test the various options in this configuration matrix
1199 it('should reply with the pixel format, client encodings, and initial update request', function () {
1200 send_server_init({ width
: 27, height
: 32 }, client
);
1202 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1203 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 24, true);
1204 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1205 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1206 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.include(encodings
.encodingTight
);
1207 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1208 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1209 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1212 it('should reply with restricted settings for Intel AMT servers', function () {
1213 send_server_init({ width
: 27, height
: 32, name
: "Intel(r) AMT KVM"}, client
);
1215 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledOnce
;
1216 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledWith(client
._sock
, 8, true);
1217 expect(RFB
.messages
.pixelFormat
).to
.have
.been
.calledBefore(RFB
.messages
.clientEncodings
);
1218 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledOnce
;
1219 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingTight
);
1220 expect(RFB
.messages
.clientEncodings
.getCall(0).args
[1]).to
.not
.include(encodings
.encodingHextile
);
1221 expect(RFB
.messages
.clientEncodings
).to
.have
.been
.calledBefore(RFB
.messages
.fbUpdateRequest
);
1222 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledOnce
;
1223 expect(RFB
.messages
.fbUpdateRequest
).to
.have
.been
.calledWith(client
._sock
, false, 0, 0, 27, 32);
1227 it('should transition to the "connected" state', function () {
1228 send_server_init({}, client
);
1229 expect(client
._rfb_connection_state
).to
.equal('connected');
1234 describe('Protocol Message Processing After Completing Initialization', function () {
1237 beforeEach(function () {
1238 client
= make_rfb();
1239 client
.connect('host', 8675);
1240 client
._sock
._websocket
._open();
1241 client
._rfb_connection_state
= 'connected';
1242 client
._fb_name
= 'some device';
1243 client
._fb_width
= 640;
1244 client
._fb_height
= 20;
1247 describe('Framebuffer Update Handling', function () {
1250 beforeEach(function () {
1251 client
= make_rfb();
1252 client
.connect('host', 8675);
1253 client
._sock
._websocket
._open();
1254 client
._rfb_connection_state
= 'connected';
1255 client
._fb_name
= 'some device';
1256 client
._fb_width
= 640;
1257 client
._fb_height
= 20;
1260 var target_data_arr
= [
1261 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1262 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1263 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1264 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1268 var target_data_check_arr
= [
1269 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1270 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1271 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1272 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1274 var target_data_check
;
1276 before(function () {
1277 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1278 target_data
= new Uint8Array(target_data_arr
);
1279 target_data_check
= new Uint8Array(target_data_check_arr
);
1282 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1285 if (!rect_cnt
|| rect_cnt
> -1) {
1287 data
.push(0); // msg type
1288 data
.push(0); // padding
1289 push16(data
, rect_cnt
|| rect_data
.length
);
1292 for (var i
= 0; i
< rect_data
.length
; i
++) {
1294 push16(data
, rect_info
[i
].x
);
1295 push16(data
, rect_info
[i
].y
);
1296 push16(data
, rect_info
[i
].width
);
1297 push16(data
, rect_info
[i
].height
);
1298 push32(data
, rect_info
[i
].encoding
);
1300 data
= data
.concat(rect_data
[i
]);
1303 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1306 it('should send an update request if there is sufficient data', function () {
1307 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1308 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1310 client
._framebufferUpdate = function () { return true; };
1311 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1313 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1316 it('should not send an update request if we need more data', function () {
1317 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1318 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1321 it('should resume receiving an update if we previously did not have enough data', function () {
1322 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1323 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 640, 20);
1325 // just enough to set FBU.rects
1326 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1327 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1329 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1330 // 247 should *not* be used as the message type here
1331 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1332 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1335 it('should not send a request in continuous updates mode', function () {
1336 client
._enabledContinuousUpdates
= true;
1337 client
._framebufferUpdate = function () { return true; };
1338 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1340 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1343 it('should parse out information from a header before any actual data comes in', function () {
1344 client
.set_onFBUReceive(sinon
.spy());
1345 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02, encodingName
: 'RRE' };
1346 send_fbu_msg([rect_info
], [[]], client
);
1348 var spy
= client
.get_onFBUReceive();
1349 expect(spy
).to
.have
.been
.calledOnce
;
1350 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1353 it('should fire onFBUComplete when the update is complete', function () {
1354 client
.set_onFBUComplete(sinon
.spy());
1355 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: -224, encodingName
: 'last_rect' };
1356 send_fbu_msg([rect_info
], [[]], client
); // last_rect
1358 var spy
= client
.get_onFBUComplete();
1359 expect(spy
).to
.have
.been
.calledOnce
;
1362 it('should not fire onFBUComplete if we have not finished processing the update', function () {
1363 client
.set_onFBUComplete(sinon
.spy());
1364 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x00, encodingName
: 'RAW' };
1365 send_fbu_msg([rect_info
], [[]], client
);
1366 expect(client
.get_onFBUComplete()).to
.not
.have
.been
.called
;
1369 it('should fail on an unsupported encoding', function () {
1370 sinon
.spy(client
, "_fail");
1371 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1372 send_fbu_msg([rect_info
], [[]], client
);
1373 expect(client
._fail
).to
.have
.been
.calledOnce
;
1376 it('should be able to pause and resume receiving rects if not enought data', function () {
1377 // seed some initial data to copy
1378 client
._fb_width
= 4;
1379 client
._fb_height
= 4;
1380 client
._display
.resize(4, 4);
1381 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1383 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1384 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1385 // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
1386 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1387 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1388 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1389 expect(client
._display
).to
.have
.displayed(target_data_check
);
1392 describe('Message Encoding Handlers', function () {
1395 beforeEach(function () {
1396 client
= make_rfb();
1397 client
.connect('host', 8675);
1398 client
._sock
._websocket
._open();
1399 client
._rfb_connection_state
= 'connected';
1400 client
._fb_name
= 'some device';
1401 // a really small frame
1402 client
._fb_width
= 4;
1403 client
._fb_height
= 4;
1404 client
._fb_depth
= 24;
1405 client
._display
.resize(4, 4);
1408 it('should handle the RAW encoding', function () {
1409 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1410 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1411 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1412 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1415 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1416 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1417 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1418 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1419 send_fbu_msg(info
, rects
, client
);
1420 expect(client
._display
).to
.have
.displayed(target_data
);
1423 it('should handle the RAW encoding in low colour mode', function () {
1424 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1425 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1426 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1427 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1429 [0x03, 0x03, 0x03, 0x03],
1430 [0x0c, 0x0c, 0x0c, 0x0c],
1431 [0x0c, 0x0c, 0x03, 0x03],
1432 [0x0c, 0x0c, 0x03, 0x03]];
1433 client
._fb_depth
= 8;
1434 send_fbu_msg(info
, rects
, client
);
1435 expect(client
._display
).to
.have
.displayed(target_data_check
);
1438 it('should handle the COPYRECT encoding', function () {
1439 // seed some initial data to copy
1440 client
._display
.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr
.slice(0, 32)), 0);
1442 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1443 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1444 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1445 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1446 send_fbu_msg(info
, rects
, client
);
1447 expect(client
._display
).to
.have
.displayed(target_data_check
);
1450 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1451 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1453 it('should handle the RRE encoding', function () {
1454 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1456 push32(rect
, 2); // 2 subrects
1457 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1458 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1462 push16(rect
, 0); // x: 0
1463 push16(rect
, 0); // y: 0
1464 push16(rect
, 2); // width: 2
1465 push16(rect
, 2); // height: 2
1466 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1470 push16(rect
, 2); // x: 2
1471 push16(rect
, 2); // y: 2
1472 push16(rect
, 2); // width: 2
1473 push16(rect
, 2); // height: 2
1475 send_fbu_msg(info
, [rect
], client
);
1476 expect(client
._display
).to
.have
.displayed(target_data_check
);
1479 describe('the HEXTILE encoding handler', function () {
1481 beforeEach(function () {
1482 client
= make_rfb();
1483 client
.connect('host', 8675);
1484 client
._sock
._websocket
._open();
1485 client
._rfb_connection_state
= 'connected';
1486 client
._fb_name
= 'some device';
1487 // a really small frame
1488 client
._fb_width
= 4;
1489 client
._fb_height
= 4;
1490 client
._display
.resize(4, 4);
1493 it('should handle a tile with fg, bg specified, normal subrects', function () {
1494 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1496 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1497 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1498 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1502 rect
.push(2); // 2 subrects
1503 rect
.push(0); // x: 0, y: 0
1504 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1505 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1506 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1507 send_fbu_msg(info
, [rect
], client
);
1508 expect(client
._display
).to
.have
.displayed(target_data_check
);
1511 it('should handle a raw tile', function () {
1512 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1514 rect
.push(0x01); // raw
1515 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1516 rect
.push(target_data
[i
+ 2]);
1517 rect
.push(target_data
[i
+ 1]);
1518 rect
.push(target_data
[i
]);
1519 rect
.push(target_data
[i
+ 3]);
1521 send_fbu_msg(info
, [rect
], client
);
1522 expect(client
._display
).to
.have
.displayed(target_data
);
1525 it('should handle a tile with only bg specified (solid bg)', function () {
1526 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1529 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1530 send_fbu_msg(info
, [rect
], client
);
1533 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1534 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1537 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1538 // set the width so we can have two tiles
1539 client
._fb_width
= 8;
1540 client
._display
.resize(8, 4);
1542 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1548 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1550 // send an empty frame
1553 send_fbu_msg(info
, [rect
], client
);
1557 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1558 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1559 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1562 it('should handle a tile with bg and coloured subrects', function () {
1563 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1565 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1566 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1567 rect
.push(2); // 2 subrects
1568 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1572 rect
.push(0); // x: 0, y: 0
1573 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1574 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1578 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1579 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1580 send_fbu_msg(info
, [rect
], client
);
1581 expect(client
._display
).to
.have
.displayed(target_data_check
);
1584 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1585 client
._fb_width
= 4;
1586 client
._fb_height
= 17;
1587 client
._display
.resize(4, 17);
1589 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1591 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1592 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1593 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1597 rect
.push(8); // 8 subrects
1599 for (i
= 0; i
< 4; i
++) {
1600 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1601 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1602 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1603 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1605 rect
.push(0x08); // anysubrects
1606 rect
.push(1); // 1 subrect
1607 rect
.push(0); // x: 0, y: 0
1608 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1609 send_fbu_msg(info
, [rect
], client
);
1612 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1613 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1614 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1617 it('should fail on an invalid subencoding', function () {
1618 sinon
.spy(client
,"_fail");
1619 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1620 var rects
= [[45]]; // an invalid subencoding
1621 send_fbu_msg(info
, rects
, client
);
1622 expect(client
._fail
).to
.have
.been
.calledOnce
;
1626 it
.skip('should handle the TIGHT encoding', function () {
1627 // TODO(directxman12): test this
1630 it
.skip('should handle the TIGHT_PNG encoding', function () {
1631 // TODO(directxman12): test this
1634 it('should handle the DesktopSize pseduo-encoding', function () {
1635 client
.set_onFBResize(sinon
.spy());
1636 sinon
.spy(client
._display
, 'resize');
1637 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1639 var spy
= client
.get_onFBResize();
1640 expect(spy
).to
.have
.been
.calledOnce
;
1641 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1643 expect(client
._fb_width
).to
.equal(20);
1644 expect(client
._fb_height
).to
.equal(50);
1646 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1647 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1650 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1653 beforeEach(function () {
1654 client
= make_rfb();
1655 client
.connect('host', 8675);
1656 client
._sock
._websocket
._open();
1657 client
._rfb_connection_state
= 'connected';
1658 client
._fb_name
= 'some device';
1659 client
._supportsSetDesktopSize
= false;
1660 // a really small frame
1661 client
._fb_width
= 4;
1662 client
._fb_height
= 4;
1663 client
._display
.resize(4, 4);
1664 sinon
.spy(client
._display
, 'resize');
1665 client
.set_onFBResize(sinon
.spy());
1668 function make_screen_data (nr_of_screens
) {
1670 push8(data
, nr_of_screens
); // number-of-screens
1671 push8(data
, 0); // padding
1672 push16(data
, 0); // padding
1673 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1674 push32(data
, 0); // id
1675 push16(data
, 0); // x-position
1676 push16(data
, 0); // y-position
1677 push16(data
, 20); // width
1678 push16(data
, 50); // height
1679 push32(data
, 0); // flags
1684 it('should handle a resize requested by this client', function () {
1685 var reason_for_change
= 1; // requested by this client
1686 var status_code
= 0; // No error
1688 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1689 width
: 20, height
: 50, encoding
: -308 }],
1690 make_screen_data(1), client
);
1692 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1693 expect(client
._fb_width
).to
.equal(20);
1694 expect(client
._fb_height
).to
.equal(50);
1696 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1697 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1699 var spy
= client
.get_onFBResize();
1700 expect(spy
).to
.have
.been
.calledOnce
;
1701 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1704 it('should handle a resize requested by another client', function () {
1705 var reason_for_change
= 2; // requested by another client
1706 var status_code
= 0; // No error
1708 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1709 width
: 20, height
: 50, encoding
: -308 }],
1710 make_screen_data(1), client
);
1712 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1713 expect(client
._fb_width
).to
.equal(20);
1714 expect(client
._fb_height
).to
.equal(50);
1716 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1717 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1719 var spy
= client
.get_onFBResize();
1720 expect(spy
).to
.have
.been
.calledOnce
;
1721 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1724 it('should be able to recieve requests which contain data for multiple screens', function () {
1725 var reason_for_change
= 2; // requested by another client
1726 var status_code
= 0; // No error
1728 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1729 width
: 60, height
: 50, encoding
: -308 }],
1730 make_screen_data(3), client
);
1732 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1733 expect(client
._fb_width
).to
.equal(60);
1734 expect(client
._fb_height
).to
.equal(50);
1736 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1737 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1739 var spy
= client
.get_onFBResize();
1740 expect(spy
).to
.have
.been
.calledOnce
;
1741 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 60, 50);
1744 it('should not handle a failed request', function () {
1745 var reason_for_change
= 1; // requested by this client
1746 var status_code
= 1; // Resize is administratively prohibited
1748 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1749 width
: 20, height
: 50, encoding
: -308 }],
1750 make_screen_data(1), client
);
1752 expect(client
._fb_width
).to
.equal(4);
1753 expect(client
._fb_height
).to
.equal(4);
1755 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1757 var spy
= client
.get_onFBResize();
1758 expect(spy
).to
.not
.have
.been
.called
;
1762 it
.skip('should handle the Cursor pseudo-encoding', function () {
1763 // TODO(directxman12): test
1766 it('should handle the last_rect pseudo-encoding', function () {
1767 client
.set_onFBUReceive(sinon
.spy());
1768 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1769 expect(client
._FBU
.rects
).to
.equal(0);
1770 expect(client
.get_onFBUReceive()).to
.have
.been
.calledOnce
;
1775 describe('XVP Message Handling', function () {
1776 beforeEach(function () {
1777 client
= make_rfb();
1778 client
.connect('host', 8675);
1779 client
._sock
._websocket
._open();
1780 client
._rfb_connection_state
= 'connected';
1781 client
._fb_name
= 'some device';
1782 client
._fb_width
= 27;
1783 client
._fb_height
= 32;
1786 it('should send a notification on XVP_FAIL', function () {
1787 client
.set_onNotification(sinon
.spy());
1788 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 0]));
1789 var spy
= client
.get_onNotification();
1790 expect(spy
).to
.have
.been
.calledOnce
;
1791 expect(spy
.args
[0][1]).to
.equal('XVP Operation Failed');
1794 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1795 client
.set_onXvpInit(sinon
.spy());
1796 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1797 expect(client
._rfb_xvp_ver
).to
.equal(10);
1798 expect(client
.get_onXvpInit()).to
.have
.been
.calledOnce
;
1799 expect(client
.get_onXvpInit()).to
.have
.been
.calledWith(10);
1802 it('should fail on unknown XVP message types', function () {
1803 sinon
.spy(client
, "_fail");
1804 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1805 expect(client
._fail
).to
.have
.been
.calledOnce
;
1809 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1810 var expected_str
= 'cheese!';
1811 var data
= [3, 0, 0, 0];
1812 push32(data
, expected_str
.length
);
1813 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1814 client
.set_onClipboard(sinon
.spy());
1816 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1817 var spy
= client
.get_onClipboard();
1818 expect(spy
).to
.have
.been
.calledOnce
;
1819 expect(spy
.args
[0][1]).to
.equal(expected_str
);
1822 it('should fire the bell callback on Bell', function () {
1823 client
.set_onBell(sinon
.spy());
1824 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1825 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
1828 it('should respond correctly to ServerFence', function () {
1829 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1830 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1832 var payload
= "foo\x00ab9";
1834 // ClientFence and ServerFence are identical in structure
1835 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
1836 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
1838 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1840 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1842 expected_msg
._sQlen
= 0;
1843 incoming_msg
._sQlen
= 0;
1845 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
1846 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
1848 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1850 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1853 it('should enable continuous updates on first EndOfContinousUpdates', function () {
1854 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1856 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
1858 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1860 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1862 expect(client
._enabledContinuousUpdates
).to
.be
.true;
1863 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1866 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
1867 client
._enabledContinuousUpdates
= true;
1868 client
._supportsContinuousUpdates
= true;
1870 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1872 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1875 it('should update continuous updates on resize', function () {
1876 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1877 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
1879 client
._resize(450, 160);
1881 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1883 client
._enabledContinuousUpdates
= true;
1885 client
._resize(90, 700);
1887 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1890 it('should fail on an unknown message type', function () {
1891 sinon
.spy(client
, "_fail");
1892 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1893 expect(client
._fail
).to
.have
.been
.calledOnce
;
1897 describe('Asynchronous Events', function () {
1898 describe('Mouse event handlers', function () {
1900 beforeEach(function () {
1901 client
= make_rfb();
1902 client
._sock
= new Websock();
1903 client
._sock
.open('ws://', 'binary');
1904 client
._sock
._websocket
._open();
1905 sinon
.spy(client
._sock
, 'flush');
1906 client
._rfb_connection_state
= 'connected';
1909 it('should not send button messages in view-only mode', function () {
1910 client
._view_only
= true;
1911 client
._mouse
._onMouseButton(0, 0, 1, 0x001);
1912 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1915 it('should not send movement messages in view-only mode', function () {
1916 client
._view_only
= true;
1917 client
._mouse
._onMouseMove(0, 0);
1918 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1921 it('should send a pointer event on mouse button presses', function () {
1922 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1923 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1924 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1925 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1928 it('should send a mask of 1 on mousedown', function () {
1929 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1930 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1931 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1932 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1935 it('should send a mask of 0 on mouseup', function () {
1936 client
._mouse_buttonMask
= 0x001;
1937 client
._mouse
._onMouseButton(10, 12, 0, 0x001);
1938 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1939 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1940 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1943 it('should send a pointer event on mouse movement', function () {
1944 client
._mouse
._onMouseMove(10, 12);
1945 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1946 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1947 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1950 it('should set the button mask so that future mouse movements use it', function () {
1951 client
._mouse
._onMouseButton(10, 12, 1, 0x010);
1952 client
._mouse
._onMouseMove(13, 9);
1953 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
1954 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1955 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1956 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1959 // NB(directxman12): we don't need to test not sending messages in
1960 // non-normal modes, since we haven't grabbed input
1961 // yet (grabbing input should be checked in the lifecycle tests).
1963 it('should not send movement messages when viewport dragging', function () {
1964 client
._viewportDragging
= true;
1965 client
._display
.viewportChangePos
= sinon
.spy();
1966 client
._mouse
._onMouseMove(13, 9);
1967 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1970 it('should not send button messages when initiating viewport dragging', function () {
1971 client
._viewportDrag
= true;
1972 client
._mouse
._onMouseButton(13, 9, 0x001);
1973 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1976 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1977 client
._viewportDrag
= true;
1978 client
._mouse
._onMouseButton(13, 9, 0x001);
1979 expect(client
._viewportDragging
).to
.be
.true;
1980 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
1983 it('should terminate viewport dragging on a button up event, if enabled', function () {
1984 client
._viewportDrag
= true;
1985 client
._viewportDragging
= true;
1986 client
._mouse
._onMouseButton(13, 9, 0x000);
1987 expect(client
._viewportDragging
).to
.be
.false;
1990 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
1993 var newX
= 123 + 11 * window
.devicePixelRatio
;
1994 var newY
= 109 + 4 * window
.devicePixelRatio
;
1996 client
._viewportDrag
= true;
1997 client
._viewportDragging
= true;
1998 client
._viewportHasMoved
= false;
1999 client
._viewportDragPos
= { x
: oldX
, y
: oldY
};
2000 client
._display
.viewportChangePos
= sinon
.spy();
2002 client
._mouse
._onMouseMove(newX
, newY
);
2004 expect(client
._viewportDragging
).to
.be
.true;
2005 expect(client
._viewportHasMoved
).to
.be
.true;
2006 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: newX
, y
: newY
});
2007 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
2008 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(oldX
- newX
, oldY
- newY
);
2012 describe('Keyboard Event Handlers', function () {
2014 beforeEach(function () {
2015 client
= make_rfb();
2016 client
._sock
= new Websock();
2017 client
._sock
.open('ws://', 'binary');
2018 client
._sock
._websocket
._open();
2019 sinon
.spy(client
._sock
, 'flush');
2020 client
._rfb_connection_state
= 'connected';
2021 client
._view_only
= false;
2024 it('should send a key message on a key press', function () {
2026 client
._keyboard
._onKeyEvent(0x41, 'KeyA', true);
2027 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
2028 RFB
.messages
.keyEvent(key_msg
, 0x41, 1);
2029 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
2032 it('should not send messages in view-only mode', function () {
2033 client
._view_only
= true;
2034 client
._keyboard
._onKeyEvent('a', 'KeyA', true);
2035 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2039 describe('WebSocket event handlers', function () {
2041 beforeEach(function () {
2042 client
= make_rfb();
2043 this.clock
= sinon
.useFakeTimers();
2046 afterEach(function () { this.clock
.restore(); });
2049 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
2050 client
.connect('host', 8675);
2051 client
._rfb_connection_state
= 'connected';
2052 client
._normal_msg
= sinon
.spy();
2053 client
._sock
._websocket
._receive_data(new Uint8Array([]));
2054 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
2057 it('should handle a message in the connected state as a normal message', function () {
2058 client
.connect('host', 8675);
2059 client
._rfb_connection_state
= 'connected';
2060 client
._normal_msg
= sinon
.spy();
2061 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2062 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
2065 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2066 client
.connect('host', 8675);
2067 client
._rfb_init_state
= 'ProtocolVersion';
2068 client
._init_msg
= sinon
.spy();
2069 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2070 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
2073 it('should process all normal messages directly', function () {
2074 client
.connect('host', 8675);
2075 client
._sock
._websocket
._open();
2076 client
._rfb_connection_state
= 'connected';
2077 client
.set_onBell(sinon
.spy());
2078 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2079 expect(client
.get_onBell()).to
.have
.been
.calledTwice
;
2083 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2084 client
.connect('host', 8675);
2085 client
._sock
._websocket
._open();
2086 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
2089 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2090 sinon
.spy(client
, "_fail");
2091 client
.connect('host', 8675);
2092 client
._rfb_connection_state
= 'some_other_state';
2093 client
._sock
._websocket
._open();
2094 expect(client
._fail
).to
.have
.been
.calledOnce
;
2098 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2099 client
.connect('host', 8675);
2100 client
._rfb_connection_state
= 'disconnecting';
2101 client
._sock
._websocket
.close();
2102 expect(client
._rfb_connection_state
).to
.equal('disconnected');
2105 it('should fail if we get a close event while connecting', function () {
2106 sinon
.spy(client
, "_fail");
2107 client
.connect('host', 8675);
2108 client
._rfb_connection_state
= 'connecting';
2109 client
._sock
._websocket
.close();
2110 expect(client
._fail
).to
.have
.been
.calledOnce
;
2113 it('should fail if we get a close event while disconnected', function () {
2114 sinon
.spy(client
, "_fail");
2115 client
.connect('host', 8675);
2116 client
._rfb_connection_state
= 'disconnected';
2117 client
._sock
._websocket
.close();
2118 expect(client
._fail
).to
.have
.been
.calledOnce
;
2121 it('should unregister close event handler', function () {
2122 sinon
.spy(client
._sock
, 'off');
2123 client
.connect('host', 8675);
2124 client
._rfb_connection_state
= 'disconnecting';
2125 client
._sock
._websocket
.close();
2126 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2129 // error events do nothing