test.rfb.js 181 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041
  1. const expect = chai.expect;
  2. import RFB from '../core/rfb.js';
  3. import Websock from '../core/websock.js';
  4. import ZStream from "../vendor/pako/lib/zlib/zstream.js";
  5. import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js";
  6. import { encodings } from '../core/encodings.js';
  7. import { toUnsigned32bit } from '../core/util/int.js';
  8. import { encodeUTF8 } from '../core/util/strings.js';
  9. import KeyTable from '../core/input/keysym.js';
  10. import FakeWebSocket from './fake.websocket.js';
  11. function push8(arr, num) {
  12. "use strict";
  13. arr.push(num & 0xFF);
  14. }
  15. function push16(arr, num) {
  16. "use strict";
  17. arr.push((num >> 8) & 0xFF,
  18. num & 0xFF);
  19. }
  20. function push32(arr, num) {
  21. "use strict";
  22. arr.push((num >> 24) & 0xFF,
  23. (num >> 16) & 0xFF,
  24. (num >> 8) & 0xFF,
  25. num & 0xFF);
  26. }
  27. function pushString(arr, string) {
  28. let utf8 = unescape(encodeURIComponent(string));
  29. for (let i = 0; i < utf8.length; i++) {
  30. arr.push(utf8.charCodeAt(i));
  31. }
  32. }
  33. function deflateWithSize(data) {
  34. // Adds the size of the string in front before deflating
  35. let unCompData = [];
  36. unCompData.push((data.length >> 24) & 0xFF,
  37. (data.length >> 16) & 0xFF,
  38. (data.length >> 8) & 0xFF,
  39. (data.length & 0xFF));
  40. for (let i = 0; i < data.length; i++) {
  41. unCompData.push(data.charCodeAt(i));
  42. }
  43. let strm = new ZStream();
  44. let chunkSize = 1024 * 10 * 10;
  45. strm.output = new Uint8Array(chunkSize);
  46. deflateInit(strm, 5);
  47. /* eslint-disable camelcase */
  48. strm.input = unCompData;
  49. strm.avail_in = strm.input.length;
  50. strm.next_in = 0;
  51. strm.next_out = 0;
  52. strm.avail_out = chunkSize;
  53. /* eslint-enable camelcase */
  54. deflate(strm, 3);
  55. return new Uint8Array(strm.output.buffer, 0, strm.next_out);
  56. }
  57. describe('Remote Frame Buffer Protocol Client', function () {
  58. let clock;
  59. let raf;
  60. let fakeResizeObserver = null;
  61. const realObserver = window.ResizeObserver;
  62. class FakeResizeObserver {
  63. constructor(handler) {
  64. this.fire = handler;
  65. fakeResizeObserver = this;
  66. }
  67. disconnect() {}
  68. observe(target, options) {}
  69. unobserve(target) {}
  70. }
  71. before(FakeWebSocket.replace);
  72. after(FakeWebSocket.restore);
  73. before(function () {
  74. this.clock = clock = sinon.useFakeTimers(Date.now());
  75. // sinon doesn't support this yet
  76. raf = window.requestAnimationFrame;
  77. window.requestAnimationFrame = setTimeout;
  78. // We must do this in a 'before' since it needs to be set before
  79. // the RFB constructor, which runs in beforeEach further down
  80. window.ResizeObserver = FakeResizeObserver;
  81. // Use a single set of buffers instead of reallocating to
  82. // speed up tests
  83. const sock = new Websock();
  84. const _sQ = new Uint8Array(sock._sQbufferSize);
  85. const rQ = new Uint8Array(sock._rQbufferSize);
  86. Websock.prototype._oldAllocateBuffers = Websock.prototype._allocateBuffers;
  87. Websock.prototype._allocateBuffers = function () {
  88. this._sQ = _sQ;
  89. this._rQ = rQ;
  90. };
  91. // Avoiding printing the entire Websock buffer on errors
  92. Websock.prototype.toString = function () { return "[object Websock]"; };
  93. });
  94. after(function () {
  95. delete Websock.prototype.toString;
  96. this.clock.restore();
  97. window.requestAnimationFrame = raf;
  98. window.ResizeObserver = realObserver;
  99. });
  100. let container;
  101. let rfbs;
  102. beforeEach(function () {
  103. // Create a container element for all RFB objects to attach to
  104. container = document.createElement('div');
  105. container.style.width = "100%";
  106. container.style.height = "100%";
  107. document.body.appendChild(container);
  108. // And track all created RFB objects
  109. rfbs = [];
  110. });
  111. afterEach(function () {
  112. // Make sure every created RFB object is properly cleaned up
  113. // or they might affect subsequent tests
  114. rfbs.forEach(function (rfb) {
  115. rfb.disconnect();
  116. expect(rfb._disconnect).to.have.been.called;
  117. });
  118. rfbs = [];
  119. document.body.removeChild(container);
  120. container = null;
  121. });
  122. function makeRFB(url, options) {
  123. url = url || 'wss://host:8675';
  124. const rfb = new RFB(container, url, options);
  125. clock.tick();
  126. rfb._sock._websocket._open();
  127. rfb._rfbConnectionState = 'connected';
  128. sinon.spy(rfb, "_disconnect");
  129. rfbs.push(rfb);
  130. return rfb;
  131. }
  132. describe('Connecting/Disconnecting', function () {
  133. describe('#RFB (constructor)', function () {
  134. let open, attach;
  135. beforeEach(function () {
  136. open = sinon.spy(Websock.prototype, 'open');
  137. attach = sinon.spy(Websock.prototype, 'attach');
  138. });
  139. afterEach(function () {
  140. open.restore();
  141. attach.restore();
  142. });
  143. it('should actually connect to the websocket', function () {
  144. new RFB(document.createElement('div'), 'ws://HOST:8675/PATH');
  145. expect(open).to.have.been.calledOnceWithExactly('ws://HOST:8675/PATH', []);
  146. });
  147. it('should pass on connection problems', function () {
  148. open.restore();
  149. open = sinon.stub(Websock.prototype, 'open');
  150. open.throws(new Error('Failure'));
  151. expect(() => new RFB(document.createElement('div'), 'ws://HOST:8675/PATH')).to.throw('Failure');
  152. });
  153. it('should handle WebSocket/RTCDataChannel objects', function () {
  154. let sock = new FakeWebSocket('ws://HOST:8675/PATH', []);
  155. new RFB(document.createElement('div'), sock);
  156. expect(open).to.not.have.been.called;
  157. expect(attach).to.have.been.calledOnceWithExactly(sock);
  158. });
  159. it('should handle already open WebSocket/RTCDataChannel objects', function () {
  160. let sock = new FakeWebSocket('ws://HOST:8675/PATH', []);
  161. sock._open();
  162. const client = new RFB(document.createElement('div'), sock);
  163. let callback = sinon.spy();
  164. client.addEventListener('disconnect', callback);
  165. expect(open).to.not.have.been.called;
  166. expect(attach).to.have.been.calledOnceWithExactly(sock);
  167. // Check if it is ready for some data
  168. sock._receiveData(new Uint8Array(['R', 'F', 'B', '0', '0', '3', '0', '0', '8']));
  169. expect(callback).to.not.have.been.called;
  170. });
  171. it('should refuse closed WebSocket/RTCDataChannel objects', function () {
  172. let sock = new FakeWebSocket('ws://HOST:8675/PATH', []);
  173. sock.readyState = WebSocket.CLOSED;
  174. expect(() => new RFB(document.createElement('div'), sock)).to.throw();
  175. });
  176. it('should pass on attach problems', function () {
  177. attach.restore();
  178. attach = sinon.stub(Websock.prototype, 'attach');
  179. attach.throws(new Error('Failure'));
  180. let sock = new FakeWebSocket('ws://HOST:8675/PATH', []);
  181. expect(() => new RFB(document.createElement('div'), sock)).to.throw('Failure');
  182. });
  183. });
  184. describe('#disconnect', function () {
  185. let client;
  186. let close;
  187. beforeEach(function () {
  188. client = makeRFB();
  189. close = sinon.stub(Websock.prototype, "close");
  190. });
  191. afterEach(function () {
  192. close.restore();
  193. });
  194. it('should start closing WebSocket', function () {
  195. let callback = sinon.spy();
  196. client.addEventListener('disconnect', callback);
  197. client.disconnect();
  198. expect(close).to.have.been.calledOnceWithExactly();
  199. expect(callback).to.not.have.been.called;
  200. });
  201. it('should send disconnect event', function () {
  202. let callback = sinon.spy();
  203. client.addEventListener('disconnect', callback);
  204. client.disconnect();
  205. close.thisValues[0]._eventHandlers.close(new CloseEvent("close", { 'code': 1000, 'reason': "", 'wasClean': true }));
  206. expect(callback).to.have.been.calledOnce;
  207. expect(callback.args[0][0].detail.clean).to.be.true;
  208. });
  209. it('should force disconnect if disconnecting takes too long', function () {
  210. let callback = sinon.spy();
  211. client.addEventListener('disconnect', callback);
  212. client.disconnect();
  213. this.clock.tick(3 * 1000);
  214. expect(callback).to.have.been.calledOnce;
  215. expect(callback.args[0][0].detail.clean).to.be.true;
  216. });
  217. it('should not fail if disconnect completes before timeout', function () {
  218. let callback = sinon.spy();
  219. client.addEventListener('disconnect', callback);
  220. client.disconnect();
  221. client._updateConnectionState('disconnecting');
  222. this.clock.tick(3 * 1000 / 2);
  223. close.thisValues[0]._eventHandlers.close(new CloseEvent("close", { 'code': 1000, 'reason': "", 'wasClean': true }));
  224. this.clock.tick(3 * 1000 / 2 + 1);
  225. expect(callback).to.have.been.calledOnce;
  226. expect(callback.args[0][0].detail.clean).to.be.true;
  227. });
  228. it('should unregister error event handler', function () {
  229. sinon.spy(client._sock, 'off');
  230. client.disconnect();
  231. expect(client._sock.off).to.have.been.calledWith('error');
  232. });
  233. it('should unregister message event handler', function () {
  234. sinon.spy(client._sock, 'off');
  235. client.disconnect();
  236. expect(client._sock.off).to.have.been.calledWith('message');
  237. });
  238. it('should unregister open event handler', function () {
  239. sinon.spy(client._sock, 'off');
  240. client.disconnect();
  241. expect(client._sock.off).to.have.been.calledWith('open');
  242. });
  243. });
  244. describe('#sendCredentials', function () {
  245. let client;
  246. beforeEach(function () {
  247. client = makeRFB();
  248. client._rfbConnectionState = 'connecting';
  249. });
  250. it('should set the rfb credentials properly"', function () {
  251. client.sendCredentials({ password: 'pass' });
  252. expect(client._rfbCredentials).to.deep.equal({ password: 'pass' });
  253. });
  254. it('should call initMsg "soon"', function () {
  255. client._initMsg = sinon.spy();
  256. client.sendCredentials({ password: 'pass' });
  257. this.clock.tick(5);
  258. expect(client._initMsg).to.have.been.calledOnce;
  259. });
  260. });
  261. });
  262. describe('Public API Basic Behavior', function () {
  263. let client;
  264. beforeEach(function () {
  265. client = makeRFB();
  266. });
  267. describe('#sendCtrlAlDel', function () {
  268. it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
  269. const expected = {_sQ: new Uint8Array(48), _sQlen: 0, flush: () => {}};
  270. RFB.messages.keyEvent(expected, 0xFFE3, 1);
  271. RFB.messages.keyEvent(expected, 0xFFE9, 1);
  272. RFB.messages.keyEvent(expected, 0xFFFF, 1);
  273. RFB.messages.keyEvent(expected, 0xFFFF, 0);
  274. RFB.messages.keyEvent(expected, 0xFFE9, 0);
  275. RFB.messages.keyEvent(expected, 0xFFE3, 0);
  276. client.sendCtrlAltDel();
  277. expect(client._sock).to.have.sent(expected._sQ);
  278. });
  279. it('should not send the keys if we are not in a normal state', function () {
  280. sinon.spy(client._sock, 'flush');
  281. client._rfbConnectionState = "connecting";
  282. client.sendCtrlAltDel();
  283. expect(client._sock.flush).to.not.have.been.called;
  284. });
  285. it('should not send the keys if we are set as view_only', function () {
  286. sinon.spy(client._sock, 'flush');
  287. client._viewOnly = true;
  288. client.sendCtrlAltDel();
  289. expect(client._sock.flush).to.not.have.been.called;
  290. });
  291. });
  292. describe('#sendKey', function () {
  293. it('should send a single key with the given code and state (down = true)', function () {
  294. const expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: () => {}};
  295. RFB.messages.keyEvent(expected, 123, 1);
  296. client.sendKey(123, 'Key123', true);
  297. expect(client._sock).to.have.sent(expected._sQ);
  298. });
  299. it('should send both a down and up event if the state is not specified', function () {
  300. const expected = {_sQ: new Uint8Array(16), _sQlen: 0, flush: () => {}};
  301. RFB.messages.keyEvent(expected, 123, 1);
  302. RFB.messages.keyEvent(expected, 123, 0);
  303. client.sendKey(123, 'Key123');
  304. expect(client._sock).to.have.sent(expected._sQ);
  305. });
  306. it('should not send the key if we are not in a normal state', function () {
  307. sinon.spy(client._sock, 'flush');
  308. client._rfbConnectionState = "connecting";
  309. client.sendKey(123, 'Key123');
  310. expect(client._sock.flush).to.not.have.been.called;
  311. });
  312. it('should not send the key if we are set as view_only', function () {
  313. sinon.spy(client._sock, 'flush');
  314. client._viewOnly = true;
  315. client.sendKey(123, 'Key123');
  316. expect(client._sock.flush).to.not.have.been.called;
  317. });
  318. it('should send QEMU extended events if supported', function () {
  319. client._qemuExtKeyEventSupported = true;
  320. const expected = {_sQ: new Uint8Array(12), _sQlen: 0, flush: () => {}};
  321. RFB.messages.QEMUExtendedKeyEvent(expected, 0x20, true, 0x0039);
  322. client.sendKey(0x20, 'Space', true);
  323. expect(client._sock).to.have.sent(expected._sQ);
  324. });
  325. it('should not send QEMU extended events if unknown key code', function () {
  326. client._qemuExtKeyEventSupported = true;
  327. const expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: () => {}};
  328. RFB.messages.keyEvent(expected, 123, 1);
  329. client.sendKey(123, 'FooBar', true);
  330. expect(client._sock).to.have.sent(expected._sQ);
  331. });
  332. });
  333. describe('#focus', function () {
  334. it('should move focus to canvas object', function () {
  335. client._canvas.focus = sinon.spy();
  336. client.focus();
  337. expect(client._canvas.focus).to.have.been.calledOnce;
  338. });
  339. });
  340. describe('#blur', function () {
  341. it('should remove focus from canvas object', function () {
  342. client._canvas.blur = sinon.spy();
  343. client.blur();
  344. expect(client._canvas.blur).to.have.been.calledOnce;
  345. });
  346. });
  347. describe('#clipboardPasteFrom', function () {
  348. describe('Clipboard update handling', function () {
  349. beforeEach(function () {
  350. sinon.spy(RFB.messages, 'clientCutText');
  351. sinon.spy(RFB.messages, 'extendedClipboardNotify');
  352. });
  353. afterEach(function () {
  354. RFB.messages.clientCutText.restore();
  355. RFB.messages.extendedClipboardNotify.restore();
  356. });
  357. it('should send the given text in an clipboard update', function () {
  358. client.clipboardPasteFrom('abc');
  359. expect(RFB.messages.clientCutText).to.have.been.calledOnce;
  360. expect(RFB.messages.clientCutText).to.have.been.calledWith(client._sock,
  361. new Uint8Array([97, 98, 99]));
  362. });
  363. it('should send an notify if extended clipboard is supported by server', function () {
  364. // Send our capabilities
  365. let data = [3, 0, 0, 0];
  366. const flags = [0x1F, 0x00, 0x00, 0x01];
  367. let fileSizes = [0x00, 0x00, 0x00, 0x1E];
  368. push32(data, toUnsigned32bit(-8));
  369. data = data.concat(flags);
  370. data = data.concat(fileSizes);
  371. client._sock._websocket._receiveData(new Uint8Array(data));
  372. client.clipboardPasteFrom('extended test');
  373. expect(RFB.messages.extendedClipboardNotify).to.have.been.calledOnce;
  374. });
  375. });
  376. it('should flush multiple times for large clipboards', function () {
  377. sinon.spy(client._sock, 'flush');
  378. let longText = "";
  379. for (let i = 0; i < client._sock._sQbufferSize + 100; i++) {
  380. longText += 'a';
  381. }
  382. client.clipboardPasteFrom(longText);
  383. expect(client._sock.flush).to.have.been.calledTwice;
  384. });
  385. it('should not send the text if we are not in a normal state', function () {
  386. sinon.spy(client._sock, 'flush');
  387. client._rfbConnectionState = "connecting";
  388. client.clipboardPasteFrom('abc');
  389. expect(client._sock.flush).to.not.have.been.called;
  390. });
  391. });
  392. describe("XVP operations", function () {
  393. beforeEach(function () {
  394. client._rfbXvpVer = 1;
  395. });
  396. it('should send the shutdown signal on #machineShutdown', function () {
  397. client.machineShutdown();
  398. expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
  399. });
  400. it('should send the reboot signal on #machineReboot', function () {
  401. client.machineReboot();
  402. expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
  403. });
  404. it('should send the reset signal on #machineReset', function () {
  405. client.machineReset();
  406. expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
  407. });
  408. it('should not send XVP operations with higher versions than we support', function () {
  409. sinon.spy(client._sock, 'flush');
  410. client._xvpOp(2, 7);
  411. expect(client._sock.flush).to.not.have.been.called;
  412. });
  413. });
  414. });
  415. describe('Clipping', function () {
  416. let client;
  417. beforeEach(function () {
  418. client = makeRFB();
  419. container.style.width = '70px';
  420. container.style.height = '80px';
  421. client.clipViewport = true;
  422. });
  423. it('should update display clip state when changing the property', function () {
  424. const spy = sinon.spy(client._display, "clipViewport", ["set"]);
  425. client.clipViewport = false;
  426. expect(spy.set).to.have.been.calledOnce;
  427. expect(spy.set).to.have.been.calledWith(false);
  428. spy.set.resetHistory();
  429. client.clipViewport = true;
  430. expect(spy.set).to.have.been.calledOnce;
  431. expect(spy.set).to.have.been.calledWith(true);
  432. });
  433. it('should update the viewport when the container size changes', function () {
  434. sinon.spy(client._display, "viewportChangeSize");
  435. container.style.width = '40px';
  436. container.style.height = '50px';
  437. fakeResizeObserver.fire();
  438. clock.tick();
  439. expect(client._display.viewportChangeSize).to.have.been.calledOnce;
  440. expect(client._display.viewportChangeSize).to.have.been.calledWith(40, 50);
  441. });
  442. it('should update the viewport when the remote session resizes', function () {
  443. // Simple ExtendedDesktopSize FBU message
  444. const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
  445. 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
  446. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  447. 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
  448. 0x00, 0x00, 0x00, 0x00 ];
  449. sinon.spy(client._display, "viewportChangeSize");
  450. client._sock._websocket._receiveData(new Uint8Array(incoming));
  451. // FIXME: Display implicitly calls viewportChangeSize() when
  452. // resizing the framebuffer, hence calledTwice.
  453. expect(client._display.viewportChangeSize).to.have.been.calledTwice;
  454. expect(client._display.viewportChangeSize).to.have.been.calledWith(70, 80);
  455. });
  456. it('should not update the viewport if not clipping', function () {
  457. client.clipViewport = false;
  458. sinon.spy(client._display, "viewportChangeSize");
  459. container.style.width = '40px';
  460. container.style.height = '50px';
  461. const event = new UIEvent('resize');
  462. window.dispatchEvent(event);
  463. clock.tick();
  464. expect(client._display.viewportChangeSize).to.not.have.been.called;
  465. });
  466. it('should not update the viewport if scaling', function () {
  467. client.scaleViewport = true;
  468. sinon.spy(client._display, "viewportChangeSize");
  469. container.style.width = '40px';
  470. container.style.height = '50px';
  471. const event = new UIEvent('resize');
  472. window.dispatchEvent(event);
  473. clock.tick();
  474. expect(client._display.viewportChangeSize).to.not.have.been.called;
  475. });
  476. describe('Dragging', function () {
  477. beforeEach(function () {
  478. client.dragViewport = true;
  479. sinon.spy(RFB.messages, "pointerEvent");
  480. });
  481. afterEach(function () {
  482. RFB.messages.pointerEvent.restore();
  483. });
  484. it('should not send button messages when initiating viewport dragging', function () {
  485. client._handleMouseButton(13, 9, 0x001);
  486. expect(RFB.messages.pointerEvent).to.not.have.been.called;
  487. });
  488. it('should send button messages when release without movement', function () {
  489. // Just up and down
  490. client._handleMouseButton(13, 9, 0x001);
  491. client._handleMouseButton(13, 9, 0x000);
  492. expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
  493. RFB.messages.pointerEvent.resetHistory();
  494. // Small movement
  495. client._handleMouseButton(13, 9, 0x001);
  496. client._handleMouseMove(15, 14);
  497. client._handleMouseButton(15, 14, 0x000);
  498. expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
  499. });
  500. it('should not send button messages when in view only', function () {
  501. client._viewOnly = true;
  502. client._handleMouseButton(13, 9, 0x001);
  503. client._handleMouseButton(13, 9, 0x000);
  504. expect(RFB.messages.pointerEvent).to.not.have.been.called;
  505. });
  506. it('should send button message directly when drag is disabled', function () {
  507. client.dragViewport = false;
  508. client._handleMouseButton(13, 9, 0x001);
  509. expect(RFB.messages.pointerEvent).to.have.been.calledOnce;
  510. });
  511. it('should be initiate viewport dragging on sufficient movement', function () {
  512. sinon.spy(client._display, "viewportChangePos");
  513. // Too small movement
  514. client._handleMouseButton(13, 9, 0x001);
  515. client._handleMouseMove(18, 9);
  516. expect(RFB.messages.pointerEvent).to.not.have.been.called;
  517. expect(client._display.viewportChangePos).to.not.have.been.called;
  518. // Sufficient movement
  519. client._handleMouseMove(43, 9);
  520. expect(RFB.messages.pointerEvent).to.not.have.been.called;
  521. expect(client._display.viewportChangePos).to.have.been.calledOnce;
  522. expect(client._display.viewportChangePos).to.have.been.calledWith(-30, 0);
  523. client._display.viewportChangePos.resetHistory();
  524. // Now a small movement should move right away
  525. client._handleMouseMove(43, 14);
  526. expect(RFB.messages.pointerEvent).to.not.have.been.called;
  527. expect(client._display.viewportChangePos).to.have.been.calledOnce;
  528. expect(client._display.viewportChangePos).to.have.been.calledWith(0, -5);
  529. });
  530. it('should not send button messages when dragging ends', function () {
  531. // First the movement
  532. client._handleMouseButton(13, 9, 0x001);
  533. client._handleMouseMove(43, 9);
  534. client._handleMouseButton(43, 9, 0x000);
  535. expect(RFB.messages.pointerEvent).to.not.have.been.called;
  536. });
  537. it('should terminate viewport dragging on a button up event', function () {
  538. // First the dragging movement
  539. client._handleMouseButton(13, 9, 0x001);
  540. client._handleMouseMove(43, 9);
  541. client._handleMouseButton(43, 9, 0x000);
  542. // Another movement now should not move the viewport
  543. sinon.spy(client._display, "viewportChangePos");
  544. client._handleMouseMove(43, 59);
  545. expect(client._display.viewportChangePos).to.not.have.been.called;
  546. });
  547. });
  548. });
  549. describe('Scaling', function () {
  550. let client;
  551. beforeEach(function () {
  552. client = makeRFB();
  553. container.style.width = '70px';
  554. container.style.height = '80px';
  555. client.scaleViewport = true;
  556. });
  557. it('should update display scale factor when changing the property', function () {
  558. const spy = sinon.spy(client._display, "scale", ["set"]);
  559. sinon.spy(client._display, "autoscale");
  560. client.scaleViewport = false;
  561. expect(spy.set).to.have.been.calledOnce;
  562. expect(spy.set).to.have.been.calledWith(1.0);
  563. expect(client._display.autoscale).to.not.have.been.called;
  564. client.scaleViewport = true;
  565. expect(client._display.autoscale).to.have.been.calledOnce;
  566. expect(client._display.autoscale).to.have.been.calledWith(70, 80);
  567. });
  568. it('should update the clipping setting when changing the property', function () {
  569. client.clipViewport = true;
  570. const spy = sinon.spy(client._display, "clipViewport", ["set"]);
  571. client.scaleViewport = false;
  572. expect(spy.set).to.have.been.calledOnce;
  573. expect(spy.set).to.have.been.calledWith(true);
  574. spy.set.resetHistory();
  575. client.scaleViewport = true;
  576. expect(spy.set).to.have.been.calledOnce;
  577. expect(spy.set).to.have.been.calledWith(false);
  578. });
  579. it('should update the scaling when the container size changes', function () {
  580. sinon.spy(client._display, "autoscale");
  581. container.style.width = '40px';
  582. container.style.height = '50px';
  583. fakeResizeObserver.fire();
  584. clock.tick();
  585. expect(client._display.autoscale).to.have.been.calledOnce;
  586. expect(client._display.autoscale).to.have.been.calledWith(40, 50);
  587. });
  588. it('should update the scaling when the remote session resizes', function () {
  589. // Simple ExtendedDesktopSize FBU message
  590. const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
  591. 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
  592. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  593. 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
  594. 0x00, 0x00, 0x00, 0x00 ];
  595. sinon.spy(client._display, "autoscale");
  596. client._sock._websocket._receiveData(new Uint8Array(incoming));
  597. expect(client._display.autoscale).to.have.been.calledOnce;
  598. expect(client._display.autoscale).to.have.been.calledWith(70, 80);
  599. });
  600. it('should not update the display scale factor if not scaling', function () {
  601. client.scaleViewport = false;
  602. sinon.spy(client._display, "autoscale");
  603. container.style.width = '40px';
  604. container.style.height = '50px';
  605. const event = new UIEvent('resize');
  606. window.dispatchEvent(event);
  607. clock.tick();
  608. expect(client._display.autoscale).to.not.have.been.called;
  609. });
  610. });
  611. describe('Remote resize', function () {
  612. let client;
  613. beforeEach(function () {
  614. client = makeRFB();
  615. client._supportsSetDesktopSize = true;
  616. client.resizeSession = true;
  617. container.style.width = '70px';
  618. container.style.height = '80px';
  619. sinon.spy(RFB.messages, "setDesktopSize");
  620. });
  621. afterEach(function () {
  622. RFB.messages.setDesktopSize.restore();
  623. });
  624. it('should only request a resize when turned on', function () {
  625. client.resizeSession = false;
  626. expect(RFB.messages.setDesktopSize).to.not.have.been.called;
  627. client.resizeSession = true;
  628. expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
  629. });
  630. it('should request a resize when initially connecting', function () {
  631. // Simple ExtendedDesktopSize FBU message
  632. const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
  633. 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
  634. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  635. 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
  636. 0x00, 0x00, 0x00, 0x00 ];
  637. // First message should trigger a resize
  638. client._supportsSetDesktopSize = false;
  639. client._sock._websocket._receiveData(new Uint8Array(incoming));
  640. expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
  641. expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 70, 80, 0, 0);
  642. RFB.messages.setDesktopSize.resetHistory();
  643. // Second message should not trigger a resize
  644. client._sock._websocket._receiveData(new Uint8Array(incoming));
  645. expect(RFB.messages.setDesktopSize).to.not.have.been.called;
  646. });
  647. it('should request a resize when the container resizes', function () {
  648. container.style.width = '40px';
  649. container.style.height = '50px';
  650. fakeResizeObserver.fire();
  651. clock.tick(1000);
  652. expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
  653. expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 40, 50, 0, 0);
  654. });
  655. it('should not resize until the container size is stable', function () {
  656. container.style.width = '20px';
  657. container.style.height = '30px';
  658. fakeResizeObserver.fire();
  659. clock.tick(400);
  660. expect(RFB.messages.setDesktopSize).to.not.have.been.called;
  661. container.style.width = '40px';
  662. container.style.height = '50px';
  663. fakeResizeObserver.fire();
  664. clock.tick(400);
  665. expect(RFB.messages.setDesktopSize).to.not.have.been.called;
  666. clock.tick(200);
  667. expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
  668. expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 40, 50, 0, 0);
  669. });
  670. it('should not resize when resize is disabled', function () {
  671. client._resizeSession = false;
  672. container.style.width = '40px';
  673. container.style.height = '50px';
  674. const event = new UIEvent('resize');
  675. window.dispatchEvent(event);
  676. clock.tick(1000);
  677. expect(RFB.messages.setDesktopSize).to.not.have.been.called;
  678. });
  679. it('should not resize when resize is not supported', function () {
  680. client._supportsSetDesktopSize = false;
  681. container.style.width = '40px';
  682. container.style.height = '50px';
  683. const event = new UIEvent('resize');
  684. window.dispatchEvent(event);
  685. clock.tick(1000);
  686. expect(RFB.messages.setDesktopSize).to.not.have.been.called;
  687. });
  688. it('should not resize when in view only mode', function () {
  689. client._viewOnly = true;
  690. container.style.width = '40px';
  691. container.style.height = '50px';
  692. const event = new UIEvent('resize');
  693. window.dispatchEvent(event);
  694. clock.tick(1000);
  695. expect(RFB.messages.setDesktopSize).to.not.have.been.called;
  696. });
  697. it('should not try to override a server resize', function () {
  698. // Simple ExtendedDesktopSize FBU message
  699. const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
  700. 0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
  701. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  702. 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
  703. 0x00, 0x00, 0x00, 0x00 ];
  704. client._sock._websocket._receiveData(new Uint8Array(incoming));
  705. expect(RFB.messages.setDesktopSize).to.not.have.been.called;
  706. });
  707. });
  708. describe('Misc Internals', function () {
  709. describe('#_fail', function () {
  710. let client;
  711. beforeEach(function () {
  712. client = makeRFB();
  713. });
  714. it('should close the WebSocket connection', function () {
  715. sinon.spy(client._sock, 'close');
  716. client._fail();
  717. expect(client._sock.close).to.have.been.calledOnce;
  718. });
  719. it('should transition to disconnected', function () {
  720. sinon.spy(client, '_updateConnectionState');
  721. client._fail();
  722. this.clock.tick(2000);
  723. expect(client._updateConnectionState).to.have.been.called;
  724. expect(client._rfbConnectionState).to.equal('disconnected');
  725. });
  726. it('should set clean_disconnect variable', function () {
  727. client._rfbCleanDisconnect = true;
  728. client._rfbConnectionState = 'connected';
  729. client._fail();
  730. expect(client._rfbCleanDisconnect).to.be.false;
  731. });
  732. it('should result in disconnect event with clean set to false', function () {
  733. client._rfbConnectionState = 'connected';
  734. const spy = sinon.spy();
  735. client.addEventListener("disconnect", spy);
  736. client._fail();
  737. this.clock.tick(2000);
  738. expect(spy).to.have.been.calledOnce;
  739. expect(spy.args[0][0].detail.clean).to.be.false;
  740. });
  741. });
  742. });
  743. describe('Protocol Initialization States', function () {
  744. let client;
  745. beforeEach(function () {
  746. client = makeRFB();
  747. client._rfbConnectionState = 'connecting';
  748. });
  749. describe('ProtocolVersion', function () {
  750. function sendVer(ver, client) {
  751. const arr = new Uint8Array(12);
  752. for (let i = 0; i < ver.length; i++) {
  753. arr[i+4] = ver.charCodeAt(i);
  754. }
  755. arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' ';
  756. arr[11] = '\n';
  757. client._sock._websocket._receiveData(arr);
  758. }
  759. describe('version parsing', function () {
  760. it('should interpret version 003.003 as version 3.3', function () {
  761. sendVer('003.003', client);
  762. expect(client._rfbVersion).to.equal(3.3);
  763. });
  764. it('should interpret version 003.006 as version 3.3', function () {
  765. sendVer('003.006', client);
  766. expect(client._rfbVersion).to.equal(3.3);
  767. });
  768. it('should interpret version 003.889 as version 3.3', function () {
  769. sendVer('003.889', client);
  770. expect(client._rfbVersion).to.equal(3.3);
  771. });
  772. it('should interpret version 003.007 as version 3.7', function () {
  773. sendVer('003.007', client);
  774. expect(client._rfbVersion).to.equal(3.7);
  775. });
  776. it('should interpret version 003.008 as version 3.8', function () {
  777. sendVer('003.008', client);
  778. expect(client._rfbVersion).to.equal(3.8);
  779. });
  780. it('should interpret version 004.000 as version 3.8', function () {
  781. sendVer('004.000', client);
  782. expect(client._rfbVersion).to.equal(3.8);
  783. });
  784. it('should interpret version 004.001 as version 3.8', function () {
  785. sendVer('004.001', client);
  786. expect(client._rfbVersion).to.equal(3.8);
  787. });
  788. it('should interpret version 005.000 as version 3.8', function () {
  789. sendVer('005.000', client);
  790. expect(client._rfbVersion).to.equal(3.8);
  791. });
  792. it('should fail on an invalid version', function () {
  793. sinon.spy(client, "_fail");
  794. sendVer('002.000', client);
  795. expect(client._fail).to.have.been.calledOnce;
  796. });
  797. });
  798. it('should send back the interpreted version', function () {
  799. sendVer('004.000', client);
  800. const expectedStr = 'RFB 003.008\n';
  801. const expected = [];
  802. for (let i = 0; i < expectedStr.length; i++) {
  803. expected[i] = expectedStr.charCodeAt(i);
  804. }
  805. expect(client._sock).to.have.sent(new Uint8Array(expected));
  806. });
  807. it('should transition to the Security state on successful negotiation', function () {
  808. sendVer('003.008', client);
  809. expect(client._rfbInitState).to.equal('Security');
  810. });
  811. describe('Repeater', function () {
  812. beforeEach(function () {
  813. client = makeRFB('wss://host:8675', { repeaterID: "12345" });
  814. client._rfbConnectionState = 'connecting';
  815. });
  816. it('should interpret version 000.000 as a repeater', function () {
  817. sendVer('000.000', client);
  818. expect(client._rfbVersion).to.equal(0);
  819. const sentData = client._sock._websocket._getSentData();
  820. expect(new Uint8Array(sentData.buffer, 0, 9)).to.array.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
  821. expect(sentData).to.have.length(250);
  822. });
  823. it('should handle two step repeater negotiation', function () {
  824. sendVer('000.000', client);
  825. sendVer('003.008', client);
  826. expect(client._rfbVersion).to.equal(3.8);
  827. });
  828. });
  829. });
  830. describe('Security', function () {
  831. beforeEach(function () {
  832. client._rfbInitState = 'Security';
  833. });
  834. it('should simply receive the auth scheme when for versions < 3.7', function () {
  835. client._rfbVersion = 3.6;
  836. const authSchemeRaw = [1, 2, 3, 4];
  837. const authScheme = (authSchemeRaw[0] << 24) + (authSchemeRaw[1] << 16) +
  838. (authSchemeRaw[2] << 8) + authSchemeRaw[3];
  839. client._sock._websocket._receiveData(new Uint8Array(authSchemeRaw));
  840. expect(client._rfbAuthScheme).to.equal(authScheme);
  841. });
  842. it('should prefer no authentication is possible', function () {
  843. client._rfbVersion = 3.7;
  844. const authSchemes = [2, 1, 3];
  845. client._sock._websocket._receiveData(new Uint8Array(authSchemes));
  846. expect(client._rfbAuthScheme).to.equal(1);
  847. expect(client._sock).to.have.sent(new Uint8Array([1, 1]));
  848. });
  849. it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
  850. client._rfbVersion = 3.7;
  851. const authSchemes = [2, 22, 16];
  852. client._sock._websocket._receiveData(new Uint8Array(authSchemes));
  853. expect(client._rfbAuthScheme).to.equal(22);
  854. expect(client._sock).to.have.sent(new Uint8Array([22]));
  855. });
  856. it('should fail if there are no supported schemes for versions >= 3.7', function () {
  857. sinon.spy(client, "_fail");
  858. client._rfbVersion = 3.7;
  859. const authSchemes = [1, 32];
  860. client._sock._websocket._receiveData(new Uint8Array(authSchemes));
  861. expect(client._fail).to.have.been.calledOnce;
  862. });
  863. it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
  864. client._rfbVersion = 3.7;
  865. const failureData = [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
  866. sinon.spy(client, '_fail');
  867. client._sock._websocket._receiveData(new Uint8Array(failureData));
  868. expect(client._fail).to.have.been.calledOnce;
  869. expect(client._fail).to.have.been.calledWith(
  870. 'Security negotiation failed on no security types (reason: whoops)');
  871. });
  872. it('should transition to the Authentication state and continue on successful negotiation', function () {
  873. client._rfbVersion = 3.7;
  874. const authSchemes = [1, 1];
  875. client._negotiateAuthentication = sinon.spy();
  876. client._sock._websocket._receiveData(new Uint8Array(authSchemes));
  877. expect(client._rfbInitState).to.equal('Authentication');
  878. expect(client._negotiateAuthentication).to.have.been.calledOnce;
  879. });
  880. });
  881. describe('Authentication', function () {
  882. beforeEach(function () {
  883. client._rfbInitState = 'Security';
  884. });
  885. function sendSecurity(type, cl) {
  886. cl._sock._websocket._receiveData(new Uint8Array([1, type]));
  887. }
  888. it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
  889. client._rfbVersion = 3.6;
  890. const errMsg = "Whoopsies";
  891. const data = [0, 0, 0, 0];
  892. const errLen = errMsg.length;
  893. push32(data, errLen);
  894. for (let i = 0; i < errLen; i++) {
  895. data.push(errMsg.charCodeAt(i));
  896. }
  897. sinon.spy(client, '_fail');
  898. client._sock._websocket._receiveData(new Uint8Array(data));
  899. expect(client._fail).to.have.been.calledWith(
  900. 'Security negotiation failed on authentication scheme (reason: Whoopsies)');
  901. });
  902. it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
  903. client._rfbVersion = 3.8;
  904. sendSecurity(1, client);
  905. expect(client._rfbInitState).to.equal('SecurityResult');
  906. });
  907. it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
  908. client._rfbVersion = 3.7;
  909. sendSecurity(1, client);
  910. expect(client._rfbInitState).to.equal('ServerInitialisation');
  911. });
  912. it('should fail on an unknown auth scheme', function () {
  913. sinon.spy(client, "_fail");
  914. client._rfbVersion = 3.8;
  915. sendSecurity(57, client);
  916. expect(client._fail).to.have.been.calledOnce;
  917. });
  918. describe('VNC Authentication (type 2) Handler', function () {
  919. beforeEach(function () {
  920. client._rfbInitState = 'Security';
  921. client._rfbVersion = 3.8;
  922. });
  923. it('should fire the credentialsrequired event if missing a password', function () {
  924. const spy = sinon.spy();
  925. client.addEventListener("credentialsrequired", spy);
  926. sendSecurity(2, client);
  927. const challenge = [];
  928. for (let i = 0; i < 16; i++) { challenge[i] = i; }
  929. client._sock._websocket._receiveData(new Uint8Array(challenge));
  930. expect(client._rfbCredentials).to.be.empty;
  931. expect(spy).to.have.been.calledOnce;
  932. expect(spy.args[0][0].detail.types).to.have.members(["password"]);
  933. });
  934. it('should encrypt the password with DES and then send it back', function () {
  935. client._rfbCredentials = { password: 'passwd' };
  936. sendSecurity(2, client);
  937. client._sock._websocket._getSentData(); // skip the choice of auth reply
  938. const challenge = [];
  939. for (let i = 0; i < 16; i++) { challenge[i] = i; }
  940. client._sock._websocket._receiveData(new Uint8Array(challenge));
  941. const desPass = RFB.genDES('passwd', challenge);
  942. expect(client._sock).to.have.sent(new Uint8Array(desPass));
  943. });
  944. it('should transition to SecurityResult immediately after sending the password', function () {
  945. client._rfbCredentials = { password: 'passwd' };
  946. sendSecurity(2, client);
  947. const challenge = [];
  948. for (let i = 0; i < 16; i++) { challenge[i] = i; }
  949. client._sock._websocket._receiveData(new Uint8Array(challenge));
  950. expect(client._rfbInitState).to.equal('SecurityResult');
  951. });
  952. });
  953. describe('XVP Authentication (type 22) Handler', function () {
  954. beforeEach(function () {
  955. client._rfbInitState = 'Security';
  956. client._rfbVersion = 3.8;
  957. });
  958. it('should fall through to standard VNC authentication upon completion', function () {
  959. client._rfbCredentials = { username: 'user',
  960. target: 'target',
  961. password: 'password' };
  962. client._negotiateStdVNCAuth = sinon.spy();
  963. sendSecurity(22, client);
  964. expect(client._negotiateStdVNCAuth).to.have.been.calledOnce;
  965. });
  966. it('should fire the credentialsrequired event if all credentials are missing', function () {
  967. const spy = sinon.spy();
  968. client.addEventListener("credentialsrequired", spy);
  969. client._rfbCredentials = {};
  970. sendSecurity(22, client);
  971. expect(client._rfbCredentials).to.be.empty;
  972. expect(spy).to.have.been.calledOnce;
  973. expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]);
  974. });
  975. it('should fire the credentialsrequired event if some credentials are missing', function () {
  976. const spy = sinon.spy();
  977. client.addEventListener("credentialsrequired", spy);
  978. client._rfbCredentials = { username: 'user',
  979. target: 'target' };
  980. sendSecurity(22, client);
  981. expect(spy).to.have.been.calledOnce;
  982. expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]);
  983. });
  984. it('should send user and target separately', function () {
  985. client._rfbCredentials = { username: 'user',
  986. target: 'target',
  987. password: 'password' };
  988. client._negotiateStdVNCAuth = sinon.spy();
  989. sendSecurity(22, client);
  990. const expected = [22, 4, 6]; // auth selection, len user, len target
  991. for (let i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); }
  992. expect(client._sock).to.have.sent(new Uint8Array(expected));
  993. });
  994. });
  995. describe('TightVNC Authentication (type 16) Handler', function () {
  996. beforeEach(function () {
  997. client._rfbInitState = 'Security';
  998. client._rfbVersion = 3.8;
  999. sendSecurity(16, client);
  1000. client._sock._websocket._getSentData(); // skip the security reply
  1001. });
  1002. function sendNumStrPairs(pairs, client) {
  1003. const data = [];
  1004. push32(data, pairs.length);
  1005. for (let i = 0; i < pairs.length; i++) {
  1006. push32(data, pairs[i][0]);
  1007. for (let j = 0; j < 4; j++) {
  1008. data.push(pairs[i][1].charCodeAt(j));
  1009. }
  1010. for (let j = 0; j < 8; j++) {
  1011. data.push(pairs[i][2].charCodeAt(j));
  1012. }
  1013. }
  1014. client._sock._websocket._receiveData(new Uint8Array(data));
  1015. }
  1016. it('should skip tunnel negotiation if no tunnels are requested', function () {
  1017. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
  1018. expect(client._rfbTightVNC).to.be.true;
  1019. });
  1020. it('should fail if no supported tunnels are listed', function () {
  1021. sinon.spy(client, "_fail");
  1022. sendNumStrPairs([[123, 'OTHR', 'SOMETHNG']], client);
  1023. expect(client._fail).to.have.been.calledOnce;
  1024. });
  1025. it('should choose the notunnel tunnel type', function () {
  1026. sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client);
  1027. expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 0]));
  1028. });
  1029. it('should choose the notunnel tunnel type for Siemens devices', function () {
  1030. sendNumStrPairs([[1, 'SICR', 'SCHANNEL'], [2, 'SICR', 'SCHANLPW']], client);
  1031. expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 0]));
  1032. });
  1033. it('should continue to sub-auth negotiation after tunnel negotiation', function () {
  1034. sendNumStrPairs([[0, 'TGHT', 'NOTUNNEL']], client);
  1035. client._sock._websocket._getSentData(); // skip the tunnel choice here
  1036. sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client);
  1037. expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
  1038. expect(client._rfbInitState).to.equal('SecurityResult');
  1039. });
  1040. /*it('should attempt to use VNC auth over no auth when possible', function () {
  1041. client._rfbTightVNC = true;
  1042. client._negotiateStdVNCAuth = sinon.spy();
  1043. sendNumStrPairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
  1044. expect(client._sock).to.have.sent([0, 0, 0, 1]);
  1045. expect(client._negotiateStdVNCAuth).to.have.been.calledOnce;
  1046. expect(client._rfbAuthScheme).to.equal(2);
  1047. });*/ // while this would make sense, the original code doesn't actually do this
  1048. it('should accept the "no auth" auth type and transition to SecurityResult', function () {
  1049. client._rfbTightVNC = true;
  1050. sendNumStrPairs([[1, 'STDV', 'NOAUTH__']], client);
  1051. expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
  1052. expect(client._rfbInitState).to.equal('SecurityResult');
  1053. });
  1054. it('should accept VNC authentication and transition to that', function () {
  1055. client._rfbTightVNC = true;
  1056. client._negotiateStdVNCAuth = sinon.spy();
  1057. sendNumStrPairs([[2, 'STDV', 'VNCAUTH__']], client);
  1058. expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 2]));
  1059. expect(client._negotiateStdVNCAuth).to.have.been.calledOnce;
  1060. expect(client._rfbAuthScheme).to.equal(2);
  1061. });
  1062. it('should fail if there are no supported auth types', function () {
  1063. sinon.spy(client, "_fail");
  1064. client._rfbTightVNC = true;
  1065. sendNumStrPairs([[23, 'stdv', 'badval__']], client);
  1066. expect(client._fail).to.have.been.calledOnce;
  1067. });
  1068. });
  1069. describe('VeNCrypt Authentication (type 19) Handler', function () {
  1070. beforeEach(function () {
  1071. client._rfbInitState = 'Security';
  1072. client._rfbVersion = 3.8;
  1073. sendSecurity(19, client);
  1074. expect(client._sock).to.have.sent(new Uint8Array([19]));
  1075. });
  1076. it('should fail with non-0.2 versions', function () {
  1077. sinon.spy(client, "_fail");
  1078. client._sock._websocket._receiveData(new Uint8Array([0, 1]));
  1079. expect(client._fail).to.have.been.calledOnce;
  1080. });
  1081. it('should fail if the Plain authentication is not present', function () {
  1082. // VeNCrypt version
  1083. client._sock._websocket._receiveData(new Uint8Array([0, 2]));
  1084. expect(client._sock).to.have.sent(new Uint8Array([0, 2]));
  1085. // Server ACK.
  1086. client._sock._websocket._receiveData(new Uint8Array([0]));
  1087. // Subtype list, only list subtype 1.
  1088. sinon.spy(client, "_fail");
  1089. client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 0, 1]));
  1090. expect(client._fail).to.have.been.calledOnce;
  1091. });
  1092. it('should support Plain authentication', function () {
  1093. client._rfbCredentials = { username: 'username', password: 'password' };
  1094. // VeNCrypt version
  1095. client._sock._websocket._receiveData(new Uint8Array([0, 2]));
  1096. expect(client._sock).to.have.sent(new Uint8Array([0, 2]));
  1097. // Server ACK.
  1098. client._sock._websocket._receiveData(new Uint8Array([0]));
  1099. // Subtype list.
  1100. client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0]));
  1101. const expectedResponse = [];
  1102. push32(expectedResponse, 256); // Chosen subtype.
  1103. push32(expectedResponse, client._rfbCredentials.username.length);
  1104. push32(expectedResponse, client._rfbCredentials.password.length);
  1105. pushString(expectedResponse, client._rfbCredentials.username);
  1106. pushString(expectedResponse, client._rfbCredentials.password);
  1107. expect(client._sock).to.have.sent(new Uint8Array(expectedResponse));
  1108. client._initMsg = sinon.spy();
  1109. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
  1110. expect(client._initMsg).to.have.been.called;
  1111. });
  1112. it('should support Plain authentication with an empty password', function () {
  1113. client._rfbCredentials = { username: 'username', password: '' };
  1114. // VeNCrypt version
  1115. client._sock._websocket._receiveData(new Uint8Array([0, 2]));
  1116. expect(client._sock).to.have.sent(new Uint8Array([0, 2]));
  1117. // Server ACK.
  1118. client._sock._websocket._receiveData(new Uint8Array([0]));
  1119. // Subtype list.
  1120. client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0]));
  1121. const expectedResponse = [];
  1122. push32(expectedResponse, 256); // Chosen subtype.
  1123. push32(expectedResponse, client._rfbCredentials.username.length);
  1124. push32(expectedResponse, client._rfbCredentials.password.length);
  1125. pushString(expectedResponse, client._rfbCredentials.username);
  1126. pushString(expectedResponse, client._rfbCredentials.password);
  1127. expect(client._sock).to.have.sent(new Uint8Array(expectedResponse));
  1128. client._initMsg = sinon.spy();
  1129. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
  1130. expect(client._initMsg).to.have.been.called;
  1131. });
  1132. it('should support Plain authentication with a very long username and password', function () {
  1133. client._rfbCredentials = { username: 'a'.repeat(300), password: 'a'.repeat(300) };
  1134. // VeNCrypt version
  1135. client._sock._websocket._receiveData(new Uint8Array([0, 2]));
  1136. expect(client._sock).to.have.sent(new Uint8Array([0, 2]));
  1137. // Server ACK.
  1138. client._sock._websocket._receiveData(new Uint8Array([0]));
  1139. // Subtype list.
  1140. client._sock._websocket._receiveData(new Uint8Array([1, 0, 0, 1, 0]));
  1141. const expectedResponse = [];
  1142. push32(expectedResponse, 256); // Chosen subtype.
  1143. push32(expectedResponse, client._rfbCredentials.username.length);
  1144. push32(expectedResponse, client._rfbCredentials.password.length);
  1145. pushString(expectedResponse, client._rfbCredentials.username);
  1146. pushString(expectedResponse, client._rfbCredentials.password);
  1147. expect(client._sock).to.have.sent(new Uint8Array(expectedResponse));
  1148. client._initMsg = sinon.spy();
  1149. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
  1150. expect(client._initMsg).to.have.been.called;
  1151. });
  1152. });
  1153. });
  1154. describe('SecurityResult', function () {
  1155. beforeEach(function () {
  1156. client._rfbInitState = 'SecurityResult';
  1157. });
  1158. it('should fall through to ServerInitialisation on a response code of 0', function () {
  1159. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
  1160. expect(client._rfbInitState).to.equal('ServerInitialisation');
  1161. });
  1162. it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
  1163. client._rfbVersion = 3.8;
  1164. sinon.spy(client, '_fail');
  1165. const failureData = [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
  1166. client._sock._websocket._receiveData(new Uint8Array(failureData));
  1167. expect(client._fail).to.have.been.calledWith(
  1168. 'Security negotiation failed on security result (reason: whoops)');
  1169. });
  1170. it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
  1171. sinon.spy(client, '_fail');
  1172. client._rfbVersion = 3.7;
  1173. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 1]));
  1174. expect(client._fail).to.have.been.calledWith(
  1175. 'Security handshake failed');
  1176. });
  1177. it('should result in securityfailure event when receiving a non zero status', function () {
  1178. const spy = sinon.spy();
  1179. client.addEventListener("securityfailure", spy);
  1180. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2]));
  1181. expect(spy).to.have.been.calledOnce;
  1182. expect(spy.args[0][0].detail.status).to.equal(2);
  1183. });
  1184. it('should include reason when provided in securityfailure event', function () {
  1185. client._rfbVersion = 3.8;
  1186. const spy = sinon.spy();
  1187. client.addEventListener("securityfailure", spy);
  1188. const failureData = [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
  1189. 32, 102, 97, 105, 108, 117, 114, 101];
  1190. client._sock._websocket._receiveData(new Uint8Array(failureData));
  1191. expect(spy.args[0][0].detail.status).to.equal(1);
  1192. expect(spy.args[0][0].detail.reason).to.equal('such failure');
  1193. });
  1194. it('should not include reason when length is zero in securityfailure event', function () {
  1195. client._rfbVersion = 3.9;
  1196. const spy = sinon.spy();
  1197. client.addEventListener("securityfailure", spy);
  1198. const failureData = [0, 0, 0, 1, 0, 0, 0, 0];
  1199. client._sock._websocket._receiveData(new Uint8Array(failureData));
  1200. expect(spy.args[0][0].detail.status).to.equal(1);
  1201. expect('reason' in spy.args[0][0].detail).to.be.false;
  1202. });
  1203. it('should not include reason in securityfailure event for version < 3.8', function () {
  1204. client._rfbVersion = 3.6;
  1205. const spy = sinon.spy();
  1206. client.addEventListener("securityfailure", spy);
  1207. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 2]));
  1208. expect(spy.args[0][0].detail.status).to.equal(2);
  1209. expect('reason' in spy.args[0][0].detail).to.be.false;
  1210. });
  1211. });
  1212. describe('ClientInitialisation', function () {
  1213. it('should transition to the ServerInitialisation state', function () {
  1214. const client = makeRFB();
  1215. client._rfbConnectionState = 'connecting';
  1216. client._rfbInitState = 'SecurityResult';
  1217. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
  1218. expect(client._rfbInitState).to.equal('ServerInitialisation');
  1219. });
  1220. it('should send 1 if we are in shared mode', function () {
  1221. const client = makeRFB('wss://host:8675', { shared: true });
  1222. client._rfbConnectionState = 'connecting';
  1223. client._rfbInitState = 'SecurityResult';
  1224. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
  1225. expect(client._sock).to.have.sent(new Uint8Array([1]));
  1226. });
  1227. it('should send 0 if we are not in shared mode', function () {
  1228. const client = makeRFB('wss://host:8675', { shared: false });
  1229. client._rfbConnectionState = 'connecting';
  1230. client._rfbInitState = 'SecurityResult';
  1231. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 0]));
  1232. expect(client._sock).to.have.sent(new Uint8Array([0]));
  1233. });
  1234. });
  1235. describe('ServerInitialisation', function () {
  1236. beforeEach(function () {
  1237. client._rfbInitState = 'ServerInitialisation';
  1238. });
  1239. function sendServerInit(opts, client) {
  1240. const fullOpts = { width: 10, height: 12, bpp: 24, depth: 24, bigEndian: 0,
  1241. trueColor: 1, redMax: 255, greenMax: 255, blueMax: 255,
  1242. redShift: 16, greenShift: 8, blueShift: 0, name: 'a name' };
  1243. for (let opt in opts) {
  1244. fullOpts[opt] = opts[opt];
  1245. }
  1246. const data = [];
  1247. push16(data, fullOpts.width);
  1248. push16(data, fullOpts.height);
  1249. data.push(fullOpts.bpp);
  1250. data.push(fullOpts.depth);
  1251. data.push(fullOpts.bigEndian);
  1252. data.push(fullOpts.trueColor);
  1253. push16(data, fullOpts.redMax);
  1254. push16(data, fullOpts.greenMax);
  1255. push16(data, fullOpts.blueMax);
  1256. push8(data, fullOpts.redShift);
  1257. push8(data, fullOpts.greenShift);
  1258. push8(data, fullOpts.blueShift);
  1259. // padding
  1260. push8(data, 0);
  1261. push8(data, 0);
  1262. push8(data, 0);
  1263. client._sock._websocket._receiveData(new Uint8Array(data));
  1264. const nameData = [];
  1265. let nameLen = [];
  1266. pushString(nameData, fullOpts.name);
  1267. push32(nameLen, nameData.length);
  1268. client._sock._websocket._receiveData(new Uint8Array(nameLen));
  1269. client._sock._websocket._receiveData(new Uint8Array(nameData));
  1270. }
  1271. it('should set the framebuffer width and height', function () {
  1272. sendServerInit({ width: 32, height: 84 }, client);
  1273. expect(client._fbWidth).to.equal(32);
  1274. expect(client._fbHeight).to.equal(84);
  1275. });
  1276. // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
  1277. it('should set the framebuffer name and call the callback', function () {
  1278. const spy = sinon.spy();
  1279. client.addEventListener("desktopname", spy);
  1280. sendServerInit({ name: 'som€ nam€' }, client);
  1281. expect(client._fbName).to.equal('som€ nam€');
  1282. expect(spy).to.have.been.calledOnce;
  1283. expect(spy.args[0][0].detail.name).to.equal('som€ nam€');
  1284. });
  1285. it('should handle the extended init message of the tight encoding', function () {
  1286. // NB(sross): we don't actually do anything with it, so just test that we can
  1287. // read it w/o throwing an error
  1288. client._rfbTightVNC = true;
  1289. sendServerInit({}, client);
  1290. const tightData = [];
  1291. push16(tightData, 1);
  1292. push16(tightData, 2);
  1293. push16(tightData, 3);
  1294. push16(tightData, 0);
  1295. for (let i = 0; i < 16 + 32 + 48; i++) {
  1296. tightData.push(i);
  1297. }
  1298. client._sock._websocket._receiveData(new Uint8Array(tightData));
  1299. expect(client._rfbConnectionState).to.equal('connected');
  1300. });
  1301. it('should resize the display', function () {
  1302. sinon.spy(client._display, 'resize');
  1303. sendServerInit({ width: 27, height: 32 }, client);
  1304. expect(client._display.resize).to.have.been.calledOnce;
  1305. expect(client._display.resize).to.have.been.calledWith(27, 32);
  1306. });
  1307. it('should grab the keyboard', function () {
  1308. sinon.spy(client._keyboard, 'grab');
  1309. sendServerInit({}, client);
  1310. expect(client._keyboard.grab).to.have.been.calledOnce;
  1311. });
  1312. describe('Initial Update Request', function () {
  1313. beforeEach(function () {
  1314. sinon.spy(RFB.messages, "pixelFormat");
  1315. sinon.spy(RFB.messages, "clientEncodings");
  1316. sinon.spy(RFB.messages, "fbUpdateRequest");
  1317. });
  1318. afterEach(function () {
  1319. RFB.messages.pixelFormat.restore();
  1320. RFB.messages.clientEncodings.restore();
  1321. RFB.messages.fbUpdateRequest.restore();
  1322. });
  1323. // TODO(directxman12): test the various options in this configuration matrix
  1324. it('should reply with the pixel format, client encodings, and initial update request', function () {
  1325. sendServerInit({ width: 27, height: 32 }, client);
  1326. expect(RFB.messages.pixelFormat).to.have.been.calledOnce;
  1327. expect(RFB.messages.pixelFormat).to.have.been.calledWith(client._sock, 24, true);
  1328. expect(RFB.messages.pixelFormat).to.have.been.calledBefore(RFB.messages.clientEncodings);
  1329. expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
  1330. expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.encodingTight);
  1331. expect(RFB.messages.clientEncodings).to.have.been.calledBefore(RFB.messages.fbUpdateRequest);
  1332. expect(RFB.messages.fbUpdateRequest).to.have.been.calledOnce;
  1333. expect(RFB.messages.fbUpdateRequest).to.have.been.calledWith(client._sock, false, 0, 0, 27, 32);
  1334. });
  1335. it('should reply with restricted settings for Intel AMT servers', function () {
  1336. sendServerInit({ width: 27, height: 32, name: "Intel(r) AMT KVM"}, client);
  1337. expect(RFB.messages.pixelFormat).to.have.been.calledOnce;
  1338. expect(RFB.messages.pixelFormat).to.have.been.calledWith(client._sock, 8, true);
  1339. expect(RFB.messages.pixelFormat).to.have.been.calledBefore(RFB.messages.clientEncodings);
  1340. expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
  1341. expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.not.include(encodings.encodingTight);
  1342. expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.not.include(encodings.encodingHextile);
  1343. expect(RFB.messages.clientEncodings).to.have.been.calledBefore(RFB.messages.fbUpdateRequest);
  1344. expect(RFB.messages.fbUpdateRequest).to.have.been.calledOnce;
  1345. expect(RFB.messages.fbUpdateRequest).to.have.been.calledWith(client._sock, false, 0, 0, 27, 32);
  1346. });
  1347. });
  1348. it('should send the "connect" event', function () {
  1349. let spy = sinon.spy();
  1350. client.addEventListener('connect', spy);
  1351. sendServerInit({}, client);
  1352. expect(spy).to.have.been.calledOnce;
  1353. });
  1354. });
  1355. });
  1356. describe('Protocol Message Processing After Completing Initialization', function () {
  1357. let client;
  1358. beforeEach(function () {
  1359. client = makeRFB();
  1360. client._fbName = 'some device';
  1361. client._fbWidth = 640;
  1362. client._fbHeight = 20;
  1363. });
  1364. describe('Framebuffer Update Handling', function () {
  1365. function sendFbuMsg(rectInfo, rectData, client, rectCnt) {
  1366. let data = [];
  1367. if (!rectCnt || rectCnt > -1) {
  1368. // header
  1369. data.push(0); // msg type
  1370. data.push(0); // padding
  1371. push16(data, rectCnt || rectData.length);
  1372. }
  1373. for (let i = 0; i < rectData.length; i++) {
  1374. if (rectInfo[i]) {
  1375. push16(data, rectInfo[i].x);
  1376. push16(data, rectInfo[i].y);
  1377. push16(data, rectInfo[i].width);
  1378. push16(data, rectInfo[i].height);
  1379. push32(data, rectInfo[i].encoding);
  1380. }
  1381. data = data.concat(rectData[i]);
  1382. }
  1383. client._sock._websocket._receiveData(new Uint8Array(data));
  1384. }
  1385. it('should send an update request if there is sufficient data', function () {
  1386. const expectedMsg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
  1387. RFB.messages.fbUpdateRequest(expectedMsg, true, 0, 0, 640, 20);
  1388. client._framebufferUpdate = () => true;
  1389. client._sock._websocket._receiveData(new Uint8Array([0]));
  1390. expect(client._sock).to.have.sent(expectedMsg._sQ);
  1391. });
  1392. it('should not send an update request if we need more data', function () {
  1393. client._sock._websocket._receiveData(new Uint8Array([0]));
  1394. expect(client._sock._websocket._getSentData()).to.have.length(0);
  1395. });
  1396. it('should resume receiving an update if we previously did not have enough data', function () {
  1397. const expectedMsg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
  1398. RFB.messages.fbUpdateRequest(expectedMsg, true, 0, 0, 640, 20);
  1399. // just enough to set FBU.rects
  1400. client._sock._websocket._receiveData(new Uint8Array([0, 0, 0, 3]));
  1401. expect(client._sock._websocket._getSentData()).to.have.length(0);
  1402. client._framebufferUpdate = function () { this._sock.rQskipBytes(1); return true; }; // we magically have enough data
  1403. // 247 should *not* be used as the message type here
  1404. client._sock._websocket._receiveData(new Uint8Array([247]));
  1405. expect(client._sock).to.have.sent(expectedMsg._sQ);
  1406. });
  1407. it('should not send a request in continuous updates mode', function () {
  1408. client._enabledContinuousUpdates = true;
  1409. client._framebufferUpdate = () => true;
  1410. client._sock._websocket._receiveData(new Uint8Array([0]));
  1411. expect(client._sock._websocket._getSentData()).to.have.length(0);
  1412. });
  1413. it('should fail on an unsupported encoding', function () {
  1414. sinon.spy(client, "_fail");
  1415. const rectInfo = { x: 8, y: 11, width: 27, height: 32, encoding: 234 };
  1416. sendFbuMsg([rectInfo], [[]], client);
  1417. expect(client._fail).to.have.been.calledOnce;
  1418. });
  1419. describe('Message Encoding Handlers', function () {
  1420. beforeEach(function () {
  1421. // a really small frame
  1422. client._fbWidth = 4;
  1423. client._fbHeight = 4;
  1424. client._fbDepth = 24;
  1425. client._display.resize(4, 4);
  1426. });
  1427. it('should handle the DesktopSize pseduo-encoding', function () {
  1428. sinon.spy(client._display, 'resize');
  1429. sendFbuMsg([{ x: 0, y: 0, width: 20, height: 50, encoding: -223 }], [[]], client);
  1430. expect(client._fbWidth).to.equal(20);
  1431. expect(client._fbHeight).to.equal(50);
  1432. expect(client._display.resize).to.have.been.calledOnce;
  1433. expect(client._display.resize).to.have.been.calledWith(20, 50);
  1434. });
  1435. describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
  1436. beforeEach(function () {
  1437. // a really small frame
  1438. client._fbWidth = 4;
  1439. client._fbHeight = 4;
  1440. client._display.resize(4, 4);
  1441. sinon.spy(client._display, 'resize');
  1442. });
  1443. function makeScreenData(nrOfScreens) {
  1444. const data = [];
  1445. push8(data, nrOfScreens); // number-of-screens
  1446. push8(data, 0); // padding
  1447. push16(data, 0); // padding
  1448. for (let i=0; i<nrOfScreens; i += 1) {
  1449. push32(data, 0); // id
  1450. push16(data, 0); // x-position
  1451. push16(data, 0); // y-position
  1452. push16(data, 20); // width
  1453. push16(data, 50); // height
  1454. push32(data, 0); // flags
  1455. }
  1456. return data;
  1457. }
  1458. it('should handle a resize requested by this client', function () {
  1459. const reasonForChange = 1; // requested by this client
  1460. const statusCode = 0; // No error
  1461. sendFbuMsg([{ x: reasonForChange, y: statusCode,
  1462. width: 20, height: 50, encoding: -308 }],
  1463. makeScreenData(1), client);
  1464. expect(client._fbWidth).to.equal(20);
  1465. expect(client._fbHeight).to.equal(50);
  1466. expect(client._display.resize).to.have.been.calledOnce;
  1467. expect(client._display.resize).to.have.been.calledWith(20, 50);
  1468. });
  1469. it('should handle a resize requested by another client', function () {
  1470. const reasonForChange = 2; // requested by another client
  1471. const statusCode = 0; // No error
  1472. sendFbuMsg([{ x: reasonForChange, y: statusCode,
  1473. width: 20, height: 50, encoding: -308 }],
  1474. makeScreenData(1), client);
  1475. expect(client._fbWidth).to.equal(20);
  1476. expect(client._fbHeight).to.equal(50);
  1477. expect(client._display.resize).to.have.been.calledOnce;
  1478. expect(client._display.resize).to.have.been.calledWith(20, 50);
  1479. });
  1480. it('should be able to recieve requests which contain data for multiple screens', function () {
  1481. const reasonForChange = 2; // requested by another client
  1482. const statusCode = 0; // No error
  1483. sendFbuMsg([{ x: reasonForChange, y: statusCode,
  1484. width: 60, height: 50, encoding: -308 }],
  1485. makeScreenData(3), client);
  1486. expect(client._fbWidth).to.equal(60);
  1487. expect(client._fbHeight).to.equal(50);
  1488. expect(client._display.resize).to.have.been.calledOnce;
  1489. expect(client._display.resize).to.have.been.calledWith(60, 50);
  1490. });
  1491. it('should not handle a failed request', function () {
  1492. const reasonForChange = 1; // requested by this client
  1493. const statusCode = 1; // Resize is administratively prohibited
  1494. sendFbuMsg([{ x: reasonForChange, y: statusCode,
  1495. width: 20, height: 50, encoding: -308 }],
  1496. makeScreenData(1), client);
  1497. expect(client._fbWidth).to.equal(4);
  1498. expect(client._fbHeight).to.equal(4);
  1499. expect(client._display.resize).to.not.have.been.called;
  1500. });
  1501. });
  1502. describe('the Cursor pseudo-encoding handler', function () {
  1503. beforeEach(function () {
  1504. sinon.spy(client._cursor, 'change');
  1505. });
  1506. it('should handle a standard cursor', function () {
  1507. const info = { x: 5, y: 7,
  1508. width: 4, height: 4,
  1509. encoding: -239};
  1510. let rect = [];
  1511. let expected = [];
  1512. for (let i = 0;i < info.width*info.height;i++) {
  1513. push32(rect, 0x11223300);
  1514. }
  1515. push32(rect, 0xa0a0a0a0);
  1516. for (let i = 0;i < info.width*info.height/2;i++) {
  1517. push32(expected, 0x332211ff);
  1518. push32(expected, 0x33221100);
  1519. }
  1520. expected = new Uint8Array(expected);
  1521. sendFbuMsg([info], [rect], client);
  1522. expect(client._cursor.change).to.have.been.calledOnce;
  1523. expect(client._cursor.change).to.have.been.calledWith(expected, 5, 7, 4, 4);
  1524. });
  1525. it('should handle an empty cursor', function () {
  1526. const info = { x: 0, y: 0,
  1527. width: 0, height: 0,
  1528. encoding: -239};
  1529. const rect = [];
  1530. sendFbuMsg([info], [rect], client);
  1531. expect(client._cursor.change).to.have.been.calledOnce;
  1532. expect(client._cursor.change).to.have.been.calledWith(new Uint8Array, 0, 0, 0, 0);
  1533. });
  1534. it('should handle a transparent cursor', function () {
  1535. const info = { x: 5, y: 7,
  1536. width: 4, height: 4,
  1537. encoding: -239};
  1538. let rect = [];
  1539. let expected = [];
  1540. for (let i = 0;i < info.width*info.height;i++) {
  1541. push32(rect, 0x11223300);
  1542. }
  1543. push32(rect, 0x00000000);
  1544. for (let i = 0;i < info.width*info.height;i++) {
  1545. push32(expected, 0x33221100);
  1546. }
  1547. expected = new Uint8Array(expected);
  1548. sendFbuMsg([info], [rect], client);
  1549. expect(client._cursor.change).to.have.been.calledOnce;
  1550. expect(client._cursor.change).to.have.been.calledWith(expected, 5, 7, 4, 4);
  1551. });
  1552. describe('dot for empty cursor', function () {
  1553. beforeEach(function () {
  1554. client.showDotCursor = true;
  1555. // Was called when we enabled dot cursor
  1556. client._cursor.change.resetHistory();
  1557. });
  1558. it('should show a standard cursor', function () {
  1559. const info = { x: 5, y: 7,
  1560. width: 4, height: 4,
  1561. encoding: -239};
  1562. let rect = [];
  1563. let expected = [];
  1564. for (let i = 0;i < info.width*info.height;i++) {
  1565. push32(rect, 0x11223300);
  1566. }
  1567. push32(rect, 0xa0a0a0a0);
  1568. for (let i = 0;i < info.width*info.height/2;i++) {
  1569. push32(expected, 0x332211ff);
  1570. push32(expected, 0x33221100);
  1571. }
  1572. expected = new Uint8Array(expected);
  1573. sendFbuMsg([info], [rect], client);
  1574. expect(client._cursor.change).to.have.been.calledOnce;
  1575. expect(client._cursor.change).to.have.been.calledWith(expected, 5, 7, 4, 4);
  1576. });
  1577. it('should handle an empty cursor', function () {
  1578. const info = { x: 0, y: 0,
  1579. width: 0, height: 0,
  1580. encoding: -239};
  1581. const rect = [];
  1582. const dot = RFB.cursors.dot;
  1583. sendFbuMsg([info], [rect], client);
  1584. expect(client._cursor.change).to.have.been.calledOnce;
  1585. expect(client._cursor.change).to.have.been.calledWith(dot.rgbaPixels,
  1586. dot.hotx,
  1587. dot.hoty,
  1588. dot.w,
  1589. dot.h);
  1590. });
  1591. it('should handle a transparent cursor', function () {
  1592. const info = { x: 5, y: 7,
  1593. width: 4, height: 4,
  1594. encoding: -239};
  1595. let rect = [];
  1596. const dot = RFB.cursors.dot;
  1597. for (let i = 0;i < info.width*info.height;i++) {
  1598. push32(rect, 0x11223300);
  1599. }
  1600. push32(rect, 0x00000000);
  1601. sendFbuMsg([info], [rect], client);
  1602. expect(client._cursor.change).to.have.been.calledOnce;
  1603. expect(client._cursor.change).to.have.been.calledWith(dot.rgbaPixels,
  1604. dot.hotx,
  1605. dot.hoty,
  1606. dot.w,
  1607. dot.h);
  1608. });
  1609. });
  1610. });
  1611. describe('the VMware Cursor pseudo-encoding handler', function () {
  1612. beforeEach(function () {
  1613. sinon.spy(client._cursor, 'change');
  1614. });
  1615. afterEach(function () {
  1616. client._cursor.change.resetHistory();
  1617. });
  1618. it('should handle the VMware cursor pseudo-encoding', function () {
  1619. let data = [0x00, 0x00, 0xff, 0,
  1620. 0x00, 0xff, 0x00, 0,
  1621. 0x00, 0xff, 0x00, 0,
  1622. 0x00, 0x00, 0xff, 0];
  1623. let rect = [];
  1624. push8(rect, 0);
  1625. push8(rect, 0);
  1626. //AND-mask
  1627. for (let i = 0; i < data.length; i++) {
  1628. push8(rect, data[i]);
  1629. }
  1630. //XOR-mask
  1631. for (let i = 0; i < data.length; i++) {
  1632. push8(rect, data[i]);
  1633. }
  1634. sendFbuMsg([{ x: 0, y: 0, width: 2, height: 2,
  1635. encoding: 0x574d5664}],
  1636. [rect], client);
  1637. expect(client._FBU.rects).to.equal(0);
  1638. });
  1639. it('should handle insufficient cursor pixel data', function () {
  1640. // Specified 14x23 pixels for the cursor,
  1641. // but only send 2x2 pixels worth of data
  1642. let w = 14;
  1643. let h = 23;
  1644. let data = [0x00, 0x00, 0xff, 0,
  1645. 0x00, 0xff, 0x00, 0];
  1646. let rect = [];
  1647. push8(rect, 0);
  1648. push8(rect, 0);
  1649. //AND-mask
  1650. for (let i = 0; i < data.length; i++) {
  1651. push8(rect, data[i]);
  1652. }
  1653. //XOR-mask
  1654. for (let i = 0; i < data.length; i++) {
  1655. push8(rect, data[i]);
  1656. }
  1657. sendFbuMsg([{ x: 0, y: 0, width: w, height: h,
  1658. encoding: 0x574d5664}],
  1659. [rect], client);
  1660. // expect one FBU to remain unhandled
  1661. expect(client._FBU.rects).to.equal(1);
  1662. });
  1663. it('should update the cursor when type is classic', function () {
  1664. let andMask =
  1665. [0xff, 0xff, 0xff, 0xff, //Transparent
  1666. 0xff, 0xff, 0xff, 0xff, //Transparent
  1667. 0x00, 0x00, 0x00, 0x00, //Opaque
  1668. 0xff, 0xff, 0xff, 0xff]; //Inverted
  1669. let xorMask =
  1670. [0x00, 0x00, 0x00, 0x00, //Transparent
  1671. 0x00, 0x00, 0x00, 0x00, //Transparent
  1672. 0x11, 0x22, 0x33, 0x44, //Opaque
  1673. 0xff, 0xff, 0xff, 0x44]; //Inverted
  1674. let rect = [];
  1675. push8(rect, 0); //cursor_type
  1676. push8(rect, 0); //padding
  1677. let hotx = 0;
  1678. let hoty = 0;
  1679. let w = 2;
  1680. let h = 2;
  1681. //AND-mask
  1682. for (let i = 0; i < andMask.length; i++) {
  1683. push8(rect, andMask[i]);
  1684. }
  1685. //XOR-mask
  1686. for (let i = 0; i < xorMask.length; i++) {
  1687. push8(rect, xorMask[i]);
  1688. }
  1689. let expectedRgba = [0x00, 0x00, 0x00, 0x00,
  1690. 0x00, 0x00, 0x00, 0x00,
  1691. 0x33, 0x22, 0x11, 0xff,
  1692. 0x00, 0x00, 0x00, 0xff];
  1693. sendFbuMsg([{ x: hotx, y: hoty,
  1694. width: w, height: h,
  1695. encoding: 0x574d5664}],
  1696. [rect], client);
  1697. expect(client._cursor.change)
  1698. .to.have.been.calledOnce;
  1699. expect(client._cursor.change)
  1700. .to.have.been.calledWith(expectedRgba,
  1701. hotx, hoty,
  1702. w, h);
  1703. });
  1704. it('should update the cursor when type is alpha', function () {
  1705. let data = [0xee, 0x55, 0xff, 0x00, // rgba
  1706. 0x00, 0xff, 0x00, 0xff,
  1707. 0x00, 0xff, 0x00, 0x22,
  1708. 0x00, 0xff, 0x00, 0x22,
  1709. 0x00, 0xff, 0x00, 0x22,
  1710. 0x00, 0x00, 0xff, 0xee];
  1711. let rect = [];
  1712. push8(rect, 1); //cursor_type
  1713. push8(rect, 0); //padding
  1714. let hotx = 0;
  1715. let hoty = 0;
  1716. let w = 3;
  1717. let h = 2;
  1718. for (let i = 0; i < data.length; i++) {
  1719. push8(rect, data[i]);
  1720. }
  1721. let expectedRgba = [0xee, 0x55, 0xff, 0x00,
  1722. 0x00, 0xff, 0x00, 0xff,
  1723. 0x00, 0xff, 0x00, 0x22,
  1724. 0x00, 0xff, 0x00, 0x22,
  1725. 0x00, 0xff, 0x00, 0x22,
  1726. 0x00, 0x00, 0xff, 0xee];
  1727. sendFbuMsg([{ x: hotx, y: hoty,
  1728. width: w, height: h,
  1729. encoding: 0x574d5664}],
  1730. [rect], client);
  1731. expect(client._cursor.change)
  1732. .to.have.been.calledOnce;
  1733. expect(client._cursor.change)
  1734. .to.have.been.calledWith(expectedRgba,
  1735. hotx, hoty,
  1736. w, h);
  1737. });
  1738. it('should not update cursor when incorrect cursor type given', function () {
  1739. let rect = [];
  1740. push8(rect, 3); // invalid cursor type
  1741. push8(rect, 0); // padding
  1742. client._cursor.change.resetHistory();
  1743. sendFbuMsg([{ x: 0, y: 0, width: 2, height: 2,
  1744. encoding: 0x574d5664}],
  1745. [rect], client);
  1746. expect(client._cursor.change)
  1747. .to.not.have.been.called;
  1748. });
  1749. });
  1750. it('should handle the last_rect pseudo-encoding', function () {
  1751. sendFbuMsg([{ x: 0, y: 0, width: 0, height: 0, encoding: -224}], [[]], client, 100);
  1752. expect(client._FBU.rects).to.equal(0);
  1753. });
  1754. it('should handle the DesktopName pseudo-encoding', function () {
  1755. let data = [];
  1756. push32(data, 13);
  1757. pushString(data, "som€ nam€");
  1758. const spy = sinon.spy();
  1759. client.addEventListener("desktopname", spy);
  1760. sendFbuMsg([{ x: 0, y: 0, width: 0, height: 0, encoding: -307 }], [data], client);
  1761. expect(client._fbName).to.equal('som€ nam€');
  1762. expect(spy).to.have.been.calledOnce;
  1763. expect(spy.args[0][0].detail.name).to.equal('som€ nam€');
  1764. });
  1765. });
  1766. });
  1767. describe('XVP Message Handling', function () {
  1768. it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
  1769. const spy = sinon.spy();
  1770. client.addEventListener("capabilities", spy);
  1771. client._sock._websocket._receiveData(new Uint8Array([250, 0, 10, 1]));
  1772. expect(client._rfbXvpVer).to.equal(10);
  1773. expect(spy).to.have.been.calledOnce;
  1774. expect(spy.args[0][0].detail.capabilities.power).to.be.true;
  1775. expect(client.capabilities.power).to.be.true;
  1776. });
  1777. it('should fail on unknown XVP message types', function () {
  1778. sinon.spy(client, "_fail");
  1779. client._sock._websocket._receiveData(new Uint8Array([250, 0, 10, 237]));
  1780. expect(client._fail).to.have.been.calledOnce;
  1781. });
  1782. });
  1783. describe('Normal Clipboard Handling Receive', function () {
  1784. it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
  1785. const expectedStr = 'cheese!';
  1786. const data = [3, 0, 0, 0];
  1787. push32(data, expectedStr.length);
  1788. for (let i = 0; i < expectedStr.length; i++) { data.push(expectedStr.charCodeAt(i)); }
  1789. const spy = sinon.spy();
  1790. client.addEventListener("clipboard", spy);
  1791. client._sock._websocket._receiveData(new Uint8Array(data));
  1792. expect(spy).to.have.been.calledOnce;
  1793. expect(spy.args[0][0].detail.text).to.equal(expectedStr);
  1794. });
  1795. });
  1796. describe('Extended clipboard Handling', function () {
  1797. describe('Extended clipboard initialization', function () {
  1798. beforeEach(function () {
  1799. sinon.spy(RFB.messages, 'extendedClipboardCaps');
  1800. });
  1801. afterEach(function () {
  1802. RFB.messages.extendedClipboardCaps.restore();
  1803. });
  1804. it('should update capabilities when receiving a Caps message', function () {
  1805. let data = [3, 0, 0, 0];
  1806. const flags = [0x1F, 0x00, 0x00, 0x03];
  1807. let fileSizes = [0x00, 0x00, 0x00, 0x1E,
  1808. 0x00, 0x00, 0x00, 0x3C];
  1809. push32(data, toUnsigned32bit(-12));
  1810. data = data.concat(flags);
  1811. data = data.concat(fileSizes);
  1812. client._sock._websocket._receiveData(new Uint8Array(data));
  1813. // Check that we give an response caps when we receive one
  1814. expect(RFB.messages.extendedClipboardCaps).to.have.been.calledOnce;
  1815. // FIXME: Can we avoid checking internal variables?
  1816. expect(client._clipboardServerCapabilitiesFormats[0]).to.not.equal(true);
  1817. expect(client._clipboardServerCapabilitiesFormats[1]).to.equal(true);
  1818. expect(client._clipboardServerCapabilitiesFormats[2]).to.equal(true);
  1819. expect(client._clipboardServerCapabilitiesActions[(1 << 24)]).to.equal(true);
  1820. });
  1821. });
  1822. describe('Extended Clipboard Handling Receive', function () {
  1823. beforeEach(function () {
  1824. // Send our capabilities
  1825. let data = [3, 0, 0, 0];
  1826. const flags = [0x1F, 0x00, 0x00, 0x01];
  1827. let fileSizes = [0x00, 0x00, 0x00, 0x1E];
  1828. push32(data, toUnsigned32bit(-8));
  1829. data = data.concat(flags);
  1830. data = data.concat(fileSizes);
  1831. client._sock._websocket._receiveData(new Uint8Array(data));
  1832. });
  1833. describe('Handle Provide', function () {
  1834. it('should update clipboard with correct Unicode data from a Provide message', function () {
  1835. let expectedData = "Aå漢字!";
  1836. let data = [3, 0, 0, 0];
  1837. const flags = [0x10, 0x00, 0x00, 0x01];
  1838. /* The size 10 (utf8 encoded string size) and the
  1839. string "Aå漢字!" utf8 encoded and deflated. */
  1840. let deflatedData = [120, 94, 99, 96, 96, 224, 114, 60,
  1841. 188, 244, 217, 158, 69, 79, 215,
  1842. 78, 87, 4, 0, 35, 207, 6, 66];
  1843. // How much data we are sending.
  1844. push32(data, toUnsigned32bit(-(4 + deflatedData.length)));
  1845. data = data.concat(flags);
  1846. data = data.concat(deflatedData);
  1847. const spy = sinon.spy();
  1848. client.addEventListener("clipboard", spy);
  1849. client._sock._websocket._receiveData(new Uint8Array(data));
  1850. expect(spy).to.have.been.calledOnce;
  1851. expect(spy.args[0][0].detail.text).to.equal(expectedData);
  1852. client.removeEventListener("clipboard", spy);
  1853. });
  1854. it('should update clipboard with correct escape characters from a Provide message ', function () {
  1855. let expectedData = "Oh\nmy!";
  1856. let data = [3, 0, 0, 0];
  1857. const flags = [0x10, 0x00, 0x00, 0x01];
  1858. let text = encodeUTF8("Oh\r\nmy!\0");
  1859. let deflatedText = deflateWithSize(text);
  1860. // How much data we are sending.
  1861. push32(data, toUnsigned32bit(-(4 + deflatedText.length)));
  1862. data = data.concat(flags);
  1863. let sendData = new Uint8Array(data.length + deflatedText.length);
  1864. sendData.set(data);
  1865. sendData.set(deflatedText, data.length);
  1866. const spy = sinon.spy();
  1867. client.addEventListener("clipboard", spy);
  1868. client._sock._websocket._receiveData(sendData);
  1869. expect(spy).to.have.been.calledOnce;
  1870. expect(spy.args[0][0].detail.text).to.equal(expectedData);
  1871. client.removeEventListener("clipboard", spy);
  1872. });
  1873. it('should be able to handle large Provide messages', function () {
  1874. let expectedData = "hello".repeat(100000);
  1875. let data = [3, 0, 0, 0];
  1876. const flags = [0x10, 0x00, 0x00, 0x01];
  1877. let text = encodeUTF8(expectedData + "\0");
  1878. let deflatedText = deflateWithSize(text);
  1879. // How much data we are sending.
  1880. push32(data, toUnsigned32bit(-(4 + deflatedText.length)));
  1881. data = data.concat(flags);
  1882. let sendData = new Uint8Array(data.length + deflatedText.length);
  1883. sendData.set(data);
  1884. sendData.set(deflatedText, data.length);
  1885. const spy = sinon.spy();
  1886. client.addEventListener("clipboard", spy);
  1887. client._sock._websocket._receiveData(sendData);
  1888. expect(spy).to.have.been.calledOnce;
  1889. expect(spy.args[0][0].detail.text).to.equal(expectedData);
  1890. client.removeEventListener("clipboard", spy);
  1891. });
  1892. });
  1893. describe('Handle Notify', function () {
  1894. beforeEach(function () {
  1895. sinon.spy(RFB.messages, 'extendedClipboardRequest');
  1896. });
  1897. afterEach(function () {
  1898. RFB.messages.extendedClipboardRequest.restore();
  1899. });
  1900. it('should make a request with supported formats when receiving a notify message', function () {
  1901. let data = [3, 0, 0, 0];
  1902. const flags = [0x08, 0x00, 0x00, 0x07];
  1903. push32(data, toUnsigned32bit(-4));
  1904. data = data.concat(flags);
  1905. let expectedData = [0x01];
  1906. client._sock._websocket._receiveData(new Uint8Array(data));
  1907. expect(RFB.messages.extendedClipboardRequest).to.have.been.calledOnce;
  1908. expect(RFB.messages.extendedClipboardRequest).to.have.been.calledWith(client._sock, expectedData);
  1909. });
  1910. });
  1911. describe('Handle Peek', function () {
  1912. beforeEach(function () {
  1913. sinon.spy(RFB.messages, 'extendedClipboardNotify');
  1914. });
  1915. afterEach(function () {
  1916. RFB.messages.extendedClipboardNotify.restore();
  1917. });
  1918. it('should send an empty Notify when receiving a Peek and no excisting clipboard data', function () {
  1919. let data = [3, 0, 0, 0];
  1920. const flags = [0x04, 0x00, 0x00, 0x00];
  1921. push32(data, toUnsigned32bit(-4));
  1922. data = data.concat(flags);
  1923. let expectedData = [];
  1924. client._sock._websocket._receiveData(new Uint8Array(data));
  1925. expect(RFB.messages.extendedClipboardNotify).to.have.been.calledOnce;
  1926. expect(RFB.messages.extendedClipboardNotify).to.have.been.calledWith(client._sock, expectedData);
  1927. });
  1928. it('should send a Notify message with supported formats when receiving a Peek', function () {
  1929. let data = [3, 0, 0, 0];
  1930. const flags = [0x04, 0x00, 0x00, 0x00];
  1931. push32(data, toUnsigned32bit(-4));
  1932. data = data.concat(flags);
  1933. let expectedData = [0x01];
  1934. // Needed to have clipboard data to read.
  1935. // This will trigger a call to Notify, reset history
  1936. client.clipboardPasteFrom("HejHej");
  1937. RFB.messages.extendedClipboardNotify.resetHistory();
  1938. client._sock._websocket._receiveData(new Uint8Array(data));
  1939. expect(RFB.messages.extendedClipboardNotify).to.have.been.calledOnce;
  1940. expect(RFB.messages.extendedClipboardNotify).to.have.been.calledWith(client._sock, expectedData);
  1941. });
  1942. });
  1943. describe('Handle Request', function () {
  1944. beforeEach(function () {
  1945. sinon.spy(RFB.messages, 'extendedClipboardProvide');
  1946. });
  1947. afterEach(function () {
  1948. RFB.messages.extendedClipboardProvide.restore();
  1949. });
  1950. it('should send a Provide message with supported formats when receiving a Request', function () {
  1951. let data = [3, 0, 0, 0];
  1952. const flags = [0x02, 0x00, 0x00, 0x01];
  1953. push32(data, toUnsigned32bit(-4));
  1954. data = data.concat(flags);
  1955. let expectedData = [0x01];
  1956. client.clipboardPasteFrom("HejHej");
  1957. expect(RFB.messages.extendedClipboardProvide).to.not.have.been.called;
  1958. client._sock._websocket._receiveData(new Uint8Array(data));
  1959. expect(RFB.messages.extendedClipboardProvide).to.have.been.calledOnce;
  1960. expect(RFB.messages.extendedClipboardProvide).to.have.been.calledWith(client._sock, expectedData, ["HejHej"]);
  1961. });
  1962. });
  1963. });
  1964. });
  1965. it('should fire the bell callback on Bell', function () {
  1966. const spy = sinon.spy();
  1967. client.addEventListener("bell", spy);
  1968. client._sock._websocket._receiveData(new Uint8Array([2]));
  1969. expect(spy).to.have.been.calledOnce;
  1970. });
  1971. it('should respond correctly to ServerFence', function () {
  1972. const expectedMsg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: () => {}};
  1973. const incomingMsg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: () => {}};
  1974. const payload = "foo\x00ab9";
  1975. // ClientFence and ServerFence are identical in structure
  1976. RFB.messages.clientFence(expectedMsg, (1<<0) | (1<<1), payload);
  1977. RFB.messages.clientFence(incomingMsg, 0xffffffff, payload);
  1978. client._sock._websocket._receiveData(incomingMsg._sQ);
  1979. expect(client._sock).to.have.sent(expectedMsg._sQ);
  1980. expectedMsg._sQlen = 0;
  1981. incomingMsg._sQlen = 0;
  1982. RFB.messages.clientFence(expectedMsg, (1<<0), payload);
  1983. RFB.messages.clientFence(incomingMsg, (1<<0) | (1<<31), payload);
  1984. client._sock._websocket._receiveData(incomingMsg._sQ);
  1985. expect(client._sock).to.have.sent(expectedMsg._sQ);
  1986. });
  1987. it('should enable continuous updates on first EndOfContinousUpdates', function () {
  1988. const expectedMsg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
  1989. RFB.messages.enableContinuousUpdates(expectedMsg, true, 0, 0, 640, 20);
  1990. expect(client._enabledContinuousUpdates).to.be.false;
  1991. client._sock._websocket._receiveData(new Uint8Array([150]));
  1992. expect(client._enabledContinuousUpdates).to.be.true;
  1993. expect(client._sock).to.have.sent(expectedMsg._sQ);
  1994. });
  1995. it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
  1996. client._enabledContinuousUpdates = true;
  1997. client._supportsContinuousUpdates = true;
  1998. client._sock._websocket._receiveData(new Uint8Array([150]));
  1999. expect(client._enabledContinuousUpdates).to.be.false;
  2000. });
  2001. it('should update continuous updates on resize', function () {
  2002. const expectedMsg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
  2003. RFB.messages.enableContinuousUpdates(expectedMsg, true, 0, 0, 90, 700);
  2004. client._resize(450, 160);
  2005. expect(client._sock._websocket._getSentData()).to.have.length(0);
  2006. client._enabledContinuousUpdates = true;
  2007. client._resize(90, 700);
  2008. expect(client._sock).to.have.sent(expectedMsg._sQ);
  2009. });
  2010. it('should fail on an unknown message type', function () {
  2011. sinon.spy(client, "_fail");
  2012. client._sock._websocket._receiveData(new Uint8Array([87]));
  2013. expect(client._fail).to.have.been.calledOnce;
  2014. });
  2015. });
  2016. describe('Asynchronous Events', function () {
  2017. let client;
  2018. let pointerEvent;
  2019. let keyEvent;
  2020. let qemuKeyEvent;
  2021. beforeEach(function () {
  2022. client = makeRFB();
  2023. client._display.resize(100, 100);
  2024. // We need to disable this as focusing the canvas will
  2025. // cause the browser to scoll to it, messing up our
  2026. // client coordinate calculations
  2027. client.focusOnClick = false;
  2028. pointerEvent = sinon.spy(RFB.messages, 'pointerEvent');
  2029. keyEvent = sinon.spy(RFB.messages, 'keyEvent');
  2030. qemuKeyEvent = sinon.spy(RFB.messages, 'QEMUExtendedKeyEvent');
  2031. });
  2032. afterEach(function () {
  2033. pointerEvent.restore();
  2034. keyEvent.restore();
  2035. qemuKeyEvent.restore();
  2036. });
  2037. function elementToClient(x, y) {
  2038. let res = { x: 0, y: 0 };
  2039. let bounds = client._canvas.getBoundingClientRect();
  2040. /*
  2041. * If the canvas is on a fractional position we will calculate
  2042. * a fractional mouse position. But that gets truncated when we
  2043. * send the event, AND the same thing happens in RFB when it
  2044. * generates the PointerEvent message. To compensate for that
  2045. * fact we round the value upwards here.
  2046. */
  2047. res.x = Math.ceil(bounds.left + x);
  2048. res.y = Math.ceil(bounds.top + y);
  2049. return res;
  2050. }
  2051. describe('Mouse Events', function () {
  2052. function sendMouseMoveEvent(x, y) {
  2053. let pos = elementToClient(x, y);
  2054. let ev;
  2055. ev = new MouseEvent('mousemove',
  2056. { 'screenX': pos.x + window.screenX,
  2057. 'screenY': pos.y + window.screenY,
  2058. 'clientX': pos.x,
  2059. 'clientY': pos.y });
  2060. client._canvas.dispatchEvent(ev);
  2061. }
  2062. function sendMouseButtonEvent(x, y, down, button) {
  2063. let pos = elementToClient(x, y);
  2064. let ev;
  2065. ev = new MouseEvent(down ? 'mousedown' : 'mouseup',
  2066. { 'screenX': pos.x + window.screenX,
  2067. 'screenY': pos.y + window.screenY,
  2068. 'clientX': pos.x,
  2069. 'clientY': pos.y,
  2070. 'button': button,
  2071. 'buttons': 1 << button });
  2072. client._canvas.dispatchEvent(ev);
  2073. }
  2074. it('should not send button messages in view-only mode', function () {
  2075. client._viewOnly = true;
  2076. sendMouseButtonEvent(10, 10, true, 0);
  2077. clock.tick(50);
  2078. expect(pointerEvent).to.not.have.been.called;
  2079. });
  2080. it('should not send movement messages in view-only mode', function () {
  2081. client._viewOnly = true;
  2082. sendMouseMoveEvent(10, 10);
  2083. clock.tick(50);
  2084. expect(pointerEvent).to.not.have.been.called;
  2085. });
  2086. it('should handle left mouse button', function () {
  2087. sendMouseButtonEvent(10, 10, true, 0);
  2088. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2089. 10, 10, 0x1);
  2090. pointerEvent.resetHistory();
  2091. sendMouseButtonEvent(10, 10, false, 0);
  2092. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2093. 10, 10, 0x0);
  2094. });
  2095. it('should handle middle mouse button', function () {
  2096. sendMouseButtonEvent(10, 10, true, 1);
  2097. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2098. 10, 10, 0x2);
  2099. pointerEvent.resetHistory();
  2100. sendMouseButtonEvent(10, 10, false, 1);
  2101. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2102. 10, 10, 0x0);
  2103. });
  2104. it('should handle right mouse button', function () {
  2105. sendMouseButtonEvent(10, 10, true, 2);
  2106. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2107. 10, 10, 0x4);
  2108. pointerEvent.resetHistory();
  2109. sendMouseButtonEvent(10, 10, false, 2);
  2110. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2111. 10, 10, 0x0);
  2112. });
  2113. it('should handle multiple mouse buttons', function () {
  2114. sendMouseButtonEvent(10, 10, true, 0);
  2115. sendMouseButtonEvent(10, 10, true, 2);
  2116. expect(pointerEvent).to.have.been.calledTwice;
  2117. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2118. 10, 10, 0x1);
  2119. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2120. 10, 10, 0x5);
  2121. pointerEvent.resetHistory();
  2122. sendMouseButtonEvent(10, 10, false, 0);
  2123. sendMouseButtonEvent(10, 10, false, 2);
  2124. expect(pointerEvent).to.have.been.calledTwice;
  2125. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2126. 10, 10, 0x4);
  2127. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2128. 10, 10, 0x0);
  2129. });
  2130. it('should handle mouse movement', function () {
  2131. sendMouseMoveEvent(50, 70);
  2132. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2133. 50, 70, 0x0);
  2134. });
  2135. it('should handle click and drag', function () {
  2136. sendMouseButtonEvent(10, 10, true, 0);
  2137. sendMouseMoveEvent(50, 70);
  2138. expect(pointerEvent).to.have.been.calledTwice;
  2139. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2140. 10, 10, 0x1);
  2141. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2142. 50, 70, 0x1);
  2143. pointerEvent.resetHistory();
  2144. sendMouseButtonEvent(50, 70, false, 0);
  2145. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2146. 50, 70, 0x0);
  2147. });
  2148. describe('Event Aggregation', function () {
  2149. it('should send a single pointer event on mouse movement', function () {
  2150. sendMouseMoveEvent(50, 70);
  2151. clock.tick(100);
  2152. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2153. 50, 70, 0x0);
  2154. });
  2155. it('should delay one move if two events are too close', function () {
  2156. sendMouseMoveEvent(18, 30);
  2157. sendMouseMoveEvent(20, 50);
  2158. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2159. 18, 30, 0x0);
  2160. pointerEvent.resetHistory();
  2161. clock.tick(100);
  2162. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2163. 20, 50, 0x0);
  2164. });
  2165. it('should only send first and last move of many close events', function () {
  2166. sendMouseMoveEvent(18, 30);
  2167. sendMouseMoveEvent(20, 50);
  2168. sendMouseMoveEvent(21, 55);
  2169. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2170. 18, 30, 0x0);
  2171. pointerEvent.resetHistory();
  2172. clock.tick(100);
  2173. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2174. 21, 55, 0x0);
  2175. });
  2176. // We selected the 17ms since that is ~60 FPS
  2177. it('should send move events every 17 ms', function () {
  2178. sendMouseMoveEvent(1, 10); // instant send
  2179. clock.tick(10);
  2180. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2181. 1, 10, 0x0);
  2182. pointerEvent.resetHistory();
  2183. sendMouseMoveEvent(2, 20); // delayed
  2184. clock.tick(10); // timeout send
  2185. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2186. 2, 20, 0x0);
  2187. pointerEvent.resetHistory();
  2188. sendMouseMoveEvent(3, 30); // delayed
  2189. clock.tick(10);
  2190. sendMouseMoveEvent(4, 40); // delayed
  2191. clock.tick(10); // timeout send
  2192. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2193. 4, 40, 0x0);
  2194. pointerEvent.resetHistory();
  2195. sendMouseMoveEvent(5, 50); // delayed
  2196. expect(pointerEvent).to.not.have.been.called;
  2197. });
  2198. it('should send waiting move events before a button press', function () {
  2199. sendMouseMoveEvent(13, 9);
  2200. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2201. 13, 9, 0x0);
  2202. pointerEvent.resetHistory();
  2203. sendMouseMoveEvent(20, 70);
  2204. expect(pointerEvent).to.not.have.been.called;
  2205. sendMouseButtonEvent(20, 70, true, 0);
  2206. expect(pointerEvent).to.have.been.calledTwice;
  2207. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2208. 20, 70, 0x0);
  2209. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2210. 20, 70, 0x1);
  2211. });
  2212. it('should send move events with enough time apart normally', function () {
  2213. sendMouseMoveEvent(58, 60);
  2214. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2215. 58, 60, 0x0);
  2216. pointerEvent.resetHistory();
  2217. clock.tick(20);
  2218. sendMouseMoveEvent(25, 60);
  2219. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2220. 25, 60, 0x0);
  2221. pointerEvent.resetHistory();
  2222. });
  2223. it('should not send waiting move events if disconnected', function () {
  2224. sendMouseMoveEvent(88, 99);
  2225. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2226. 88, 99, 0x0);
  2227. pointerEvent.resetHistory();
  2228. sendMouseMoveEvent(66, 77);
  2229. client.disconnect();
  2230. clock.tick(20);
  2231. expect(pointerEvent).to.not.have.been.called;
  2232. });
  2233. });
  2234. it.skip('should block click events', function () {
  2235. /* FIXME */
  2236. });
  2237. it.skip('should block contextmenu events', function () {
  2238. /* FIXME */
  2239. });
  2240. });
  2241. describe('Wheel Events', function () {
  2242. function sendWheelEvent(x, y, dx, dy, mode=0) {
  2243. let pos = elementToClient(x, y);
  2244. let ev;
  2245. ev = new WheelEvent('wheel',
  2246. { 'screenX': pos.x + window.screenX,
  2247. 'screenY': pos.y + window.screenY,
  2248. 'clientX': pos.x,
  2249. 'clientY': pos.y,
  2250. 'deltaX': dx,
  2251. 'deltaY': dy,
  2252. 'deltaMode': mode });
  2253. client._canvas.dispatchEvent(ev);
  2254. }
  2255. it('should handle wheel up event', function () {
  2256. sendWheelEvent(10, 10, 0, -50);
  2257. expect(pointerEvent).to.have.been.calledTwice;
  2258. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2259. 10, 10, 1<<3);
  2260. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2261. 10, 10, 0);
  2262. });
  2263. it('should handle wheel down event', function () {
  2264. sendWheelEvent(10, 10, 0, 50);
  2265. expect(pointerEvent).to.have.been.calledTwice;
  2266. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2267. 10, 10, 1<<4);
  2268. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2269. 10, 10, 0);
  2270. });
  2271. it('should handle wheel left event', function () {
  2272. sendWheelEvent(10, 10, -50, 0);
  2273. expect(pointerEvent).to.have.been.calledTwice;
  2274. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2275. 10, 10, 1<<5);
  2276. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2277. 10, 10, 0);
  2278. });
  2279. it('should handle wheel right event', function () {
  2280. sendWheelEvent(10, 10, 50, 0);
  2281. expect(pointerEvent).to.have.been.calledTwice;
  2282. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2283. 10, 10, 1<<6);
  2284. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2285. 10, 10, 0);
  2286. });
  2287. it('should ignore wheel when in view only', function () {
  2288. client._viewOnly = true;
  2289. sendWheelEvent(10, 10, 50, 0);
  2290. expect(pointerEvent).to.not.have.been.called;
  2291. });
  2292. it('should accumulate wheel events if small enough', function () {
  2293. sendWheelEvent(10, 10, 0, 20);
  2294. sendWheelEvent(10, 10, 0, 20);
  2295. expect(pointerEvent).to.not.have.been.called;
  2296. sendWheelEvent(10, 10, 0, 20);
  2297. expect(pointerEvent).to.have.been.calledTwice;
  2298. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2299. 10, 10, 1<<4);
  2300. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2301. 10, 10, 0);
  2302. });
  2303. it('should not accumulate large wheel events', function () {
  2304. sendWheelEvent(10, 10, 0, 400);
  2305. expect(pointerEvent).to.have.been.calledTwice;
  2306. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2307. 10, 10, 1<<4);
  2308. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2309. 10, 10, 0);
  2310. });
  2311. it('should handle line based wheel event', function () {
  2312. sendWheelEvent(10, 10, 0, 3, 1);
  2313. expect(pointerEvent).to.have.been.calledTwice;
  2314. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2315. 10, 10, 1<<4);
  2316. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2317. 10, 10, 0);
  2318. });
  2319. it('should handle page based wheel event', function () {
  2320. sendWheelEvent(10, 10, 0, 3, 2);
  2321. expect(pointerEvent).to.have.been.calledTwice;
  2322. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2323. 10, 10, 1<<4);
  2324. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2325. 10, 10, 0);
  2326. });
  2327. });
  2328. describe('Keyboard Events', function () {
  2329. it('should send a key message on a key press', function () {
  2330. client._handleKeyEvent(0x41, 'KeyA', true);
  2331. const keyMsg = {_sQ: new Uint8Array(8), _sQlen: 0, flush: () => {}};
  2332. RFB.messages.keyEvent(keyMsg, 0x41, 1);
  2333. expect(client._sock).to.have.sent(keyMsg._sQ);
  2334. });
  2335. it('should not send messages in view-only mode', function () {
  2336. client._viewOnly = true;
  2337. sinon.spy(client._sock, 'flush');
  2338. client._handleKeyEvent('a', 'KeyA', true);
  2339. expect(client._sock.flush).to.not.have.been.called;
  2340. });
  2341. });
  2342. describe('Gesture event handlers', function () {
  2343. function gestureStart(gestureType, x, y,
  2344. magnitudeX = 0, magnitudeY = 0) {
  2345. let pos = elementToClient(x, y);
  2346. let detail = {type: gestureType, clientX: pos.x, clientY: pos.y};
  2347. detail.magnitudeX = magnitudeX;
  2348. detail.magnitudeY = magnitudeY;
  2349. let ev = new CustomEvent('gesturestart', { detail: detail });
  2350. client._canvas.dispatchEvent(ev);
  2351. }
  2352. function gestureMove(gestureType, x, y,
  2353. magnitudeX = 0, magnitudeY = 0) {
  2354. let pos = elementToClient(x, y);
  2355. let detail = {type: gestureType, clientX: pos.x, clientY: pos.y};
  2356. detail.magnitudeX = magnitudeX;
  2357. detail.magnitudeY = magnitudeY;
  2358. let ev = new CustomEvent('gesturemove', { detail: detail });
  2359. client._canvas.dispatchEvent(ev);
  2360. }
  2361. function gestureEnd(gestureType, x, y) {
  2362. let pos = elementToClient(x, y);
  2363. let detail = {type: gestureType, clientX: pos.x, clientY: pos.y};
  2364. let ev = new CustomEvent('gestureend', { detail: detail });
  2365. client._canvas.dispatchEvent(ev);
  2366. }
  2367. describe('Gesture onetap', function () {
  2368. it('should handle onetap events', function () {
  2369. let bmask = 0x1;
  2370. gestureStart('onetap', 20, 40);
  2371. gestureEnd('onetap', 20, 40);
  2372. expect(pointerEvent).to.have.been.calledThrice;
  2373. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2374. 20, 40, 0x0);
  2375. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2376. 20, 40, bmask);
  2377. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2378. 20, 40, 0x0);
  2379. });
  2380. it('should keep same position for multiple onetap events', function () {
  2381. let bmask = 0x1;
  2382. gestureStart('onetap', 20, 40);
  2383. gestureEnd('onetap', 20, 40);
  2384. expect(pointerEvent).to.have.been.calledThrice;
  2385. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2386. 20, 40, 0x0);
  2387. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2388. 20, 40, bmask);
  2389. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2390. 20, 40, 0x0);
  2391. pointerEvent.resetHistory();
  2392. gestureStart('onetap', 20, 50);
  2393. gestureEnd('onetap', 20, 50);
  2394. expect(pointerEvent).to.have.been.calledThrice;
  2395. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2396. 20, 40, 0x0);
  2397. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2398. 20, 40, bmask);
  2399. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2400. 20, 40, 0x0);
  2401. pointerEvent.resetHistory();
  2402. gestureStart('onetap', 30, 50);
  2403. gestureEnd('onetap', 30, 50);
  2404. expect(pointerEvent).to.have.been.calledThrice;
  2405. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2406. 20, 40, 0x0);
  2407. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2408. 20, 40, bmask);
  2409. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2410. 20, 40, 0x0);
  2411. });
  2412. it('should not keep same position for onetap events when too far apart', function () {
  2413. let bmask = 0x1;
  2414. gestureStart('onetap', 20, 40);
  2415. gestureEnd('onetap', 20, 40);
  2416. expect(pointerEvent).to.have.been.calledThrice;
  2417. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2418. 20, 40, 0x0);
  2419. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2420. 20, 40, bmask);
  2421. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2422. 20, 40, 0x0);
  2423. pointerEvent.resetHistory();
  2424. gestureStart('onetap', 80, 95);
  2425. gestureEnd('onetap', 80, 95);
  2426. expect(pointerEvent).to.have.been.calledThrice;
  2427. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2428. 80, 95, 0x0);
  2429. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2430. 80, 95, bmask);
  2431. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2432. 80, 95, 0x0);
  2433. });
  2434. it('should not keep same position for onetap events when enough time inbetween', function () {
  2435. let bmask = 0x1;
  2436. gestureStart('onetap', 10, 20);
  2437. gestureEnd('onetap', 10, 20);
  2438. expect(pointerEvent).to.have.been.calledThrice;
  2439. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2440. 10, 20, 0x0);
  2441. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2442. 10, 20, bmask);
  2443. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2444. 10, 20, 0x0);
  2445. pointerEvent.resetHistory();
  2446. this.clock.tick(1500);
  2447. gestureStart('onetap', 15, 20);
  2448. gestureEnd('onetap', 15, 20);
  2449. expect(pointerEvent).to.have.been.calledThrice;
  2450. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2451. 15, 20, 0x0);
  2452. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2453. 15, 20, bmask);
  2454. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2455. 15, 20, 0x0);
  2456. pointerEvent.resetHistory();
  2457. });
  2458. });
  2459. describe('Gesture twotap', function () {
  2460. it('should handle gesture twotap events', function () {
  2461. let bmask = 0x4;
  2462. gestureStart("twotap", 20, 40);
  2463. expect(pointerEvent).to.have.been.calledThrice;
  2464. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2465. 20, 40, 0x0);
  2466. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2467. 20, 40, bmask);
  2468. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2469. 20, 40, 0x0);
  2470. });
  2471. it('should keep same position for multiple twotap events', function () {
  2472. let bmask = 0x4;
  2473. for (let offset = 0;offset < 30;offset += 10) {
  2474. pointerEvent.resetHistory();
  2475. gestureStart('twotap', 20, 40 + offset);
  2476. gestureEnd('twotap', 20, 40 + offset);
  2477. expect(pointerEvent).to.have.been.calledThrice;
  2478. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2479. 20, 40, 0x0);
  2480. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2481. 20, 40, bmask);
  2482. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2483. 20, 40, 0x0);
  2484. }
  2485. });
  2486. });
  2487. describe('Gesture threetap', function () {
  2488. it('should handle gesture start for threetap events', function () {
  2489. let bmask = 0x2;
  2490. gestureStart("threetap", 20, 40);
  2491. expect(pointerEvent).to.have.been.calledThrice;
  2492. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2493. 20, 40, 0x0);
  2494. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2495. 20, 40, bmask);
  2496. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2497. 20, 40, 0x0);
  2498. });
  2499. it('should keep same position for multiple threetap events', function () {
  2500. let bmask = 0x2;
  2501. for (let offset = 0;offset < 30;offset += 10) {
  2502. pointerEvent.resetHistory();
  2503. gestureStart('threetap', 20, 40 + offset);
  2504. gestureEnd('threetap', 20, 40 + offset);
  2505. expect(pointerEvent).to.have.been.calledThrice;
  2506. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2507. 20, 40, 0x0);
  2508. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2509. 20, 40, bmask);
  2510. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2511. 20, 40, 0x0);
  2512. }
  2513. });
  2514. });
  2515. describe('Gesture drag', function () {
  2516. it('should handle gesture drag events', function () {
  2517. let bmask = 0x1;
  2518. gestureStart('drag', 20, 40);
  2519. expect(pointerEvent).to.have.been.calledTwice;
  2520. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2521. 20, 40, 0x0);
  2522. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2523. 20, 40, bmask);
  2524. pointerEvent.resetHistory();
  2525. gestureMove('drag', 30, 50);
  2526. clock.tick(50);
  2527. expect(pointerEvent).to.have.been.calledOnce;
  2528. expect(pointerEvent).to.have.been.calledWith(client._sock,
  2529. 30, 50, bmask);
  2530. pointerEvent.resetHistory();
  2531. gestureEnd('drag', 30, 50);
  2532. expect(pointerEvent).to.have.been.calledTwice;
  2533. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2534. 30, 50, bmask);
  2535. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2536. 30, 50, 0x0);
  2537. });
  2538. });
  2539. describe('Gesture long press', function () {
  2540. it('should handle long press events', function () {
  2541. let bmask = 0x4;
  2542. gestureStart('longpress', 20, 40);
  2543. expect(pointerEvent).to.have.been.calledTwice;
  2544. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2545. 20, 40, 0x0);
  2546. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2547. 20, 40, bmask);
  2548. pointerEvent.resetHistory();
  2549. gestureMove('longpress', 40, 60);
  2550. clock.tick(50);
  2551. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2552. 40, 60, bmask);
  2553. pointerEvent.resetHistory();
  2554. gestureEnd('longpress', 40, 60);
  2555. expect(pointerEvent).to.have.been.calledTwice;
  2556. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2557. 40, 60, bmask);
  2558. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2559. 40, 60, 0x0);
  2560. });
  2561. });
  2562. describe('Gesture twodrag', function () {
  2563. it('should handle gesture twodrag up events', function () {
  2564. let bmask = 0x10; // Button mask for scroll down
  2565. gestureStart('twodrag', 20, 40, 0, 0);
  2566. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2567. 20, 40, 0x0);
  2568. pointerEvent.resetHistory();
  2569. gestureMove('twodrag', 20, 40, 0, -60);
  2570. expect(pointerEvent).to.have.been.calledThrice;
  2571. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2572. 20, 40, 0x0);
  2573. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2574. 20, 40, bmask);
  2575. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2576. 20, 40, 0x0);
  2577. });
  2578. it('should handle gesture twodrag down events', function () {
  2579. let bmask = 0x8; // Button mask for scroll up
  2580. gestureStart('twodrag', 20, 40, 0, 0);
  2581. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2582. 20, 40, 0x0);
  2583. pointerEvent.resetHistory();
  2584. gestureMove('twodrag', 20, 40, 0, 60);
  2585. expect(pointerEvent).to.have.been.calledThrice;
  2586. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2587. 20, 40, 0x0);
  2588. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2589. 20, 40, bmask);
  2590. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2591. 20, 40, 0x0);
  2592. });
  2593. it('should handle gesture twodrag right events', function () {
  2594. let bmask = 0x20; // Button mask for scroll right
  2595. gestureStart('twodrag', 20, 40, 0, 0);
  2596. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2597. 20, 40, 0x0);
  2598. pointerEvent.resetHistory();
  2599. gestureMove('twodrag', 20, 40, 60, 0);
  2600. expect(pointerEvent).to.have.been.calledThrice;
  2601. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2602. 20, 40, 0x0);
  2603. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2604. 20, 40, bmask);
  2605. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2606. 20, 40, 0x0);
  2607. });
  2608. it('should handle gesture twodrag left events', function () {
  2609. let bmask = 0x40; // Button mask for scroll left
  2610. gestureStart('twodrag', 20, 40, 0, 0);
  2611. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2612. 20, 40, 0x0);
  2613. pointerEvent.resetHistory();
  2614. gestureMove('twodrag', 20, 40, -60, 0);
  2615. expect(pointerEvent).to.have.been.calledThrice;
  2616. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2617. 20, 40, 0x0);
  2618. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2619. 20, 40, bmask);
  2620. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2621. 20, 40, 0x0);
  2622. });
  2623. it('should handle gesture twodrag diag events', function () {
  2624. let scrlUp = 0x8; // Button mask for scroll up
  2625. let scrlRight = 0x20; // Button mask for scroll right
  2626. gestureStart('twodrag', 20, 40, 0, 0);
  2627. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2628. 20, 40, 0x0);
  2629. pointerEvent.resetHistory();
  2630. gestureMove('twodrag', 20, 40, 60, 60);
  2631. expect(pointerEvent).to.have.been.callCount(5);
  2632. expect(pointerEvent.getCall(0)).to.have.been.calledWith(client._sock,
  2633. 20, 40, 0x0);
  2634. expect(pointerEvent.getCall(1)).to.have.been.calledWith(client._sock,
  2635. 20, 40, scrlUp);
  2636. expect(pointerEvent.getCall(2)).to.have.been.calledWith(client._sock,
  2637. 20, 40, 0x0);
  2638. expect(pointerEvent.getCall(3)).to.have.been.calledWith(client._sock,
  2639. 20, 40, scrlRight);
  2640. expect(pointerEvent.getCall(4)).to.have.been.calledWith(client._sock,
  2641. 20, 40, 0x0);
  2642. });
  2643. it('should handle multiple small gesture twodrag events', function () {
  2644. let bmask = 0x8; // Button mask for scroll up
  2645. gestureStart('twodrag', 20, 40, 0, 0);
  2646. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2647. 20, 40, 0x0);
  2648. pointerEvent.resetHistory();
  2649. gestureMove('twodrag', 20, 40, 0, 10);
  2650. clock.tick(50);
  2651. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2652. 20, 40, 0x0);
  2653. pointerEvent.resetHistory();
  2654. gestureMove('twodrag', 20, 40, 0, 20);
  2655. clock.tick(50);
  2656. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2657. 20, 40, 0x0);
  2658. pointerEvent.resetHistory();
  2659. gestureMove('twodrag', 20, 40, 0, 60);
  2660. expect(pointerEvent).to.have.been.calledThrice;
  2661. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2662. 20, 40, 0x0);
  2663. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2664. 20, 40, bmask);
  2665. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2666. 20, 40, 0x0);
  2667. });
  2668. it('should handle large gesture twodrag events', function () {
  2669. let bmask = 0x8; // Button mask for scroll up
  2670. gestureStart('twodrag', 30, 50, 0, 0);
  2671. expect(pointerEvent).
  2672. to.have.been.calledOnceWith(client._sock, 30, 50, 0x0);
  2673. pointerEvent.resetHistory();
  2674. gestureMove('twodrag', 30, 50, 0, 200);
  2675. expect(pointerEvent).to.have.callCount(7);
  2676. expect(pointerEvent.getCall(0)).to.have.been.calledWith(client._sock,
  2677. 30, 50, 0x0);
  2678. expect(pointerEvent.getCall(1)).to.have.been.calledWith(client._sock,
  2679. 30, 50, bmask);
  2680. expect(pointerEvent.getCall(2)).to.have.been.calledWith(client._sock,
  2681. 30, 50, 0x0);
  2682. expect(pointerEvent.getCall(3)).to.have.been.calledWith(client._sock,
  2683. 30, 50, bmask);
  2684. expect(pointerEvent.getCall(4)).to.have.been.calledWith(client._sock,
  2685. 30, 50, 0x0);
  2686. expect(pointerEvent.getCall(5)).to.have.been.calledWith(client._sock,
  2687. 30, 50, bmask);
  2688. expect(pointerEvent.getCall(6)).to.have.been.calledWith(client._sock,
  2689. 30, 50, 0x0);
  2690. });
  2691. });
  2692. describe('Gesture pinch', function () {
  2693. it('should handle gesture pinch in events', function () {
  2694. let keysym = KeyTable.XK_Control_L;
  2695. let bmask = 0x10; // Button mask for scroll down
  2696. gestureStart('pinch', 20, 40, 90, 90);
  2697. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2698. 20, 40, 0x0);
  2699. expect(keyEvent).to.not.have.been.called;
  2700. pointerEvent.resetHistory();
  2701. gestureMove('pinch', 20, 40, 30, 30);
  2702. expect(pointerEvent).to.have.been.calledThrice;
  2703. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2704. 20, 40, 0x0);
  2705. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2706. 20, 40, bmask);
  2707. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2708. 20, 40, 0x0);
  2709. expect(keyEvent).to.have.been.calledTwice;
  2710. expect(keyEvent.firstCall).to.have.been.calledWith(client._sock,
  2711. keysym, 1);
  2712. expect(keyEvent.secondCall).to.have.been.calledWith(client._sock,
  2713. keysym, 0);
  2714. expect(keyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall);
  2715. expect(keyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall);
  2716. pointerEvent.resetHistory();
  2717. keyEvent.resetHistory();
  2718. gestureEnd('pinch', 20, 40);
  2719. expect(pointerEvent).to.not.have.been.called;
  2720. expect(keyEvent).to.not.have.been.called;
  2721. });
  2722. it('should handle gesture pinch out events', function () {
  2723. let keysym = KeyTable.XK_Control_L;
  2724. let bmask = 0x8; // Button mask for scroll up
  2725. gestureStart('pinch', 10, 20, 10, 20);
  2726. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2727. 10, 20, 0x0);
  2728. expect(keyEvent).to.not.have.been.called;
  2729. pointerEvent.resetHistory();
  2730. gestureMove('pinch', 10, 20, 70, 80);
  2731. expect(pointerEvent).to.have.been.calledThrice;
  2732. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2733. 10, 20, 0x0);
  2734. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2735. 10, 20, bmask);
  2736. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2737. 10, 20, 0x0);
  2738. expect(keyEvent).to.have.been.calledTwice;
  2739. expect(keyEvent.firstCall).to.have.been.calledWith(client._sock,
  2740. keysym, 1);
  2741. expect(keyEvent.secondCall).to.have.been.calledWith(client._sock,
  2742. keysym, 0);
  2743. expect(keyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall);
  2744. expect(keyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall);
  2745. pointerEvent.resetHistory();
  2746. keyEvent.resetHistory();
  2747. gestureEnd('pinch', 10, 20);
  2748. expect(pointerEvent).to.not.have.been.called;
  2749. expect(keyEvent).to.not.have.been.called;
  2750. });
  2751. it('should handle large gesture pinch', function () {
  2752. let keysym = KeyTable.XK_Control_L;
  2753. let bmask = 0x10; // Button mask for scroll down
  2754. gestureStart('pinch', 20, 40, 150, 150);
  2755. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2756. 20, 40, 0x0);
  2757. expect(keyEvent).to.not.have.been.called;
  2758. pointerEvent.resetHistory();
  2759. gestureMove('pinch', 20, 40, 30, 30);
  2760. expect(pointerEvent).to.have.been.callCount(5);
  2761. expect(pointerEvent.getCall(0)).to.have.been.calledWith(client._sock,
  2762. 20, 40, 0x0);
  2763. expect(pointerEvent.getCall(1)).to.have.been.calledWith(client._sock,
  2764. 20, 40, bmask);
  2765. expect(pointerEvent.getCall(2)).to.have.been.calledWith(client._sock,
  2766. 20, 40, 0x0);
  2767. expect(pointerEvent.getCall(3)).to.have.been.calledWith(client._sock,
  2768. 20, 40, bmask);
  2769. expect(pointerEvent.getCall(4)).to.have.been.calledWith(client._sock,
  2770. 20, 40, 0x0);
  2771. expect(keyEvent).to.have.been.calledTwice;
  2772. expect(keyEvent.firstCall).to.have.been.calledWith(client._sock,
  2773. keysym, 1);
  2774. expect(keyEvent.secondCall).to.have.been.calledWith(client._sock,
  2775. keysym, 0);
  2776. expect(keyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall);
  2777. expect(keyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall);
  2778. pointerEvent.resetHistory();
  2779. keyEvent.resetHistory();
  2780. gestureEnd('pinch', 20, 40);
  2781. expect(pointerEvent).to.not.have.been.called;
  2782. expect(keyEvent).to.not.have.been.called;
  2783. });
  2784. it('should handle multiple small gesture pinch out events', function () {
  2785. let keysym = KeyTable.XK_Control_L;
  2786. let bmask = 0x8; // Button mask for scroll down
  2787. gestureStart('pinch', 20, 40, 0, 10);
  2788. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2789. 20, 40, 0x0);
  2790. expect(keyEvent).to.not.have.been.called;
  2791. pointerEvent.resetHistory();
  2792. gestureMove('pinch', 20, 40, 0, 30);
  2793. clock.tick(50);
  2794. expect(pointerEvent).to.have.been.calledWith(client._sock,
  2795. 20, 40, 0x0);
  2796. pointerEvent.resetHistory();
  2797. gestureMove('pinch', 20, 40, 0, 60);
  2798. clock.tick(50);
  2799. expect(pointerEvent).to.have.been.calledWith(client._sock,
  2800. 20, 40, 0x0);
  2801. pointerEvent.resetHistory();
  2802. keyEvent.resetHistory();
  2803. gestureMove('pinch', 20, 40, 0, 90);
  2804. expect(pointerEvent).to.have.been.calledThrice;
  2805. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2806. 20, 40, 0x0);
  2807. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2808. 20, 40, bmask);
  2809. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2810. 20, 40, 0x0);
  2811. expect(keyEvent).to.have.been.calledTwice;
  2812. expect(keyEvent.firstCall).to.have.been.calledWith(client._sock,
  2813. keysym, 1);
  2814. expect(keyEvent.secondCall).to.have.been.calledWith(client._sock,
  2815. keysym, 0);
  2816. expect(keyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall);
  2817. expect(keyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall);
  2818. pointerEvent.resetHistory();
  2819. keyEvent.resetHistory();
  2820. gestureEnd('pinch', 20, 40);
  2821. expect(keyEvent).to.not.have.been.called;
  2822. });
  2823. it('should send correct key control code', function () {
  2824. let keysym = KeyTable.XK_Control_L;
  2825. let code = 0x1d;
  2826. let bmask = 0x10; // Button mask for scroll down
  2827. client._qemuExtKeyEventSupported = true;
  2828. gestureStart('pinch', 20, 40, 90, 90);
  2829. expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
  2830. 20, 40, 0x0);
  2831. expect(qemuKeyEvent).to.not.have.been.called;
  2832. pointerEvent.resetHistory();
  2833. gestureMove('pinch', 20, 40, 30, 30);
  2834. expect(pointerEvent).to.have.been.calledThrice;
  2835. expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
  2836. 20, 40, 0x0);
  2837. expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
  2838. 20, 40, bmask);
  2839. expect(pointerEvent.thirdCall).to.have.been.calledWith(client._sock,
  2840. 20, 40, 0x0);
  2841. expect(qemuKeyEvent).to.have.been.calledTwice;
  2842. expect(qemuKeyEvent.firstCall).to.have.been.calledWith(client._sock,
  2843. keysym,
  2844. true,
  2845. code);
  2846. expect(qemuKeyEvent.secondCall).to.have.been.calledWith(client._sock,
  2847. keysym,
  2848. false,
  2849. code);
  2850. expect(qemuKeyEvent.firstCall).to.have.been.calledBefore(pointerEvent.secondCall);
  2851. expect(qemuKeyEvent.lastCall).to.have.been.calledAfter(pointerEvent.lastCall);
  2852. pointerEvent.resetHistory();
  2853. qemuKeyEvent.resetHistory();
  2854. gestureEnd('pinch', 20, 40);
  2855. expect(pointerEvent).to.not.have.been.called;
  2856. expect(qemuKeyEvent).to.not.have.been.called;
  2857. });
  2858. });
  2859. });
  2860. describe('WebSocket Events', function () {
  2861. // message events
  2862. it('should do nothing if we receive an empty message and have nothing in the queue', function () {
  2863. client._normalMsg = sinon.spy();
  2864. client._sock._websocket._receiveData(new Uint8Array([]));
  2865. expect(client._normalMsg).to.not.have.been.called;
  2866. });
  2867. it('should handle a message in the connected state as a normal message', function () {
  2868. client._normalMsg = sinon.spy();
  2869. client._sock._websocket._receiveData(new Uint8Array([1, 2, 3]));
  2870. expect(client._normalMsg).to.have.been.called;
  2871. });
  2872. it('should handle a message in any non-disconnected/failed state like an init message', function () {
  2873. client._rfbConnectionState = 'connecting';
  2874. client._rfbInitState = 'ProtocolVersion';
  2875. client._initMsg = sinon.spy();
  2876. client._sock._websocket._receiveData(new Uint8Array([1, 2, 3]));
  2877. expect(client._initMsg).to.have.been.called;
  2878. });
  2879. it('should process all normal messages directly', function () {
  2880. const spy = sinon.spy();
  2881. client.addEventListener("bell", spy);
  2882. client._sock._websocket._receiveData(new Uint8Array([0x02, 0x02]));
  2883. expect(spy).to.have.been.calledTwice;
  2884. });
  2885. // open events
  2886. it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
  2887. client = new RFB(document.createElement('div'), 'wss://host:8675');
  2888. this.clock.tick();
  2889. client._sock._websocket._open();
  2890. expect(client._rfbInitState).to.equal('ProtocolVersion');
  2891. });
  2892. it('should fail if we are not currently ready to connect and we get an "open" event', function () {
  2893. sinon.spy(client, "_fail");
  2894. client._rfbConnectionState = 'connected';
  2895. client._sock._websocket._open();
  2896. expect(client._fail).to.have.been.calledOnce;
  2897. });
  2898. // close events
  2899. it('should transition to "disconnected" from "disconnecting" on a close event', function () {
  2900. const real = client._sock._websocket.close;
  2901. client._sock._websocket.close = () => {};
  2902. client.disconnect();
  2903. expect(client._rfbConnectionState).to.equal('disconnecting');
  2904. client._sock._websocket.close = real;
  2905. client._sock._websocket.close();
  2906. expect(client._rfbConnectionState).to.equal('disconnected');
  2907. });
  2908. it('should fail if we get a close event while connecting', function () {
  2909. sinon.spy(client, "_fail");
  2910. client._rfbConnectionState = 'connecting';
  2911. client._sock._websocket.close();
  2912. expect(client._fail).to.have.been.calledOnce;
  2913. });
  2914. it('should unregister close event handler', function () {
  2915. sinon.spy(client._sock, 'off');
  2916. client.disconnect();
  2917. client._sock._websocket.close();
  2918. expect(client._sock.off).to.have.been.calledWith('close');
  2919. });
  2920. // error events do nothing
  2921. });
  2922. });
  2923. describe('Quality level setting', function () {
  2924. const defaultQuality = 6;
  2925. let client;
  2926. beforeEach(function () {
  2927. client = makeRFB();
  2928. sinon.spy(RFB.messages, "clientEncodings");
  2929. });
  2930. afterEach(function () {
  2931. RFB.messages.clientEncodings.restore();
  2932. });
  2933. it(`should equal ${defaultQuality} by default`, function () {
  2934. expect(client._qualityLevel).to.equal(defaultQuality);
  2935. });
  2936. it('should ignore non-integers when set', function () {
  2937. client.qualityLevel = '1';
  2938. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2939. RFB.messages.clientEncodings.resetHistory();
  2940. client.qualityLevel = 1.5;
  2941. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2942. RFB.messages.clientEncodings.resetHistory();
  2943. client.qualityLevel = null;
  2944. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2945. RFB.messages.clientEncodings.resetHistory();
  2946. client.qualityLevel = undefined;
  2947. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2948. RFB.messages.clientEncodings.resetHistory();
  2949. client.qualityLevel = {};
  2950. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2951. });
  2952. it('should ignore integers out of range [0, 9]', function () {
  2953. client.qualityLevel = -1;
  2954. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2955. RFB.messages.clientEncodings.resetHistory();
  2956. client.qualityLevel = 10;
  2957. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2958. });
  2959. it('should send clientEncodings with new quality value', function () {
  2960. let newQuality;
  2961. newQuality = 8;
  2962. client.qualityLevel = newQuality;
  2963. expect(client.qualityLevel).to.equal(newQuality);
  2964. expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
  2965. expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality);
  2966. });
  2967. it('should not send clientEncodings if quality is the same', function () {
  2968. let newQuality;
  2969. newQuality = 2;
  2970. client.qualityLevel = newQuality;
  2971. expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
  2972. expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality);
  2973. RFB.messages.clientEncodings.resetHistory();
  2974. client.qualityLevel = newQuality;
  2975. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2976. });
  2977. it('should not send clientEncodings if not in connected state', function () {
  2978. let newQuality;
  2979. client._rfbConnectionState = '';
  2980. newQuality = 2;
  2981. client.qualityLevel = newQuality;
  2982. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2983. RFB.messages.clientEncodings.resetHistory();
  2984. client._rfbConnectionState = 'connnecting';
  2985. newQuality = 6;
  2986. client.qualityLevel = newQuality;
  2987. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  2988. RFB.messages.clientEncodings.resetHistory();
  2989. client._rfbConnectionState = 'connected';
  2990. newQuality = 5;
  2991. client.qualityLevel = newQuality;
  2992. expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
  2993. expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingQualityLevel0 + newQuality);
  2994. });
  2995. });
  2996. describe('Compression level setting', function () {
  2997. const defaultCompression = 2;
  2998. let client;
  2999. beforeEach(function () {
  3000. client = makeRFB();
  3001. sinon.spy(RFB.messages, "clientEncodings");
  3002. });
  3003. afterEach(function () {
  3004. RFB.messages.clientEncodings.restore();
  3005. });
  3006. it(`should equal ${defaultCompression} by default`, function () {
  3007. expect(client._compressionLevel).to.equal(defaultCompression);
  3008. });
  3009. it('should ignore non-integers when set', function () {
  3010. client.compressionLevel = '1';
  3011. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3012. RFB.messages.clientEncodings.resetHistory();
  3013. client.compressionLevel = 1.5;
  3014. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3015. RFB.messages.clientEncodings.resetHistory();
  3016. client.compressionLevel = null;
  3017. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3018. RFB.messages.clientEncodings.resetHistory();
  3019. client.compressionLevel = undefined;
  3020. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3021. RFB.messages.clientEncodings.resetHistory();
  3022. client.compressionLevel = {};
  3023. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3024. });
  3025. it('should ignore integers out of range [0, 9]', function () {
  3026. client.compressionLevel = -1;
  3027. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3028. RFB.messages.clientEncodings.resetHistory();
  3029. client.compressionLevel = 10;
  3030. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3031. });
  3032. it('should send clientEncodings with new compression value', function () {
  3033. let newCompression;
  3034. newCompression = 5;
  3035. client.compressionLevel = newCompression;
  3036. expect(client.compressionLevel).to.equal(newCompression);
  3037. expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
  3038. expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression);
  3039. });
  3040. it('should not send clientEncodings if compression is the same', function () {
  3041. let newCompression;
  3042. newCompression = 9;
  3043. client.compressionLevel = newCompression;
  3044. expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
  3045. expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression);
  3046. RFB.messages.clientEncodings.resetHistory();
  3047. client.compressionLevel = newCompression;
  3048. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3049. });
  3050. it('should not send clientEncodings if not in connected state', function () {
  3051. let newCompression;
  3052. client._rfbConnectionState = '';
  3053. newCompression = 7;
  3054. client.compressionLevel = newCompression;
  3055. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3056. RFB.messages.clientEncodings.resetHistory();
  3057. client._rfbConnectionState = 'connnecting';
  3058. newCompression = 6;
  3059. client.compressionLevel = newCompression;
  3060. expect(RFB.messages.clientEncodings).to.not.have.been.called;
  3061. RFB.messages.clientEncodings.resetHistory();
  3062. client._rfbConnectionState = 'connected';
  3063. newCompression = 5;
  3064. client.compressionLevel = newCompression;
  3065. expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
  3066. expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.pseudoEncodingCompressLevel0 + newCompression);
  3067. });
  3068. });
  3069. });
  3070. describe('RFB messages', function () {
  3071. let sock;
  3072. before(function () {
  3073. FakeWebSocket.replace();
  3074. sock = new Websock();
  3075. sock.open();
  3076. });
  3077. after(function () {
  3078. FakeWebSocket.restore();
  3079. });
  3080. describe('Extended Clipboard Handling Send', function () {
  3081. beforeEach(function () {
  3082. sinon.spy(RFB.messages, 'clientCutText');
  3083. });
  3084. afterEach(function () {
  3085. RFB.messages.clientCutText.restore();
  3086. });
  3087. it('should call clientCutText with correct Caps data', function () {
  3088. let formats = {
  3089. 0: 2,
  3090. 2: 4121
  3091. };
  3092. let expectedData = new Uint8Array([0x1F, 0x00, 0x00, 0x05,
  3093. 0x00, 0x00, 0x00, 0x02,
  3094. 0x00, 0x00, 0x10, 0x19]);
  3095. let actions = [
  3096. 1 << 24, // Caps
  3097. 1 << 25, // Request
  3098. 1 << 26, // Peek
  3099. 1 << 27, // Notify
  3100. 1 << 28 // Provide
  3101. ];
  3102. RFB.messages.extendedClipboardCaps(sock, actions, formats);
  3103. expect(RFB.messages.clientCutText).to.have.been.calledOnce;
  3104. expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData);
  3105. });
  3106. it('should call clientCutText with correct Request data', function () {
  3107. let formats = new Uint8Array([0x01]);
  3108. let expectedData = new Uint8Array([0x02, 0x00, 0x00, 0x01]);
  3109. RFB.messages.extendedClipboardRequest(sock, formats);
  3110. expect(RFB.messages.clientCutText).to.have.been.calledOnce;
  3111. expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData);
  3112. });
  3113. it('should call clientCutText with correct Notify data', function () {
  3114. let formats = new Uint8Array([0x01]);
  3115. let expectedData = new Uint8Array([0x08, 0x00, 0x00, 0x01]);
  3116. RFB.messages.extendedClipboardNotify(sock, formats);
  3117. expect(RFB.messages.clientCutText).to.have.been.calledOnce;
  3118. expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData);
  3119. });
  3120. it('should call clientCutText with correct Provide data', function () {
  3121. let testText = "Test string";
  3122. let expectedText = encodeUTF8(testText + "\0");
  3123. let deflatedData = deflateWithSize(expectedText);
  3124. // Build Expected with flags and deflated data
  3125. let expectedData = new Uint8Array(4 + deflatedData.length);
  3126. expectedData[0] = 0x10; // The client capabilities
  3127. expectedData[1] = 0x00; // Reserved flags
  3128. expectedData[2] = 0x00; // Reserved flags
  3129. expectedData[3] = 0x01; // The formats client supports
  3130. expectedData.set(deflatedData, 4);
  3131. RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]);
  3132. expect(RFB.messages.clientCutText).to.have.been.calledOnce;
  3133. expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true);
  3134. });
  3135. describe('End of line characters', function () {
  3136. it('Carriage return', function () {
  3137. let testText = "Hello\rworld\r\r!";
  3138. let expectedText = encodeUTF8("Hello\r\nworld\r\n\r\n!\0");
  3139. let deflatedData = deflateWithSize(expectedText);
  3140. // Build Expected with flags and deflated data
  3141. let expectedData = new Uint8Array(4 + deflatedData.length);
  3142. expectedData[0] = 0x10; // The client capabilities
  3143. expectedData[1] = 0x00; // Reserved flags
  3144. expectedData[2] = 0x00; // Reserved flags
  3145. expectedData[3] = 0x01; // The formats client supports
  3146. expectedData.set(deflatedData, 4);
  3147. RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]);
  3148. expect(RFB.messages.clientCutText).to.have.been.calledOnce;
  3149. expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true);
  3150. });
  3151. it('Carriage return Line feed', function () {
  3152. let testText = "Hello\r\n\r\nworld\r\n!";
  3153. let expectedText = encodeUTF8(testText + "\0");
  3154. let deflatedData = deflateWithSize(expectedText);
  3155. // Build Expected with flags and deflated data
  3156. let expectedData = new Uint8Array(4 + deflatedData.length);
  3157. expectedData[0] = 0x10; // The client capabilities
  3158. expectedData[1] = 0x00; // Reserved flags
  3159. expectedData[2] = 0x00; // Reserved flags
  3160. expectedData[3] = 0x01; // The formats client supports
  3161. expectedData.set(deflatedData, 4);
  3162. RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]);
  3163. expect(RFB.messages.clientCutText).to.have.been.calledOnce;
  3164. expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true);
  3165. });
  3166. it('Line feed', function () {
  3167. let testText = "Hello\n\n\nworld\n!";
  3168. let expectedText = encodeUTF8("Hello\r\n\r\n\r\nworld\r\n!\0");
  3169. let deflatedData = deflateWithSize(expectedText);
  3170. // Build Expected with flags and deflated data
  3171. let expectedData = new Uint8Array(4 + deflatedData.length);
  3172. expectedData[0] = 0x10; // The client capabilities
  3173. expectedData[1] = 0x00; // Reserved flags
  3174. expectedData[2] = 0x00; // Reserved flags
  3175. expectedData[3] = 0x01; // The formats client supports
  3176. expectedData.set(deflatedData, 4);
  3177. RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]);
  3178. expect(RFB.messages.clientCutText).to.have.been.calledOnce;
  3179. expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true);
  3180. });
  3181. it('Carriage return and Line feed mixed', function () {
  3182. let testText = "\rHello\r\n\rworld\n\n!";
  3183. let expectedText = encodeUTF8("\r\nHello\r\n\r\nworld\r\n\r\n!\0");
  3184. let deflatedData = deflateWithSize(expectedText);
  3185. // Build Expected with flags and deflated data
  3186. let expectedData = new Uint8Array(4 + deflatedData.length);
  3187. expectedData[0] = 0x10; // The client capabilities
  3188. expectedData[1] = 0x00; // Reserved flags
  3189. expectedData[2] = 0x00; // Reserved flags
  3190. expectedData[3] = 0x01; // The formats client supports
  3191. expectedData.set(deflatedData, 4);
  3192. RFB.messages.extendedClipboardProvide(sock, [0x01], [testText]);
  3193. expect(RFB.messages.clientCutText).to.have.been.calledOnce;
  3194. expect(RFB.messages.clientCutText).to.have.been.calledWith(sock, expectedData, true);
  3195. });
  3196. });
  3197. });
  3198. });