]> git.proxmox.com Git - mirror_novnc.git/commitdiff
Make vnc_playback.html functional once more
authorSolly Ross <sross@redhat.com>
Thu, 2 Mar 2017 02:10:09 +0000 (21:10 -0500)
committerSolly Ross <sross@redhat.com>
Tue, 21 Mar 2017 21:39:07 +0000 (17:39 -0400)
This commit makes vnc_playback.html functional once more, and completely
refactors tests/playback.js to make it usable in other scenarios.

In order for vnc_playback.js to properly load playback files now, they
must `export` their variables.

tests/playback-ui.js [new file with mode: 0644]
tests/playback.js
tests/vnc_playback.html
vendor/browser-es-module-loader/dist/browser-es-module-loader.js
vendor/browser-es-module-loader/src/browser-es-module-loader.js

diff --git a/tests/playback-ui.js b/tests/playback-ui.js
new file mode 100644 (file)
index 0000000..f47b9d8
--- /dev/null
@@ -0,0 +1,174 @@
+import * as WebUtil from '../app/webutil.js';
+import RecordingPlayer from './playback.js';
+
+var frames = null;
+var encoding = null;
+
+function message(str) {
+    console.log(str);
+    var cell = document.getElementById('messages');
+    cell.textContent += str + "\n";
+    cell.scrollTop = cell.scrollHeight;
+}
+
+function loadFile() {
+    const fname = WebUtil.getQueryVar('data', null);
+
+    if (!fname) {
+        return Promise.reject("Must specify data=FOO in query string.");
+    }
+
+    message("Loading " + fname);
+    return import(`../recordings/${fname}#nocache`);
+}
+
+function enableUI(recording) {
+    var iterations = WebUtil.getQueryVar('iterations', 3);
+    document.getElementById('iterations').value = iterations;
+
+    var mode = WebUtil.getQueryVar('mode', 3);
+    if (mode === 'realtime') {
+        document.getElementById('mode2').checked = true;
+    } else {
+        document.getElementById('mode1').checked = true;
+    }
+
+    message("VNC_frame_data.length: " + recording.VNC_frame_data.length);
+
+    const startButton = document.getElementById('startButton');
+    startButton.disabled = false
+    startButton.addEventListener('click', start);
+
+    frames = recording.VNC_frame_data;
+    encoding = recording.VNC_frame_encoding;
+}
+
+const notification = function (rfb, mesg, level, options) {
+    document.getElementById('VNC_status').textContent = mesg;
+}
+
+function IterationPlayer (iterations, frames, encoding) {
+    this._iterations = iterations;
+
+    this._iteration = undefined;
+    this._player = undefined;
+
+    this._start_time = undefined;
+
+    this._frames = frames;
+    this._encoding = encoding;
+
+    this._state = 'running';
+
+    this.onfinish = function() {};
+    this.oniterationfinish = function() {};
+    this.rfbdisconnected = function() {};
+    this.rfbnotification = function() {};
+}
+
+IterationPlayer.prototype = {
+    start: function (mode) {
+        this._iteration = 0;
+        this._start_time = (new Date()).getTime();
+
+        this._realtime = mode.startsWith('realtime');
+        this._trafficMgmt = !mode.endsWith('-no-mgmt');
+
+        this._nextIteration();
+    },
+
+    _nextIteration: function () {
+        const player = new RecordingPlayer(this._frames, this._encoding, this._disconnected.bind(this), this._notification.bind(this));
+        player.onfinish = this._iterationFinish.bind(this);
+
+        if (this._state !== 'running') { return; }
+
+        this._iteration++;
+        if (this._iteration > this._iterations) {
+            this._finish();
+            return;
+        }
+
+        player.run(this._realtime, this._trafficMgmt);
+    },
+
+    _finish: function () {
+        const endTime = (new Date()).getTime();
+        const totalDuration = endTime - this._start_time;
+
+        const evt = new Event('finish');
+        evt.duration = totalDuration;
+        evt.iterations = this._iterations;
+        this.onfinish(evt);
+    },
+
+    _iterationFinish: function (duration) {
+        const evt = new Event('iterationfinish');
+        evt.duration = duration;
+        evt.number = this._iteration;
+        this.oniterationfinish(evt);
+
+        this._nextIteration();
+    },
+
+    _disconnected: function (rfb, reason, frame) {
+        if (reason) {
+            this._state = 'failed';
+        }
+
+        var evt = new Event('rfbdisconnected');
+        evt.reason = reason;
+        evt.frame = frame;
+
+        this.onrfbdisconnected(evt);
+    },
+
+    _notification: function (rfb, msg, level, options) {
+        var evt = new Event('rfbnotification');
+        evt.message = msg;
+        evt.level = level;
+        evt.options = options;
+
+        this.onrfbnotification(evt);
+    },
+};
+
+function start() {
+    document.getElementById('startButton').value = "Running";
+    document.getElementById('startButton').disabled = true;
+
+    const iterations = document.getElementById('iterations').value;
+
+    var mode;
+
+    if (document.getElementById('mode1').checked) {
+        message(`Starting performance playback (fullspeed) [${iterations} iteration(s)]`);
+        mode = 'perftest';
+    } else {
+        message(`Starting realtime playback [${iterations} iteration(s)]`);
+        mode = 'realtime';
+    }
+
+    const player = new IterationPlayer(iterations, frames, encoding);
+    player.oniterationfinish = function (evt) {
+        message(`Iteration ${evt.number} took ${evt.duration}ms`);
+    };
+    player.onrfbdisconnected = function (evt) {
+        if (evt.reason) {
+            message(`noVNC sent disconnected during iteration ${evt.iteration} frame ${evt.frame}`);
+        }
+    };
+    player.onrfbnotification = function (evt) {
+        document.getElementById('VNC_status').textContent = evt.message;
+    };
+    player.onfinish = function (evt) {
+        const iterTime = parseInt(evt.duration / evt.iterations, 10);
+        message(`${evt.iterations} iterations took ${evt.duration}ms (average ${iterTime}ms / iteration)`);
+
+        document.getElementById('startButton').disabled = false;
+        document.getElementById('startButton').value = "Start";
+    };
+    player.start(mode);
+}
+
+loadFile().then(enableUI).catch(message);
index a25fac49c980c471df6cbe3e6462be050a8cdcd2..b39c9818f079dec9a18588aa8483fa3571545029 100644 (file)
@@ -4,29 +4,17 @@
  * Licensed under MPL 2.0 (see LICENSE.txt)
  */
 
-"use strict";
-/*jslint browser: true, white: false */
-/*global Util, VNC_frame_data, finish */
-
-var rfb, mode, test_state, frame_idx, frame_length,
-    iteration, iterations, istart_time, encoding,
-
-    // Pre-declarations for jslint
-    send_array, next_iteration, end_iteration, queue_next_packet,
-    do_packet, enable_test_mode;
-
-// Override send_array
-send_array = function (arr) {
-    // Stub out send_array
-};
+import RFB from '../core/rfb.js';
+import * as Log from '../core/util/logging.js';
+import Base64 from '../core/base64.js';
 
 // Immediate polyfill
-if (window.setImmediate === undefined) {
+if (setImmediate === undefined) {
     var _immediateIdCounter = 1;
     var _immediateFuncs = {};
 
-    window.setImmediate = function (func) {
-        var index = Util._immediateIdCounter++;
+    var setImmediate = function (func) {
+        var index = _immediateIdCounter++;
         _immediateFuncs[index] = func;
         window.postMessage("noVNC immediate trigger:" + index, "*");
         return index;
@@ -56,143 +44,160 @@ if (window.setImmediate === undefined) {
     window.addEventListener("message", _onMessage);
 }
 
-enable_test_mode = function () {
-    rfb._sock.send = send_array;
-    rfb._sock.close = function () {};
-    rfb._sock.flush = function () {};
-    rfb._checkEvents = function () {};
-    rfb.connect = function (host, port, password, path) {
-        this._rfb_host = host;
-        this._rfb_port = port;
-        this._rfb_password = (password !== undefined) ? password : "";
-        this._rfb_path = (path !== undefined) ? path : "";
-        this._sock.init('binary', 'ws');
-        this._rfb_connection_state = 'connecting';
-        this._rfb_init_state = 'ProtocolVersion';
-    };
-};
+export default function RecordingPlayer (frames, encoding, disconnected, notification) {
+    this._frames = frames;
+    this._encoding = encoding;
 
-next_iteration = function () {
-    rfb = new RFB({'target': document.getElementById('VNC_canvas'),
-                   'view_only': true,
-                   'onDisconnected': disconnected,
-                   'onNotification': notification});
-    enable_test_mode();
+    this._disconnected = disconnected;
+    this._notification = notification;
 
-    // Missing in older recordings
-    if (typeof VNC_frame_encoding === 'undefined') {
-        var frame = VNC_frame_data[0];
-        var start = frame.indexOf('{', 1) + 1;
+    if (this._encoding === undefined) {
+        let frame = this._frames[0];
+        let start = frame.indexOf('{', 1) + 1;
         if (frame.slice(start).startsWith('UkZC')) {
-            encoding = 'base64';
+            this._encoding = 'base64';
         } else {
-            encoding = 'binary';
+            this._encoding = 'binary';
         }
-    } else {
-        encoding = VNC_frame_encoding;
     }
 
-    if (iteration === 0) {
-        frame_length = VNC_frame_data.length;
-        test_state = 'running';
-    }
-
-    if (test_state !== 'running') { return; }
-
-    iteration += 1;
-    if (iteration > iterations) {
-        finish();
-        return;
-    }
+    this._rfb = undefined;
+    this._frame_length = this._frames.length;
 
-    frame_idx = 0;
-    istart_time = (new Date()).getTime();
-    rfb.connect('test', 0, "bogus");
+    this._frame_index = 0;
+    this._start_time = undefined;
+    this._realtime = true;
+    this._trafficManagement = true;
 
-    queue_next_packet();
+    this._running = false;
 
-};
+    this.onfinish = function () {};
+}
 
-end_iteration = function () {
-    if (rfb._display.pending()) {
-        rfb._display.set_onFlush(function () {
-            if (rfb._flushing) {
-                rfb._onFlush();
-            }
-            end_iteration();
-        });
-        rfb._display.flush();
-    } else {
-        next_iteration();
-    }
-};
+RecordingPlayer.prototype = {
+    run: function (realtime, trafficManagement) {
+        // initialize a new RFB
+        this._rfb = new RFB({'target': document.getElementById('VNC_canvas'),
+                             'view_only': true,
+                             'onDisconnected': this._handleDisconnect.bind(this),
+                             'onNotification': this._notification});
+        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;
+
+        // launch the tests
+        this._rfb.connect('test', 0, 'bogus');
+
+        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 (host, port, password, path) {
+            this._rfb_host = host;
+            this._rfb_port = port;
+            this._rfb_password = (password !== undefined) ? password : "";
+            this._rfb_path = (path !== undefined) ? path : "";
+            this._sock.init('binary', 'ws');
+            this._rfb_connection_state = 'connecting';
+            this._rfb_init_state = 'ProtocolVersion';
+        };
+    },
+
+    _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];
+        }
 
-queue_next_packet = function () {
-    var frame, foffset, toffset, delay;
-    if (test_state !== 'running') { return; }
+        if (frame === 'EOF') {
+            Log.Debug('Finished, found EOF');
+            this._finish();
+            return;
+        }
 
-    frame = VNC_frame_data[frame_idx];
-    while ((frame_idx < frame_length) && (frame.charAt(0) === "}")) {
-        //Util.Debug("Send frame " + frame_idx);
-        frame_idx += 1;
-        frame = VNC_frame_data[frame_idx];
-    }
+        if (this._frame_index >= this._frame_length) {
+            Log.Debug('Finished, no more frames');
+            this._finish();
+            return;
+        }
 
-    if (frame === 'EOF') {
-        Util.Debug("Finished, found EOF");
-        end_iteration();
-        return;
-    }
-    if (frame_idx >= frame_length) {
-        Util.Debug("Finished, no more frames");
-        end_iteration();
-        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;
 
-    if (mode === 'realtime') {
-        foffset = frame.slice(1, frame.indexOf('{', 1));
-        toffset = (new Date()).getTime() - istart_time;
-        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;
+            this._rfb.display.set_onFlush(function () {
+                this._rfb._display.set_onFlush(this._rfb._onFlush.bind(this._rfb));
+                this._rfb._onFlush();
+                player._doPacket();
+            });
+            return;
         }
 
-        setTimeout(do_packet, delay);
-    } else {
-        window.setImmediate(do_packet);
-    }
-};
-
-var bytes_processed = 0;
-
-do_packet = function () {
-    // Avoid having an excessive queue buildup
-    if (rfb._flushing && (mode !== 'realtime')) {
-        rfb._display.set_onFlush(function () {
-            rfb._display.set_onFlush(rfb._onFlush.bind(rfb));
-            rfb._onFlush();
-            do_packet();
-        });
-        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);
+            }
+        }
 
-    //Util.Debug("Processing frame: " + frame_idx);
-    var frame = VNC_frame_data[frame_idx],
-        start = frame.indexOf('{', 1) + 1;
-    var u8;
-    if (encoding === 'base64') {
-        u8 = Base64.decode(frame.slice(start));
-        start = 0;
-    } else {
-        u8 = new Uint8Array(frame.length - start);
-        for (var 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.set_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);
         }
-    }
-    bytes_processed += u8.length;
-    rfb._sock._recv_message({'data' : u8});
-    frame_idx += 1;
+    },
 
-    queue_next_packet();
+    _handleDisconnect(rfb, reason) {
+        this._running = false;
+        this._disconnected(rfb, reason, this._frame_index);
+    }
 };
-
index 65b735e7b53f37e79029a5d9bb2721dd86636a74..66f4057654b5ecaa641c228614a131412f4e8816 100644 (file)
@@ -2,6 +2,8 @@
 <html>
     <head>
         <title>VNC Playback</title>
+        <script src="/vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
+        <script type="module" src="./playback.js"></script>
     </head>
     <body>
 
@@ -9,8 +11,7 @@
         Perftest:<input type='radio' id='mode1' name='mode' checked>&nbsp;
         Realtime:<input type='radio' id='mode2' name='mode'>&nbsp;&nbsp;
 
-        <input id='startButton' type='button' value='Start' style='width:100px'
-            onclick="start();" disabled>&nbsp;
+        <input id='startButton' type='button' value='Start' style='width:100px' disabled>&nbsp;
 
         <br><br>
 
         src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
     -->
 
-    <script type="text/javascript">
-        var INCLUDE_URI= "../";
-    </script>
-    <script src="../core/util.js"></script>
-    <script src="../app/webutil.js"></script>
-
-    <script>
-        var fname, start_time;
-
-        function message(str) {
-            console.log(str);
-            var cell = document.getElementById('messages');
-            cell.textContent += str + "\n";
-            cell.scrollTop = cell.scrollHeight;
-        }
-
-        fname = WebUtil.getQueryVar('data', null);
-        if (fname) {
-            message("Loading " + fname);
-            // Load supporting scripts
-            WebUtil.load_scripts({
-                'core': ["base64.js", "websock.js", "des.js", "input/keysym.js",
-                         "input/keysymdef.js", "input/xtscancodes.js", "input/util.js",
-                         "input/devices.js", "display.js", "rfb.js", "inflator.js"],
-                'tests': ["playback.js"],
-                'recordings': [fname]});
-
-        } else {
-            message("Must specify data=FOO in query string.");
-        }
-
-        disconnected = function (rfb, reason) {
-            if (reason) {
-                message("noVNC sent '" + state + "' state during iteration " + iteration + " frame " + frame_idx);
-                test_state = 'failed';
-            }
-        }
-
-        notification = function (rfb, mesg, level, options) {
-            document.getElementById('VNC_status').textContent = mesg;
-        }
-
-        function start() {
-            document.getElementById('startButton').value = "Running";
-            document.getElementById('startButton').disabled = true;
-
-            iterations = document.getElementById('iterations').value;
-            iteration = 0;
-            start_time = (new Date()).getTime();
-
-            if (document.getElementById('mode1').checked) {
-                message("Starting performance playback (fullspeed) [" + iterations + " iteration(s)]");
-                mode = 'perftest';
-            } else {
-                message("Starting realtime playback [" + iterations + " iteration(s)]");
-                mode = 'realtime';
-            }
-
-            //recv_message = rfb.testMode(send_array, VNC_frame_encoding);
-
-            next_iteration();
-        }
-
-        function finish() {
-            // Finished with all iterations
-            var total_time, end_time = (new Date()).getTime();
-            total_time = end_time - start_time;
-
-            iter_time = parseInt(total_time / iterations, 10);
-            message(iterations + " iterations took " + total_time + "ms, " +
-                    iter_time + "ms per iteration");
-            // Shut-off event interception
-            rfb.get_mouse().ungrab();
-            rfb.get_keyboard().ungrab();
-            document.getElementById('startButton').disabled = false;
-            document.getElementById('startButton').value = "Start";
-
-        }
-
-        window.onscriptsload = function () {
-            iterations = WebUtil.getQueryVar('iterations', 3);
-            document.getElementById('iterations').value = iterations;
-            mode = WebUtil.getQueryVar('mode', 3);
-            if (mode === 'realtime') {
-                document.getElementById('mode2').checked = true;
-            } else {
-                document.getElementById('mode1').checked = true;
-            }
-            if (fname) {
-                message("VNC_frame_data.length: " + VNC_frame_data.length);
-            }
-            document.getElementById('startButton').disabled = false;
-        }
-    </script>
+    <script type="module" src="./playback-ui.js">
 </html>
index 8d3a2eea3c3d582fa5f6894cab45981271353deb..f191d1a589d1b5203c10d2c72798d5f607adcba6 100644 (file)
@@ -1350,7 +1350,7 @@ WorkerPool.prototype = {
 };
 
 var promiseMap = new Map();
-var babelWorker = new WorkerPool('vendor/browser-es-module-loader/dist/babel-worker.js', 3);
+var babelWorker = new WorkerPool('/vendor/browser-es-module-loader/dist/babel-worker.js', 3);
 babelWorker.onmessage = function (evt) {
     var promFuncs = promiseMap.get(evt.data.key);
     promFuncs.resolve(evt.data);
@@ -1391,8 +1391,10 @@ BrowserESModuleLoader.prototype[RegisterLoader$1.instantiate] = function(key, pr
   }).then(function (data) {
     // evaluate without require, exports and module variables
     // we leave module in for now to allow module.require access
-    localStorage.setItem(key+'!raw', data.source);
-    localStorage.setItem(data.key+'!transpiled', data.code);
+    if (data.key.slice(-8) !== '#nocache') {
+        localStorage.setItem(key+'!raw', data.source);
+        localStorage.setItem(data.key+'!transpiled', data.code);
+    }
     (0, eval)(data.code + '\n//# sourceURL=' + data.key + '!transpiled');
     processAnonRegister();
   });
index 6154e446d4db24ebe00f4bfd9fc75de6d0729231..4b968eba37b9d5f487e8686cad2b0fc2d5ced4c1 100644 (file)
@@ -190,7 +190,7 @@ WorkerPool.prototype = {
 };
 
 var promiseMap = new Map();
-var babelWorker = new WorkerPool('vendor/browser-es-module-loader/dist/babel-worker.js', 3);
+var babelWorker = new WorkerPool('/vendor/browser-es-module-loader/dist/babel-worker.js', 3);
 babelWorker.onmessage = function (evt) {
     var promFuncs = promiseMap.get(evt.data.key);
     promFuncs.resolve(evt.data);
@@ -231,8 +231,10 @@ BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, proc
   }).then(function (data) {
     // evaluate without require, exports and module variables
     // we leave module in for now to allow module.require access
-    localStorage.setItem(key+'!raw', data.source);
-    localStorage.setItem(data.key+'!transpiled', data.code);
+    if (data.key.slice(-8) !== '#nocache') {
+        localStorage.setItem(key+'!raw', data.source);
+        localStorage.setItem(data.key+'!transpiled', data.code);
+    }
     (0, eval)(data.code + '\n//# sourceURL=' + data.key + '!transpiled');
     processAnonRegister();
   });