| /* |
| * noVNC: HTML5 VNC client |
| * Copyright (C) 2018 The noVNC Authors |
| * Licensed under MPL 2.0 (see LICENSE.txt) |
| */ |
| |
| import RFB from '../core/rfb.js'; |
| import * as Log from '../core/util/logging.js'; |
| |
| // Immediate polyfill |
| if (window.setImmediate === undefined) { |
| let _immediateIdCounter = 1; |
| const _immediateFuncs = {}; |
| |
| window.setImmediate = (func) => { |
| const index = _immediateIdCounter++; |
| _immediateFuncs[index] = func; |
| window.postMessage("noVNC immediate trigger:" + index, "*"); |
| return index; |
| }; |
| |
| window.clearImmediate = (id) => { |
| _immediateFuncs[id]; |
| }; |
| |
| window.addEventListener("message", (event) => { |
| if ((typeof event.data !== "string") || |
| (event.data.indexOf("noVNC immediate trigger:") !== 0)) { |
| return; |
| } |
| |
| const index = event.data.slice("noVNC immediate trigger:".length); |
| |
| const callback = _immediateFuncs[index]; |
| if (callback === undefined) { |
| return; |
| } |
| |
| delete _immediateFuncs[index]; |
| |
| callback(); |
| }); |
| } |
| |
| export default class RecordingPlayer { |
| constructor(frames, disconnected) { |
| this._frames = frames; |
| |
| this._disconnected = disconnected; |
| |
| 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 = () => {}; |
| } |
| |
| run(realtime, trafficManagement) { |
| // initialize a new RFB |
| this._rfb = new RFB(document.getElementById('VNC_screen'), 'wss://test'); |
| this._rfb.viewOnly = true; |
| this._rfb.addEventListener("disconnect", |
| this._handleDisconnect.bind(this)); |
| this._rfb.addEventListener("credentialsrequired", |
| this._handleCredentials.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; |
| } |
| |
| // _enablePlaybackMode mocks out things not required for running playback |
| _enablePlaybackMode() { |
| const self = this; |
| this._rfb._sock.send = () => {}; |
| this._rfb._sock.close = () => {}; |
| this._rfb._sock.flush = () => {}; |
| this._rfb._sock.open = function () { |
| this.init(); |
| this._eventHandlers.open(); |
| self._queueNextPacket(); |
| }; |
| } |
| |
| _queueNextPacket() { |
| if (!this._running) { return; } |
| |
| let frame = this._frames[this._frame_index]; |
| |
| // skip send frames |
| while (this._frame_index < this._frame_length && frame.fromClient) { |
| this._frame_index++; |
| frame = this._frames[this._frame_index]; |
| } |
| |
| if (this._frame_index >= this._frame_length) { |
| Log.Debug('Finished, no more frames'); |
| this._finish(); |
| return; |
| } |
| |
| if (this._realtime) { |
| const toffset = (new Date()).getTime() - this._start_time; |
| let delay = frame.timestamp - toffset; |
| if (delay < 1) delay = 1; |
| |
| setTimeout(this._doPacket.bind(this), delay); |
| } else { |
| setImmediate(this._doPacket.bind(this)); |
| } |
| } |
| |
| _doPacket() { |
| // Avoid having excessive queue buildup in non-realtime mode |
| if (this._trafficManagement && this._rfb._flushing) { |
| const orig = this._rfb._display.onflush; |
| this._rfb._display.onflush = () => { |
| this._rfb._display.onflush = orig; |
| this._rfb._onFlush(); |
| this._doPacket(); |
| }; |
| return; |
| } |
| |
| const frame = this._frames[this._frame_index]; |
| |
| this._rfb._sock._recv_message({'data': frame.data}); |
| this._frame_index++; |
| |
| this._queueNextPacket(); |
| } |
| |
| _finish() { |
| if (this._rfb._display.pending()) { |
| this._rfb._display.onflush = () => { |
| if (this._rfb._flushing) { |
| this._rfb._onFlush(); |
| } |
| this._finish(); |
| }; |
| this._rfb._display.flush(); |
| } else { |
| this._running = false; |
| this._rfb._sock._eventHandlers.close({code: 1000, reason: ""}); |
| delete this._rfb; |
| this.onfinish((new Date()).getTime() - this._start_time); |
| } |
| } |
| |
| _handleDisconnect(evt) { |
| this._running = false; |
| this._disconnected(evt.detail.clean, this._frame_index); |
| } |
| |
| _handleCredentials(evt) { |
| this._rfb.sendCredentials({"username": "Foo", |
| "password": "Bar", |
| "target": "Baz"}); |
| } |
| } |