]> git.proxmox.com Git - mirror_novnc.git/blob - tests/playback.js
Add deflator helper class for deflating data
[mirror_novnc.git] / tests / playback.js
1 /*
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2018 The noVNC Authors
4 * Licensed under MPL 2.0 (see LICENSE.txt)
5 */
6
7 import RFB from '../core/rfb.js';
8 import * as Log from '../core/util/logging.js';
9
10 // Immediate polyfill
11 if (window.setImmediate === undefined) {
12 let _immediateIdCounter = 1;
13 const _immediateFuncs = {};
14
15 window.setImmediate = (func) => {
16 const index = _immediateIdCounter++;
17 _immediateFuncs[index] = func;
18 window.postMessage("noVNC immediate trigger:" + index, "*");
19 return index;
20 };
21
22 window.clearImmediate = (id) => {
23 _immediateFuncs[id];
24 };
25
26 window.addEventListener("message", (event) => {
27 if ((typeof event.data !== "string") ||
28 (event.data.indexOf("noVNC immediate trigger:") !== 0)) {
29 return;
30 }
31
32 const index = event.data.slice("noVNC immediate trigger:".length);
33
34 const callback = _immediateFuncs[index];
35 if (callback === undefined) {
36 return;
37 }
38
39 delete _immediateFuncs[index];
40
41 callback();
42 });
43 }
44
45 export default class RecordingPlayer {
46 constructor(frames, disconnected) {
47 this._frames = frames;
48
49 this._disconnected = disconnected;
50
51 this._rfb = undefined;
52 this._frame_length = this._frames.length;
53
54 this._frame_index = 0;
55 this._start_time = undefined;
56 this._realtime = true;
57 this._trafficManagement = true;
58
59 this._running = false;
60
61 this.onfinish = () => {};
62 }
63
64 run(realtime, trafficManagement) {
65 // initialize a new RFB
66 this._rfb = new RFB(document.getElementById('VNC_screen'), 'wss://test');
67 this._rfb.viewOnly = true;
68 this._rfb.addEventListener("disconnect",
69 this._handleDisconnect.bind(this));
70 this._rfb.addEventListener("credentialsrequired",
71 this._handleCredentials.bind(this));
72 this._enablePlaybackMode();
73
74 // reset the frame index and timer
75 this._frame_index = 0;
76 this._start_time = (new Date()).getTime();
77
78 this._realtime = realtime;
79 this._trafficManagement = (trafficManagement === undefined) ? !realtime : trafficManagement;
80
81 this._running = true;
82 }
83
84 // _enablePlaybackMode mocks out things not required for running playback
85 _enablePlaybackMode() {
86 const self = this;
87 this._rfb._sock.send = () => {};
88 this._rfb._sock.close = () => {};
89 this._rfb._sock.flush = () => {};
90 this._rfb._sock.open = function () {
91 this.init();
92 this._eventHandlers.open();
93 self._queueNextPacket();
94 };
95 }
96
97 _queueNextPacket() {
98 if (!this._running) { return; }
99
100 let frame = this._frames[this._frame_index];
101
102 // skip send frames
103 while (this._frame_index < this._frame_length && frame.fromClient) {
104 this._frame_index++;
105 frame = this._frames[this._frame_index];
106 }
107
108 if (this._frame_index >= this._frame_length) {
109 Log.Debug('Finished, no more frames');
110 this._finish();
111 return;
112 }
113
114 if (this._realtime) {
115 const toffset = (new Date()).getTime() - this._start_time;
116 let delay = frame.timestamp - toffset;
117 if (delay < 1) delay = 1;
118
119 setTimeout(this._doPacket.bind(this), delay);
120 } else {
121 setImmediate(this._doPacket.bind(this));
122 }
123 }
124
125 _doPacket() {
126 // Avoid having excessive queue buildup in non-realtime mode
127 if (this._trafficManagement && this._rfb._flushing) {
128 const orig = this._rfb._display.onflush;
129 this._rfb._display.onflush = () => {
130 this._rfb._display.onflush = orig;
131 this._rfb._onFlush();
132 this._doPacket();
133 };
134 return;
135 }
136
137 const frame = this._frames[this._frame_index];
138
139 this._rfb._sock._recv_message({'data': frame.data});
140 this._frame_index++;
141
142 this._queueNextPacket();
143 }
144
145 _finish() {
146 if (this._rfb._display.pending()) {
147 this._rfb._display.onflush = () => {
148 if (this._rfb._flushing) {
149 this._rfb._onFlush();
150 }
151 this._finish();
152 };
153 this._rfb._display.flush();
154 } else {
155 this._running = false;
156 this._rfb._sock._eventHandlers.close({code: 1000, reason: ""});
157 delete this._rfb;
158 this.onfinish((new Date()).getTime() - this._start_time);
159 }
160 }
161
162 _handleDisconnect(evt) {
163 this._running = false;
164 this._disconnected(evt.detail.clean, this._frame_index);
165 }
166
167 _handleCredentials(evt) {
168 this._rfb.sendCredentials({"username": "Foo",
169 "password": "Bar",
170 "target": "Baz"});
171 }
172 }