@novnc/novnc 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/AUTHORS +13 -0
  2. package/LICENSE.txt +2 -1
  3. package/README.md +69 -3
  4. package/core/base64.js +35 -41
  5. package/core/decoders/copyrect.js +22 -0
  6. package/core/decoders/hextile.js +137 -0
  7. package/core/decoders/raw.js +56 -0
  8. package/core/decoders/rre.js +44 -0
  9. package/core/decoders/tight.js +315 -0
  10. package/core/decoders/tightpng.js +27 -0
  11. package/core/deflator.js +85 -0
  12. package/core/des.js +90 -95
  13. package/core/display.js +254 -297
  14. package/core/encodings.js +7 -3
  15. package/core/inflator.js +48 -20
  16. package/core/input/domkeytable.js +21 -24
  17. package/core/input/fixedkeys.js +3 -1
  18. package/core/input/gesturehandler.js +567 -0
  19. package/core/input/keyboard.js +194 -120
  20. package/core/input/keysym.js +2 -0
  21. package/core/input/keysymdef.js +3 -3
  22. package/core/input/util.js +53 -12
  23. package/core/input/vkeys.js +2 -1
  24. package/core/rfb.js +1937 -1496
  25. package/core/util/browser.js +80 -29
  26. package/core/util/cursor.js +253 -0
  27. package/core/util/element.js +32 -0
  28. package/core/util/events.js +59 -55
  29. package/core/util/eventtarget.js +25 -30
  30. package/core/util/int.js +15 -0
  31. package/core/util/logging.js +21 -16
  32. package/core/util/polyfill.js +15 -8
  33. package/core/util/strings.js +21 -8
  34. package/core/websock.js +145 -167
  35. package/docs/API.md +31 -10
  36. package/lib/base64.js +115 -0
  37. package/lib/decoders/copyrect.js +44 -0
  38. package/lib/decoders/hextile.js +173 -0
  39. package/lib/decoders/raw.js +78 -0
  40. package/lib/decoders/rre.js +65 -0
  41. package/lib/decoders/tight.js +350 -0
  42. package/lib/decoders/tightpng.js +67 -0
  43. package/lib/deflator.js +99 -0
  44. package/lib/des.js +314 -0
  45. package/lib/display.js +733 -0
  46. package/lib/encodings.js +64 -0
  47. package/lib/inflator.js +87 -0
  48. package/lib/input/domkeytable.js +282 -0
  49. package/lib/input/fixedkeys.js +123 -0
  50. package/lib/input/gesturehandler.js +642 -0
  51. package/lib/input/keyboard.js +429 -0
  52. package/lib/input/keysym.js +1135 -0
  53. package/lib/input/keysymdef.js +1354 -0
  54. package/lib/input/util.js +304 -0
  55. package/lib/input/vkeys.js +127 -0
  56. package/lib/input/xtscancodes.js +505 -0
  57. package/lib/rfb.js +3448 -0
  58. package/lib/util/browser.js +131 -0
  59. package/lib/util/cursor.js +314 -0
  60. package/lib/util/element.js +43 -0
  61. package/lib/util/events.js +142 -0
  62. package/lib/util/eventtarget.js +64 -0
  63. package/lib/util/int.js +22 -0
  64. package/lib/util/logging.js +79 -0
  65. package/lib/util/polyfill.js +72 -0
  66. package/lib/util/strings.js +38 -0
  67. package/lib/vendor/pako/lib/utils/common.js +67 -0
  68. package/lib/vendor/pako/lib/zlib/adler32.js +33 -0
  69. package/lib/vendor/pako/lib/zlib/constants.js +51 -0
  70. package/lib/vendor/pako/lib/zlib/crc32.js +42 -0
  71. package/lib/vendor/pako/lib/zlib/deflate.js +2159 -0
  72. package/lib/vendor/pako/lib/zlib/gzheader.js +53 -0
  73. package/lib/vendor/pako/lib/zlib/inffast.js +445 -0
  74. package/lib/vendor/pako/lib/zlib/inflate.js +2114 -0
  75. package/lib/vendor/pako/lib/zlib/inftrees.js +418 -0
  76. package/lib/vendor/pako/lib/zlib/messages.js +36 -0
  77. package/lib/vendor/pako/lib/zlib/trees.js +1499 -0
  78. package/lib/vendor/pako/lib/zlib/zstream.js +46 -0
  79. package/lib/vendor/promise.js +255 -0
  80. package/lib/websock.js +374 -0
  81. package/package.json +48 -28
  82. package/vendor/pako/lib/zlib/deflate.js +30 -30
  83. package/vendor/pako/lib/zlib/inflate.js +17 -17
  84. package/core/input/mouse.js +0 -280
  85. package/docs/API-internal.md +0 -125
  86. package/docs/EMBEDDING.md +0 -83
  87. package/docs/VERSION +0 -1
package/core/display.js CHANGED
@@ -1,7 +1,6 @@
1
1
  /*
2
2
  * noVNC: HTML5 VNC client
3
- * Copyright (C) 2012 Joel Martin
4
- * Copyright (C) 2015 Samuel Mannehed for Cendio AB
3
+ * Copyright (C) 2019 The noVNC Authors
5
4
  * Licensed under MPL 2.0 (see LICENSE.txt)
6
5
  *
7
6
  * See README.md for usage and integration instructions.
@@ -9,111 +8,104 @@
9
8
 
10
9
  import * as Log from './util/logging.js';
11
10
  import Base64 from "./base64.js";
11
+ import { supportsImageMetadata } from './util/browser.js';
12
+ import { toSigned32bit } from './util/int.js';
12
13
 
13
- export default function Display(target) {
14
- this._drawCtx = null;
15
- this._c_forceCanvas = false;
14
+ export default class Display {
15
+ constructor(target) {
16
+ this._drawCtx = null;
16
17
 
17
- this._renderQ = []; // queue drawing actions for in-oder rendering
18
- this._flushing = false;
18
+ this._renderQ = []; // queue drawing actions for in-oder rendering
19
+ this._flushing = false;
19
20
 
20
- // the full frame buffer (logical canvas) size
21
- this._fb_width = 0;
22
- this._fb_height = 0;
21
+ // the full frame buffer (logical canvas) size
22
+ this._fbWidth = 0;
23
+ this._fbHeight = 0;
23
24
 
24
- this._prevDrawStyle = "";
25
- this._tile = null;
26
- this._tile16x16 = null;
27
- this._tile_x = 0;
28
- this._tile_y = 0;
25
+ this._prevDrawStyle = "";
26
+ this._tile = null;
27
+ this._tile16x16 = null;
28
+ this._tileX = 0;
29
+ this._tileY = 0;
29
30
 
30
- Log.Debug(">> Display.constructor");
31
+ Log.Debug(">> Display.constructor");
31
32
 
32
- // The visible canvas
33
- this._target = target;
33
+ // The visible canvas
34
+ this._target = target;
34
35
 
35
- if (!this._target) {
36
- throw new Error("Target must be set");
37
- }
36
+ if (!this._target) {
37
+ throw new Error("Target must be set");
38
+ }
38
39
 
39
- if (typeof this._target === 'string') {
40
- throw new Error('target must be a DOM element');
41
- }
40
+ if (typeof this._target === 'string') {
41
+ throw new Error('target must be a DOM element');
42
+ }
42
43
 
43
- if (!this._target.getContext) {
44
- throw new Error("no getContext method");
45
- }
44
+ if (!this._target.getContext) {
45
+ throw new Error("no getContext method");
46
+ }
46
47
 
47
- this._targetCtx = this._target.getContext('2d');
48
+ this._targetCtx = this._target.getContext('2d');
48
49
 
49
- // the visible canvas viewport (i.e. what actually gets seen)
50
- this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
50
+ // the visible canvas viewport (i.e. what actually gets seen)
51
+ this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
51
52
 
52
- // The hidden canvas, where we do the actual rendering
53
- this._backbuffer = document.createElement('canvas');
54
- this._drawCtx = this._backbuffer.getContext('2d');
53
+ // The hidden canvas, where we do the actual rendering
54
+ this._backbuffer = document.createElement('canvas');
55
+ this._drawCtx = this._backbuffer.getContext('2d');
55
56
 
56
- this._damageBounds = { left:0, top:0,
57
- right: this._backbuffer.width,
58
- bottom: this._backbuffer.height };
57
+ this._damageBounds = { left: 0, top: 0,
58
+ right: this._backbuffer.width,
59
+ bottom: this._backbuffer.height };
59
60
 
60
- Log.Debug("User Agent: " + navigator.userAgent);
61
+ Log.Debug("User Agent: " + navigator.userAgent);
61
62
 
62
- this.clear();
63
+ // Check canvas features
64
+ if (!('createImageData' in this._drawCtx)) {
65
+ throw new Error("Canvas does not support createImageData");
66
+ }
63
67
 
64
- // Check canvas features
65
- if (!('createImageData' in this._drawCtx)) {
66
- throw new Error("Canvas does not support createImageData");
67
- }
68
+ this._tile16x16 = this._drawCtx.createImageData(16, 16);
69
+ Log.Debug("<< Display.constructor");
68
70
 
69
- this._tile16x16 = this._drawCtx.createImageData(16, 16);
70
- Log.Debug("<< Display.constructor");
71
- };
71
+ // ===== PROPERTIES =====
72
72
 
73
- var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
74
- try {
75
- new ImageData(new Uint8ClampedArray(4), 1, 1);
76
- SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
77
- } catch (ex) {
78
- // ignore failure
79
- }
73
+ this._scale = 1.0;
74
+ this._clipViewport = false;
75
+
76
+ // ===== EVENT HANDLERS =====
77
+
78
+ this.onflush = () => {}; // A flush request has finished
79
+ }
80
80
 
81
- Display.prototype = {
82
81
  // ===== PROPERTIES =====
83
82
 
84
- _scale: 1.0,
85
- get scale() { return this._scale; },
83
+ get scale() { return this._scale; }
86
84
  set scale(scale) {
87
85
  this._rescale(scale);
88
- },
86
+ }
89
87
 
90
- _clipViewport: false,
91
- get clipViewport() { return this._clipViewport; },
88
+ get clipViewport() { return this._clipViewport; }
92
89
  set clipViewport(viewport) {
93
90
  this._clipViewport = viewport;
94
91
  // May need to readjust the viewport dimensions
95
- var vp = this._viewportLoc;
92
+ const vp = this._viewportLoc;
96
93
  this.viewportChangeSize(vp.w, vp.h);
97
94
  this.viewportChangePos(0, 0);
98
- },
95
+ }
99
96
 
100
97
  get width() {
101
- return this._fb_width;
102
- },
103
- get height() {
104
- return this._fb_height;
105
- },
106
-
107
- logo: null,
108
-
109
- // ===== EVENT HANDLERS =====
98
+ return this._fbWidth;
99
+ }
110
100
 
111
- onflush: function () {}, // A flush request has finished
101
+ get height() {
102
+ return this._fbHeight;
103
+ }
112
104
 
113
105
  // ===== PUBLIC METHODS =====
114
106
 
115
- viewportChangePos: function (deltaX, deltaY) {
116
- var vp = this._viewportLoc;
107
+ viewportChangePos(deltaX, deltaY) {
108
+ const vp = this._viewportLoc;
117
109
  deltaX = Math.floor(deltaX);
118
110
  deltaY = Math.floor(deltaY);
119
111
 
@@ -122,23 +114,23 @@ Display.prototype = {
122
114
  deltaY = -vp.h;
123
115
  }
124
116
 
125
- var vx2 = vp.x + vp.w - 1;
126
- var vy2 = vp.y + vp.h - 1;
117
+ const vx2 = vp.x + vp.w - 1;
118
+ const vy2 = vp.y + vp.h - 1;
127
119
 
128
120
  // Position change
129
121
 
130
122
  if (deltaX < 0 && vp.x + deltaX < 0) {
131
123
  deltaX = -vp.x;
132
124
  }
133
- if (vx2 + deltaX >= this._fb_width) {
134
- deltaX -= vx2 + deltaX - this._fb_width + 1;
125
+ if (vx2 + deltaX >= this._fbWidth) {
126
+ deltaX -= vx2 + deltaX - this._fbWidth + 1;
135
127
  }
136
128
 
137
129
  if (vp.y + deltaY < 0) {
138
130
  deltaY = -vp.y;
139
131
  }
140
- if (vy2 + deltaY >= this._fb_height) {
141
- deltaY -= (vy2 + deltaY - this._fb_height + 1);
132
+ if (vy2 + deltaY >= this._fbHeight) {
133
+ deltaY -= (vy2 + deltaY - this._fbHeight + 1);
142
134
  }
143
135
 
144
136
  if (deltaX === 0 && deltaY === 0) {
@@ -152,32 +144,35 @@ Display.prototype = {
152
144
  this._damage(vp.x, vp.y, vp.w, vp.h);
153
145
 
154
146
  this.flip();
155
- },
147
+ }
156
148
 
157
- viewportChangeSize: function(width, height) {
149
+ viewportChangeSize(width, height) {
158
150
 
159
151
  if (!this._clipViewport ||
160
152
  typeof(width) === "undefined" ||
161
153
  typeof(height) === "undefined") {
162
154
 
163
155
  Log.Debug("Setting viewport to full display region");
164
- width = this._fb_width;
165
- height = this._fb_height;
156
+ width = this._fbWidth;
157
+ height = this._fbHeight;
166
158
  }
167
159
 
168
- if (width > this._fb_width) {
169
- width = this._fb_width;
160
+ width = Math.floor(width);
161
+ height = Math.floor(height);
162
+
163
+ if (width > this._fbWidth) {
164
+ width = this._fbWidth;
170
165
  }
171
- if (height > this._fb_height) {
172
- height = this._fb_height;
166
+ if (height > this._fbHeight) {
167
+ height = this._fbHeight;
173
168
  }
174
169
 
175
- var vp = this._viewportLoc;
170
+ const vp = this._viewportLoc;
176
171
  if (vp.w !== width || vp.h !== height) {
177
172
  vp.w = width;
178
173
  vp.h = height;
179
174
 
180
- var canvas = this._target;
175
+ const canvas = this._target;
181
176
  canvas.width = width;
182
177
  canvas.height = height;
183
178
 
@@ -190,27 +185,33 @@ Display.prototype = {
190
185
  // Update the visible size of the target canvas
191
186
  this._rescale(this._scale);
192
187
  }
193
- },
188
+ }
194
189
 
195
- absX: function (x) {
196
- return x / this._scale + this._viewportLoc.x;
197
- },
190
+ absX(x) {
191
+ if (this._scale === 0) {
192
+ return 0;
193
+ }
194
+ return toSigned32bit(x / this._scale + this._viewportLoc.x);
195
+ }
198
196
 
199
- absY: function (y) {
200
- return y / this._scale + this._viewportLoc.y;
201
- },
197
+ absY(y) {
198
+ if (this._scale === 0) {
199
+ return 0;
200
+ }
201
+ return toSigned32bit(y / this._scale + this._viewportLoc.y);
202
+ }
202
203
 
203
- resize: function (width, height) {
204
+ resize(width, height) {
204
205
  this._prevDrawStyle = "";
205
206
 
206
- this._fb_width = width;
207
- this._fb_height = height;
207
+ this._fbWidth = width;
208
+ this._fbHeight = height;
208
209
 
209
- var canvas = this._backbuffer;
210
+ const canvas = this._backbuffer;
210
211
  if (canvas.width !== width || canvas.height !== height) {
211
212
 
212
213
  // We have to save the canvas data since changing the size will clear it
213
- var saveImg = null;
214
+ let saveImg = null;
214
215
  if (canvas.width > 0 && canvas.height > 0) {
215
216
  saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height);
216
217
  }
@@ -229,13 +230,13 @@ Display.prototype = {
229
230
 
230
231
  // Readjust the viewport as it may be incorrectly sized
231
232
  // and positioned
232
- var vp = this._viewportLoc;
233
+ const vp = this._viewportLoc;
233
234
  this.viewportChangeSize(vp.w, vp.h);
234
235
  this.viewportChangePos(0, 0);
235
- },
236
+ }
236
237
 
237
238
  // Track what parts of the visible canvas that need updating
238
- _damage: function(x, y, w, h) {
239
+ _damage(x, y, w, h) {
239
240
  if (x < this._damageBounds.left) {
240
241
  this._damageBounds.left = x;
241
242
  }
@@ -248,25 +249,23 @@ Display.prototype = {
248
249
  if ((y + h) > this._damageBounds.bottom) {
249
250
  this._damageBounds.bottom = y + h;
250
251
  }
251
- },
252
+ }
252
253
 
253
254
  // Update the visible canvas with the contents of the
254
255
  // rendering canvas
255
- flip: function(from_queue) {
256
- if (this._renderQ.length !== 0 && !from_queue) {
257
- this._renderQ_push({
256
+ flip(fromQueue) {
257
+ if (this._renderQ.length !== 0 && !fromQueue) {
258
+ this._renderQPush({
258
259
  'type': 'flip'
259
260
  });
260
261
  } else {
261
- var x, y, vx, vy, w, h;
262
-
263
- x = this._damageBounds.left;
264
- y = this._damageBounds.top;
265
- w = this._damageBounds.right - x;
266
- h = this._damageBounds.bottom - y;
262
+ let x = this._damageBounds.left;
263
+ let y = this._damageBounds.top;
264
+ let w = this._damageBounds.right - x;
265
+ let h = this._damageBounds.bottom - y;
267
266
 
268
- vx = x - this._viewportLoc.x;
269
- vy = y - this._viewportLoc.y;
267
+ let vx = x - this._viewportLoc.x;
268
+ let vy = y - this._viewportLoc.y;
270
269
 
271
270
  if (vx < 0) {
272
271
  w += vx;
@@ -298,34 +297,23 @@ Display.prototype = {
298
297
  this._damageBounds.left = this._damageBounds.top = 65535;
299
298
  this._damageBounds.right = this._damageBounds.bottom = 0;
300
299
  }
301
- },
302
-
303
- clear: function () {
304
- if (this._logo) {
305
- this.resize(this._logo.width, this._logo.height);
306
- this.imageRect(0, 0, this._logo.type, this._logo.data);
307
- } else {
308
- this.resize(240, 20);
309
- this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
310
- }
311
- this.flip();
312
- },
300
+ }
313
301
 
314
- pending: function() {
302
+ pending() {
315
303
  return this._renderQ.length > 0;
316
- },
304
+ }
317
305
 
318
- flush: function() {
306
+ flush() {
319
307
  if (this._renderQ.length === 0) {
320
308
  this.onflush();
321
309
  } else {
322
310
  this._flushing = true;
323
311
  }
324
- },
312
+ }
325
313
 
326
- fillRect: function (x, y, width, height, color, from_queue) {
327
- if (this._renderQ.length !== 0 && !from_queue) {
328
- this._renderQ_push({
314
+ fillRect(x, y, width, height, color, fromQueue) {
315
+ if (this._renderQ.length !== 0 && !fromQueue) {
316
+ this._renderQPush({
329
317
  'type': 'fill',
330
318
  'x': x,
331
319
  'y': y,
@@ -338,16 +326,16 @@ Display.prototype = {
338
326
  this._drawCtx.fillRect(x, y, width, height);
339
327
  this._damage(x, y, width, height);
340
328
  }
341
- },
329
+ }
342
330
 
343
- copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) {
344
- if (this._renderQ.length !== 0 && !from_queue) {
345
- this._renderQ_push({
331
+ copyImage(oldX, oldY, newX, newY, w, h, fromQueue) {
332
+ if (this._renderQ.length !== 0 && !fromQueue) {
333
+ this._renderQPush({
346
334
  'type': 'copy',
347
- 'old_x': old_x,
348
- 'old_y': old_y,
349
- 'x': new_x,
350
- 'y': new_y,
335
+ 'oldX': oldX,
336
+ 'oldY': oldY,
337
+ 'x': newX,
338
+ 'y': newY,
351
339
  'width': w,
352
340
  'height': h,
353
341
  });
@@ -365,84 +353,92 @@ Display.prototype = {
365
353
  this._drawCtx.imageSmoothingEnabled = false;
366
354
 
367
355
  this._drawCtx.drawImage(this._backbuffer,
368
- old_x, old_y, w, h,
369
- new_x, new_y, w, h);
370
- this._damage(new_x, new_y, w, h);
356
+ oldX, oldY, w, h,
357
+ newX, newY, w, h);
358
+ this._damage(newX, newY, w, h);
371
359
  }
372
- },
360
+ }
373
361
 
374
- imageRect: function(x, y, mime, arr) {
375
- var img = new Image();
362
+ imageRect(x, y, width, height, mime, arr) {
363
+ /* The internal logic cannot handle empty images, so bail early */
364
+ if ((width === 0) || (height === 0)) {
365
+ return;
366
+ }
367
+
368
+ const img = new Image();
376
369
  img.src = "data: " + mime + ";base64," + Base64.encode(arr);
377
- this._renderQ_push({
370
+
371
+ this._renderQPush({
378
372
  'type': 'img',
379
373
  'img': img,
380
374
  'x': x,
381
- 'y': y
375
+ 'y': y,
376
+ 'width': width,
377
+ 'height': height
382
378
  });
383
- },
379
+ }
384
380
 
385
381
  // start updating a tile
386
- startTile: function (x, y, width, height, color) {
387
- this._tile_x = x;
388
- this._tile_y = y;
382
+ startTile(x, y, width, height, color) {
383
+ this._tileX = x;
384
+ this._tileY = y;
389
385
  if (width === 16 && height === 16) {
390
386
  this._tile = this._tile16x16;
391
387
  } else {
392
388
  this._tile = this._drawCtx.createImageData(width, height);
393
389
  }
394
390
 
395
- var red = color[2];
396
- var green = color[1];
397
- var blue = color[0];
391
+ const red = color[2];
392
+ const green = color[1];
393
+ const blue = color[0];
398
394
 
399
- var data = this._tile.data;
400
- for (var i = 0; i < width * height * 4; i += 4) {
395
+ const data = this._tile.data;
396
+ for (let i = 0; i < width * height * 4; i += 4) {
401
397
  data[i] = red;
402
398
  data[i + 1] = green;
403
399
  data[i + 2] = blue;
404
400
  data[i + 3] = 255;
405
401
  }
406
- },
402
+ }
407
403
 
408
404
  // update sub-rectangle of the current tile
409
- subTile: function (x, y, w, h, color) {
410
- var red = color[2];
411
- var green = color[1];
412
- var blue = color[0];
413
- var xend = x + w;
414
- var yend = y + h;
415
-
416
- var data = this._tile.data;
417
- var width = this._tile.width;
418
- for (var j = y; j < yend; j++) {
419
- for (var i = x; i < xend; i++) {
420
- var p = (i + (j * width)) * 4;
405
+ subTile(x, y, w, h, color) {
406
+ const red = color[2];
407
+ const green = color[1];
408
+ const blue = color[0];
409
+ const xend = x + w;
410
+ const yend = y + h;
411
+
412
+ const data = this._tile.data;
413
+ const width = this._tile.width;
414
+ for (let j = y; j < yend; j++) {
415
+ for (let i = x; i < xend; i++) {
416
+ const p = (i + (j * width)) * 4;
421
417
  data[p] = red;
422
418
  data[p + 1] = green;
423
419
  data[p + 2] = blue;
424
420
  data[p + 3] = 255;
425
421
  }
426
422
  }
427
- },
423
+ }
428
424
 
429
425
  // draw the current tile to the screen
430
- finishTile: function () {
431
- this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
432
- this._damage(this._tile_x, this._tile_y,
426
+ finishTile() {
427
+ this._drawCtx.putImageData(this._tile, this._tileX, this._tileY);
428
+ this._damage(this._tileX, this._tileY,
433
429
  this._tile.width, this._tile.height);
434
- },
430
+ }
435
431
 
436
- blitImage: function (x, y, width, height, arr, offset, from_queue) {
437
- if (this._renderQ.length !== 0 && !from_queue) {
432
+ blitImage(x, y, width, height, arr, offset, fromQueue) {
433
+ if (this._renderQ.length !== 0 && !fromQueue) {
438
434
  // NB(directxman12): it's technically more performant here to use preallocated arrays,
439
435
  // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
440
436
  // this probably isn't getting called *nearly* as much
441
- var new_arr = new Uint8Array(width * height * 4);
442
- new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
443
- this._renderQ_push({
437
+ const newArr = new Uint8Array(width * height * 4);
438
+ newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
439
+ this._renderQPush({
444
440
  'type': 'blit',
445
- 'data': new_arr,
441
+ 'data': newArr,
446
442
  'x': x,
447
443
  'y': y,
448
444
  'width': width,
@@ -451,18 +447,18 @@ Display.prototype = {
451
447
  } else {
452
448
  this._bgrxImageData(x, y, width, height, arr, offset);
453
449
  }
454
- },
450
+ }
455
451
 
456
- blitRgbImage: function (x, y , width, height, arr, offset, from_queue) {
457
- if (this._renderQ.length !== 0 && !from_queue) {
452
+ blitRgbImage(x, y, width, height, arr, offset, fromQueue) {
453
+ if (this._renderQ.length !== 0 && !fromQueue) {
458
454
  // NB(directxman12): it's technically more performant here to use preallocated arrays,
459
455
  // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
460
456
  // this probably isn't getting called *nearly* as much
461
- var new_arr = new Uint8Array(width * height * 3);
462
- new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
463
- this._renderQ_push({
457
+ const newArr = new Uint8Array(width * height * 3);
458
+ newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
459
+ this._renderQPush({
464
460
  'type': 'blitRgb',
465
- 'data': new_arr,
461
+ 'data': newArr,
466
462
  'x': x,
467
463
  'y': y,
468
464
  'width': width,
@@ -471,18 +467,18 @@ Display.prototype = {
471
467
  } else {
472
468
  this._rgbImageData(x, y, width, height, arr, offset);
473
469
  }
474
- },
470
+ }
475
471
 
476
- blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) {
477
- if (this._renderQ.length !== 0 && !from_queue) {
472
+ blitRgbxImage(x, y, width, height, arr, offset, fromQueue) {
473
+ if (this._renderQ.length !== 0 && !fromQueue) {
478
474
  // NB(directxman12): it's technically more performant here to use preallocated arrays,
479
475
  // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
480
476
  // this probably isn't getting called *nearly* as much
481
- var new_arr = new Uint8Array(width * height * 4);
482
- new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
483
- this._renderQ_push({
477
+ const newArr = new Uint8Array(width * height * 4);
478
+ newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
479
+ this._renderQPush({
484
480
  'type': 'blitRgbx',
485
- 'data': new_arr,
481
+ 'data': newArr,
486
482
  'x': x,
487
483
  'y': y,
488
484
  'width': width,
@@ -491,72 +487,67 @@ Display.prototype = {
491
487
  } else {
492
488
  this._rgbxImageData(x, y, width, height, arr, offset);
493
489
  }
494
- },
490
+ }
495
491
 
496
- drawImage: function (img, x, y) {
492
+ drawImage(img, x, y) {
497
493
  this._drawCtx.drawImage(img, x, y);
498
494
  this._damage(x, y, img.width, img.height);
499
- },
495
+ }
500
496
 
501
- changeCursor: function (pixels, mask, hotx, hoty, w, h) {
502
- Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h);
503
- },
497
+ autoscale(containerWidth, containerHeight) {
498
+ let scaleRatio;
504
499
 
505
- defaultCursor: function () {
506
- this._target.style.cursor = "default";
507
- },
500
+ if (containerWidth === 0 || containerHeight === 0) {
501
+ scaleRatio = 0;
508
502
 
509
- disableLocalCursor: function () {
510
- this._target.style.cursor = "none";
511
- },
503
+ } else {
512
504
 
513
- autoscale: function (containerWidth, containerHeight) {
514
- var vp = this._viewportLoc;
515
- var targetAspectRatio = containerWidth / containerHeight;
516
- var fbAspectRatio = vp.w / vp.h;
505
+ const vp = this._viewportLoc;
506
+ const targetAspectRatio = containerWidth / containerHeight;
507
+ const fbAspectRatio = vp.w / vp.h;
517
508
 
518
- var scaleRatio;
519
- if (fbAspectRatio >= targetAspectRatio) {
520
- scaleRatio = containerWidth / vp.w;
521
- } else {
522
- scaleRatio = containerHeight / vp.h;
509
+ if (fbAspectRatio >= targetAspectRatio) {
510
+ scaleRatio = containerWidth / vp.w;
511
+ } else {
512
+ scaleRatio = containerHeight / vp.h;
513
+ }
523
514
  }
524
515
 
525
516
  this._rescale(scaleRatio);
526
- },
517
+ }
527
518
 
528
519
  // ===== PRIVATE METHODS =====
529
520
 
530
- _rescale: function (factor) {
521
+ _rescale(factor) {
531
522
  this._scale = factor;
532
- var vp = this._viewportLoc;
523
+ const vp = this._viewportLoc;
533
524
 
534
525
  // NB(directxman12): If you set the width directly, or set the
535
526
  // style width to a number, the canvas is cleared.
536
527
  // However, if you set the style width to a string
537
528
  // ('NNNpx'), the canvas is scaled without clearing.
538
- var width = Math.round(factor * vp.w) + 'px';
539
- var height = Math.round(factor * vp.h) + 'px';
529
+ const width = factor * vp.w + 'px';
530
+ const height = factor * vp.h + 'px';
540
531
 
541
532
  if ((this._target.style.width !== width) ||
542
533
  (this._target.style.height !== height)) {
543
534
  this._target.style.width = width;
544
535
  this._target.style.height = height;
545
536
  }
546
- },
537
+ }
547
538
 
548
- _setFillColor: function (color) {
549
- var newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
539
+ _setFillColor(color) {
540
+ const newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
550
541
  if (newStyle !== this._prevDrawStyle) {
551
542
  this._drawCtx.fillStyle = newStyle;
552
543
  this._prevDrawStyle = newStyle;
553
544
  }
554
- },
545
+ }
555
546
 
556
- _rgbImageData: function (x, y, width, height, arr, offset) {
557
- var img = this._drawCtx.createImageData(width, height);
558
- var data = img.data;
559
- for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
547
+ _rgbImageData(x, y, width, height, arr, offset) {
548
+ const img = this._drawCtx.createImageData(width, height);
549
+ const data = img.data;
550
+ for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
560
551
  data[i] = arr[j];
561
552
  data[i + 1] = arr[j + 1];
562
553
  data[i + 2] = arr[j + 2];
@@ -564,12 +555,12 @@ Display.prototype = {
564
555
  }
565
556
  this._drawCtx.putImageData(img, x, y);
566
557
  this._damage(x, y, img.width, img.height);
567
- },
558
+ }
568
559
 
569
- _bgrxImageData: function (x, y, width, height, arr, offset) {
570
- var img = this._drawCtx.createImageData(width, height);
571
- var data = img.data;
572
- for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
560
+ _bgrxImageData(x, y, width, height, arr, offset) {
561
+ const img = this._drawCtx.createImageData(width, height);
562
+ const data = img.data;
563
+ for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
573
564
  data[i] = arr[j + 2];
574
565
  data[i + 1] = arr[j + 1];
575
566
  data[i + 2] = arr[j];
@@ -577,12 +568,12 @@ Display.prototype = {
577
568
  }
578
569
  this._drawCtx.putImageData(img, x, y);
579
570
  this._damage(x, y, img.width, img.height);
580
- },
571
+ }
581
572
 
582
- _rgbxImageData: function (x, y, width, height, arr, offset) {
573
+ _rgbxImageData(x, y, width, height, arr, offset) {
583
574
  // NB(directxman12): arr must be an Type Array view
584
- var img;
585
- if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
575
+ let img;
576
+ if (supportsImageMetadata) {
586
577
  img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
587
578
  } else {
588
579
  img = this._drawCtx.createImageData(width, height);
@@ -590,34 +581,34 @@ Display.prototype = {
590
581
  }
591
582
  this._drawCtx.putImageData(img, x, y);
592
583
  this._damage(x, y, img.width, img.height);
593
- },
584
+ }
594
585
 
595
- _renderQ_push: function (action) {
586
+ _renderQPush(action) {
596
587
  this._renderQ.push(action);
597
588
  if (this._renderQ.length === 1) {
598
589
  // If this can be rendered immediately it will be, otherwise
599
590
  // the scanner will wait for the relevant event
600
- this._scan_renderQ();
591
+ this._scanRenderQ();
601
592
  }
602
- },
593
+ }
603
594
 
604
- _resume_renderQ: function() {
595
+ _resumeRenderQ() {
605
596
  // "this" is the object that is ready, not the
606
597
  // display object
607
- this.removeEventListener('load', this._noVNC_display._resume_renderQ);
608
- this._noVNC_display._scan_renderQ();
609
- },
598
+ this.removeEventListener('load', this._noVNCDisplay._resumeRenderQ);
599
+ this._noVNCDisplay._scanRenderQ();
600
+ }
610
601
 
611
- _scan_renderQ: function () {
612
- var ready = true;
602
+ _scanRenderQ() {
603
+ let ready = true;
613
604
  while (ready && this._renderQ.length > 0) {
614
- var a = this._renderQ[0];
605
+ const a = this._renderQ[0];
615
606
  switch (a.type) {
616
607
  case 'flip':
617
608
  this.flip(true);
618
609
  break;
619
610
  case 'copy':
620
- this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true);
611
+ this.copyImage(a.oldX, a.oldY, a.x, a.y, a.width, a.height, true);
621
612
  break;
622
613
  case 'fill':
623
614
  this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
@@ -632,11 +623,18 @@ Display.prototype = {
632
623
  this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
633
624
  break;
634
625
  case 'img':
635
- if (a.img.complete) {
626
+ /* IE tends to set "complete" prematurely, so check dimensions */
627
+ if (a.img.complete && (a.img.width !== 0) && (a.img.height !== 0)) {
628
+ if (a.img.width !== a.width || a.img.height !== a.height) {
629
+ Log.Error("Decoded image has incorrect dimensions. Got " +
630
+ a.img.width + "x" + a.img.height + ". Expected " +
631
+ a.width + "x" + a.height + ".");
632
+ return;
633
+ }
636
634
  this.drawImage(a.img, a.x, a.y);
637
635
  } else {
638
- a.img._noVNC_display = this;
639
- a.img.addEventListener('load', this._resume_renderQ);
636
+ a.img._noVNCDisplay = this;
637
+ a.img.addEventListener('load', this._resumeRenderQ);
640
638
  // We need to wait for this image to 'load'
641
639
  // to keep things in-order
642
640
  ready = false;
@@ -653,46 +651,5 @@ Display.prototype = {
653
651
  this._flushing = false;
654
652
  this.onflush();
655
653
  }
656
- },
657
- };
658
-
659
- // Class Methods
660
- Display.changeCursor = function (target, pixels, mask, hotx, hoty, w, h) {
661
- if ((w === 0) || (h === 0)) {
662
- target.style.cursor = 'none';
663
- return;
664
- }
665
-
666
- var cur = []
667
- var y, x;
668
- for (y = 0; y < h; y++) {
669
- for (x = 0; x < w; x++) {
670
- var idx = y * Math.ceil(w / 8) + Math.floor(x / 8);
671
- var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
672
- idx = ((w * y) + x) * 4;
673
- cur.push(pixels[idx + 2]); // red
674
- cur.push(pixels[idx + 1]); // green
675
- cur.push(pixels[idx]); // blue
676
- cur.push(alpha); // alpha
677
- }
678
654
  }
679
-
680
- var canvas = document.createElement('canvas');
681
- var ctx = canvas.getContext('2d');
682
-
683
- canvas.width = w;
684
- canvas.height = h;
685
-
686
- var img;
687
- if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
688
- img = new ImageData(new Uint8ClampedArray(cur), w, h);
689
- } else {
690
- img = ctx.createImageData(w, h);
691
- img.data.set(new Uint8ClampedArray(cur));
692
- }
693
- ctx.clearRect(0, 0, w, h);
694
- ctx.putImageData(img, 0, 0);
695
-
696
- var url = canvas.toDataURL();
697
- target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
698
- };
655
+ }