diff --git a/.gitmodules b/.gitmodules
index 45574aeb..e69de29b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "include/web-socket-js-project"]
- path = include/web-socket-js-project
- url = https://github.com/gimite/web-socket-js.git
diff --git a/LICENSE.txt b/LICENSE.txt
index e896efca..82e8a6a1 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -51,14 +51,14 @@ licenses (all MPL 2.0 compatible):
include/jsunzip.js : zlib/libpng license
- include/web-socket-js/ : New BSD license (3-clause). Source code at
- http://github.com/gimite/web-socket-js
-
include/chrome-app/tcp-stream.js
: Apache 2.0 license
utils/websockify
utils/websocket.py : LGPL 3
+
+ utils/inflator.partial.js
+ include/inflator.js : MIT (for pako)
The following license texts are included:
@@ -70,6 +70,7 @@ The following license texts are included:
docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
docs/LICENSE.zlib
docs/LICENSE.Apache-2.0
+ docs/LICENSE.pako
Or alternatively the license texts may be found here:
diff --git a/README.md b/README.md
index b5679cdd..59a72b10 100644
--- a/README.md
+++ b/README.md
@@ -69,11 +69,7 @@ See more screenshots h
* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS
Safari, Opera 11+, Internet Explorer 9+, etc.
-* HTML5 WebSockets: For browsers that do not have builtin
- WebSockets support, the project includes
- web-socket-js,
- a WebSockets emulator using Adobe Flash. iOS 4.2+ has built-in
- WebSocket support.
+* HTML5 WebSockets and Typed Arrays
* Fast Javascript Engine: this is not strictly a requirement, but
without a fast Javascript engine, noVNC might be painfully slow.
@@ -130,9 +126,7 @@ use a WebSockets to TCP socket proxy. There is a python proxy included
* tight encoding : Michael Tinglof (Mercuri.ca)
* Included libraries:
- * web-socket-js : Hiroshi Ichikawa (github.com/gimite/web-socket-js)
* as3crypto : Henri Torgemane (code.google.com/p/as3crypto)
* base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
- * jsunzip : Erik Moller (github.com/operasoftware/jsunzip),
- * tinflate : Joergen Ibsen (ibsensoftware.com)
* DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs)
+ * Pako : Vitaly Puzrin (https://github.com/nodeca/pako)
diff --git a/docs/LICENSE.pako b/docs/LICENSE.pako
new file mode 100644
index 00000000..e6c9e5a5
--- /dev/null
+++ b/docs/LICENSE.pako
@@ -0,0 +1,21 @@
+(The MIT License)
+
+Copyright (C) 2014 by Vitaly Puzrin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/docs/notes b/docs/notes
index 9bcc6af3..6ff5ec19 100644
--- a/docs/notes
+++ b/docs/notes
@@ -1,17 +1,5 @@
-Some implementation notes:
+Rebuilding inflator.js
-There is an included flash object (web-socket-js) that is used to
-emulate websocket support on browsers without websocket support
-(currently only Chrome has WebSocket support).
-
-Javascript doesn't have a bytearray type, so what you get out of
-a WebSocket object is just Javascript strings. Javascript has UTF-16
-unicode strings and anything sent through the WebSocket gets converted
-to UTF-8 and vice-versa. So, one additional (and necessary) function
-of websockify is base64 encoding/decoding what is sent to/from the
-browser.
-
-Building web-socket-js emulator:
-
-cd include/web-socket-js/flash-src
-mxmlc -static-link-runtime-shared-libraries WebSocketMain.as
+- Download pako from npm
+- Install browserify using npm
+- browserify utils/inflator.partial.js -o include/inflator.js
diff --git a/include/display.js b/include/display.js
index f20a5571..80530022 100644
--- a/include/display.js
+++ b/include/display.js
@@ -15,6 +15,14 @@ var Display;
(function () {
"use strict";
+ var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
+ try {
+ new ImageData(new Uint8ClampedArray(1), 1, 1);
+ SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
+ } catch (ex) {
+ // ignore failure
+ }
+
Display = function (defaults) {
this._drawCtx = null;
this._c_forceCanvas = false;
@@ -351,18 +359,41 @@ var Display;
this._renderQ = [];
},
- fillRect: function (x, y, width, height, color) {
- this._setFillColor(color);
- this._drawCtx.fillRect(x - this._viewportLoc.x, y - this._viewportLoc.y, width, height);
+ fillRect: function (x, y, width, height, color, from_queue) {
+ if (this._renderQ.length !== 0 && !from_queue) {
+ this.renderQ_push({
+ 'type': 'fill',
+ 'x': x,
+ 'y': y,
+ 'width': width,
+ 'height': height,
+ 'color': color
+ });
+ } else {
+ this._setFillColor(color);
+ this._drawCtx.fillRect(x - this._viewportLoc.x, y - this._viewportLoc.y, width, height);
+ }
},
- copyImage: function (old_x, old_y, new_x, new_y, w, h) {
- var x1 = old_x - this._viewportLoc.x;
- var y1 = old_y - this._viewportLoc.y;
- var x2 = new_x - this._viewportLoc.x;
- var y2 = new_y - this._viewportLoc.y;
+ copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) {
+ if (this._renderQ.length !== 0 && !from_queue) {
+ this.renderQ_push({
+ 'type': 'copy',
+ 'old_x': old_x,
+ 'old_y': old_y,
+ 'x': new_x,
+ 'y': new_y,
+ 'width': w,
+ 'height': h,
+ });
+ } else {
+ var x1 = old_x - this._viewportLoc.x;
+ var y1 = old_y - this._viewportLoc.y;
+ var x2 = new_x - this._viewportLoc.x;
+ var y2 = new_y - this._viewportLoc.y;
- this._drawCtx.drawImage(this._target, x1, y1, w, h, x2, y2, w, h);
+ this._drawCtx.drawImage(this._target, x1, y1, w, h, x2, y2, w, h);
+ }
},
// start updating a tile
@@ -394,7 +425,7 @@ var Display;
data[i + 3] = 255;
}
} else {
- this.fillRect(x, y, width, height, color);
+ this.fillRect(x, y, width, height, color, true);
}
},
@@ -425,7 +456,7 @@ var Display;
}
}
} else {
- this.fillRect(this._tile_x + x, this._tile_y + y, w, h, color);
+ this.fillRect(this._tile_x + x, this._tile_y + y, w, h, color, true);
}
},
@@ -438,16 +469,34 @@ var Display;
// else: No-op -- already done by setSubTile
},
- blitImage: function (x, y, width, height, arr, offset) {
- if (this._true_color) {
+ blitImage: function (x, y, width, height, arr, offset, from_queue) {
+ if (this._renderQ.length !== 0 && !from_queue) {
+ this.renderQ_push({
+ 'type': 'blit',
+ 'data': arr,
+ 'x': x,
+ 'y': y,
+ 'width': width,
+ 'height': height,
+ });
+ } else if (this._true_color) {
this._bgrxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
} else {
this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
}
},
- blitRgbImage: function (x, y , width, height, arr, offset) {
- if (this._true_color) {
+ blitRgbImage: function (x, y , width, height, arr, offset, from_queue) {
+ if (this._renderQ.length !== 0 && !from_queue) {
+ this.renderQ_push({
+ 'type': 'blitRgb',
+ 'data': arr,
+ 'x': x,
+ 'y': y,
+ 'width': width,
+ 'height': height,
+ });
+ } else if (this._true_color) {
this._rgbImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
} else {
// probably wrong?
@@ -455,6 +504,26 @@ var Display;
}
},
+ blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) {
+ if (this._renderQ.length !== 0 && !from_queue) {
+ // NB(directxman12): it's technically more performant here to use preallocated arrays, but it
+ // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
+ // this probably isn't getting called *nearly* as much
+ var new_arr = new Uint8Array(width * height * 4);
+ new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
+ this.renderQ_push({
+ 'type': 'blitRgbx',
+ 'data': new_arr,
+ 'x': x,
+ 'y': y,
+ 'width': width,
+ 'height': height,
+ });
+ } else {
+ this._rgbxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
+ }
+ },
+
blitStringImage: function (str, x, y) {
var img = new Image();
img.onload = function () {
@@ -632,6 +701,18 @@ var Display;
this._drawCtx.putImageData(img, x - vx, y - vy);
},
+ _rgbxImageData: function (x, y, vx, vy, width, height, arr, offset) {
+ // NB(directxman12): arr must be an Type Array view
+ var img;
+ if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
+ img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
+ } else {
+ img = this._drawCtx.createImageData(width, height);
+ img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4));
+ }
+ this._drawCtx.putImageData(img, x - vx, y - vy);
+ },
+
_cmapImageData: function (x, y, vx, vy, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height);
var data = img.data;
@@ -652,16 +733,19 @@ var Display;
var a = this._renderQ[0];
switch (a.type) {
case 'copy':
- this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height);
+ this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true);
break;
case 'fill':
- this.fillRect(a.x, a.y, a.width, a.height, a.color);
+ this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
break;
case 'blit':
- this.blitImage(a.x, a.y, a.width, a.height, a.data, 0);
+ this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true);
break;
case 'blitRgb':
- this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0);
+ this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0, true);
+ break;
+ case 'blitRgbx':
+ this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
break;
case 'img':
if (a.img.complete) {
diff --git a/include/inflator.js b/include/inflator.js
new file mode 100644
index 00000000..a9c75a62
--- /dev/null
+++ b/include/inflator.js
@@ -0,0 +1,2409 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.inflator = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o>> 16) & 0xffff) |0,
+ n = 0;
+
+ while (len !== 0) {
+ // Set limit ~ twice less than 5552, to keep
+ // s2 in 31-bits, because we force signed ints.
+ // in other case %= will fail.
+ n = len > 2000 ? 2000 : len;
+ len -= n;
+
+ do {
+ s1 = (s1 + buf[pos++]) |0;
+ s2 = (s2 + s1) |0;
+ } while (--n);
+
+ s1 %= 65521;
+ s2 %= 65521;
+ }
+
+ return (s1 | (s2 << 16)) |0;
+}
+
+
+module.exports = adler32;
+
+},{}],3:[function(require,module,exports){
+'use strict';
+
+// Note: we can't get significant speed boost here.
+// So write code to minimize size - no pregenerated tables
+// and array tools dependencies.
+
+
+// Use ordinary array, since untyped makes no boost here
+function makeTable() {
+ var c, table = [];
+
+ for (var n =0; n < 256; n++) {
+ c = n;
+ for (var k =0; k < 8; k++) {
+ c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
+ }
+ table[n] = c;
+ }
+
+ return table;
+}
+
+// Create table on load. Just 255 signed longs. Not a problem.
+var crcTable = makeTable();
+
+
+function crc32(crc, buf, len, pos) {
+ var t = crcTable,
+ end = pos + len;
+
+ crc = crc ^ (-1);
+
+ for (var i = pos; i < end; i++) {
+ crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
+ }
+
+ return (crc ^ (-1)); // >>> 0;
+}
+
+
+module.exports = crc32;
+
+},{}],4:[function(require,module,exports){
+'use strict';
+
+// See state defs from inflate.js
+var BAD = 30; /* got a data error -- remain here until reset */
+var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
+
+/*
+ Decode literal, length, and distance codes and write out the resulting
+ literal and match bytes until either not enough input or output is
+ available, an end-of-block is encountered, or a data error is encountered.
+ When large enough input and output buffers are supplied to inflate(), for
+ example, a 16K input buffer and a 64K output buffer, more than 95% of the
+ inflate execution time is spent in this routine.
+
+ Entry assumptions:
+
+ state.mode === LEN
+ strm.avail_in >= 6
+ strm.avail_out >= 258
+ start >= strm.avail_out
+ state.bits < 8
+
+ On return, state.mode is one of:
+
+ LEN -- ran out of enough output space or enough available input
+ TYPE -- reached end of block code, inflate() to interpret next block
+ BAD -- error in block data
+
+ Notes:
+
+ - The maximum input bits used by a length/distance pair is 15 bits for the
+ length code, 5 bits for the length extra, 15 bits for the distance code,
+ and 13 bits for the distance extra. This totals 48 bits, or six bytes.
+ Therefore if strm.avail_in >= 6, then there is enough input to avoid
+ checking for available input while decoding.
+
+ - The maximum bytes that a single length/distance pair can output is 258
+ bytes, which is the maximum length that can be coded. inflate_fast()
+ requires strm.avail_out >= 258 for each loop to avoid checking for
+ output space.
+ */
+module.exports = function inflate_fast(strm, start) {
+ var state;
+ var _in; /* local strm.input */
+ var last; /* have enough input while in < last */
+ var _out; /* local strm.output */
+ var beg; /* inflate()'s initial strm.output */
+ var end; /* while out < end, enough space available */
+//#ifdef INFLATE_STRICT
+ var dmax; /* maximum distance from zlib header */
+//#endif
+ var wsize; /* window size or zero if not using window */
+ var whave; /* valid bytes in the window */
+ var wnext; /* window write index */
+ var window; /* allocated sliding window, if wsize != 0 */
+ var hold; /* local strm.hold */
+ var bits; /* local strm.bits */
+ var lcode; /* local strm.lencode */
+ var dcode; /* local strm.distcode */
+ var lmask; /* mask for first level of length codes */
+ var dmask; /* mask for first level of distance codes */
+ var here; /* retrieved table entry */
+ var op; /* code bits, operation, extra bits, or */
+ /* window position, window bytes to copy */
+ var len; /* match length, unused bytes */
+ var dist; /* match distance */
+ var from; /* where to copy match from */
+ var from_source;
+
+
+ var input, output; // JS specific, because we have no pointers
+
+ /* copy state to local variables */
+ state = strm.state;
+ //here = state.here;
+ _in = strm.next_in;
+ input = strm.input;
+ last = _in + (strm.avail_in - 5);
+ _out = strm.next_out;
+ output = strm.output;
+ beg = _out - (start - strm.avail_out);
+ end = _out + (strm.avail_out - 257);
+//#ifdef INFLATE_STRICT
+ dmax = state.dmax;
+//#endif
+ wsize = state.wsize;
+ whave = state.whave;
+ wnext = state.wnext;
+ window = state.window;
+ hold = state.hold;
+ bits = state.bits;
+ lcode = state.lencode;
+ dcode = state.distcode;
+ lmask = (1 << state.lenbits) - 1;
+ dmask = (1 << state.distbits) - 1;
+
+
+ /* decode literals and length/distances until end-of-block or not enough
+ input data or output space */
+
+ top:
+ do {
+ if (bits < 15) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ hold += input[_in++] << bits;
+ bits += 8;
+ }
+
+ here = lcode[hold & lmask];
+
+ dolen:
+ for (;;) { // Goto emulation
+ op = here >>> 24/*here.bits*/;
+ hold >>>= op;
+ bits -= op;
+ op = (here >>> 16) & 0xff/*here.op*/;
+ if (op === 0) { /* literal */
+ //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ // "inflate: literal '%c'\n" :
+ // "inflate: literal 0x%02x\n", here.val));
+ output[_out++] = here & 0xffff/*here.val*/;
+ }
+ else if (op & 16) { /* length base */
+ len = here & 0xffff/*here.val*/;
+ op &= 15; /* number of extra bits */
+ if (op) {
+ if (bits < op) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ }
+ len += hold & ((1 << op) - 1);
+ hold >>>= op;
+ bits -= op;
+ }
+ //Tracevv((stderr, "inflate: length %u\n", len));
+ if (bits < 15) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ hold += input[_in++] << bits;
+ bits += 8;
+ }
+ here = dcode[hold & dmask];
+
+ dodist:
+ for (;;) { // goto emulation
+ op = here >>> 24/*here.bits*/;
+ hold >>>= op;
+ bits -= op;
+ op = (here >>> 16) & 0xff/*here.op*/;
+
+ if (op & 16) { /* distance base */
+ dist = here & 0xffff/*here.val*/;
+ op &= 15; /* number of extra bits */
+ if (bits < op) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ if (bits < op) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ }
+ }
+ dist += hold & ((1 << op) - 1);
+//#ifdef INFLATE_STRICT
+ if (dist > dmax) {
+ strm.msg = 'invalid distance too far back';
+ state.mode = BAD;
+ break top;
+ }
+//#endif
+ hold >>>= op;
+ bits -= op;
+ //Tracevv((stderr, "inflate: distance %u\n", dist));
+ op = _out - beg; /* max distance in output */
+ if (dist > op) { /* see if copy from window */
+ op = dist - op; /* distance back in window */
+ if (op > whave) {
+ if (state.sane) {
+ strm.msg = 'invalid distance too far back';
+ state.mode = BAD;
+ break top;
+ }
+
+// (!) This block is disabled in zlib defailts,
+// don't enable it for binary compatibility
+//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+// if (len <= op - whave) {
+// do {
+// output[_out++] = 0;
+// } while (--len);
+// continue top;
+// }
+// len -= op - whave;
+// do {
+// output[_out++] = 0;
+// } while (--op > whave);
+// if (op === 0) {
+// from = _out - dist;
+// do {
+// output[_out++] = output[from++];
+// } while (--len);
+// continue top;
+// }
+//#endif
+ }
+ from = 0; // window index
+ from_source = window;
+ if (wnext === 0) { /* very common case */
+ from += wsize - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ output[_out++] = window[from++];
+ } while (--op);
+ from = _out - dist; /* rest from output */
+ from_source = output;
+ }
+ }
+ else if (wnext < op) { /* wrap around window */
+ from += wsize + wnext - op;
+ op -= wnext;
+ if (op < len) { /* some from end of window */
+ len -= op;
+ do {
+ output[_out++] = window[from++];
+ } while (--op);
+ from = 0;
+ if (wnext < len) { /* some from start of window */
+ op = wnext;
+ len -= op;
+ do {
+ output[_out++] = window[from++];
+ } while (--op);
+ from = _out - dist; /* rest from output */
+ from_source = output;
+ }
+ }
+ }
+ else { /* contiguous in window */
+ from += wnext - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ output[_out++] = window[from++];
+ } while (--op);
+ from = _out - dist; /* rest from output */
+ from_source = output;
+ }
+ }
+ while (len > 2) {
+ output[_out++] = from_source[from++];
+ output[_out++] = from_source[from++];
+ output[_out++] = from_source[from++];
+ len -= 3;
+ }
+ if (len) {
+ output[_out++] = from_source[from++];
+ if (len > 1) {
+ output[_out++] = from_source[from++];
+ }
+ }
+ }
+ else {
+ from = _out - dist; /* copy direct from output */
+ do { /* minimum length is three */
+ output[_out++] = output[from++];
+ output[_out++] = output[from++];
+ output[_out++] = output[from++];
+ len -= 3;
+ } while (len > 2);
+ if (len) {
+ output[_out++] = output[from++];
+ if (len > 1) {
+ output[_out++] = output[from++];
+ }
+ }
+ }
+ }
+ else if ((op & 64) === 0) { /* 2nd level distance code */
+ here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
+ continue dodist;
+ }
+ else {
+ strm.msg = 'invalid distance code';
+ state.mode = BAD;
+ break top;
+ }
+
+ break; // need to emulate goto via "continue"
+ }
+ }
+ else if ((op & 64) === 0) { /* 2nd level length code */
+ here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
+ continue dolen;
+ }
+ else if (op & 32) { /* end-of-block */
+ //Tracevv((stderr, "inflate: end of block\n"));
+ state.mode = TYPE;
+ break top;
+ }
+ else {
+ strm.msg = 'invalid literal/length code';
+ state.mode = BAD;
+ break top;
+ }
+
+ break; // need to emulate goto via "continue"
+ }
+ } while (_in < last && _out < end);
+
+ /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+ len = bits >> 3;
+ _in -= len;
+ bits -= len << 3;
+ hold &= (1 << bits) - 1;
+
+ /* update state and return */
+ strm.next_in = _in;
+ strm.next_out = _out;
+ strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));
+ strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
+ state.hold = hold;
+ state.bits = bits;
+ return;
+};
+
+},{}],5:[function(require,module,exports){
+'use strict';
+
+
+var utils = require('../utils/common');
+var adler32 = require('./adler32');
+var crc32 = require('./crc32');
+var inflate_fast = require('./inffast');
+var inflate_table = require('./inftrees');
+
+var CODES = 0;
+var LENS = 1;
+var DISTS = 2;
+
+/* Public constants ==========================================================*/
+/* ===========================================================================*/
+
+
+/* Allowed flush values; see deflate() and inflate() below for details */
+//var Z_NO_FLUSH = 0;
+//var Z_PARTIAL_FLUSH = 1;
+//var Z_SYNC_FLUSH = 2;
+//var Z_FULL_FLUSH = 3;
+var Z_FINISH = 4;
+var Z_BLOCK = 5;
+var Z_TREES = 6;
+
+
+/* Return codes for the compression/decompression functions. Negative values
+ * are errors, positive values are used for special but normal events.
+ */
+var Z_OK = 0;
+var Z_STREAM_END = 1;
+var Z_NEED_DICT = 2;
+//var Z_ERRNO = -1;
+var Z_STREAM_ERROR = -2;
+var Z_DATA_ERROR = -3;
+var Z_MEM_ERROR = -4;
+var Z_BUF_ERROR = -5;
+//var Z_VERSION_ERROR = -6;
+
+/* The deflate compression method */
+var Z_DEFLATED = 8;
+
+
+/* STATES ====================================================================*/
+/* ===========================================================================*/
+
+
+var HEAD = 1; /* i: waiting for magic header */
+var FLAGS = 2; /* i: waiting for method and flags (gzip) */
+var TIME = 3; /* i: waiting for modification time (gzip) */
+var OS = 4; /* i: waiting for extra flags and operating system (gzip) */
+var EXLEN = 5; /* i: waiting for extra length (gzip) */
+var EXTRA = 6; /* i: waiting for extra bytes (gzip) */
+var NAME = 7; /* i: waiting for end of file name (gzip) */
+var COMMENT = 8; /* i: waiting for end of comment (gzip) */
+var HCRC = 9; /* i: waiting for header crc (gzip) */
+var DICTID = 10; /* i: waiting for dictionary check value */
+var DICT = 11; /* waiting for inflateSetDictionary() call */
+var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
+var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */
+var STORED = 14; /* i: waiting for stored size (length and complement) */
+var COPY_ = 15; /* i/o: same as COPY below, but only first time in */
+var COPY = 16; /* i/o: waiting for input or output to copy stored block */
+var TABLE = 17; /* i: waiting for dynamic block table lengths */
+var LENLENS = 18; /* i: waiting for code length code lengths */
+var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */
+var LEN_ = 20; /* i: same as LEN below, but only first time in */
+var LEN = 21; /* i: waiting for length/lit/eob code */
+var LENEXT = 22; /* i: waiting for length extra bits */
+var DIST = 23; /* i: waiting for distance code */
+var DISTEXT = 24; /* i: waiting for distance extra bits */
+var MATCH = 25; /* o: waiting for output space to copy string */
+var LIT = 26; /* o: waiting for output space to write literal */
+var CHECK = 27; /* i: waiting for 32-bit check value */
+var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */
+var DONE = 29; /* finished check, done -- remain here until reset */
+var BAD = 30; /* got a data error -- remain here until reset */
+var MEM = 31; /* got an inflate() memory error -- remain here until reset */
+var SYNC = 32; /* looking for synchronization bytes to restart inflate() */
+
+/* ===========================================================================*/
+
+
+
+var ENOUGH_LENS = 852;
+var ENOUGH_DISTS = 592;
+//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
+
+var MAX_WBITS = 15;
+/* 32K LZ77 window */
+var DEF_WBITS = MAX_WBITS;
+
+
+function ZSWAP32(q) {
+ return (((q >>> 24) & 0xff) +
+ ((q >>> 8) & 0xff00) +
+ ((q & 0xff00) << 8) +
+ ((q & 0xff) << 24));
+}
+
+
+function InflateState() {
+ this.mode = 0; /* current inflate mode */
+ this.last = false; /* true if processing last block */
+ this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
+ this.havedict = false; /* true if dictionary provided */
+ this.flags = 0; /* gzip header method and flags (0 if zlib) */
+ this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */
+ this.check = 0; /* protected copy of check value */
+ this.total = 0; /* protected copy of output count */
+ // TODO: may be {}
+ this.head = null; /* where to save gzip header information */
+
+ /* sliding window */
+ this.wbits = 0; /* log base 2 of requested window size */
+ this.wsize = 0; /* window size or zero if not using window */
+ this.whave = 0; /* valid bytes in the window */
+ this.wnext = 0; /* window write index */
+ this.window = null; /* allocated sliding window, if needed */
+
+ /* bit accumulator */
+ this.hold = 0; /* input bit accumulator */
+ this.bits = 0; /* number of bits in "in" */
+
+ /* for string and stored block copying */
+ this.length = 0; /* literal or length of data to copy */
+ this.offset = 0; /* distance back to copy string from */
+
+ /* for table and code decoding */
+ this.extra = 0; /* extra bits needed */
+
+ /* fixed and dynamic code tables */
+ this.lencode = null; /* starting table for length/literal codes */
+ this.distcode = null; /* starting table for distance codes */
+ this.lenbits = 0; /* index bits for lencode */
+ this.distbits = 0; /* index bits for distcode */
+
+ /* dynamic table building */
+ this.ncode = 0; /* number of code length code lengths */
+ this.nlen = 0; /* number of length code lengths */
+ this.ndist = 0; /* number of distance code lengths */
+ this.have = 0; /* number of code lengths in lens[] */
+ this.next = null; /* next available space in codes[] */
+
+ this.lens = new utils.Buf16(320); /* temporary storage for code lengths */
+ this.work = new utils.Buf16(288); /* work area for code table building */
+
+ /*
+ because we don't have pointers in js, we use lencode and distcode directly
+ as buffers so we don't need codes
+ */
+ //this.codes = new utils.Buf32(ENOUGH); /* space for code tables */
+ this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */
+ this.distdyn = null; /* dynamic table for distance codes (JS specific) */
+ this.sane = 0; /* if false, allow invalid distance too far */
+ this.back = 0; /* bits back of last unprocessed length/lit */
+ this.was = 0; /* initial length of match */
+}
+
+function inflateResetKeep(strm) {
+ var state;
+
+ if (!strm || !strm.state) { return Z_STREAM_ERROR; }
+ state = strm.state;
+ strm.total_in = strm.total_out = state.total = 0;
+ strm.msg = ''; /*Z_NULL*/
+ if (state.wrap) { /* to support ill-conceived Java test suite */
+ strm.adler = state.wrap & 1;
+ }
+ state.mode = HEAD;
+ state.last = 0;
+ state.havedict = 0;
+ state.dmax = 32768;
+ state.head = null/*Z_NULL*/;
+ state.hold = 0;
+ state.bits = 0;
+ //state.lencode = state.distcode = state.next = state.codes;
+ state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS);
+ state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS);
+
+ state.sane = 1;
+ state.back = -1;
+ //Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+function inflateReset(strm) {
+ var state;
+
+ if (!strm || !strm.state) { return Z_STREAM_ERROR; }
+ state = strm.state;
+ state.wsize = 0;
+ state.whave = 0;
+ state.wnext = 0;
+ return inflateResetKeep(strm);
+
+}
+
+function inflateReset2(strm, windowBits) {
+ var wrap;
+ var state;
+
+ /* get the state */
+ if (!strm || !strm.state) { return Z_STREAM_ERROR; }
+ state = strm.state;
+
+ /* extract wrap request from windowBits parameter */
+ if (windowBits < 0) {
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+ else {
+ wrap = (windowBits >> 4) + 1;
+ if (windowBits < 48) {
+ windowBits &= 15;
+ }
+ }
+
+ /* set number of window bits, free window if different */
+ if (windowBits && (windowBits < 8 || windowBits > 15)) {
+ return Z_STREAM_ERROR;
+ }
+ if (state.window !== null && state.wbits !== windowBits) {
+ state.window = null;
+ }
+
+ /* update state and reset the rest of it */
+ state.wrap = wrap;
+ state.wbits = windowBits;
+ return inflateReset(strm);
+}
+
+function inflateInit2(strm, windowBits) {
+ var ret;
+ var state;
+
+ if (!strm) { return Z_STREAM_ERROR; }
+ //strm.msg = Z_NULL; /* in case we return an error */
+
+ state = new InflateState();
+
+ //if (state === Z_NULL) return Z_MEM_ERROR;
+ //Tracev((stderr, "inflate: allocated\n"));
+ strm.state = state;
+ state.window = null/*Z_NULL*/;
+ ret = inflateReset2(strm, windowBits);
+ if (ret !== Z_OK) {
+ strm.state = null/*Z_NULL*/;
+ }
+ return ret;
+}
+
+function inflateInit(strm) {
+ return inflateInit2(strm, DEF_WBITS);
+}
+
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+var virgin = true;
+
+var lenfix, distfix; // We have no pointers in JS, so keep tables separate
+
+function fixedtables(state) {
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ var sym;
+
+ lenfix = new utils.Buf32(512);
+ distfix = new utils.Buf32(32);
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) { state.lens[sym++] = 8; }
+ while (sym < 256) { state.lens[sym++] = 9; }
+ while (sym < 280) { state.lens[sym++] = 7; }
+ while (sym < 288) { state.lens[sym++] = 8; }
+
+ inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, {bits: 9});
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) { state.lens[sym++] = 5; }
+
+ inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, {bits: 5});
+
+ /* do this just once */
+ virgin = false;
+ }
+
+ state.lencode = lenfix;
+ state.lenbits = 9;
+ state.distcode = distfix;
+ state.distbits = 5;
+}
+
+
+/*
+ Update the window with the last wsize (normally 32K) bytes written before
+ returning. If window does not exist yet, create it. This is only called
+ when a window is already in use, or when output has been written during this
+ inflate call, but the end of the deflate stream has not been reached yet.
+ It is also called to create a window for dictionary data when a dictionary
+ is loaded.
+
+ Providing output buffers larger than 32K to inflate() should provide a speed
+ advantage, since only the last 32K of output is copied to the sliding window
+ upon return from inflate(), and since all distances after the first 32K of
+ output will fall in the output data, making match copies simpler and faster.
+ The advantage may be dependent on the size of the processor's data caches.
+ */
+function updatewindow(strm, src, end, copy) {
+ var dist;
+ var state = strm.state;
+
+ /* if it hasn't been done already, allocate space for the window */
+ if (state.window === null) {
+ state.wsize = 1 << state.wbits;
+ state.wnext = 0;
+ state.whave = 0;
+
+ state.window = new utils.Buf8(state.wsize);
+ }
+
+ /* copy state->wsize or less output bytes into the circular window */
+ if (copy >= state.wsize) {
+ utils.arraySet(state.window,src, end - state.wsize, state.wsize, 0);
+ state.wnext = 0;
+ state.whave = state.wsize;
+ }
+ else {
+ dist = state.wsize - state.wnext;
+ if (dist > copy) {
+ dist = copy;
+ }
+ //zmemcpy(state->window + state->wnext, end - copy, dist);
+ utils.arraySet(state.window,src, end - copy, dist, state.wnext);
+ copy -= dist;
+ if (copy) {
+ //zmemcpy(state->window, end - copy, copy);
+ utils.arraySet(state.window,src, end - copy, copy, 0);
+ state.wnext = copy;
+ state.whave = state.wsize;
+ }
+ else {
+ state.wnext += dist;
+ if (state.wnext === state.wsize) { state.wnext = 0; }
+ if (state.whave < state.wsize) { state.whave += dist; }
+ }
+ }
+ return 0;
+}
+
+function inflate(strm, flush) {
+ var state;
+ var input, output; // input/output buffers
+ var next; /* next input INDEX */
+ var put; /* next output INDEX */
+ var have, left; /* available input and output */
+ var hold; /* bit buffer */
+ var bits; /* bits in bit buffer */
+ var _in, _out; /* save starting available input and output */
+ var copy; /* number of stored or match bytes to copy */
+ var from; /* where to copy match bytes from */
+ var from_source;
+ var here = 0; /* current decoding table entry */
+ var here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
+ //var last; /* parent table entry */
+ var last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
+ var len; /* length to copy for repeats, bits to drop */
+ var ret; /* return code */
+ var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */
+ var opts;
+
+ var n; // temporary var for NEED_BITS
+
+ var order = /* permutation of code lengths */
+ [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
+
+
+ if (!strm || !strm.state || !strm.output ||
+ (!strm.input && strm.avail_in !== 0)) {
+ return Z_STREAM_ERROR;
+ }
+
+ state = strm.state;
+ if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */
+
+
+ //--- LOAD() ---
+ put = strm.next_out;
+ output = strm.output;
+ left = strm.avail_out;
+ next = strm.next_in;
+ input = strm.input;
+ have = strm.avail_in;
+ hold = state.hold;
+ bits = state.bits;
+ //---
+
+ _in = have;
+ _out = left;
+ ret = Z_OK;
+
+ inf_leave: // goto emulation
+ for (;;) {
+ switch (state.mode) {
+ case HEAD:
+ if (state.wrap === 0) {
+ state.mode = TYPEDO;
+ break;
+ }
+ //=== NEEDBITS(16);
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */
+ state.check = 0/*crc32(0L, Z_NULL, 0)*/;
+ //=== CRC2(state.check, hold);
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ state.check = crc32(state.check, hbuf, 2, 0);
+ //===//
+
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = FLAGS;
+ break;
+ }
+ state.flags = 0; /* expect zlib header */
+ if (state.head) {
+ state.head.done = false;
+ }
+ if (!(state.wrap & 1) || /* check if zlib header allowed */
+ (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {
+ strm.msg = 'incorrect header check';
+ state.mode = BAD;
+ break;
+ }
+ if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {
+ strm.msg = 'unknown compression method';
+ state.mode = BAD;
+ break;
+ }
+ //--- DROPBITS(4) ---//
+ hold >>>= 4;
+ bits -= 4;
+ //---//
+ len = (hold & 0x0f)/*BITS(4)*/ + 8;
+ if (state.wbits === 0) {
+ state.wbits = len;
+ }
+ else if (len > state.wbits) {
+ strm.msg = 'invalid window size';
+ state.mode = BAD;
+ break;
+ }
+ state.dmax = 1 << len;
+ //Tracev((stderr, "inflate: zlib header ok\n"));
+ strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
+ state.mode = hold & 0x200 ? DICTID : TYPE;
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ break;
+ case FLAGS:
+ //=== NEEDBITS(16); */
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.flags = hold;
+ if ((state.flags & 0xff) !== Z_DEFLATED) {
+ strm.msg = 'unknown compression method';
+ state.mode = BAD;
+ break;
+ }
+ if (state.flags & 0xe000) {
+ strm.msg = 'unknown header flags set';
+ state.mode = BAD;
+ break;
+ }
+ if (state.head) {
+ state.head.text = ((hold >> 8) & 1);
+ }
+ if (state.flags & 0x0200) {
+ //=== CRC2(state.check, hold);
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ state.check = crc32(state.check, hbuf, 2, 0);
+ //===//
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = TIME;
+ /* falls through */
+ case TIME:
+ //=== NEEDBITS(32); */
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if (state.head) {
+ state.head.time = hold;
+ }
+ if (state.flags & 0x0200) {
+ //=== CRC4(state.check, hold)
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ hbuf[2] = (hold >>> 16) & 0xff;
+ hbuf[3] = (hold >>> 24) & 0xff;
+ state.check = crc32(state.check, hbuf, 4, 0);
+ //===
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = OS;
+ /* falls through */
+ case OS:
+ //=== NEEDBITS(16); */
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if (state.head) {
+ state.head.xflags = (hold & 0xff);
+ state.head.os = (hold >> 8);
+ }
+ if (state.flags & 0x0200) {
+ //=== CRC2(state.check, hold);
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ state.check = crc32(state.check, hbuf, 2, 0);
+ //===//
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = EXLEN;
+ /* falls through */
+ case EXLEN:
+ if (state.flags & 0x0400) {
+ //=== NEEDBITS(16); */
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.length = hold;
+ if (state.head) {
+ state.head.extra_len = hold;
+ }
+ if (state.flags & 0x0200) {
+ //=== CRC2(state.check, hold);
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ state.check = crc32(state.check, hbuf, 2, 0);
+ //===//
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ }
+ else if (state.head) {
+ state.head.extra = null/*Z_NULL*/;
+ }
+ state.mode = EXTRA;
+ /* falls through */
+ case EXTRA:
+ if (state.flags & 0x0400) {
+ copy = state.length;
+ if (copy > have) { copy = have; }
+ if (copy) {
+ if (state.head) {
+ len = state.head.extra_len - state.length;
+ if (!state.head.extra) {
+ // Use untyped array for more conveniend processing later
+ state.head.extra = new Array(state.head.extra_len);
+ }
+ utils.arraySet(
+ state.head.extra,
+ input,
+ next,
+ // extra field is limited to 65536 bytes
+ // - no need for additional size check
+ copy,
+ /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
+ len
+ );
+ //zmemcpy(state.head.extra + len, next,
+ // len + copy > state.head.extra_max ?
+ // state.head.extra_max - len : copy);
+ }
+ if (state.flags & 0x0200) {
+ state.check = crc32(state.check, input, copy, next);
+ }
+ have -= copy;
+ next += copy;
+ state.length -= copy;
+ }
+ if (state.length) { break inf_leave; }
+ }
+ state.length = 0;
+ state.mode = NAME;
+ /* falls through */
+ case NAME:
+ if (state.flags & 0x0800) {
+ if (have === 0) { break inf_leave; }
+ copy = 0;
+ do {
+ // TODO: 2 or 1 bytes?
+ len = input[next + copy++];
+ /* use constant limit because in js we should not preallocate memory */
+ if (state.head && len &&
+ (state.length < 65536 /*state.head.name_max*/)) {
+ state.head.name += String.fromCharCode(len);
+ }
+ } while (len && copy < have);
+
+ if (state.flags & 0x0200) {
+ state.check = crc32(state.check, input, copy, next);
+ }
+ have -= copy;
+ next += copy;
+ if (len) { break inf_leave; }
+ }
+ else if (state.head) {
+ state.head.name = null;
+ }
+ state.length = 0;
+ state.mode = COMMENT;
+ /* falls through */
+ case COMMENT:
+ if (state.flags & 0x1000) {
+ if (have === 0) { break inf_leave; }
+ copy = 0;
+ do {
+ len = input[next + copy++];
+ /* use constant limit because in js we should not preallocate memory */
+ if (state.head && len &&
+ (state.length < 65536 /*state.head.comm_max*/)) {
+ state.head.comment += String.fromCharCode(len);
+ }
+ } while (len && copy < have);
+ if (state.flags & 0x0200) {
+ state.check = crc32(state.check, input, copy, next);
+ }
+ have -= copy;
+ next += copy;
+ if (len) { break inf_leave; }
+ }
+ else if (state.head) {
+ state.head.comment = null;
+ }
+ state.mode = HCRC;
+ /* falls through */
+ case HCRC:
+ if (state.flags & 0x0200) {
+ //=== NEEDBITS(16); */
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if (hold !== (state.check & 0xffff)) {
+ strm.msg = 'header crc mismatch';
+ state.mode = BAD;
+ break;
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ }
+ if (state.head) {
+ state.head.hcrc = ((state.flags >> 9) & 1);
+ state.head.done = true;
+ }
+ strm.adler = state.check = 0 /*crc32(0L, Z_NULL, 0)*/;
+ state.mode = TYPE;
+ break;
+ case DICTID:
+ //=== NEEDBITS(32); */
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ strm.adler = state.check = ZSWAP32(hold);
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = DICT;
+ /* falls through */
+ case DICT:
+ if (state.havedict === 0) {
+ //--- RESTORE() ---
+ strm.next_out = put;
+ strm.avail_out = left;
+ strm.next_in = next;
+ strm.avail_in = have;
+ state.hold = hold;
+ state.bits = bits;
+ //---
+ return Z_NEED_DICT;
+ }
+ strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
+ state.mode = TYPE;
+ /* falls through */
+ case TYPE:
+ if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }
+ /* falls through */
+ case TYPEDO:
+ if (state.last) {
+ //--- BYTEBITS() ---//
+ hold >>>= bits & 7;
+ bits -= bits & 7;
+ //---//
+ state.mode = CHECK;
+ break;
+ }
+ //=== NEEDBITS(3); */
+ while (bits < 3) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.last = (hold & 0x01)/*BITS(1)*/;
+ //--- DROPBITS(1) ---//
+ hold >>>= 1;
+ bits -= 1;
+ //---//
+
+ switch ((hold & 0x03)/*BITS(2)*/) {
+ case 0: /* stored block */
+ //Tracev((stderr, "inflate: stored block%s\n",
+ // state.last ? " (last)" : ""));
+ state.mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ //Tracev((stderr, "inflate: fixed codes block%s\n",
+ // state.last ? " (last)" : ""));
+ state.mode = LEN_; /* decode codes */
+ if (flush === Z_TREES) {
+ //--- DROPBITS(2) ---//
+ hold >>>= 2;
+ bits -= 2;
+ //---//
+ break inf_leave;
+ }
+ break;
+ case 2: /* dynamic block */
+ //Tracev((stderr, "inflate: dynamic codes block%s\n",
+ // state.last ? " (last)" : ""));
+ state.mode = TABLE;
+ break;
+ case 3:
+ strm.msg = 'invalid block type';
+ state.mode = BAD;
+ }
+ //--- DROPBITS(2) ---//
+ hold >>>= 2;
+ bits -= 2;
+ //---//
+ break;
+ case STORED:
+ //--- BYTEBITS() ---// /* go to byte boundary */
+ hold >>>= bits & 7;
+ bits -= bits & 7;
+ //---//
+ //=== NEEDBITS(32); */
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {
+ strm.msg = 'invalid stored block lengths';
+ state.mode = BAD;
+ break;
+ }
+ state.length = hold & 0xffff;
+ //Tracev((stderr, "inflate: stored length %u\n",
+ // state.length));
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = COPY_;
+ if (flush === Z_TREES) { break inf_leave; }
+ /* falls through */
+ case COPY_:
+ state.mode = COPY;
+ /* falls through */
+ case COPY:
+ copy = state.length;
+ if (copy) {
+ if (copy > have) { copy = have; }
+ if (copy > left) { copy = left; }
+ if (copy === 0) { break inf_leave; }
+ //--- zmemcpy(put, next, copy); ---
+ utils.arraySet(output, input, next, copy, put);
+ //---//
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state.length -= copy;
+ break;
+ }
+ //Tracev((stderr, "inflate: stored end\n"));
+ state.mode = TYPE;
+ break;
+ case TABLE:
+ //=== NEEDBITS(14); */
+ while (bits < 14) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;
+ //--- DROPBITS(5) ---//
+ hold >>>= 5;
+ bits -= 5;
+ //---//
+ state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;
+ //--- DROPBITS(5) ---//
+ hold >>>= 5;
+ bits -= 5;
+ //---//
+ state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;
+ //--- DROPBITS(4) ---//
+ hold >>>= 4;
+ bits -= 4;
+ //---//
+//#ifndef PKZIP_BUG_WORKAROUND
+ if (state.nlen > 286 || state.ndist > 30) {
+ strm.msg = 'too many length or distance symbols';
+ state.mode = BAD;
+ break;
+ }
+//#endif
+ //Tracev((stderr, "inflate: table sizes ok\n"));
+ state.have = 0;
+ state.mode = LENLENS;
+ /* falls through */
+ case LENLENS:
+ while (state.have < state.ncode) {
+ //=== NEEDBITS(3);
+ while (bits < 3) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);
+ //--- DROPBITS(3) ---//
+ hold >>>= 3;
+ bits -= 3;
+ //---//
+ }
+ while (state.have < 19) {
+ state.lens[order[state.have++]] = 0;
+ }
+ // We have separate tables & no pointers. 2 commented lines below not needed.
+ //state.next = state.codes;
+ //state.lencode = state.next;
+ // Switch to use dynamic table
+ state.lencode = state.lendyn;
+ state.lenbits = 7;
+
+ opts = {bits: state.lenbits};
+ ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
+ state.lenbits = opts.bits;
+
+ if (ret) {
+ strm.msg = 'invalid code lengths set';
+ state.mode = BAD;
+ break;
+ }
+ //Tracev((stderr, "inflate: code lengths ok\n"));
+ state.have = 0;
+ state.mode = CODELENS;
+ /* falls through */
+ case CODELENS:
+ while (state.have < state.nlen + state.ndist) {
+ for (;;) {
+ here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if ((here_bits) <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ if (here_val < 16) {
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ state.lens[state.have++] = here_val;
+ }
+ else {
+ if (here_val === 16) {
+ //=== NEEDBITS(here.bits + 2);
+ n = here_bits + 2;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ if (state.have === 0) {
+ strm.msg = 'invalid bit length repeat';
+ state.mode = BAD;
+ break;
+ }
+ len = state.lens[state.have - 1];
+ copy = 3 + (hold & 0x03);//BITS(2);
+ //--- DROPBITS(2) ---//
+ hold >>>= 2;
+ bits -= 2;
+ //---//
+ }
+ else if (here_val === 17) {
+ //=== NEEDBITS(here.bits + 3);
+ n = here_bits + 3;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ len = 0;
+ copy = 3 + (hold & 0x07);//BITS(3);
+ //--- DROPBITS(3) ---//
+ hold >>>= 3;
+ bits -= 3;
+ //---//
+ }
+ else {
+ //=== NEEDBITS(here.bits + 7);
+ n = here_bits + 7;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ len = 0;
+ copy = 11 + (hold & 0x7f);//BITS(7);
+ //--- DROPBITS(7) ---//
+ hold >>>= 7;
+ bits -= 7;
+ //---//
+ }
+ if (state.have + copy > state.nlen + state.ndist) {
+ strm.msg = 'invalid bit length repeat';
+ state.mode = BAD;
+ break;
+ }
+ while (copy--) {
+ state.lens[state.have++] = len;
+ }
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state.mode === BAD) { break; }
+
+ /* check for end-of-block code (better have one) */
+ if (state.lens[256] === 0) {
+ strm.msg = 'invalid code -- missing end-of-block';
+ state.mode = BAD;
+ break;
+ }
+
+ /* build code tables -- note: do not change the lenbits or distbits
+ values here (9 and 6) without reading the comments in inftrees.h
+ concerning the ENOUGH constants, which depend on those values */
+ state.lenbits = 9;
+
+ opts = {bits: state.lenbits};
+ ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
+ // We have separate tables & no pointers. 2 commented lines below not needed.
+ // state.next_index = opts.table_index;
+ state.lenbits = opts.bits;
+ // state.lencode = state.next;
+
+ if (ret) {
+ strm.msg = 'invalid literal/lengths set';
+ state.mode = BAD;
+ break;
+ }
+
+ state.distbits = 6;
+ //state.distcode.copy(state.codes);
+ // Switch to use dynamic table
+ state.distcode = state.distdyn;
+ opts = {bits: state.distbits};
+ ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
+ // We have separate tables & no pointers. 2 commented lines below not needed.
+ // state.next_index = opts.table_index;
+ state.distbits = opts.bits;
+ // state.distcode = state.next;
+
+ if (ret) {
+ strm.msg = 'invalid distances set';
+ state.mode = BAD;
+ break;
+ }
+ //Tracev((stderr, 'inflate: codes ok\n'));
+ state.mode = LEN_;
+ if (flush === Z_TREES) { break inf_leave; }
+ /* falls through */
+ case LEN_:
+ state.mode = LEN;
+ /* falls through */
+ case LEN:
+ if (have >= 6 && left >= 258) {
+ //--- RESTORE() ---
+ strm.next_out = put;
+ strm.avail_out = left;
+ strm.next_in = next;
+ strm.avail_in = have;
+ state.hold = hold;
+ state.bits = bits;
+ //---
+ inflate_fast(strm, _out);
+ //--- LOAD() ---
+ put = strm.next_out;
+ output = strm.output;
+ left = strm.avail_out;
+ next = strm.next_in;
+ input = strm.input;
+ have = strm.avail_in;
+ hold = state.hold;
+ bits = state.bits;
+ //---
+
+ if (state.mode === TYPE) {
+ state.back = -1;
+ }
+ break;
+ }
+ state.back = 0;
+ for (;;) {
+ here = state.lencode[hold & ((1 << state.lenbits) -1)]; /*BITS(state.lenbits)*/
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if (here_bits <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ if (here_op && (here_op & 0xf0) === 0) {
+ last_bits = here_bits;
+ last_op = here_op;
+ last_val = here_val;
+ for (;;) {
+ here = state.lencode[last_val +
+ ((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)];
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if ((last_bits + here_bits) <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ //--- DROPBITS(last.bits) ---//
+ hold >>>= last_bits;
+ bits -= last_bits;
+ //---//
+ state.back += last_bits;
+ }
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ state.back += here_bits;
+ state.length = here_val;
+ if (here_op === 0) {
+ //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ // "inflate: literal '%c'\n" :
+ // "inflate: literal 0x%02x\n", here.val));
+ state.mode = LIT;
+ break;
+ }
+ if (here_op & 32) {
+ //Tracevv((stderr, "inflate: end of block\n"));
+ state.back = -1;
+ state.mode = TYPE;
+ break;
+ }
+ if (here_op & 64) {
+ strm.msg = 'invalid literal/length code';
+ state.mode = BAD;
+ break;
+ }
+ state.extra = here_op & 15;
+ state.mode = LENEXT;
+ /* falls through */
+ case LENEXT:
+ if (state.extra) {
+ //=== NEEDBITS(state.extra);
+ n = state.extra;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.length += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/;
+ //--- DROPBITS(state.extra) ---//
+ hold >>>= state.extra;
+ bits -= state.extra;
+ //---//
+ state.back += state.extra;
+ }
+ //Tracevv((stderr, "inflate: length %u\n", state.length));
+ state.was = state.length;
+ state.mode = DIST;
+ /* falls through */
+ case DIST:
+ for (;;) {
+ here = state.distcode[hold & ((1 << state.distbits) -1)];/*BITS(state.distbits)*/
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if ((here_bits) <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ if ((here_op & 0xf0) === 0) {
+ last_bits = here_bits;
+ last_op = here_op;
+ last_val = here_val;
+ for (;;) {
+ here = state.distcode[last_val +
+ ((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)];
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if ((last_bits + here_bits) <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ //--- DROPBITS(last.bits) ---//
+ hold >>>= last_bits;
+ bits -= last_bits;
+ //---//
+ state.back += last_bits;
+ }
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ state.back += here_bits;
+ if (here_op & 64) {
+ strm.msg = 'invalid distance code';
+ state.mode = BAD;
+ break;
+ }
+ state.offset = here_val;
+ state.extra = (here_op) & 15;
+ state.mode = DISTEXT;
+ /* falls through */
+ case DISTEXT:
+ if (state.extra) {
+ //=== NEEDBITS(state.extra);
+ n = state.extra;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.offset += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/;
+ //--- DROPBITS(state.extra) ---//
+ hold >>>= state.extra;
+ bits -= state.extra;
+ //---//
+ state.back += state.extra;
+ }
+//#ifdef INFLATE_STRICT
+ if (state.offset > state.dmax) {
+ strm.msg = 'invalid distance too far back';
+ state.mode = BAD;
+ break;
+ }
+//#endif
+ //Tracevv((stderr, "inflate: distance %u\n", state.offset));
+ state.mode = MATCH;
+ /* falls through */
+ case MATCH:
+ if (left === 0) { break inf_leave; }
+ copy = _out - left;
+ if (state.offset > copy) { /* copy from window */
+ copy = state.offset - copy;
+ if (copy > state.whave) {
+ if (state.sane) {
+ strm.msg = 'invalid distance too far back';
+ state.mode = BAD;
+ break;
+ }
+// (!) This block is disabled in zlib defailts,
+// don't enable it for binary compatibility
+//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+// Trace((stderr, "inflate.c too far\n"));
+// copy -= state.whave;
+// if (copy > state.length) { copy = state.length; }
+// if (copy > left) { copy = left; }
+// left -= copy;
+// state.length -= copy;
+// do {
+// output[put++] = 0;
+// } while (--copy);
+// if (state.length === 0) { state.mode = LEN; }
+// break;
+//#endif
+ }
+ if (copy > state.wnext) {
+ copy -= state.wnext;
+ from = state.wsize - copy;
+ }
+ else {
+ from = state.wnext - copy;
+ }
+ if (copy > state.length) { copy = state.length; }
+ from_source = state.window;
+ }
+ else { /* copy from output */
+ from_source = output;
+ from = put - state.offset;
+ copy = state.length;
+ }
+ if (copy > left) { copy = left; }
+ left -= copy;
+ state.length -= copy;
+ do {
+ output[put++] = from_source[from++];
+ } while (--copy);
+ if (state.length === 0) { state.mode = LEN; }
+ break;
+ case LIT:
+ if (left === 0) { break inf_leave; }
+ output[put++] = state.length;
+ left--;
+ state.mode = LEN;
+ break;
+ case CHECK:
+ if (state.wrap) {
+ //=== NEEDBITS(32);
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ // Use '|' insdead of '+' to make sure that result is signed
+ hold |= input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ _out -= left;
+ strm.total_out += _out;
+ state.total += _out;
+ if (_out) {
+ strm.adler = state.check =
+ /*UPDATE(state.check, put - _out, _out);*/
+ (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));
+
+ }
+ _out = left;
+ // NB: crc32 stored as signed 32-bit int, ZSWAP32 returns signed too
+ if ((state.flags ? hold : ZSWAP32(hold)) !== state.check) {
+ strm.msg = 'incorrect data check';
+ state.mode = BAD;
+ break;
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ //Tracev((stderr, "inflate: check matches trailer\n"));
+ }
+ state.mode = LENGTH;
+ /* falls through */
+ case LENGTH:
+ if (state.wrap && state.flags) {
+ //=== NEEDBITS(32);
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if (hold !== (state.total & 0xffffffff)) {
+ strm.msg = 'incorrect length check';
+ state.mode = BAD;
+ break;
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ //Tracev((stderr, "inflate: length matches trailer\n"));
+ }
+ state.mode = DONE;
+ /* falls through */
+ case DONE:
+ ret = Z_STREAM_END;
+ break inf_leave;
+ case BAD:
+ ret = Z_DATA_ERROR;
+ break inf_leave;
+ case MEM:
+ return Z_MEM_ERROR;
+ case SYNC:
+ /* falls through */
+ default:
+ return Z_STREAM_ERROR;
+ }
+ }
+
+ // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"
+
+ /*
+ Return from inflate(), updating the total counts and the check value.
+ If there was no progress during the inflate() call, return a buffer
+ error. Call updatewindow() to create and/or update the window state.
+ Note: a memory error from inflate() is non-recoverable.
+ */
+
+ //--- RESTORE() ---
+ strm.next_out = put;
+ strm.avail_out = left;
+ strm.next_in = next;
+ strm.avail_in = have;
+ state.hold = hold;
+ state.bits = bits;
+ //---
+
+ if (state.wsize || (_out !== strm.avail_out && state.mode < BAD &&
+ (state.mode < CHECK || flush !== Z_FINISH))) {
+ if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {
+ state.mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ }
+ _in -= strm.avail_in;
+ _out -= strm.avail_out;
+ strm.total_in += _in;
+ strm.total_out += _out;
+ state.total += _out;
+ if (state.wrap && _out) {
+ strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/
+ (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out));
+ }
+ strm.data_type = state.bits + (state.last ? 64 : 0) +
+ (state.mode === TYPE ? 128 : 0) +
+ (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);
+ if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) {
+ ret = Z_BUF_ERROR;
+ }
+ return ret;
+}
+
+function inflateEnd(strm) {
+
+ if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) {
+ return Z_STREAM_ERROR;
+ }
+
+ var state = strm.state;
+ if (state.window) {
+ state.window = null;
+ }
+ strm.state = null;
+ return Z_OK;
+}
+
+function inflateGetHeader(strm, head) {
+ var state;
+
+ /* check state */
+ if (!strm || !strm.state) { return Z_STREAM_ERROR; }
+ state = strm.state;
+ if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; }
+
+ /* save header structure */
+ state.head = head;
+ head.done = false;
+ return Z_OK;
+}
+
+
+exports.inflateReset = inflateReset;
+exports.inflateReset2 = inflateReset2;
+exports.inflateResetKeep = inflateResetKeep;
+exports.inflateInit = inflateInit;
+exports.inflateInit2 = inflateInit2;
+exports.inflate = inflate;
+exports.inflateEnd = inflateEnd;
+exports.inflateGetHeader = inflateGetHeader;
+exports.inflateInfo = 'pako inflate (from Nodeca project)';
+
+/* Not implemented
+exports.inflateCopy = inflateCopy;
+exports.inflateGetDictionary = inflateGetDictionary;
+exports.inflateMark = inflateMark;
+exports.inflatePrime = inflatePrime;
+exports.inflateSetDictionary = inflateSetDictionary;
+exports.inflateSync = inflateSync;
+exports.inflateSyncPoint = inflateSyncPoint;
+exports.inflateUndermine = inflateUndermine;
+*/
+
+},{"../utils/common":1,"./adler32":2,"./crc32":3,"./inffast":4,"./inftrees":6}],6:[function(require,module,exports){
+'use strict';
+
+
+var utils = require('../utils/common');
+
+var MAXBITS = 15;
+var ENOUGH_LENS = 852;
+var ENOUGH_DISTS = 592;
+//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
+
+var CODES = 0;
+var LENS = 1;
+var DISTS = 2;
+
+var lbase = [ /* Length codes 257..285 base */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+];
+
+var lext = [ /* Length codes 257..285 extra */
+ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78
+];
+
+var dbase = [ /* Distance codes 0..29 base */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577, 0, 0
+];
+
+var dext = [ /* Distance codes 0..29 extra */
+ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+ 28, 28, 29, 29, 64, 64
+];
+
+module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts)
+{
+ var bits = opts.bits;
+ //here = opts.here; /* table entry for duplication */
+
+ var len = 0; /* a code's length in bits */
+ var sym = 0; /* index of code symbols */
+ var min = 0, max = 0; /* minimum and maximum code lengths */
+ var root = 0; /* number of index bits for root table */
+ var curr = 0; /* number of index bits for current table */
+ var drop = 0; /* code bits to drop for sub-table */
+ var left = 0; /* number of prefix codes available */
+ var used = 0; /* code entries in table used */
+ var huff = 0; /* Huffman code */
+ var incr; /* for incrementing code, index */
+ var fill; /* index for replicating entries */
+ var low; /* low bits for current root entry */
+ var mask; /* mask for low root bits */
+ var next; /* next available space in table */
+ var base = null; /* base value table to use */
+ var base_index = 0;
+// var shoextra; /* extra bits table to use */
+ var end; /* use base and extra for symbol > end */
+ var count = new utils.Buf16(MAXBITS+1); //[MAXBITS+1]; /* number of codes of each length */
+ var offs = new utils.Buf16(MAXBITS+1); //[MAXBITS+1]; /* offsets in table for each length */
+ var extra = null;
+ var extra_index = 0;
+
+ var here_bits, here_op, here_val;
+
+ /*
+ Process a set of code lengths to create a canonical Huffman code. The
+ code lengths are lens[0..codes-1]. Each length corresponds to the
+ symbols 0..codes-1. The Huffman code is generated by first sorting the
+ symbols by length from short to long, and retaining the symbol order
+ for codes with equal lengths. Then the code starts with all zero bits
+ for the first code of the shortest length, and the codes are integer
+ increments for the same length, and zeros are appended as the length
+ increases. For the deflate format, these bits are stored backwards
+ from their more natural integer increment ordering, and so when the
+ decoding tables are built in the large loop below, the integer codes
+ are incremented backwards.
+
+ This routine assumes, but does not check, that all of the entries in
+ lens[] are in the range 0..MAXBITS. The caller must assure this.
+ 1..MAXBITS is interpreted as that code length. zero means that that
+ symbol does not occur in this code.
+
+ The codes are sorted by computing a count of codes for each length,
+ creating from that a table of starting indices for each length in the
+ sorted table, and then entering the symbols in order in the sorted
+ table. The sorted table is work[], with that space being provided by
+ the caller.
+
+ The length counts are used for other purposes as well, i.e. finding
+ the minimum and maximum length codes, determining if there are any
+ codes at all, checking for a valid set of lengths, and looking ahead
+ at length counts to determine sub-table sizes when building the
+ decoding tables.
+ */
+
+ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+ for (len = 0; len <= MAXBITS; len++) {
+ count[len] = 0;
+ }
+ for (sym = 0; sym < codes; sym++) {
+ count[lens[lens_index + sym]]++;
+ }
+
+ /* bound code lengths, force root to be within code lengths */
+ root = bits;
+ for (max = MAXBITS; max >= 1; max--) {
+ if (count[max] !== 0) { break; }
+ }
+ if (root > max) {
+ root = max;
+ }
+ if (max === 0) { /* no symbols to code at all */
+ //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */
+ //table.bits[opts.table_index] = 1; //here.bits = (var char)1;
+ //table.val[opts.table_index++] = 0; //here.val = (var short)0;
+ table[table_index++] = (1 << 24) | (64 << 16) | 0;
+
+
+ //table.op[opts.table_index] = 64;
+ //table.bits[opts.table_index] = 1;
+ //table.val[opts.table_index++] = 0;
+ table[table_index++] = (1 << 24) | (64 << 16) | 0;
+
+ opts.bits = 1;
+ return 0; /* no symbols, but wait for decoding to report error */
+ }
+ for (min = 1; min < max; min++) {
+ if (count[min] !== 0) { break; }
+ }
+ if (root < min) {
+ root = min;
+ }
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1;
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= count[len];
+ if (left < 0) {
+ return -1;
+ } /* over-subscribed */
+ }
+ if (left > 0 && (type === CODES || max !== 1)) {
+ return -1; /* incomplete set */
+ }
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++) {
+ offs[len + 1] = offs[len] + count[len];
+ }
+
+ /* sort symbols by length, by symbol order within each length */
+ for (sym = 0; sym < codes; sym++) {
+ if (lens[lens_index + sym] !== 0) {
+ work[offs[lens[lens_index + sym]]++] = sym;
+ }
+ }
+
+ /*
+ Create and fill in decoding tables. In this loop, the table being
+ filled is at next and has curr index bits. The code being used is huff
+ with length len. That code is converted to an index by dropping drop
+ bits off of the bottom. For codes where len is less than drop + curr,
+ those top drop + curr - len bits are incremented through all values to
+ fill the table with replicated entries.
+
+ root is the number of index bits for the root table. When len exceeds
+ root, sub-tables are created pointed to by the root entry with an index
+ of the low root bits of huff. This is saved in low to check for when a
+ new sub-table should be started. drop is zero when the root table is
+ being filled, and drop is root when sub-tables are being filled.
+
+ When a new sub-table is needed, it is necessary to look ahead in the
+ code lengths to determine what size sub-table is needed. The length
+ counts are used for this, and so count[] is decremented as codes are
+ entered in the tables.
+
+ used keeps track of how many table entries have been allocated from the
+ provided *table space. It is checked for LENS and DIST tables against
+ the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
+ the initial root table size constants. See the comments in inftrees.h
+ for more information.
+
+ sym increments through all symbols, and the loop terminates when
+ all codes of length max, i.e. all codes, have been processed. This
+ routine permits incomplete codes, so another loop after this one fills
+ in the rest of the decoding tables with invalid code markers.
+ */
+
+ /* set up for code type */
+ // poor man optimization - use if-else instead of switch,
+ // to avoid deopts in old v8
+ if (type === CODES) {
+ base = extra = work; /* dummy value--not used */
+ end = 19;
+
+ } else if (type === LENS) {
+ base = lbase;
+ base_index -= 257;
+ extra = lext;
+ extra_index -= 257;
+ end = 256;
+
+ } else { /* DISTS */
+ base = dbase;
+ extra = dext;
+ end = -1;
+ }
+
+ /* initialize opts for loop */
+ huff = 0; /* starting code */
+ sym = 0; /* starting code symbol */
+ len = min; /* starting code length */
+ next = table_index; /* current table to fill in */
+ curr = root; /* current table index bits */
+ drop = 0; /* current bits to drop from code for index */
+ low = -1; /* trigger new sub-table when len > root */
+ used = 1 << root; /* use root table entries */
+ mask = used - 1; /* mask for comparing low */
+
+ /* check available table space */
+ if ((type === LENS && used > ENOUGH_LENS) ||
+ (type === DISTS && used > ENOUGH_DISTS)) {
+ return 1;
+ }
+
+ var i=0;
+ /* process all codes and make table entries */
+ for (;;) {
+ i++;
+ /* create table entry */
+ here_bits = len - drop;
+ if (work[sym] < end) {
+ here_op = 0;
+ here_val = work[sym];
+ }
+ else if (work[sym] > end) {
+ here_op = extra[extra_index + work[sym]];
+ here_val = base[base_index + work[sym]];
+ }
+ else {
+ here_op = 32 + 64; /* end of block */
+ here_val = 0;
+ }
+
+ /* replicate for those indices with low len bits equal to huff */
+ incr = 1 << (len - drop);
+ fill = 1 << curr;
+ min = fill; /* save offset to next table */
+ do {
+ fill -= incr;
+ table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;
+ } while (fill !== 0);
+
+ /* backwards increment the len-bit code huff */
+ incr = 1 << (len - 1);
+ while (huff & incr) {
+ incr >>= 1;
+ }
+ if (incr !== 0) {
+ huff &= incr - 1;
+ huff += incr;
+ } else {
+ huff = 0;
+ }
+
+ /* go to next symbol, update count, len */
+ sym++;
+ if (--count[len] === 0) {
+ if (len === max) { break; }
+ len = lens[lens_index + work[sym]];
+ }
+
+ /* create new sub-table if needed */
+ if (len > root && (huff & mask) !== low) {
+ /* if first time, transition to sub-tables */
+ if (drop === 0) {
+ drop = root;
+ }
+
+ /* increment past last table */
+ next += min; /* here min is 1 << curr */
+
+ /* determine length of next table */
+ curr = len - drop;
+ left = 1 << curr;
+ while (curr + drop < max) {
+ left -= count[curr + drop];
+ if (left <= 0) { break; }
+ curr++;
+ left <<= 1;
+ }
+
+ /* check for enough space */
+ used += 1 << curr;
+ if ((type === LENS && used > ENOUGH_LENS) ||
+ (type === DISTS && used > ENOUGH_DISTS)) {
+ return 1;
+ }
+
+ /* point entry in root table to sub-table */
+ low = huff & mask;
+ /*table.op[low] = curr;
+ table.bits[low] = root;
+ table.val[low] = next - opts.table_index;*/
+ table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;
+ }
+ }
+
+ /* fill in remaining table entry if code is incomplete (guaranteed to have
+ at most one remaining entry, since if the code is incomplete, the
+ maximum code length that was allowed to get this far is one bit) */
+ if (huff !== 0) {
+ //table.op[next + huff] = 64; /* invalid code marker */
+ //table.bits[next + huff] = len - drop;
+ //table.val[next + huff] = 0;
+ table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;
+ }
+
+ /* set return parameters */
+ //opts.table_index += used;
+ opts.bits = root;
+ return 0;
+};
+
+},{"../utils/common":1}],7:[function(require,module,exports){
+'use strict';
+
+
+function ZStream() {
+ /* next input byte */
+ this.input = null; // JS specific, because we have no pointers
+ this.next_in = 0;
+ /* number of bytes available at input */
+ this.avail_in = 0;
+ /* total number of input bytes read so far */
+ this.total_in = 0;
+ /* next output byte should be put there */
+ this.output = null; // JS specific, because we have no pointers
+ this.next_out = 0;
+ /* remaining free space at output */
+ this.avail_out = 0;
+ /* total number of bytes output so far */
+ this.total_out = 0;
+ /* last error message, NULL if no error */
+ this.msg = ''/*Z_NULL*/;
+ /* not visible by applications */
+ this.state = null;
+ /* best guess about the data type: binary or text */
+ this.data_type = 2/*Z_UNKNOWN*/;
+ /* adler32 value of the uncompressed data */
+ this.adler = 0;
+}
+
+module.exports = ZStream;
+
+},{}],"/partial_inflator.js":[function(require,module,exports){
+var zlib = require('./lib/zlib/inflate.js');
+var ZStream = require('./lib/zlib/zstream.js');
+
+var Inflate = function () {
+ this.strm = new ZStream();
+ this.chunkSize = 1024 * 10 * 10;
+ this.strm.output = new Uint8Array(this.chunkSize);
+ this.windowBits = 5;
+
+ zlib.inflateInit(this.strm, this.windowBits);
+};
+
+Inflate.prototype = {
+ inflate: function (data, flush) {
+ this.strm.input = data;
+ this.strm.avail_in = this.strm.input.length;
+ this.strm.next_in = 0;
+ this.strm.next_out = 0;
+
+ this.strm.avail_out = this.chunkSize;
+
+ zlib.inflate(this.strm, flush);
+
+ return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
+ },
+
+ reset: function () {
+ zlib.inflateReset(this.strm);
+ }
+};
+
+module.exports = {Inflate: Inflate};
+
+},{"./lib/zlib/inflate.js":5,"./lib/zlib/zstream.js":7}]},{},[])("/partial_inflator.js")
+});
diff --git a/include/playback.js b/include/playback.js
index 7756529d..203576f6 100644
--- a/include/playback.js
+++ b/include/playback.js
@@ -12,21 +12,39 @@ var rfb, mode, test_state, frame_idx, frame_length,
iteration, iterations, istart_time,
// Pre-declarations for jslint
- send_array, next_iteration, queue_next_packet, do_packet;
+ send_array, next_iteration, queue_next_packet, do_packet, enable_test_mode;
// Override send_array
send_array = function (arr) {
// Stub out send_array
};
+enable_test_mode = function () {
+ rfb._sock._mode = VNC_frame_encoding;
+ 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._updateState('ProtocolVersion', "Starting VNC handshake");
+ };
+};
+
next_iteration = function () {
+ rfb = new RFB({'target': $D('VNC_canvas'),
+ 'onUpdateState': updateState});
+ enable_test_mode();
+
if (iteration === 0) {
frame_length = VNC_frame_data.length;
test_state = 'running';
- } else {
- rfb.disconnect();
}
-
+
if (test_state !== 'running') { return; }
iteration += 1;
@@ -91,9 +109,9 @@ do_packet = function () {
for (var i = 0; i < frame.length - start; i++) {
u8[i] = frame.charCodeAt(start + i);
}
- rfb.recv_message({'data' : u8});
+ rfb._sock._recv_message({'data' : u8});
} else {
- rfb.recv_message({'data' : frame.slice(start)});
+ rfb._sock._recv_message({'data' : frame.slice(start)});
}
frame_idx += 1;
diff --git a/include/rfb.js b/include/rfb.js
index a591ca2b..b7a811d5 100644
--- a/include/rfb.js
+++ b/include/rfb.js
@@ -92,6 +92,9 @@ var RFB;
this._fb_height = 0;
this._fb_name = "";
+ this._destBuff = null;
+ this._paletteBuff = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
+
this._rre_chunk_sz = 100;
this._timing = {
@@ -128,7 +131,7 @@ var RFB;
'view_only': false, // Disable client mouse/keyboard
'xvp_password_sep': '@', // Separator for XVP password fields
'disconnectTimeout': 3, // Time (s) to wait for disconnection
- 'wsProtocols': ['binary', 'base64'], // Protocols to use in the WebSocket connection
+ 'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection
'repeaterID': '', // [UltraVNC] RepeaterID to connect to
'viewportDrag': false, // Move the viewport on mouse drags
@@ -217,16 +220,8 @@ var RFB;
Util.Info("Using native WebSockets");
this._updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode);
} else {
- Util.Warn("Using web-socket-js bridge. Flash version: " + Util.Flash.version);
- if (!Util.Flash || Util.Flash.version < 9) {
- this._cleanupSocket('fatal');
- throw new Exception("WebSockets or Adobe Flash is required");
- } else if (document.location.href.substr(0, 7) === 'file://') {
- this._cleanupSocket('fatal');
- throw new Exception("'file://' URL is incompatible with Adobe Flash");
- } else {
- this._updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode);
- }
+ this._cleanupSocket('fatal');
+ throw new Error("WebSocket support is required to use noVNC");
}
Util.Debug("<< RFB.constructor");
@@ -264,14 +259,14 @@ var RFB;
if (this._rfb_state !== 'normal' || this._view_only) { return false; }
Util.Info("Sending Ctrl-Alt-Del");
- var arr = [];
- arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 1));
- arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 1));
- arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 1));
- arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 0));
- arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 0));
- arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 0));
- this._sock.send(arr);
+ RFB.messages.keyEvent(this._sock, XK_Control_L, 1);
+ RFB.messages.keyEvent(this._sock, XK_Alt_L, 1);
+ RFB.messages.keyEvent(this._sock, XK_Delete, 1);
+ RFB.messages.keyEvent(this._sock, XK_Delete, 0);
+ RFB.messages.keyEvent(this._sock, XK_Alt_L, 0);
+ RFB.messages.keyEvent(this._sock, XK_Control_L, 0);
+
+ this._sock.flush();
},
xvpOp: function (ver, op) {
@@ -297,21 +292,22 @@ var RFB;
// followed by an up key.
sendKey: function (code, down) {
if (this._rfb_state !== "normal" || this._view_only) { return false; }
- var arr = [];
if (typeof down !== 'undefined') {
Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code);
- arr = arr.concat(RFB.messages.keyEvent(code, down ? 1 : 0));
+ RFB.messages.keyEvent(this._sock, code, down ? 1 : 0);
} else {
Util.Info("Sending key code (down + up): " + code);
- arr = arr.concat(RFB.messages.keyEvent(code, 1));
- arr = arr.concat(RFB.messages.keyEvent(code, 0));
+ RFB.messages.keyEvent(this._sock, code, 1);
+ RFB.messages.keyEvent(this._sock, code, 0);
}
- this._sock.send(arr);
+
+ this._sock.flush();
},
clipboardPasteFrom: function (text) {
if (this._rfb_state !== 'normal') { return; }
- this._sock.send(RFB.messages.clientCutText(text));
+ RFB.messages.clientCutText(this._sock, text);
+ this._sock.flush();
},
setDesktopSize: function (width, height) {
@@ -362,8 +358,6 @@ var RFB;
_init_vars: function () {
// reset state
- this._sock.init();
-
this._FBU.rects = 0;
this._FBU.subrects = 0; // RRE and HEXTILE
this._FBU.lines = 0; // RAW
@@ -380,8 +374,9 @@ var RFB;
}
for (i = 0; i < 4; i++) {
- this._FBU.zlibs[i] = new TINF();
- this._FBU.zlibs[i].init();
+ //this._FBU.zlibs[i] = new TINF();
+ //this._FBU.zlibs[i].init();
+ this._FBU.zlibs[i] = new inflator.Inflate();
}
},
@@ -578,16 +573,10 @@ var RFB;
}
},
- _checkEvents: function () {
- if (this._rfb_state === 'normal' && !this._viewportDragging && this._mouse_arr.length > 0) {
- this._sock.send(this._mouse_arr);
- this._mouse_arr = [];
- }
- },
-
_handleKeyPress: function (keysym, down) {
if (this._view_only) { return; } // View only, skip keyboard, events
- this._sock.send(RFB.messages.keyEvent(keysym, down));
+ RFB.messages.keyEvent(this._sock, keysym, down);
+ this._sock.flush();
},
_handleMouseButton: function (x, y, down, bmask) {
@@ -611,10 +600,8 @@ var RFB;
if (this._view_only) { return; } // View only, skip mouse events
- this._mouse_arr = this._mouse_arr.concat(
- RFB.messages.pointerEvent(this._display.absX(x), this._display.absY(y), this._mouse_buttonMask));
- this._sock.send(this._mouse_arr);
- this._mouse_arr = [];
+ if (this._rfb_state !== "normal") { return; }
+ RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
},
_handleMouseMove: function (x, y) {
@@ -631,10 +618,8 @@ var RFB;
if (this._view_only) { return; } // View only, skip mouse events
- this._mouse_arr = this._mouse_arr.concat(
- RFB.messages.pointerEvent(this._display.absX(x), this._display.absY(y), this._mouse_buttonMask));
-
- this._checkEvents();
+ if (this._rfb_state !== "normal") { return; }
+ RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
},
// Message Handlers
@@ -758,7 +743,8 @@ var RFB;
if (this._sock.rQwait("auth challenge", 16)) { return false; }
- var challenge = this._sock.rQshiftBytes(16);
+ // TODO(directxman12): make genDES not require an Array
+ var challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16));
var response = RFB.genDES(this._rfb_password, challenge);
this._sock.send(response);
this._updateState("SecurityResult");
@@ -900,6 +886,7 @@ var RFB;
/* Screen size */
this._fb_width = this._sock.rQshift16();
this._fb_height = this._sock.rQshift16();
+ this._destBuff = new Uint8Array(this._fb_width * this._fb_height * 4);
/* PIXEL_FORMAT */
var bpp = this._sock.rQshift8();
@@ -995,18 +982,13 @@ var RFB;
this._fb_depth = 1;
}
- var response = RFB.messages.pixelFormat(this._fb_Bpp, this._fb_depth, this._true_color);
- response = response.concat(
- RFB.messages.clientEncodings(this._encodings, this._local_cursor, this._true_color));
- response = response.concat(
- RFB.messages.fbUpdateRequests(this._display.getCleanDirtyReset(),
- this._fb_width, this._fb_height));
+ RFB.messages.pixelFormat(this._sock, this._fb_Bpp, this._fb_depth, this._true_color);
+ RFB.messages.clientEncodings(this._sock, this._encodings, this._local_cursor, this._true_color);
+ RFB.messages.fbUpdateRequests(this._sock, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height);
this._timing.fbu_rt_start = (new Date()).getTime();
this._timing.pixels = 0;
- this._sock.send(response);
-
- this._checkEvents();
+ this._sock.flush();
if (this._encrypt) {
this._updateState('normal', 'Connected (encrypted) to: ' + this._fb_name);
@@ -1108,8 +1090,8 @@ var RFB;
case 0: // FramebufferUpdate
var ret = this._framebufferUpdate();
if (ret) {
- this._sock.send(RFB.messages.fbUpdateRequests(this._display.getCleanDirtyReset(),
- this._fb_width, this._fb_height));
+ RFB.messages.fbUpdateRequests(this._sock, this._display.getCleanDirtyReset(), this._fb_width, this._fb_height);
+ this._sock.flush();
}
return ret;
@@ -1181,7 +1163,14 @@ var RFB;
this._timing.last_fbu = (new Date()).getTime();
- ret = this._encHandlers[this._FBU.encoding]();
+ var handler = this._encHandlers[this._FBU.encoding];
+ try {
+ //ret = this._encHandlers[this._FBU.encoding]();
+ ret = handler();
+ } catch (ex) {
+ console.log("missed " + this._FBU.encoding + ": " + handler);
+ ret = this._encHandlers[this._FBU.encoding]();
+ }
now = (new Date()).getTime();
this._timing.cur_fbu += (now - this._timing.last_fbu);
@@ -1276,64 +1265,111 @@ var RFB;
// Class Methods
RFB.messages = {
- keyEvent: function (keysym, down) {
- var arr = [4];
- arr.push8(down);
- arr.push16(0);
- arr.push32(keysym);
- return arr;
+ keyEvent: function (sock, keysym, down) {
+ var buff = sock._sQ;
+ var offset = sock._sQlen;
+
+ buff[offset] = 4; // msg-type
+ buff[offset + 1] = down;
+
+ buff[offset + 2] = 0;
+ buff[offset + 3] = 0;
+
+ buff[offset + 4] = (keysym >> 24);
+ buff[offset + 5] = (keysym >> 16);
+ buff[offset + 6] = (keysym >> 8);
+ buff[offset + 7] = keysym;
+
+ sock._sQlen += 8;
},
- pointerEvent: function (x, y, mask) {
- var arr = [5]; // msg-type
- arr.push8(mask);
- arr.push16(x);
- arr.push16(y);
- return arr;
+ pointerEvent: function (sock, x, y, mask) {
+ var buff = sock._sQ;
+ var offset = sock._sQlen;
+
+ buff[offset] = 5; // msg-type
+
+ buff[offset + 1] = mask;
+
+ buff[offset + 2] = x >> 8;
+ buff[offset + 3] = x;
+
+ buff[offset + 4] = y >> 8;
+ buff[offset + 5] = y;
+
+ sock._sQlen += 6;
},
// TODO(directxman12): make this unicode compatible?
- clientCutText: function (text) {
- var arr = [6]; // msg-type
- arr.push8(0); // padding
- arr.push8(0); // padding
- arr.push8(0); // padding
- arr.push32(text.length);
+ clientCutText: function (sock, text) {
+ var buff = sock._sQ;
+ var offset = sock._sQlen;
+
+ buff[offset] = 6; // msg-type
+
+ buff[offset + 1] = 0; // padding
+ buff[offset + 2] = 0; // padding
+ buff[offset + 3] = 0; // padding
+
var n = text.length;
+
+ buff[offset + 4] = n >> 24;
+ buff[offset + 5] = n >> 16;
+ buff[offset + 6] = n >> 8;
+ buff[offset + 7] = n;
+
for (var i = 0; i < n; i++) {
- arr.push(text.charCodeAt(i));
+ buff[offset + 8 + i] = text.charCodeAt(i);
}
- return arr;
+ sock._sQlen += 8 + n;
},
- pixelFormat: function (bpp, depth, true_color) {
- var arr = [0]; // msg-type
- arr.push8(0); // padding
- arr.push8(0); // padding
- arr.push8(0); // padding
+ pixelFormat: function (sock, bpp, depth, true_color) {
+ var buff = sock._sQ;
+ var offset = sock._sQlen;
- arr.push8(bpp * 8); // bits-per-pixel
- arr.push8(depth * 8); // depth
- arr.push8(0); // little-endian
- arr.push8(true_color ? 1 : 0); // true-color
+ buff[offset] = 0; // msg-type
- arr.push16(255); // red-max
- arr.push16(255); // green-max
- arr.push16(255); // blue-max
- arr.push8(16); // red-shift
- arr.push8(8); // green-shift
- arr.push8(0); // blue-shift
+ buff[offset + 1] = 0; // padding
+ buff[offset + 2] = 0; // padding
+ buff[offset + 3] = 0; // padding
- arr.push8(0); // padding
- arr.push8(0); // padding
- arr.push8(0); // padding
- return arr;
+ buff[offset + 4] = bpp * 8; // bits-per-pixel
+ buff[offset + 5] = depth * 8; // depth
+ buff[offset + 6] = 0; // little-endian
+ buff[offset + 7] = true_color ? 1 : 0; // true-color
+
+ buff[offset + 8] = 0; // red-max
+ buff[offset + 9] = 255; // red-max
+
+ buff[offset + 10] = 0; // green-max
+ buff[offset + 11] = 255; // green-max
+
+ buff[offset + 12] = 0; // blue-max
+ buff[offset + 13] = 255; // blue-max
+
+ buff[offset + 14] = 16; // red-shift
+ buff[offset + 15] = 8; // green-shift
+ buff[offset + 16] = 0; // blue-shift
+
+ buff[offset + 17] = 0; // padding
+ buff[offset + 18] = 0; // padding
+ buff[offset + 19] = 0; // padding
+
+ sock._sQlen += 20;
},
- clientEncodings: function (encodings, local_cursor, true_color) {
- var i, encList = [];
+ clientEncodings: function (sock, encodings, local_cursor, true_color) {
+ var buff = sock._sQ;
+ var offset = sock._sQlen;
+ buff[offset] = 2; // msg-type
+ buff[offset + 1] = 0; // padding
+
+ // offset + 2 and offset + 3 are encoding count
+
+ var i, j = offset + 4, cnt = 0;
for (i = 0; i < encodings.length; i++) {
if (encodings[i][0] === "Cursor" && !local_cursor) {
Util.Debug("Skipping Cursor pseudo-encoding");
@@ -1341,23 +1377,25 @@ var RFB;
// TODO: remove this when we have tight+non-true-color
Util.Warn("Skipping tight as it is only supported with true color");
} else {
- encList.push(encodings[i][1]);
+ var enc = encodings[i][1];
+ buff[j] = enc >> 24;
+ buff[j + 1] = enc >> 16;
+ buff[j + 2] = enc >> 8;
+ buff[j + 3] = enc;
+
+ j += 4;
+ cnt++;
}
}
- var arr = [2]; // msg-type
- arr.push8(0); // padding
+ buff[offset + 2] = cnt >> 8;
+ buff[offset + 3] = cnt;
- arr.push16(encList.length); // encoding count
- for (i = 0; i < encList.length; i++) {
- arr.push32(encList[i]);
- }
-
- return arr;
+ sock._sQlen += j - offset;
},
- fbUpdateRequests: function (cleanDirty, fb_width, fb_height) {
- var arr = [];
+ fbUpdateRequests: function (sock, cleanDirty, fb_width, fb_height) {
+ var offsetIncrement = 0;
var cb = cleanDirty.cleanBox;
var w, h;
@@ -1365,7 +1403,7 @@ var RFB;
w = typeof cb.w === "undefined" ? fb_width : cb.w;
h = typeof cb.h === "undefined" ? fb_height : cb.h;
// Request incremental for clean box
- arr = arr.concat(RFB.messages.fbUpdateRequest(1, cb.x, cb.y, w, h));
+ RFB.messages.fbUpdateRequest(sock, 1, cb.x, cb.y, w, h);
}
for (var i = 0; i < cleanDirty.dirtyBoxes.length; i++) {
@@ -1373,24 +1411,33 @@ var RFB;
// Force all (non-incremental) for dirty box
w = typeof db.w === "undefined" ? fb_width : db.w;
h = typeof db.h === "undefined" ? fb_height : db.h;
- arr = arr.concat(RFB.messages.fbUpdateRequest(0, db.x, db.y, w, h));
+ RFB.messages.fbUpdateRequest(sock, 0, db.x, db.y, w, h);
}
-
- return arr;
},
- fbUpdateRequest: function (incremental, x, y, w, h) {
+ fbUpdateRequest: function (sock, incremental, x, y, w, h) {
+ var buff = sock._sQ;
+ var offset = sock._sQlen;
+
if (typeof(x) === "undefined") { x = 0; }
if (typeof(y) === "undefined") { y = 0; }
- var arr = [3]; // msg-type
- arr.push8(incremental);
- arr.push16(x);
- arr.push16(y);
- arr.push16(w);
- arr.push16(h);
+ buff[offset] = 3; // msg-type
+ buff[offset + 1] = incremental;
- return arr;
+ buff[offset + 2] = (x >> 8) & 0xFF;
+ buff[offset + 3] = x & 0xFF;
+
+ buff[offset + 4] = (y >> 8) & 0xFF;
+ buff[offset + 5] = y & 0xFF;
+
+ buff[offset + 6] = (w >> 8) & 0xFF;
+ buff[offset + 7] = w & 0xFF;
+
+ buff[offset + 8] = (h >> 8) & 0xFF;
+ buff[offset + 9] = h & 0xFF;
+
+ sock._sQlen += 10;
}
};
@@ -1436,15 +1483,10 @@ var RFB;
COPYRECT: function () {
this._FBU.bytes = 4;
if (this._sock.rQwait("COPYRECT", 4)) { return false; }
- this._display.renderQ_push({
- 'type': 'copy',
- 'old_x': this._sock.rQshift16(),
- 'old_y': this._sock.rQshift16(),
- 'x': this._FBU.x,
- 'y': this._FBU.y,
- 'width': this._FBU.width,
- 'height': this._FBU.height
- });
+ this._display.copyImage(this._sock.rQshift16(), this._sock.rQshift16(),
+ this._FBU.x, this._FBU.y, this._FBU.width,
+ this._FBU.height);
+
this._FBU.rects--;
this._FBU.bytes = 0;
return true;
@@ -1549,11 +1591,21 @@ var RFB;
rQi += this._FBU.bytes - 1;
} else {
if (this._FBU.subencoding & 0x02) { // Background
- this._FBU.background = rQ.slice(rQi, rQi + this._fb_Bpp);
+ if (this._fb_Bpp == 1) {
+ this._FBU.background = rQ[rQi];
+ } else {
+ // fb_Bpp is 4
+ this._FBU.background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
+ }
rQi += this._fb_Bpp;
}
if (this._FBU.subencoding & 0x04) { // Foreground
- this._FBU.foreground = rQ.slice(rQi, rQi + this._fb_Bpp);
+ if (this._fb_Bpp == 1) {
+ this._FBU.foreground = rQ[rQi];
+ } else {
+ // this._fb_Bpp is 4
+ this._FBU.foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
+ }
rQi += this._fb_Bpp;
}
@@ -1565,7 +1617,12 @@ var RFB;
for (var s = 0; s < subrects; s++) {
var color;
if (this._FBU.subencoding & 0x10) { // SubrectsColoured
- color = rQ.slice(rQi, rQi + this._fb_Bpp);
+ if (this._fb_Bpp === 1) {
+ color = rQ[rQi];
+ } else {
+ // _fb_Bpp is 4
+ color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
+ }
rQi += this._fb_Bpp;
} else {
color = this._FBU.foreground;
@@ -1639,61 +1696,96 @@ var RFB;
}
}
- var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0);
- if (uncompressed.status !== 0) {
+ //var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0);
+ var uncompressed = this._FBU.zlibs[streamId].inflate(data, true);
+ /*if (uncompressed.status !== 0) {
Util.Error("Invalid data in zlib stream");
- }
+ }*/
- return uncompressed.data;
+ //return uncompressed.data;
+ return uncompressed;
}.bind(this);
- var indexedToRGB = function (data, numColors, palette, width, height) {
+ var indexedToRGBX2Color = function (data, palette, width, height) {
// Convert indexed (palette based) image data to RGB
// TODO: reduce number of calculations inside loop
- var dest = [];
- var x, y, dp, sp;
- if (numColors === 2) {
- var w = Math.floor((width + 7) / 8);
- var w1 = Math.floor(width / 8);
+ var dest = this._destBuff;
+ var w = Math.floor((width + 7) / 8);
+ var w1 = Math.floor(width / 8);
- for (y = 0; y < height; y++) {
- var b;
- for (x = 0; x < w1; x++) {
- for (b = 7; b >= 0; b--) {
- dp = (y * width + x * 8 + 7 - b) * 3;
- sp = (data[y * w + x] >> b & 1) * 3;
- dest[dp] = palette[sp];
- dest[dp + 1] = palette[sp + 1];
- dest[dp + 2] = palette[sp + 2];
- }
+ /*for (var y = 0; y < height; y++) {
+ var b, x, dp, sp;
+ var yoffset = y * width;
+ var ybitoffset = y * w;
+ var xoffset, targetbyte;
+ for (x = 0; x < w1; x++) {
+ xoffset = yoffset + x * 8;
+ targetbyte = data[ybitoffset + x];
+ for (b = 7; b >= 0; b--) {
+ dp = (xoffset + 7 - b) * 3;
+ sp = (targetbyte >> b & 1) * 3;
+ dest[dp] = palette[sp];
+ dest[dp + 1] = palette[sp + 1];
+ dest[dp + 2] = palette[sp + 2];
}
+ }
- for (b = 7; b >= 8 - width % 8; b--) {
- dp = (y * width + x * 8 + 7 - b) * 3;
+ xoffset = yoffset + x * 8;
+ targetbyte = data[ybitoffset + x];
+ for (b = 7; b >= 8 - width % 8; b--) {
+ dp = (xoffset + 7 - b) * 3;
+ sp = (targetbyte >> b & 1) * 3;
+ dest[dp] = palette[sp];
+ dest[dp + 1] = palette[sp + 1];
+ dest[dp + 2] = palette[sp + 2];
+ }
+ }*/
+
+ for (var y = 0; y < height; y++) {
+ var b, x, dp, sp;
+ for (x = 0; x < w1; x++) {
+ for (b = 7; b >= 0; b--) {
+ dp = (y * width + x * 8 + 7 - b) * 4;
sp = (data[y * w + x] >> b & 1) * 3;
dest[dp] = palette[sp];
dest[dp + 1] = palette[sp + 1];
dest[dp + 2] = palette[sp + 2];
+ dest[dp + 3] = 255;
}
}
- } else {
- for (y = 0; y < height; y++) {
- for (x = 0; x < width; x++) {
- dp = (y * width + x) * 3;
- sp = data[y * width + x] * 3;
- dest[dp] = palette[sp];
- dest[dp + 1] = palette[sp + 1];
- dest[dp + 2] = palette[sp + 2];
- }
+
+ for (b = 7; b >= 8 - width % 8; b--) {
+ dp = (y * width + x * 8 + 7 - b) * 4;
+ sp = (data[y * w + x] >> b & 1) * 3;
+ dest[dp] = palette[sp];
+ dest[dp + 1] = palette[sp + 1];
+ dest[dp + 2] = palette[sp + 2];
+ dest[dp + 3] = 255;
}
}
return dest;
}.bind(this);
+ var indexedToRGBX = function (data, palette, width, height) {
+ // Convert indexed (palette based) image data to RGB
+ var dest = this._destBuff;
+ var total = width * height * 4;
+ for (var i = 0, j = 0; i < total; i += 4, j++) {
+ var sp = data[j] * 3;
+ dest[i] = palette[sp];
+ dest[i + 1] = palette[sp + 1];
+ dest[i + 2] = palette[sp + 2];
+ dest[i + 3] = 255;
+ }
+
+ return dest;
+ }.bind(this);
+
var rQ = this._sock.get_rQ();
var rQi = this._sock.get_rQi();
- var cmode, clength, data;
+ var cmode, data;
+ var cl_header, cl_data;
var handlePalette = function () {
var numColors = rQ[rQi + 2] + 1;
@@ -1706,37 +1798,51 @@ var RFB;
var raw = false;
if (rowSize * this._FBU.height < 12) {
raw = true;
- clength = [0, rowSize * this._FBU.height];
+ cl_header = 0;
+ cl_data = rowSize * this._FBU.height;
+ //clength = [0, rowSize * this._FBU.height];
} else {
- clength = RFB.encodingHandlers.getTightCLength(this._sock.rQslice(3 + paletteSize,
- 3 + paletteSize + 3));
+ // begin inline getTightCLength (returning two-item arrays is bad for performance with GC)
+ var cl_offset = rQi + 3 + paletteSize;
+ cl_header = 1;
+ cl_data = 0;
+ cl_data += rQ[cl_offset] & 0x7f;
+ if (rQ[cl_offset] & 0x80) {
+ cl_header++;
+ cl_data += (rQ[cl_offset + 1] & 0x7f) << 7;
+ if (rQ[cl_offset + 1] & 0x80) {
+ cl_header++;
+ cl_data += rQ[cl_offset + 2] << 14;
+ }
+ }
+ // end inline getTightCLength
}
- this._FBU.bytes += clength[0] + clength[1];
+ this._FBU.bytes += cl_header + cl_data;
if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; }
// Shift ctl, filter id, num colors, palette entries, and clength off
this._sock.rQskipBytes(3);
- var palette = this._sock.rQshiftBytes(paletteSize);
- this._sock.rQskipBytes(clength[0]);
+ //var palette = this._sock.rQshiftBytes(paletteSize);
+ this._sock.rQshiftTo(this._paletteBuff, paletteSize);
+ this._sock.rQskipBytes(cl_header);
if (raw) {
- data = this._sock.rQshiftBytes(clength[1]);
+ data = this._sock.rQshiftBytes(cl_data);
} else {
- data = decompress(this._sock.rQshiftBytes(clength[1]));
+ data = decompress(this._sock.rQshiftBytes(cl_data));
}
// Convert indexed (palette based) image data to RGB
- var rgb = indexedToRGB(data, numColors, palette, this._FBU.width, this._FBU.height);
+ var rgbx;
+ if (numColors == 2) {
+ rgbx = indexedToRGBX2Color(data, this._paletteBuff, this._FBU.width, this._FBU.height);
+ this._display.blitRgbxImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, rgbx, 0, false);
+ } else {
+ rgbx = indexedToRGBX(data, this._paletteBuff, this._FBU.width, this._FBU.height);
+ this._display.blitRgbxImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, rgbx, 0, false);
+ }
- this._display.renderQ_push({
- 'type': 'blitRgb',
- 'data': rgb,
- 'x': this._FBU.x,
- 'y': this._FBU.y,
- 'width': this._FBU.width,
- 'height': this._FBU.height
- });
return true;
}.bind(this);
@@ -1746,30 +1852,37 @@ var RFB;
var uncompressedSize = this._FBU.width * this._FBU.height * this._fb_depth;
if (uncompressedSize < 12) {
raw = true;
- clength = [0, uncompressedSize];
+ cl_header = 0;
+ cl_data = uncompressedSize;
} else {
- clength = RFB.encodingHandlers.getTightCLength(this._sock.rQslice(1, 4));
+ // begin inline getTightCLength (returning two-item arrays is for peformance with GC)
+ var cl_offset = rQi + 1;
+ cl_header = 1;
+ cl_data = 0;
+ cl_data += rQ[cl_offset] & 0x7f;
+ if (rQ[cl_offset] & 0x80) {
+ cl_header++;
+ cl_data += (rQ[cl_offset + 1] & 0x7f) << 7;
+ if (rQ[cl_offset + 1] & 0x80) {
+ cl_header++;
+ cl_data += rQ[cl_offset + 2] << 14;
+ }
+ }
+ // end inline getTightCLength
}
- this._FBU.bytes = 1 + clength[0] + clength[1];
+ this._FBU.bytes = 1 + cl_header + cl_data;
if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; }
// Shift ctl, clength off
- this._sock.rQshiftBytes(1 + clength[0]);
+ this._sock.rQshiftBytes(1 + cl_header);
if (raw) {
- data = this._sock.rQshiftBytes(clength[1]);
+ data = this._sock.rQshiftBytes(cl_data);
} else {
- data = decompress(this._sock.rQshiftBytes(clength[1]));
+ data = decompress(this._sock.rQshiftBytes(cl_data));
}
- this._display.renderQ_push({
- 'type': 'blitRgb',
- 'data': data,
- 'x': this._FBU.x,
- 'y': this._FBU.y,
- 'width': this._FBU.width,
- 'height': this._FBU.height
- });
+ this._display.blitRgbImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, data, 0, false);
return true;
}.bind(this);
@@ -1817,28 +1930,34 @@ var RFB;
// Determine FBU.bytes
switch (cmode) {
case "fill":
- this._sock.rQskip8(); // shift off ctl
- var color = this._sock.rQshiftBytes(this._fb_depth);
- this._display.renderQ_push({
- 'type': 'fill',
- 'x': this._FBU.x,
- 'y': this._FBU.y,
- 'width': this._FBU.width,
- 'height': this._FBU.height,
- 'color': [color[2], color[1], color[0]]
- });
+ // skip ctl byte
+ this._display.fillRect(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, [rQ[rQi + 3], rQ[rQi + 2], rQ[rQi + 1]], false);
+ this._sock.rQskipBytes(4);
break;
case "png":
case "jpeg":
- clength = RFB.encodingHandlers.getTightCLength(this._sock.rQslice(1, 4));
- this._FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data
+ // begin inline getTightCLength (returning two-item arrays is for peformance with GC)
+ var cl_offset = rQi + 1;
+ cl_header = 1;
+ cl_data = 0;
+ cl_data += rQ[cl_offset] & 0x7f;
+ if (rQ[cl_offset] & 0x80) {
+ cl_header++;
+ cl_data += (rQ[cl_offset + 1] & 0x7f) << 7;
+ if (rQ[cl_offset + 1] & 0x80) {
+ cl_header++;
+ cl_data += rQ[cl_offset + 2] << 14;
+ }
+ }
+ // end inline getTightCLength
+ this._FBU.bytes = 1 + cl_header + cl_data; // ctl + clength size + jpeg-data
if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; }
// We have everything, render it
- this._sock.rQskipBytes(1 + clength[0]); // shift off clt + compact length
+ this._sock.rQskipBytes(1 + cl_header); // shift off clt + compact length
var img = new Image();
img.src = "data: image/" + cmode +
- RFB.extract_data_uri(this._sock.rQshiftBytes(clength[1]));
+ RFB.extract_data_uri(this._sock.rQshiftBytes(cl_data));
this._display.renderQ_push({
'type': 'img',
'img': img,
@@ -1881,6 +2000,7 @@ var RFB;
handle_FB_resize: function () {
this._fb_width = this._FBU.width;
this._fb_height = this._FBU.height;
+ this._destBuff = new Uint8Array(this._fb_width * this._fb_height * 4);
this._display.resize(this._fb_width, this._fb_height);
this._onFBResize(this, this._fb_width, this._fb_height);
this._timing.fbu_rt_start = (new Date()).getTime();
@@ -1903,9 +2023,9 @@ var RFB;
this._sock.rQskipBytes(1); // number-of-screens
this._sock.rQskipBytes(3); // padding
- for (var i=0; i
- is released under the MIT License
-*/
-var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab
-// License: New BSD License
-// Reference: http://dev.w3.org/html5/websockets/
-// Reference: http://tools.ietf.org/html/rfc6455
-
-(function() {
-
- if (window.WEB_SOCKET_FORCE_FLASH) {
- // Keeps going.
- } else if (window.WebSocket) {
- return;
- } else if (window.MozWebSocket) {
- // Firefox.
- window.WebSocket = MozWebSocket;
- return;
- }
-
- var logger;
- if (window.WEB_SOCKET_LOGGER) {
- logger = WEB_SOCKET_LOGGER;
- } else if (window.console && window.console.log && window.console.error) {
- // In some environment, console is defined but console.log or console.error is missing.
- logger = window.console;
- } else {
- logger = {log: function(){ }, error: function(){ }};
- }
-
- // swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
- if (swfobject.getFlashPlayerVersion().major < 10) {
- logger.error("Flash Player >= 10.0.0 is required.");
- return;
- }
- if (location.protocol == "file:") {
- logger.error(
- "WARNING: web-socket-js doesn't work in file:///... URL " +
- "unless you set Flash Security Settings properly. " +
- "Open the page via Web server i.e. http://...");
- }
-
- /**
- * Our own implementation of WebSocket class using Flash.
- * @param {string} url
- * @param {array or string} protocols
- * @param {string} proxyHost
- * @param {int} proxyPort
- * @param {string} headers
- */
- window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
- var self = this;
- self.__id = WebSocket.__nextId++;
- WebSocket.__instances[self.__id] = self;
- self.readyState = WebSocket.CONNECTING;
- self.bufferedAmount = 0;
- self.__events = {};
- if (!protocols) {
- protocols = [];
- } else if (typeof protocols == "string") {
- protocols = [protocols];
- }
- // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
- // Otherwise, when onopen fires immediately, onopen is called before it is set.
- self.__createTask = setTimeout(function() {
- WebSocket.__addTask(function() {
- self.__createTask = null;
- WebSocket.__flash.create(
- self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
- });
- }, 0);
- };
-
- /**
- * Send data to the web socket.
- * @param {string} data The data to send to the socket.
- * @return {boolean} True for success, false for failure.
- */
- WebSocket.prototype.send = function(data) {
- if (this.readyState == WebSocket.CONNECTING) {
- throw "INVALID_STATE_ERR: Web Socket connection has not been established";
- }
- // We use encodeURIComponent() here, because FABridge doesn't work if
- // the argument includes some characters. We don't use escape() here
- // because of this:
- // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
- // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
- // preserve all Unicode characters either e.g. "\uffff" in Firefox.
- // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
- // additional testing.
- var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
- if (result < 0) { // success
- return true;
- } else {
- this.bufferedAmount += result;
- return false;
- }
- };
-
- /**
- * Close this web socket gracefully.
- */
- WebSocket.prototype.close = function() {
- if (this.__createTask) {
- clearTimeout(this.__createTask);
- this.__createTask = null;
- this.readyState = WebSocket.CLOSED;
- return;
- }
- if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
- return;
- }
- this.readyState = WebSocket.CLOSING;
- WebSocket.__flash.close(this.__id);
- };
-
- /**
- * Implementation of {@link DOM 2 EventTarget Interface}
- *
- * @param {string} type
- * @param {function} listener
- * @param {boolean} useCapture
- * @return void
- */
- WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
- if (!(type in this.__events)) {
- this.__events[type] = [];
- }
- this.__events[type].push(listener);
- };
-
- /**
- * Implementation of {@link DOM 2 EventTarget Interface}
- *
- * @param {string} type
- * @param {function} listener
- * @param {boolean} useCapture
- * @return void
- */
- WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
- if (!(type in this.__events)) return;
- var events = this.__events[type];
- for (var i = events.length - 1; i >= 0; --i) {
- if (events[i] === listener) {
- events.splice(i, 1);
- break;
- }
- }
- };
-
- /**
- * Implementation of {@link DOM 2 EventTarget Interface}
- *
- * @param {Event} event
- * @return void
- */
- WebSocket.prototype.dispatchEvent = function(event) {
- var events = this.__events[event.type] || [];
- for (var i = 0; i < events.length; ++i) {
- events[i](event);
- }
- var handler = this["on" + event.type];
- if (handler) handler.apply(this, [event]);
- };
-
- /**
- * Handles an event from Flash.
- * @param {Object} flashEvent
- */
- WebSocket.prototype.__handleEvent = function(flashEvent) {
-
- if ("readyState" in flashEvent) {
- this.readyState = flashEvent.readyState;
- }
- if ("protocol" in flashEvent) {
- this.protocol = flashEvent.protocol;
- }
-
- var jsEvent;
- if (flashEvent.type == "open" || flashEvent.type == "error") {
- jsEvent = this.__createSimpleEvent(flashEvent.type);
- } else if (flashEvent.type == "close") {
- jsEvent = this.__createSimpleEvent("close");
- jsEvent.wasClean = flashEvent.wasClean ? true : false;
- jsEvent.code = flashEvent.code;
- jsEvent.reason = flashEvent.reason;
- } else if (flashEvent.type == "message") {
- var data = decodeURIComponent(flashEvent.message);
- jsEvent = this.__createMessageEvent("message", data);
- } else {
- throw "unknown event type: " + flashEvent.type;
- }
-
- this.dispatchEvent(jsEvent);
-
- };
-
- WebSocket.prototype.__createSimpleEvent = function(type) {
- if (document.createEvent && window.Event) {
- var event = document.createEvent("Event");
- event.initEvent(type, false, false);
- return event;
- } else {
- return {type: type, bubbles: false, cancelable: false};
- }
- };
-
- WebSocket.prototype.__createMessageEvent = function(type, data) {
- if (document.createEvent && window.MessageEvent && !window.opera) {
- var event = document.createEvent("MessageEvent");
- event.initMessageEvent("message", false, false, data, null, null, window, null);
- return event;
- } else {
- // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
- return {type: type, data: data, bubbles: false, cancelable: false};
- }
- };
-
- /**
- * Define the WebSocket readyState enumeration.
- */
- WebSocket.CONNECTING = 0;
- WebSocket.OPEN = 1;
- WebSocket.CLOSING = 2;
- WebSocket.CLOSED = 3;
-
- // Field to check implementation of WebSocket.
- WebSocket.__isFlashImplementation = true;
- WebSocket.__initialized = false;
- WebSocket.__flash = null;
- WebSocket.__instances = {};
- WebSocket.__tasks = [];
- WebSocket.__nextId = 0;
-
- /**
- * Load a new flash security policy file.
- * @param {string} url
- */
- WebSocket.loadFlashPolicyFile = function(url){
- WebSocket.__addTask(function() {
- WebSocket.__flash.loadManualPolicyFile(url);
- });
- };
-
- /**
- * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
- */
- WebSocket.__initialize = function() {
-
- if (WebSocket.__initialized) return;
- WebSocket.__initialized = true;
-
- if (WebSocket.__swfLocation) {
- // For backword compatibility.
- window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
- }
- if (!window.WEB_SOCKET_SWF_LOCATION) {
- logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
- return;
- }
- if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
- !WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
- WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
- var swfHost = RegExp.$1;
- if (location.host != swfHost) {
- logger.error(
- "[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
- "('" + location.host + "' != '" + swfHost + "'). " +
- "See also 'How to host HTML file and SWF file in different domains' section " +
- "in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
- "by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
- }
- }
- var container = document.createElement("div");
- container.id = "webSocketContainer";
- // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
- // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
- // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
- // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
- // the best we can do as far as we know now.
- container.style.position = "absolute";
- if (WebSocket.__isFlashLite()) {
- container.style.left = "0px";
- container.style.top = "0px";
- } else {
- container.style.left = "-100px";
- container.style.top = "-100px";
- }
- var holder = document.createElement("div");
- holder.id = "webSocketFlash";
- container.appendChild(holder);
- document.body.appendChild(container);
- // See this article for hasPriority:
- // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
- swfobject.embedSWF(
- WEB_SOCKET_SWF_LOCATION,
- "webSocketFlash",
- "1" /* width */,
- "1" /* height */,
- "10.0.0" /* SWF version */,
- null,
- null,
- {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
- null,
- function(e) {
- if (!e.success) {
- logger.error("[WebSocket] swfobject.embedSWF failed");
- }
- }
- );
-
- };
-
- /**
- * Called by Flash to notify JS that it's fully loaded and ready
- * for communication.
- */
- WebSocket.__onFlashInitialized = function() {
- // We need to set a timeout here to avoid round-trip calls
- // to flash during the initialization process.
- setTimeout(function() {
- WebSocket.__flash = document.getElementById("webSocketFlash");
- WebSocket.__flash.setCallerUrl(location.href);
- WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
- for (var i = 0; i < WebSocket.__tasks.length; ++i) {
- WebSocket.__tasks[i]();
- }
- WebSocket.__tasks = [];
- }, 0);
- };
-
- /**
- * Called by Flash to notify WebSockets events are fired.
- */
- WebSocket.__onFlashEvent = function() {
- setTimeout(function() {
- try {
- // Gets events using receiveEvents() instead of getting it from event object
- // of Flash event. This is to make sure to keep message order.
- // It seems sometimes Flash events don't arrive in the same order as they are sent.
- var events = WebSocket.__flash.receiveEvents();
- for (var i = 0; i < events.length; ++i) {
- WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
- }
- } catch (e) {
- logger.error(e);
- }
- }, 0);
- return true;
- };
-
- // Called by Flash.
- WebSocket.__log = function(message) {
- logger.log(decodeURIComponent(message));
- };
-
- // Called by Flash.
- WebSocket.__error = function(message) {
- logger.error(decodeURIComponent(message));
- };
-
- WebSocket.__addTask = function(task) {
- if (WebSocket.__flash) {
- task();
- } else {
- WebSocket.__tasks.push(task);
- }
- };
-
- /**
- * Test if the browser is running flash lite.
- * @return {boolean} True if flash lite is running, false otherwise.
- */
- WebSocket.__isFlashLite = function() {
- if (!window.navigator || !window.navigator.mimeTypes) {
- return false;
- }
- var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
- if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
- return false;
- }
- return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
- };
-
- if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
- // NOTE:
- // This fires immediately if web_socket.js is dynamically loaded after
- // the document is loaded.
- swfobject.addDomLoadEvent(function() {
- WebSocket.__initialize();
- });
- }
-
-})();
diff --git a/include/websock.js b/include/websock.js
index cc82e5a2..61d94672 100644
--- a/include/websock.js
+++ b/include/websock.js
@@ -15,7 +15,7 @@
*/
/*jslint browser: true, bitwise: true */
-/*global Util, Base64 */
+/*global Util*/
// Load Flash WebSocket emulator if needed
@@ -34,29 +34,26 @@ if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
/* no builtin WebSocket so load web_socket.js */
Websock_native = false;
- (function () {
- window.WEB_SOCKET_SWF_LOCATION = Util.get_include_uri() +
- "web-socket-js/WebSocketMain.swf";
- if (Util.Engine.trident) {
- Util.Debug("Forcing uncached load of WebSocketMain.swf");
- window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
- }
- Util.load_scripts(["web-socket-js/swfobject.js",
- "web-socket-js/web_socket.js"]);
- })();
}
-
function Websock() {
"use strict";
this._websocket = null; // WebSocket object
- this._rQ = []; // Receive queue
- this._rQi = 0; // Receive queue index
- this._rQmax = 10000; // Max receive queue size before compacting
- this._sQ = []; // Send queue
- this._mode = 'base64'; // Current WebSocket mode: 'binary', 'base64'
+ this._rQi = 0; // Receive queue index
+ this._rQlen = 0; // Next write position in the receive queue
+ this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
+ this._rQmax = this._rQbufferSize / 8;
+ // called in init: this._rQ = new Uint8Array(this._rQbufferSize);
+ this._rQ = null; // Receive queue
+
+ this._sQbufferSize = 1024 * 10; // 10 KiB
+ // called in init: this._sQ = new Uint8Array(this._sQbufferSize);
+ this._sQlen = 0;
+ this._sQ = null; // Send queue
+
+ this._mode = 'binary'; // Current WebSocket mode: 'binary', 'base64'
this.maxBufferedAmount = 200;
this._eventHandlers = {
@@ -69,6 +66,22 @@ function Websock() {
(function () {
"use strict";
+
+ var typedArrayToString = (function () {
+ // This is only for PhantomJS, which doesn't like apply-ing
+ // with Typed Arrays
+ try {
+ var arr = new Uint8Array([1, 2, 3]);
+ String.fromCharCode.apply(null, arr);
+ return function (a) { return String.fromCharCode.apply(null, a); };
+ } catch (ex) {
+ return function (a) {
+ return String.fromCharCode.apply(
+ null, Array.prototype.slice.call(a));
+ };
+ }
+ })();
+
Websock.prototype = {
// Getters and Setters
get_sQ: function () {
@@ -89,7 +102,7 @@ function Websock() {
// Receive Queue
rQlen: function () {
- return this._rQ.length - this._rQi;
+ return this._rQlen - this._rQi;
},
rQpeek8: function () {
@@ -108,15 +121,7 @@ function Websock() {
this._rQi += num;
},
- rQunshift8: function (num) {
- if (this._rQi === 0) {
- this._rQ.unshift(num);
- } else {
- this._rQi--;
- this._rQ[this._rQi] = num;
- }
- },
-
+ // TODO(directxman12): test performance with these vs a DataView
rQshift16: function () {
return (this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
@@ -131,22 +136,29 @@ function Websock() {
rQshiftStr: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
- var arr = this._rQ.slice(this._rQi, this._rQi + len);
+ var arr = new Uint8Array(this._rQ.buffer, this._rQi, len);
this._rQi += len;
- return String.fromCharCode.apply(null, arr);
+ return typedArrayToString(arr);
},
rQshiftBytes: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
this._rQi += len;
- return this._rQ.slice(this._rQi - len, this._rQi);
+ return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
+ },
+
+ rQshiftTo: function (target, len) {
+ if (len === undefined) { len = this.rQlen(); }
+ // TODO: make this just use set with views when using a ArrayBuffer to store the rQ
+ target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
+ this._rQi += len;
},
rQslice: function (start, end) {
if (end) {
- return this._rQ.slice(this._rQi + start, this._rQi + end);
+ return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
} else {
- return this._rQ.slice(this._rQi + start);
+ return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start);
}
},
@@ -154,7 +166,7 @@ function Websock() {
// to be available in the receive queue. Return true if we need to
// wait (and possibly print a debug message), otherwise false.
rQwait: function (msg, num, goback) {
- var rQlen = this._rQ.length - this._rQi; // Skip rQlen() function call
+ var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call
if (rQlen < num) {
if (goback) {
if (this._rQi < goback) {
@@ -175,9 +187,9 @@ function Websock() {
}
if (this._websocket.bufferedAmount < this.maxBufferedAmount) {
- if (this._sQ.length > 0) {
+ if (this._sQlen > 0) {
this._websocket.send(this._encode_message());
- this._sQ = [];
+ this._sQlen = 0;
}
return true;
@@ -189,8 +201,9 @@ function Websock() {
},
send: function (arr) {
- this._sQ = this._sQ.concat(arr);
- return this.flush();
+ this._sQ.set(arr, this._sQlen);
+ this._sQlen += arr.length;
+ return this.flush();
},
send_string: function (str) {
@@ -208,10 +221,14 @@ function Websock() {
this._eventHandlers[evt] = handler;
},
+ _allocate_buffers: function () {
+ this._rQ = new Uint8Array(this._rQbufferSize);
+ this._sQ = new Uint8Array(this._sQbufferSize);
+ },
+
init: function (protocols, ws_schema) {
- this._rQ = [];
+ this._allocate_buffers();
this._rQi = 0;
- this._sQ = [];
this._websocket = null;
// Check for full typed array support
@@ -238,35 +255,21 @@ function Websock() {
// Default protocols if not specified
if (typeof(protocols) === "undefined") {
- if (wsbt) {
- protocols = ['binary', 'base64'];
- } else {
- protocols = 'base64';
- }
+ protocols = 'binary';
+ }
+
+ if (Array.isArray(protocols) && protocols.indexOf('binary') > -1) {
+ protocols = 'binary';
}
if (!wsbt) {
- if (protocols === 'binary') {
- throw new Error('WebSocket binary sub-protocol requested but not supported');
- }
+ throw new Error("noVNC no longer supports base64 WebSockets. " +
+ "Please use a browser which supports binary WebSockets.");
+ }
- if (typeof(protocols) === 'object') {
- var new_protocols = [];
-
- for (var i = 0; i < protocols.length; i++) {
- if (protocols[i] === 'binary') {
- Util.Error('Skipping unsupported WebSocket binary sub-protocol');
- } else {
- new_protocols.push(protocols[i]);
- }
- }
-
- if (new_protocols.length > 0) {
- protocols = new_protocols;
- } else {
- throw new Error("Only WebSocket binary sub-protocol was requested and is not supported.");
- }
- }
+ if (protocols != 'binary') {
+ throw new Error("noVNC no longer supports base64 WebSockets. Please " +
+ "use the binary subprotocol instead.");
}
return protocols;
@@ -289,9 +292,16 @@ function Websock() {
this._mode = this._websocket.protocol;
Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
} else {
- this._mode = 'base64';
+ this._mode = 'binary';
Util.Error('Server select no sub-protocol!: ' + this._websocket.protocol);
}
+
+ if (this._mode != 'binary') {
+ throw new Error("noVNC no longer supports base64 WebSockets. Please " +
+ "use the binary subprotocol instead.");
+
+ }
+
this._eventHandlers.open();
Util.Debug("<< WebSock.onopen");
}).bind(this);
@@ -321,26 +331,16 @@ function Websock() {
// private methods
_encode_message: function () {
- if (this._mode === 'binary') {
- // Put in a binary arraybuffer
- return (new Uint8Array(this._sQ)).buffer;
- } else {
- // base64 encode
- return Base64.encode(this._sQ);
- }
+ // Put in a binary arraybuffer
+ // according to the spec, you can send ArrayBufferViews with the send method
+ return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
},
_decode_message: function (data) {
- if (this._mode === 'binary') {
- // push arraybuffer values onto the end
- var u8 = new Uint8Array(data);
- for (var i = 0; i < u8.length; i++) {
- this._rQ.push(u8[i]);
- }
- } else {
- // base64 decode and concat to end
- this._rQ = this._rQ.concat(Base64.decode(data, 0));
- }
+ // push arraybuffer values onto the end
+ var u8 = new Uint8Array(data);
+ this._rQ.set(u8, this._rQlen);
+ this._rQlen += u8.length;
},
_recv_message: function (e) {
@@ -349,8 +349,26 @@ function Websock() {
if (this.rQlen() > 0) {
this._eventHandlers.message();
// Compact the receive queue
- if (this._rQ.length > this._rQmax) {
- this._rQ = this._rQ.slice(this._rQi);
+ if (this._rQlen == this._rQi) {
+ this._rQlen = 0;
+ this._rQi = 0;
+ } else if (this._rQlen > this._rQmax) {
+ if (this._rQlen - this._rQi > 0.5 * this._rQbufferSize) {
+ var old_rQbuffer = this._rQ.buffer;
+ this._rQbufferSize *= 2;
+ this._rQmax = this._rQbufferSize / 8;
+ this._rQ = new Uint8Array(this._rQbufferSize);
+ this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
+ } else {
+ if (this._rQ.copyWithin) {
+ // Firefox only, ATM
+ this._rQ.copyWithin(0, this._rQi);
+ } else {
+ this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
+ }
+ }
+
+ this._rQlen = this._rQlen - this._rQi;
this._rQi = 0;
}
} else {
diff --git a/karma.conf.js b/karma.conf.js
index d8b8e905..870b8551 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -119,9 +119,9 @@ module.exports = function(config) {
'include/input.js',
'include/websock.js',
'include/rfb.js',
- 'include/jsunzip.js',
'include/des.js',
'include/display.js',
+ 'include/inflator.js',
'tests/test.*.js'
],
diff --git a/tests/assertions.js b/tests/assertions.js
index 92b11d1f..930e1460 100644
--- a/tests/assertions.js
+++ b/tests/assertions.js
@@ -5,7 +5,17 @@ chai.use(function (_chai, utils) {
var data_cl = obj._drawCtx.getImageData(0, 0, obj._viewportLoc.w, obj._viewportLoc.h).data;
// NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray, so work around that
var data = new Uint8Array(data_cl);
- this.assert(utils.eql(data, target_data),
+ var same = true;
+ for (var i = 0; i < obj.length; i++) {
+ if (data[i] != target_data[i]) {
+ same = false;
+ break;
+ }
+ }
+ if (!same) {
+ console.log("expected data: %o, actual data: %o", target_data, data);
+ }
+ this.assert(same,
"expected #{this} to have displayed the image #{exp}, but instead it displayed #{act}",
"expected #{this} not to have displayed the image #{act}",
target_data,
@@ -14,11 +24,70 @@ chai.use(function (_chai, utils) {
_chai.Assertion.addMethod('sent', function (target_data) {
var obj = this._obj;
+ obj.inspect = function () {
+ var res = { _websocket: obj._websocket, rQi: obj._rQi, _rQ: new Uint8Array(obj._rQ.buffer, 0, obj._rQlen),
+ _sQ: new Uint8Array(obj._sQ.buffer, 0, obj._sQlen) };
+ res.prototype = obj;
+ return res;
+ };
var data = obj._websocket._get_sent_data();
- this.assert(utils.eql(data, target_data),
+ var same = true;
+ for (var i = 0; i < obj.length; i++) {
+ if (data[i] != target_data[i]) {
+ same = false;
+ break;
+ }
+ }
+ if (!same) {
+ console.log("expected data: %o, actual data: %o", target_data, data);
+ }
+ this.assert(same,
"expected #{this} to have sent the data #{exp}, but it actually sent #{act}",
"expected #{this} not to have sent the data #{act}",
- target_data,
- data);
+ Array.prototype.slice.call(target_data),
+ Array.prototype.slice.call(data));
+ });
+
+ _chai.Assertion.addProperty('array', function () {
+ utils.flag(this, 'array', true);
+ });
+
+ _chai.Assertion.overwriteMethod('equal', function (_super) {
+ return function assertArrayEqual(target) {
+ if (utils.flag(this, 'array')) {
+ var obj = this._obj;
+
+ var i;
+ var same = true;
+
+ if (utils.flag(this, 'deep')) {
+ for (i = 0; i < obj.length; i++) {
+ if (!utils.eql(obj[i], target[i])) {
+ same = false;
+ break;
+ }
+ }
+
+ this.assert(same,
+ "expected #{this} to have elements deeply equal to #{exp}",
+ "expected #{this} not to have elements deeply equal to #{exp}",
+ Array.prototype.slice.call(target));
+ } else {
+ for (i = 0; i < obj.length; i++) {
+ if (obj[i] != target[i]) {
+ same = false;
+ break;
+ }
+ }
+
+ this.assert(same,
+ "expected #{this} to have elements equal to #{exp}",
+ "expected #{this} not to have elements equal to #{exp}",
+ Array.prototype.slice.call(target));
+ }
+ } else {
+ _super.apply(this, arguments);
+ }
+ };
});
});
diff --git a/tests/fake.websocket.js b/tests/fake.websocket.js
index 749c0eaf..21012059 100644
--- a/tests/fake.websocket.js
+++ b/tests/fake.websocket.js
@@ -51,14 +51,9 @@ var FakeWebSocket;
},
_get_sent_data: function () {
- var arr = [];
- for (var i = 0; i < this.bufferedAmount; i++) {
- arr[i] = this._send_queue[i];
- }
-
+ var res = new Uint8Array(this._send_queue.buffer, 0, this.bufferedAmount);
this.bufferedAmount = 0;
-
- return arr;
+ return res;
},
_open: function (data) {
diff --git a/tests/run_from_console.casper.js b/tests/run_from_console.casper.js
index 57ed2be2..6a738a3e 100644
--- a/tests/run_from_console.casper.js
+++ b/tests/run_from_console.casper.js
@@ -15,7 +15,19 @@ var casper_opts = {
}
};
-var provide_emitter = function(file_paths) {
+var provide_emitter = function(file_paths, debug_port) {
+ if (debug_port) {
+ casper_opts.child['remote-debugger-port'] = debug_port;
+ var debug_url = ('https://localhost:' + debug_port +
+ '/webkit/inspector/inspector.html?page=');
+ console.info('[remote-debugger] Navigate to ' + debug_url + '1 and ' +
+ 'run `__run();` in the console to continue loading.' +
+ '\n[remote-debugger] Navigate to ' + debug_url + '2 to ' +
+ 'view the actual page source.\n' +
+ '[remote-debugger] Use the `debugger;` statement to ' +
+ 'trigger an initial breakpoint.');
+ }
+
var spooky = new Spooky(casper_opts, function(err) {
if (err) {
if (err.stack) console.warn(err.stack);
diff --git a/tests/run_from_console.js b/tests/run_from_console.js
index 2a5bb70b..371e861a 100755
--- a/tests/run_from_console.js
+++ b/tests/run_from_console.js
@@ -20,6 +20,7 @@ program
.option('--output-html', 'Instead of running the tests, just output the generated HTML source to STDOUT (should be used with .js tests)')
.option('-d, --debug', 'Show debug output (the "console" event) from the provider')
.option('-r, --relative', 'Use relative paths in the generated HTML file')
+ .option('--debugger ', 'Enable the remote debugger for CasperJS')
.parse(process.argv);
if (program.tests.length === 0) {
@@ -202,7 +203,7 @@ if (!program.outputHtml && !program.generateHtml) {
.write("\n");
//console.log("Running tests %s using provider %s", program.tests.join(', '), prov.name);
- var provider = prov.provide_emitter(file_paths);
+ var provider = prov.provide_emitter(file_paths, program.debugger);
provider.on('test_ready', function(test_json) {
console.log('');
@@ -249,6 +250,24 @@ if (!program.outputHtml && !program.generateHtml) {
console.log('');
if (test_json.num_fails > 0 || program.printAll) {
+ var extract_error_lines = function (err) {
+ // the split is to avoid a weird thing where in PhantomJS where we get a stack trace too
+ var err_lines = err.split('\n');
+ if (err_lines.length == 1) {
+ return err_lines[0];
+ } else {
+ var ind;
+ for (ind = 0; ind < err_lines.length; ind++) {
+ var at_ind = err_lines[ind].trim().indexOf('at ');
+ if (at_ind === 0) {
+ break;
+ }
+ }
+
+ return err_lines.slice(0, ind).join('\n');
+ }
+ };
+
var traverse_tree = function(indentation, node) {
if (node.type == 'suite') {
if (!node.has_subfailures && !program.printAll) return;
@@ -280,7 +299,7 @@ if (!program.outputHtml && !program.generateHtml) {
cursor.magenta();
console.log('- failed: '+node.text+test_json.replay);
cursor.red();
- console.log(' '+node.error.split("\n")[0]); // the split is to avoid a weird thing where in PhantomJS where we get a stack trace too
+ console.log(' '+extract_error_lines(node.error));
cursor.reset();
console.log('');
}
diff --git a/tests/test.rfb.js b/tests/test.rfb.js
index 006b5fa9..961d9eb5 100644
--- a/tests/test.rfb.js
+++ b/tests/test.rfb.js
@@ -1,4 +1,4 @@
-// requires local modules: util, base64, websock, rfb, keyboard, keysym, keysymdef, input, jsunzip, des, display
+// requires local modules: util, websock, rfb, keyboard, keysym, keysymdef, input, inflator, des, display
// requires test modules: fake.websocket, assertions
/* jshint expr: true */
var assert = chai.assert;
@@ -18,6 +18,27 @@ describe('Remote Frame Buffer Protocol Client', function() {
before(FakeWebSocket.replace);
after(FakeWebSocket.restore);
+ before(function () {
+ this.clock = sinon.useFakeTimers();
+ // Use a single set of buffers instead of reallocating to
+ // speed up tests
+ var sock = new Websock();
+ var _sQ = new Uint8Array(sock._sQbufferSize);
+ var rQ = new Uint8Array(sock._rQbufferSize);
+
+ Websock.prototype._old_allocate_buffers = Websock.prototype._allocate_buffers;
+ Websock.prototype._allocate_buffers = function () {
+ this._sQ = _sQ;
+ this._rQ = rQ;
+ };
+
+ });
+
+ after(function () {
+ Websock.prototype._allocate_buffers = Websock.prototype._old_allocate_buffers;
+ this.clock.restore();
+ });
+
describe('Public API Basic Behavior', function () {
var client;
beforeEach(function () {
@@ -105,34 +126,34 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
- sinon.spy(client._sock, 'send');
+ sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
});
it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
- var expected = [];
- expected = expected.concat(RFB.messages.keyEvent(0xFFE3, 1));
- expected = expected.concat(RFB.messages.keyEvent(0xFFE9, 1));
- expected = expected.concat(RFB.messages.keyEvent(0xFFFF, 1));
- expected = expected.concat(RFB.messages.keyEvent(0xFFFF, 0));
- expected = expected.concat(RFB.messages.keyEvent(0xFFE9, 0));
- expected = expected.concat(RFB.messages.keyEvent(0xFFE3, 0));
+ var expected = {_sQ: new Uint8Array(48), _sQlen: 0};
+ RFB.messages.keyEvent(expected, 0xFFE3, 1);
+ RFB.messages.keyEvent(expected, 0xFFE9, 1);
+ RFB.messages.keyEvent(expected, 0xFFFF, 1);
+ RFB.messages.keyEvent(expected, 0xFFFF, 0);
+ RFB.messages.keyEvent(expected, 0xFFE9, 0);
+ RFB.messages.keyEvent(expected, 0xFFE3, 0);
client.sendCtrlAltDel();
- expect(client._sock).to.have.sent(expected);
+ expect(client._sock).to.have.sent(expected._sQ);
});
it('should not send the keys if we are not in a normal state', function () {
client._rfb_state = "broken";
client.sendCtrlAltDel();
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
it('should not send the keys if we are set as view_only', function () {
client._view_only = true;
client.sendCtrlAltDel();
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
});
@@ -141,34 +162,36 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
- sinon.spy(client._sock, 'send');
+ sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
});
it('should send a single key with the given code and state (down = true)', function () {
- var expected = RFB.messages.keyEvent(123, 1);
+ var expected = {_sQ: new Uint8Array(8), _sQlen: 0};
+ RFB.messages.keyEvent(expected, 123, 1);
client.sendKey(123, true);
- expect(client._sock).to.have.sent(expected);
+ expect(client._sock).to.have.sent(expected._sQ);
});
it('should send both a down and up event if the state is not specified', function () {
- var expected = RFB.messages.keyEvent(123, 1);
- expected = expected.concat(RFB.messages.keyEvent(123, 0));
+ var expected = {_sQ: new Uint8Array(16), _sQlen: 0};
+ RFB.messages.keyEvent(expected, 123, 1);
+ RFB.messages.keyEvent(expected, 123, 0);
client.sendKey(123);
- expect(client._sock).to.have.sent(expected);
+ expect(client._sock).to.have.sent(expected._sQ);
});
it('should not send the key if we are not in a normal state', function () {
client._rfb_state = "broken";
client.sendKey(123);
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
it('should not send the key if we are set as view_only', function () {
client._view_only = true;
client.sendKey(123);
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
});
@@ -177,21 +200,22 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
- sinon.spy(client._sock, 'send');
+ sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
});
it('should send the given text in a paste event', function () {
- var expected = RFB.messages.clientCutText('abc');
+ var expected = {_sQ: new Uint8Array(11), _sQlen: 0};
+ RFB.messages.clientCutText(expected, 'abc');
client.clipboardPasteFrom('abc');
- expect(client._sock).to.have.sent(expected);
+ expect(client._sock).to.have.sent(expected._sQ);
});
it('should not send the text if we are not in a normal state', function () {
client._rfb_state = "broken";
client.clipboardPasteFrom('abc');
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
});
@@ -200,7 +224,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
- sinon.spy(client._sock, 'send');
+ sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
client._supportsSetDesktopSize = true;
@@ -221,19 +245,19 @@ describe('Remote Frame Buffer Protocol Client', function() {
expected.push32(0); // flags
client.setDesktopSize(1, 2);
- expect(client._sock).to.have.sent(expected);
+ expect(client._sock).to.have.sent(new Uint8Array(expected));
});
it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
client._supportsSetDesktopSize = false;
client.setDesktopSize(1,2);
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
it('should not send the request if we are not in a normal state', function () {
client._rfb_state = "broken";
client.setDesktopSize(1,2);
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
});
@@ -242,7 +266,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock = new Websock();
client._sock.open('ws://', 'binary');
client._sock._websocket._open();
- sinon.spy(client._sock, 'send');
+ sinon.spy(client._sock, 'flush');
client._rfb_state = "normal";
client._view_only = false;
client._rfb_xvp_ver = 1;
@@ -250,27 +274,27 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should send the shutdown signal on #xvpShutdown', function () {
client.xvpShutdown();
- expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x02]);
+ expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
});
it('should send the reboot signal on #xvpReboot', function () {
client.xvpReboot();
- expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x03]);
+ expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
});
it('should send the reset signal on #xvpReset', function () {
client.xvpReset();
- expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x04]);
+ expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
});
it('should support sending arbitrary XVP operations via #xvpOp', function () {
client.xvpOp(1, 7);
- expect(client._sock).to.have.sent([0xFA, 0x00, 0x01, 0x07]);
+ expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x07]));
});
it('should not send XVP operations with higher versions than we support', function () {
expect(client.xvpOp(2, 7)).to.be.false;
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
});
});
@@ -483,7 +507,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
expect(client._rfb_version).to.equal(0);
var sent_data = client._sock._websocket._get_sent_data();
- expect(sent_data.slice(0, 5)).to.deep.equal([1, 2, 3, 4, 5]);
+ expect(new Uint8Array(sent_data.buffer, 0, 5)).to.array.equal(new Uint8Array([1, 2, 3, 4, 5]));
});
it('should interpret version 003.003 as version 3.3', function () {
@@ -540,7 +564,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
send_ver('000.000', client);
expect(client._rfb_version).to.equal(0);
var sent_data = client._sock._websocket._get_sent_data();
- expect(sent_data.slice(0, 5)).to.deep.equal([1, 2, 3, 4, 5]);
+ expect(new Uint8Array(sent_data.buffer, 0, 5)).to.array.equal(new Uint8Array([1, 2, 3, 4, 5]));
expect(sent_data).to.have.length(250);
send_ver('003.008', client);
@@ -563,7 +587,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
expected[i] = expected_str.charCodeAt(i);
}
- expect(client._sock).to.have.sent(expected);
+ expect(client._sock).to.have.sent(new Uint8Array(expected));
});
it('should transition to the Security state on successful negotiation', function () {
@@ -596,7 +620,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
var auth_schemes = [2, 1, 2];
client._sock._websocket._receive_data(auth_schemes);
expect(client._rfb_auth_scheme).to.equal(2);
- expect(client._sock).to.have.sent([2]);
+ expect(client._sock).to.have.sent(new Uint8Array([2]));
});
it('should fail if there are no supported schemes for versions >= 3.7', function () {
@@ -702,7 +726,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._sock._websocket._receive_data(new Uint8Array(challenge));
var des_pass = RFB.genDES('passwd', challenge);
- expect(client._sock).to.have.sent(des_pass);
+ expect(client._sock).to.have.sent(new Uint8Array(des_pass));
});
it('should transition to SecurityResult immediately after sending the password', function () {
@@ -759,7 +783,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
var expected = [22, 4, 6]; // auth selection, len user, len target
for (var i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); }
- expect(client._sock).to.have.sent(expected);
+ expect(client._sock).to.have.sent(new Uint8Array(expected));
});
});
@@ -807,14 +831,14 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should choose the notunnel tunnel type', function () {
send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client);
- expect(client._sock).to.have.sent([0, 0, 0, 0]);
+ expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 0]));
});
it('should continue to sub-auth negotiation after tunnel negotiation', function () {
send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client);
client._sock._websocket._get_sent_data(); // skip the tunnel choice here
send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
- expect(client._sock).to.have.sent([0, 0, 0, 1]);
+ expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
expect(client._rfb_state).to.equal('SecurityResult');
});
@@ -830,7 +854,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should accept the "no auth" auth type and transition to SecurityResult', function () {
client._rfb_tightvnc = true;
send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
- expect(client._sock).to.have.sent([0, 0, 0, 1]);
+ expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
expect(client._rfb_state).to.equal('SecurityResult');
});
@@ -838,7 +862,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._rfb_tightvnc = true;
client._negotiate_std_vnc_auth = sinon.spy();
send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client);
- expect(client._sock).to.have.sent([0, 0, 0, 2]);
+ expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 2]));
expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
expect(client._rfb_auth_scheme).to.equal(2);
});
@@ -902,13 +926,13 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should send 1 if we are in shared mode', function () {
client.set_shared(true);
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
- expect(client._sock).to.have.sent([1]);
+ expect(client._sock).to.have.sent(new Uint8Array([1]));
});
it('should send 0 if we are not in shared mode', function () {
client.set_shared(false);
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
- expect(client._sock).to.have.sent([0]);
+ expect(client._sock).to.have.sent(new Uint8Array([0]));
});
});
@@ -1045,21 +1069,16 @@ describe('Remote Frame Buffer Protocol Client', function() {
it('should reply with the pixel format, client encodings, and initial update request', function () {
client.set_true_color(true);
client.set_local_cursor(false);
- var expected = RFB.messages.pixelFormat(4, 3, true);
- expected = expected.concat(RFB.messages.clientEncodings(client._encodings, false, true));
+ // we skip the cursor encoding
+ var expected = {_sQ: new Uint8Array(34 + 4 * (client._encodings.length - 1)), _sQlen: 0};
+ RFB.messages.pixelFormat(expected, 4, 3, true);
+ RFB.messages.clientEncodings(expected, client._encodings, false, true);
var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
dirtyBoxes: [ { x: 0, y: 0, w: 27, h: 32 } ] };
- expected = expected.concat(RFB.messages.fbUpdateRequests(expected_cdr, 27, 32));
+ RFB.messages.fbUpdateRequests(expected, expected_cdr, 27, 32);
send_server_init({ width: 27, height: 32 }, client);
- expect(client._sock).to.have.sent(expected);
- });
-
- it('should check for sending mouse events', function () {
- // be lazy with our checking so we don't have to check through the whole sent buffer
- sinon.spy(client, '_checkEvents');
- send_server_init({}, client);
- expect(client._checkEvents).to.have.been.calledOnce;
+ expect(client._sock).to.have.sent(expected._sQ);
});
it('should transition to the "normal" state', function () {
@@ -1142,14 +1161,15 @@ describe('Remote Frame Buffer Protocol Client', function() {
}
it('should send an update request if there is sufficient data', function () {
+ var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0};
var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
dirtyBoxes: [ { x: 0, y: 0, w: 240, h: 20 } ] };
- var expected_msg = RFB.messages.fbUpdateRequests(expected_cdr, 240, 20);
+ RFB.messages.fbUpdateRequests(expected_msg, expected_cdr, 240, 20);
client._framebufferUpdate = function () { return true; };
client._sock._websocket._receive_data(new Uint8Array([0]));
- expect(client._sock).to.have.sent(expected_msg);
+ expect(client._sock).to.have.sent(expected_msg._sQ);
});
it('should not send an update request if we need more data', function () {
@@ -1158,9 +1178,10 @@ describe('Remote Frame Buffer Protocol Client', function() {
});
it('should resume receiving an update if we previously did not have enough data', function () {
+ var expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0};
var expected_cdr = { cleanBox: { x: 0, y: 0, w: 0, h: 0 },
dirtyBoxes: [ { x: 0, y: 0, w: 240, h: 20 } ] };
- var expected_msg = RFB.messages.fbUpdateRequests(expected_cdr, 240, 20);
+ RFB.messages.fbUpdateRequests(expected_msg, expected_cdr, 240, 20);
// just enough to set FBU.rects
client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 3]));
@@ -1169,7 +1190,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._framebufferUpdate = function () { return true; }; // we magically have enough data
// 247 should *not* be used as the message type here
client._sock._websocket._receive_data(new Uint8Array([247]));
- expect(client._sock).to.have.sent(expected_msg);
+ expect(client._sock).to.have.sent(expected_msg._sQ);
});
it('should parse out information from a header before any actual data comes in', function () {
@@ -1691,58 +1712,61 @@ describe('Remote Frame Buffer Protocol Client', function() {
var client;
beforeEach(function () {
client = make_rfb();
- client._sock.send = sinon.spy();
+ client._sock = new Websock();
+ client._sock.open('ws://', 'binary');
+ client._sock._websocket._open();
+ sinon.spy(client._sock, 'flush');
client._rfb_state = 'normal';
});
it('should not send button messages in view-only mode', function () {
client._view_only = true;
client._mouse._onMouseButton(0, 0, 1, 0x001);
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
it('should not send movement messages in view-only mode', function () {
client._view_only = true;
client._mouse._onMouseMove(0, 0);
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
it('should send a pointer event on mouse button presses', function () {
client._mouse._onMouseButton(10, 12, 1, 0x001);
- expect(client._sock.send).to.have.been.calledOnce;
- var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x001);
- expect(client._sock.send).to.have.been.calledWith(pointer_msg);
+ var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
+ RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x001);
+ expect(client._sock).to.have.sent(pointer_msg._sQ);
});
it('should send a mask of 1 on mousedown', function () {
client._mouse._onMouseButton(10, 12, 1, 0x001);
- expect(client._sock.send).to.have.been.calledOnce;
- var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x001);
- expect(client._sock.send).to.have.been.calledWith(pointer_msg);
+ var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
+ RFB.messages.pointerEvent(pointer_msg, 0, 10, 12, 0x001);
+ expect(client._sock).to.have.sent(pointer_msg._sQ);
});
it('should send a mask of 0 on mouseup', function () {
client._mouse_buttonMask = 0x001;
client._mouse._onMouseButton(10, 12, 0, 0x001);
- expect(client._sock.send).to.have.been.calledOnce;
- var pointer_msg = RFB.messages.pointerEvent(10, 12, 0x000);
- expect(client._sock.send).to.have.been.calledWith(pointer_msg);
+ var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
+ RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
+ expect(client._sock).to.have.sent(pointer_msg._sQ);
});
it('should send a pointer event on mouse movement', function () {
client._mouse._onMouseMove(10, 12);
- expect(client._sock.send).to.have.been.calledOnce;
- var pointer_msg = RFB.messages.pointerEvent(10, 12, 0);
- expect(client._sock.send).to.have.been.calledWith(pointer_msg);
+ var pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0};
+ RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
+ expect(client._sock).to.have.sent(pointer_msg._sQ);
});
it('should set the button mask so that future mouse movements use it', function () {
client._mouse._onMouseButton(10, 12, 1, 0x010);
- client._sock.send = sinon.spy();
client._mouse._onMouseMove(13, 9);
- expect(client._sock.send).to.have.been.calledOnce;
- var pointer_msg = RFB.messages.pointerEvent(13, 9, 0x010);
- expect(client._sock.send).to.have.been.calledWith(pointer_msg);
+ var pointer_msg = {_sQ: new Uint8Array(12), _sQlen: 0};
+ RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x010);
+ RFB.messages.pointerEvent(pointer_msg, 13, 9, 0x010);
+ expect(client._sock).to.have.sent(pointer_msg._sQ);
});
// NB(directxman12): we don't need to test not sending messages in
@@ -1753,13 +1777,13 @@ describe('Remote Frame Buffer Protocol Client', function() {
client._viewportDragging = true;
client._display.viewportChangePos = sinon.spy();
client._mouse._onMouseMove(13, 9);
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
it('should not send button messages when initiating viewport dragging', function () {
client._viewportDrag = true;
client._mouse._onMouseButton(13, 9, 0x001);
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
it('should be initiate viewport dragging on a button down event, if enabled', function () {
@@ -1795,20 +1819,23 @@ describe('Remote Frame Buffer Protocol Client', function() {
var client;
beforeEach(function () {
client = make_rfb();
- client._sock.send = sinon.spy();
+ client._sock = new Websock();
+ client._sock.open('ws://', 'binary');
+ client._sock._websocket._open();
+ sinon.spy(client._sock, 'flush');
});
it('should send a key message on a key press', function () {
client._keyboard._onKeyPress(1234, 1);
- expect(client._sock.send).to.have.been.calledOnce;
- var key_msg = RFB.messages.keyEvent(1234, 1);
- expect(client._sock.send).to.have.been.calledWith(key_msg);
+ var key_msg = {_sQ: new Uint8Array(8), _sQlen: 0};
+ RFB.messages.keyEvent(key_msg, 1234, 1);
+ expect(client._sock).to.have.sent(key_msg._sQ);
});
it('should not send messages in view-only mode', function () {
client._view_only = true;
client._keyboard._onKeyPress(1234, 1);
- expect(client._sock.send).to.not.have.been.called;
+ expect(client._sock.flush).to.not.have.been.called;
});
});
@@ -1826,7 +1853,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client.connect('host', 8675);
client._rfb_state = 'normal';
client._normal_msg = sinon.spy();
- client._sock._websocket._receive_data(Base64.encode([]));
+ client._sock._websocket._receive_data(new Uint8Array([]));
expect(client._normal_msg).to.not.have.been.called;
});
@@ -1834,7 +1861,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client.connect('host', 8675);
client._rfb_state = 'normal';
client._normal_msg = sinon.spy();
- client._sock._websocket._receive_data(Base64.encode([1, 2, 3]));
+ client._sock._websocket._receive_data(new Uint8Array([1, 2, 3]));
expect(client._normal_msg).to.have.been.calledOnce;
});
@@ -1842,7 +1869,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
client.connect('host', 8675);
client._rfb_state = 'ProtocolVersion';
client._init_msg = sinon.spy();
- client._sock._websocket._receive_data(Base64.encode([1, 2, 3]));
+ client._sock._websocket._receive_data(new Uint8Array([1, 2, 3]));
expect(client._init_msg).to.have.been.calledOnce;
});
diff --git a/tests/test.websock.js b/tests/test.websock.js
index 7d242d3e..14d57832 100644
--- a/tests/test.websock.js
+++ b/tests/test.websock.js
@@ -1,5 +1,5 @@
-// requires local modules: websock, base64, util
-// requires test modules: fake.websocket
+// requires local modules: websock, util
+// requires test modules: fake.websocket, assertions
/* jshint expr: true */
var assert = chai.assert;
var expect = chai.expect;
@@ -9,13 +9,14 @@ describe('Websock', function() {
describe('Queue methods', function () {
var sock;
- var RQ_TEMPLATE = [0, 1, 2, 3, 4, 5, 6, 7];
+ var RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
beforeEach(function () {
sock = new Websock();
- for (var i = RQ_TEMPLATE.length - 1; i >= 0; i--) {
- sock.rQunshift8(RQ_TEMPLATE[i]);
- }
+ // skip init
+ sock._allocate_buffers();
+ sock._rQ.set(RQ_TEMPLATE);
+ sock._rQlen = RQ_TEMPLATE.length;
});
describe('rQlen', function () {
it('should return the length of the receive queue', function () {
@@ -49,14 +50,6 @@ describe('Websock', function() {
});
});
- describe('rQunshift8', function () {
- it('should place a byte at the front of the queue', function () {
- sock.rQunshift8(255);
- expect(sock.rQpeek8()).to.equal(255);
- expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length + 1);
- });
- });
-
describe('rQshift16', function () {
it('should pop two bytes from the receive queue and return a single number', function () {
var bef_len = sock.rQlen();
@@ -84,7 +77,7 @@ describe('Websock', function() {
var bef_rQi = sock.get_rQi();
var shifted = sock.rQshiftStr(3);
expect(shifted).to.be.a('string');
- expect(shifted).to.equal(String.fromCharCode.apply(null, RQ_TEMPLATE.slice(bef_rQi, bef_rQi + 3)));
+ expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3))));
expect(sock.rQlen()).to.equal(bef_len - 3);
});
@@ -99,8 +92,8 @@ describe('Websock', function() {
var bef_len = sock.rQlen();
var bef_rQi = sock.get_rQi();
var shifted = sock.rQshiftBytes(3);
- expect(shifted).to.be.an.instanceof(Array);
- expect(shifted).to.deep.equal(RQ_TEMPLATE.slice(bef_rQi, bef_rQi + 3));
+ expect(shifted).to.be.an.instanceof(Uint8Array);
+ expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3));
expect(sock.rQlen()).to.equal(bef_len - 3);
});
@@ -123,19 +116,19 @@ describe('Websock', function() {
it('should return an array containing the given slice of the receive queue', function () {
var sl = sock.rQslice(0, 2);
- expect(sl).to.be.an.instanceof(Array);
- expect(sl).to.deep.equal(RQ_TEMPLATE.slice(0, 2));
+ expect(sl).to.be.an.instanceof(Uint8Array);
+ expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 0, 2));
});
it('should use the rest of the receive queue if no end is given', function () {
var sl = sock.rQslice(1);
expect(sl).to.have.length(RQ_TEMPLATE.length - 1);
- expect(sl).to.deep.equal(RQ_TEMPLATE.slice(1));
+ expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1));
});
it('should take the current rQi in to account', function () {
sock.set_rQi(1);
- expect(sock.rQslice(0, 2)).to.deep.equal(RQ_TEMPLATE.slice(1, 3));
+ expect(sock.rQslice(0, 2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2));
});
});
@@ -180,7 +173,8 @@ describe('Websock', function() {
it('should actually send on the websocket if the websocket does not have too much buffered', function () {
sock.maxBufferedAmount = 10;
sock._websocket.bufferedAmount = 8;
- sock._sQ = [1, 2, 3];
+ sock._sQ = new Uint8Array([1, 2, 3]);
+ sock._sQlen = 3;
var encoded = sock._encode_message();
sock.flush();
@@ -196,7 +190,7 @@ describe('Websock', function() {
});
it('should not call send if we do not have anything queued up', function () {
- sock._sQ = [];
+ sock._sQlen = 0;
sock.maxBufferedAmount = 10;
sock._websocket.bufferedAmount = 8;
@@ -222,7 +216,7 @@ describe('Websock', function() {
it('should add to the send queue', function () {
sock.send([1, 2, 3]);
var sq = sock.get_sQ();
- expect(sock.get_sQ().slice(sq.length - 3)).to.deep.equal([1, 2, 3]);
+ expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3]));
});
it('should call flush', function () {
@@ -257,6 +251,8 @@ describe('Websock', function() {
WebSocket.CONNECTING = old_WS.CONNECTING;
WebSocket.CLOSING = old_WS.CLOSING;
WebSocket.CLOSED = old_WS.CLOSED;
+
+ WebSocket.prototype.binaryType = 'arraybuffer';
});
describe('opening', function () {
@@ -265,22 +261,14 @@ describe('Websock', function() {
});
it('should open the actual websocket', function () {
- sock.open('ws://localhost:8675', 'base64');
- expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'base64');
+ sock.open('ws://localhost:8675', 'binary');
+ expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary');
});
- it('should fail if we try to use binary but do not support it', function () {
- expect(function () { sock.open('ws:///', 'binary'); }).to.throw(Error);
+ it('should fail if we specify a protocol besides binary', function () {
+ expect(function () { sock.open('ws:///', 'base64'); }).to.throw(Error);
});
- it('should fail if we specified an array with only binary and we do not support it', function () {
- expect(function () { sock.open('ws:///', ['binary']); }).to.throw(Error);
- });
-
- it('should skip binary if we have multiple options for encoding and do not support binary', function () {
- sock.open('ws:///', ['binary', 'base64']);
- expect(WebSocket).to.have.been.calledWith('ws:///', ['base64']);
- });
// it('should initialize the event handlers')?
});
@@ -340,16 +328,15 @@ describe('Websock', function() {
expect(sock._recv_message).to.have.been.calledOnce;
});
- it('should copy the mode over upon opening', function () {
- sock._websocket.protocol = 'cheese';
- sock._websocket.onopen();
- expect(sock._mode).to.equal('cheese');
+ it('should fail if a protocol besides binary is requested', function () {
+ sock._websocket.protocol = 'base64';
+ expect(sock._websocket.onopen).to.throw(Error);
});
- it('should assume base64 if no protocol was available on opening', function () {
+ it('should assume binary if no protocol was available on opening', function () {
sock._websocket.protocol = null;
sock._websocket.onopen();
- expect(sock._mode).to.equal('base64');
+ expect(sock._mode).to.equal('binary');
});
it('should call the open event handler on opening', function () {
@@ -377,13 +364,7 @@ describe('Websock', function() {
var sock;
beforeEach(function () {
sock = new Websock();
- });
-
- it('should support decoding base64 string data to add it to the receive queue', function () {
- var msg = { data: Base64.encode([1, 2, 3]) };
- sock._mode = 'base64';
- sock._recv_message(msg);
- expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03');
+ sock._allocate_buffers();
});
it('should support adding binary Uint8Array data to the receive queue', function () {
@@ -395,16 +376,16 @@ describe('Websock', function() {
it('should call the message event handler if present', function () {
sock._eventHandlers.message = sinon.spy();
- var msg = { data: Base64.encode([1, 2, 3]) };
- sock._mode = 'base64';
+ var msg = { data: new Uint8Array([1, 2, 3]).buffer };
+ sock._mode = 'binary';
sock._recv_message(msg);
expect(sock._eventHandlers.message).to.have.been.calledOnce;
});
it('should not call the message event handler if there is nothing in the receive queue', function () {
sock._eventHandlers.message = sinon.spy();
- var msg = { data: Base64.encode([]) };
- sock._mode = 'base64';
+ var msg = { data: new Uint8Array([]).buffer };
+ sock._mode = 'binary';
sock._recv_message(msg);
expect(sock._eventHandlers.message).not.to.have.been.called;
});
@@ -412,21 +393,22 @@ describe('Websock', function() {
it('should compact the receive queue', function () {
// NB(sross): while this is an internal implementation detail, it's important to
// test, otherwise the receive queue could become very large very quickly
- sock._rQ = [0, 1, 2, 3, 4, 5];
+ sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]);
+ sock._rQlen = 6;
sock.set_rQi(6);
sock._rQmax = 3;
- var msg = { data: Base64.encode([1, 2, 3]) };
- sock._mode = 'base64';
+ var msg = { data: new Uint8Array([1, 2, 3]).buffer };
+ sock._mode = 'binary';
sock._recv_message(msg);
- expect(sock._rQ.length).to.equal(3);
+ expect(sock._rQlen).to.equal(3);
expect(sock.get_rQi()).to.equal(0);
});
it('should call the error event handler on an exception', function () {
sock._eventHandlers.error = sinon.spy();
sock._eventHandlers.message = sinon.stub().throws();
- var msg = { data: Base64.encode([1, 2, 3]) };
- sock._mode = 'base64';
+ var msg = { data: new Uint8Array([1, 2, 3]).buffer };
+ sock._mode = 'binary';
sock._recv_message(msg);
expect(sock._eventHandlers.error).to.have.been.calledOnce;
});
@@ -444,37 +426,17 @@ describe('Websock', function() {
sock._websocket._open();
});
- it('should convert the send queue into an ArrayBuffer', function () {
- sock._sQ = [1, 2, 3];
- var res = sock._encode_message(); // An ArrayBuffer
- expect(new Uint8Array(res)).to.deep.equal(new Uint8Array(res));
+ it('should only send the send queue up to the send queue length', function () {
+ sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
+ sock._sQlen = 3;
+ var res = sock._encode_message();
+ expect(res).to.array.equal(new Uint8Array([1, 2, 3]));
});
it('should properly pass the encoded data off to the actual WebSocket', function () {
sock.send([1, 2, 3]);
- expect(sock._websocket._get_sent_data()).to.deep.equal([1, 2, 3]);
+ expect(sock._websocket._get_sent_data()).to.array.equal(new Uint8Array([1, 2, 3]));
});
});
-
- describe('as Base64 data', function () {
- var sock;
- beforeEach(function () {
- sock = new Websock();
- sock.open('ws://', 'base64');
- sock._websocket._open();
- });
-
- it('should convert the send queue into a Base64-encoded string', function () {
- sock._sQ = [1, 2, 3];
- expect(sock._encode_message()).to.equal(Base64.encode([1, 2, 3]));
- });
-
- it('should properly pass the encoded data off to the actual WebSocket', function () {
- sock.send([1, 2, 3]);
- expect(sock._websocket._get_sent_data()).to.deep.equal([1, 2, 3]);
- });
-
- });
-
});
});
diff --git a/tests/vnc_playback.html b/tests/vnc_playback.html
index b5faf93c..f36f1e65 100644
--- a/tests/vnc_playback.html
+++ b/tests/vnc_playback.html
@@ -59,9 +59,9 @@
if (fname) {
message("Loading " + fname);
// Load supporting scripts
- Util.load_scripts(["base64.js", "websock.js", "des.js",
+ Util.load_scripts(["base64.js", "websock.js", "des.js", "keysym.js",
"keysymdef.js", "keyboard.js", "input.js", "display.js",
- "jsunzip.js", "rfb.js", "playback.js", fname]);
+ "rfb.js", "playback.js", "inflator.js", fname]);
} else {
message("Must specify data=FOO in query string.");
@@ -75,7 +75,6 @@
test_state = 'failed';
break;
case 'loaded':
- $D('startButton').disabled = false;
break;
}
if (typeof msg !== 'undefined') {
@@ -99,7 +98,8 @@
mode = 'realtime';
}
- recv_message = rfb.testMode(send_array, VNC_frame_encoding);
+ //recv_message = rfb.testMode(send_array, VNC_frame_encoding);
+
next_iteration();
}
@@ -130,9 +130,8 @@
}
if (fname) {
message("VNC_frame_data.length: " + VNC_frame_data.length);
- rfb = new RFB({'target': $D('VNC_canvas'),
- 'onUpdateState': updateState});
}
+ $D('startButton').disabled = false;
}