mirror of
https://github.com/novnc/noVNC.git
synced 2026-05-26 23:19:41 +00:00
The API allowed strings to be passed from the RFB module to the application using the disconnect reason. This caused problems since the application didn't have control over translations for these strings. Most of the information being passed using this string was very technical and not helpful to the end user. One exception to this was the security result information regarding for example authentication failures. The protocol allows the VNC server to pass a string directly to the user in the security result. So the disconnect reason is replaced by a boolean saying if the disconnection was clean or not. And for the security result information from the server, a new event has been added.
195 lines
5.6 KiB
JavaScript
195 lines
5.6 KiB
JavaScript
/*
|
|
* noVNC: HTML5 VNC client
|
|
* Copyright (C) 2012 Joel Martin
|
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
|
*/
|
|
|
|
import RFB from '../core/rfb.js';
|
|
import * as Log from '../core/util/logging.js';
|
|
import Base64 from '../core/base64.js';
|
|
|
|
// Immediate polyfill
|
|
if (setImmediate === undefined) {
|
|
var _immediateIdCounter = 1;
|
|
var _immediateFuncs = {};
|
|
|
|
var setImmediate = function (func) {
|
|
var index = _immediateIdCounter++;
|
|
_immediateFuncs[index] = func;
|
|
window.postMessage("noVNC immediate trigger:" + index, "*");
|
|
return index;
|
|
};
|
|
|
|
window.clearImmediate = function (id) {
|
|
_immediateFuncs[id];
|
|
};
|
|
|
|
var _onMessage = function (event) {
|
|
if ((typeof event.data !== "string") ||
|
|
(event.data.indexOf("noVNC immediate trigger:") !== 0)) {
|
|
return;
|
|
}
|
|
|
|
var index = event.data.slice("noVNC immediate trigger:".length);
|
|
|
|
var callback = _immediateFuncs[index];
|
|
if (callback === undefined) {
|
|
return;
|
|
}
|
|
|
|
delete _immediateFuncs[index];
|
|
|
|
callback();
|
|
};
|
|
window.addEventListener("message", _onMessage);
|
|
}
|
|
|
|
export default function RecordingPlayer (frames, encoding, disconnected) {
|
|
this._frames = frames;
|
|
this._encoding = encoding;
|
|
|
|
this._disconnected = disconnected;
|
|
|
|
if (this._encoding === undefined) {
|
|
let frame = this._frames[0];
|
|
let start = frame.indexOf('{', 1) + 1;
|
|
if (frame.slice(start).startsWith('UkZC')) {
|
|
this._encoding = 'base64';
|
|
} else {
|
|
this._encoding = 'binary';
|
|
}
|
|
}
|
|
|
|
this._rfb = undefined;
|
|
this._frame_length = this._frames.length;
|
|
|
|
this._frame_index = 0;
|
|
this._start_time = undefined;
|
|
this._realtime = true;
|
|
this._trafficManagement = true;
|
|
|
|
this._running = false;
|
|
|
|
this.onfinish = function () {};
|
|
}
|
|
|
|
RecordingPlayer.prototype = {
|
|
run: function (realtime, trafficManagement) {
|
|
// initialize a new RFB
|
|
this._rfb = new RFB(document.getElementById('VNC_canvas'), 'wss://test');
|
|
this._rfb.viewOnly = true;
|
|
this._rfb.addEventListener("disconnect",
|
|
this._handleDisconnect.bind(this));
|
|
this._enablePlaybackMode();
|
|
|
|
// reset the frame index and timer
|
|
this._frame_index = 0;
|
|
this._start_time = (new Date()).getTime();
|
|
|
|
this._realtime = realtime;
|
|
this._trafficManagement = (trafficManagement === undefined) ? !realtime : trafficManagement;
|
|
|
|
this._running = true;
|
|
|
|
this._queueNextPacket();
|
|
},
|
|
|
|
// _enablePlaybackMode mocks out things not required for running playback
|
|
_enablePlaybackMode: function () {
|
|
this._rfb._sock.send = function (arr) {};
|
|
this._rfb._sock.close = function () {};
|
|
this._rfb._sock.flush = function () {};
|
|
this._rfb._checkEvents = function () {};
|
|
this._rfb._connect = function () {
|
|
this._sock.init('binary', 'ws');
|
|
};
|
|
},
|
|
|
|
_queueNextPacket: function () {
|
|
if (!this._running) { return; }
|
|
|
|
var frame = this._frames[this._frame_index];
|
|
|
|
// skip send frames
|
|
while (this._frame_index < this._frame_length && frame.charAt(0) === "}") {
|
|
this._frame_index++;
|
|
frame = this._frames[this._frame_index];
|
|
}
|
|
|
|
if (frame === 'EOF') {
|
|
Log.Debug('Finished, found EOF');
|
|
this._finish();
|
|
return;
|
|
}
|
|
|
|
if (this._frame_index >= this._frame_length) {
|
|
Log.Debug('Finished, no more frames');
|
|
this._finish();
|
|
return;
|
|
}
|
|
|
|
if (this._realtime) {
|
|
let foffset = frame.slice(1, frame.indexOf('{', 1));
|
|
let toffset = (new Date()).getTime() - this._start_time;
|
|
let delay = foffset - toffset;
|
|
if (delay < 1) delay = 1;
|
|
|
|
setTimeout(this._doPacket.bind(this), delay);
|
|
} else {
|
|
setImmediate(this._doPacket.bind(this));
|
|
}
|
|
},
|
|
|
|
_doPacket: function () {
|
|
// Avoid having excessive queue buildup in non-realtime mode
|
|
if (this._trafficManagement && this._rfb._flushing) {
|
|
let player = this;
|
|
let orig = this._rfb._display.onflush;
|
|
this._rfb._display.onflush = function () {
|
|
player._rfb._display.onflush = orig;
|
|
player._rfb._onFlush();
|
|
player._doPacket();
|
|
};
|
|
return;
|
|
}
|
|
|
|
const frame = this._frames[this._frame_index];
|
|
var start = frame.indexOf('{', 1) + 1;
|
|
if (this._encoding === 'base64') {
|
|
var u8 = Base64.decode(frame.slice(start));
|
|
start = 0;
|
|
} else {
|
|
var u8 = new Uint8Array(frame.length - start);
|
|
for (let i = 0; i < frame.length - start; i++) {
|
|
u8[i] = frame.charCodeAt(start + i);
|
|
}
|
|
}
|
|
|
|
this._rfb._sock._recv_message({'data': u8});
|
|
this._frame_index++;
|
|
|
|
this._queueNextPacket();
|
|
},
|
|
|
|
_finish() {
|
|
if (this._rfb._display.pending()) {
|
|
var player = this;
|
|
this._rfb._display.onflush = function () {
|
|
if (player._rfb._flushing) {
|
|
player._rfb._onFlush();
|
|
}
|
|
player._finish();
|
|
};
|
|
this._rfb._display.flush();
|
|
} else {
|
|
this._running = false;
|
|
this.onfinish((new Date()).getTime() - this._start_time);
|
|
}
|
|
},
|
|
|
|
_handleDisconnect(rfb, clean) {
|
|
this._running = false;
|
|
this._disconnected(rfb, clean, this._frame_index);
|
|
}
|
|
};
|