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