@novnc/novnc 1.3.0-g1d148a8 → 1.3.0-g1ff2ecd

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 (55) hide show
  1. package/core/des.js +1 -1
  2. package/core/display.js +12 -0
  3. package/core/input/keyboard.js +10 -0
  4. package/core/rfb.js +109 -4
  5. package/core/util/browser.js +56 -7
  6. package/core/util/cursor.js +4 -0
  7. package/docs/API.md +255 -179
  8. package/lib/base64.js +19 -33
  9. package/lib/decoders/copyrect.js +4 -11
  10. package/lib/decoders/hextile.js +16 -46
  11. package/lib/decoders/jpeg.js +4 -43
  12. package/lib/decoders/raw.js +8 -21
  13. package/lib/decoders/rre.js +4 -15
  14. package/lib/decoders/tight.js +12 -78
  15. package/lib/decoders/tightpng.js +3 -23
  16. package/lib/decoders/zrle.js +6 -52
  17. package/lib/deflator.js +8 -22
  18. package/lib/des.js +23 -36
  19. package/lib/display.js +61 -107
  20. package/lib/encodings.js +1 -10
  21. package/lib/inflator.js +5 -18
  22. package/lib/input/domkeytable.js +77 -48
  23. package/lib/input/fixedkeys.js +8 -3
  24. package/lib/input/gesturehandler.js +85 -152
  25. package/lib/input/keyboard.js +61 -90
  26. package/lib/input/keysym.js +14 -270
  27. package/lib/input/keysymdef.js +5 -7
  28. package/lib/input/util.js +42 -84
  29. package/lib/input/vkeys.js +0 -3
  30. package/lib/input/xtscancodes.js +1 -168
  31. package/lib/ra2.js +501 -755
  32. package/lib/rfb.js +477 -1016
  33. package/lib/util/browser.js +65 -28
  34. package/lib/util/cursor.js +28 -65
  35. package/lib/util/element.js +3 -5
  36. package/lib/util/events.js +23 -30
  37. package/lib/util/eventtarget.js +4 -13
  38. package/lib/util/int.js +1 -2
  39. package/lib/util/logging.js +1 -19
  40. package/lib/util/md5.js +10 -36
  41. package/lib/util/strings.js +3 -5
  42. package/lib/vendor/pako/lib/utils/common.js +8 -17
  43. package/lib/vendor/pako/lib/zlib/adler32.js +3 -7
  44. package/lib/vendor/pako/lib/zlib/constants.js +2 -5
  45. package/lib/vendor/pako/lib/zlib/crc32.js +5 -12
  46. package/lib/vendor/pako/lib/zlib/deflate.js +212 -617
  47. package/lib/vendor/pako/lib/zlib/gzheader.js +1 -13
  48. package/lib/vendor/pako/lib/zlib/inffast.js +60 -176
  49. package/lib/vendor/pako/lib/zlib/inflate.js +397 -887
  50. package/lib/vendor/pako/lib/zlib/inftrees.js +62 -168
  51. package/lib/vendor/pako/lib/zlib/messages.js +1 -11
  52. package/lib/vendor/pako/lib/zlib/trees.js +245 -587
  53. package/lib/vendor/pako/lib/zlib/zstream.js +2 -18
  54. package/lib/websock.js +36 -87
  55. package/package.json +31 -31
package/core/des.js CHANGED
@@ -81,7 +81,7 @@
81
81
  const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
82
82
  25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
83
83
  50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
84
- totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];
84
+ totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];
85
85
 
86
86
  const z = 0x0;
87
87
  let a,b,c,d,e,f;
package/core/display.js CHANGED
@@ -224,6 +224,18 @@ export default class Display {
224
224
  this.viewportChangePos(0, 0);
225
225
  }
226
226
 
227
+ getImageData() {
228
+ return this._drawCtx.getImageData(0, 0, this.width, this.height);
229
+ }
230
+
231
+ toDataURL(type, encoderOptions) {
232
+ return this._backbuffer.toDataURL(type, encoderOptions);
233
+ }
234
+
235
+ toBlob(callback, type, quality) {
236
+ return this._backbuffer.toBlob(callback, type, quality);
237
+ }
238
+
227
239
  // Track what parts of the visible canvas that need updating
228
240
  _damage(x, y, w, h) {
229
241
  if (x < this._damageBounds.left) {
@@ -153,6 +153,16 @@ export default class Keyboard {
153
153
  keysym = this._keyDownList[code];
154
154
  }
155
155
 
156
+ // macOS doesn't send proper key releases if a key is pressed
157
+ // while meta is held down
158
+ if ((browser.isMac() || browser.isIOS()) &&
159
+ (e.metaKey && code !== 'MetaLeft' && code !== 'MetaRight')) {
160
+ this._sendKeyEvent(keysym, code, true);
161
+ this._sendKeyEvent(keysym, code, false);
162
+ stopEvent(e);
163
+ return;
164
+ }
165
+
156
166
  // macOS doesn't send proper key events for modifiers, only
157
167
  // state change events. That gets extra confusing for CapsLock
158
168
  // which toggles on each press, but not on release. So pretend
package/core/rfb.js CHANGED
@@ -62,6 +62,7 @@ const securityTypeTight = 16;
62
62
  const securityTypeVeNCrypt = 19;
63
63
  const securityTypeXVP = 22;
64
64
  const securityTypeARD = 30;
65
+ const securityTypeMSLogonII = 113;
65
66
 
66
67
  // Special Tight security types
67
68
  const securityTypeUnixLogon = 129;
@@ -286,6 +287,7 @@ export default class RFB extends EventTargetMixin {
286
287
 
287
288
  this._viewOnly = false;
288
289
  this._clipViewport = false;
290
+ this._clippingViewport = false;
289
291
  this._scaleViewport = false;
290
292
  this._resizeSession = false;
291
293
 
@@ -317,6 +319,16 @@ export default class RFB extends EventTargetMixin {
317
319
 
318
320
  get capabilities() { return this._capabilities; }
319
321
 
322
+ get clippingViewport() { return this._clippingViewport; }
323
+ _setClippingViewport(on) {
324
+ if (on === this._clippingViewport) {
325
+ return;
326
+ }
327
+ this._clippingViewport = on;
328
+ this.dispatchEvent(new CustomEvent("clippingviewport",
329
+ { detail: this._clippingViewport }));
330
+ }
331
+
320
332
  get touchButton() { return 0; }
321
333
  set touchButton(button) { Log.Warn("Using old API!"); }
322
334
 
@@ -490,16 +502,45 @@ export default class RFB extends EventTargetMixin {
490
502
  this._clipboardText = text;
491
503
  RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
492
504
  } else {
493
- let data = new Uint8Array(text.length);
494
- for (let i = 0; i < text.length; i++) {
495
- // FIXME: text can have values outside of Latin1/Uint8
496
- data[i] = text.charCodeAt(i);
505
+ let length, i;
506
+ let data;
507
+
508
+ length = 0;
509
+ // eslint-disable-next-line no-unused-vars
510
+ for (let codePoint of text) {
511
+ length++;
512
+ }
513
+
514
+ data = new Uint8Array(length);
515
+
516
+ i = 0;
517
+ for (let codePoint of text) {
518
+ let code = codePoint.codePointAt(0);
519
+
520
+ /* Only ISO 8859-1 is supported */
521
+ if (code > 0xff) {
522
+ code = 0x3f; // '?'
523
+ }
524
+
525
+ data[i++] = code;
497
526
  }
498
527
 
499
528
  RFB.messages.clientCutText(this._sock, data);
500
529
  }
501
530
  }
502
531
 
532
+ getImageData() {
533
+ return this._display.getImageData();
534
+ }
535
+
536
+ toDataURL(type, encoderOptions) {
537
+ return this._display.toDataURL(type, encoderOptions);
538
+ }
539
+
540
+ toBlob(callback, type, quality) {
541
+ return this._display.toBlob(callback, type, quality);
542
+ }
543
+
503
544
  // ===== PRIVATE METHODS =====
504
545
 
505
546
  _connect() {
@@ -719,6 +760,10 @@ export default class RFB extends EventTargetMixin {
719
760
  const size = this._screenSize();
720
761
  this._display.viewportChangeSize(size.w, size.h);
721
762
  this._fixScrollbars();
763
+ this._setClippingViewport(size.w < this._display.width ||
764
+ size.h < this._display.height);
765
+ } else {
766
+ this._setClippingViewport(false);
722
767
  }
723
768
 
724
769
  // When changing clipping we might show or hide scrollbars.
@@ -1363,6 +1408,7 @@ export default class RFB extends EventTargetMixin {
1363
1408
  securityTypeVeNCrypt,
1364
1409
  securityTypeXVP,
1365
1410
  securityTypeARD,
1411
+ securityTypeMSLogonII,
1366
1412
  securityTypePlain,
1367
1413
  ];
1368
1414
 
@@ -1874,6 +1920,62 @@ export default class RFB extends EventTargetMixin {
1874
1920
  return false;
1875
1921
  }
1876
1922
 
1923
+ _negotiateMSLogonIIAuth() {
1924
+ if (this._sock.rQwait("mslogonii dh param", 24)) { return false; }
1925
+
1926
+ if (this._rfbCredentials.username === undefined ||
1927
+ this._rfbCredentials.password === undefined) {
1928
+ this.dispatchEvent(new CustomEvent(
1929
+ "credentialsrequired",
1930
+ { detail: { types: ["username", "password"] } }));
1931
+ return false;
1932
+ }
1933
+
1934
+ const g = this._sock.rQshiftBytes(8);
1935
+ const p = this._sock.rQshiftBytes(8);
1936
+ const A = this._sock.rQshiftBytes(8);
1937
+ const b = window.crypto.getRandomValues(new Uint8Array(8));
1938
+ const B = new Uint8Array(this._modPow(g, b, p));
1939
+ const secret = new Uint8Array(this._modPow(A, b, p));
1940
+
1941
+ const des = new DES(secret);
1942
+ const username = encodeUTF8(this._rfbCredentials.username).substring(0, 255);
1943
+ const password = encodeUTF8(this._rfbCredentials.password).substring(0, 63);
1944
+ const usernameBytes = new Uint8Array(256);
1945
+ const passwordBytes = new Uint8Array(64);
1946
+ window.crypto.getRandomValues(usernameBytes);
1947
+ window.crypto.getRandomValues(passwordBytes);
1948
+ for (let i = 0; i < username.length; i++) {
1949
+ usernameBytes[i] = username.charCodeAt(i);
1950
+ }
1951
+ usernameBytes[username.length] = 0;
1952
+ for (let i = 0; i < password.length; i++) {
1953
+ passwordBytes[i] = password.charCodeAt(i);
1954
+ }
1955
+ passwordBytes[password.length] = 0;
1956
+ let x = new Uint8Array(secret);
1957
+ for (let i = 0; i < 32; i++) {
1958
+ for (let j = 0; j < 8; j++) {
1959
+ x[j] ^= usernameBytes[i * 8 + j];
1960
+ }
1961
+ x = des.enc8(x);
1962
+ usernameBytes.set(x, i * 8);
1963
+ }
1964
+ x = new Uint8Array(secret);
1965
+ for (let i = 0; i < 8; i++) {
1966
+ for (let j = 0; j < 8; j++) {
1967
+ x[j] ^= passwordBytes[i * 8 + j];
1968
+ }
1969
+ x = des.enc8(x);
1970
+ passwordBytes.set(x, i * 8);
1971
+ }
1972
+ this._sock.send(B);
1973
+ this._sock.send(usernameBytes);
1974
+ this._sock.send(passwordBytes);
1975
+ this._rfbInitState = "SecurityResult";
1976
+ return true;
1977
+ }
1978
+
1877
1979
  _negotiateAuthentication() {
1878
1980
  switch (this._rfbAuthScheme) {
1879
1981
  case securityTypeNone:
@@ -1904,6 +2006,9 @@ export default class RFB extends EventTargetMixin {
1904
2006
  case securityTypeRA2ne:
1905
2007
  return this._negotiateRA2neAuth();
1906
2008
 
2009
+ case securityTypeMSLogonII:
2010
+ return this._negotiateMSLogonIIAuth();
2011
+
1907
2012
  default:
1908
2013
  return this._fail("Unsupported auth scheme (scheme: " +
1909
2014
  this._rfbAuthScheme + ")");
@@ -77,27 +77,76 @@ export const hasScrollbarGutter = _hasScrollbarGutter;
77
77
  * It's better to use feature detection than platform detection.
78
78
  */
79
79
 
80
+ /* OS */
81
+
80
82
  export function isMac() {
81
- return navigator && !!(/mac/i).exec(navigator.platform);
83
+ return !!(/mac/i).exec(navigator.platform);
82
84
  }
83
85
 
84
86
  export function isWindows() {
85
- return navigator && !!(/win/i).exec(navigator.platform);
87
+ return !!(/win/i).exec(navigator.platform);
86
88
  }
87
89
 
88
90
  export function isIOS() {
89
- return navigator &&
90
- (!!(/ipad/i).exec(navigator.platform) ||
91
+ return (!!(/ipad/i).exec(navigator.platform) ||
91
92
  !!(/iphone/i).exec(navigator.platform) ||
92
93
  !!(/ipod/i).exec(navigator.platform));
93
94
  }
94
95
 
96
+ export function isAndroid() {
97
+ /* Android sets navigator.platform to Linux :/ */
98
+ return !!navigator.userAgent.match('Android ');
99
+ }
100
+
101
+ export function isChromeOS() {
102
+ /* ChromeOS sets navigator.platform to Linux :/ */
103
+ return !!navigator.userAgent.match(' CrOS ');
104
+ }
105
+
106
+ /* Browser */
107
+
95
108
  export function isSafari() {
96
- return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
97
- navigator.userAgent.indexOf('Chrome') === -1);
109
+ return !!navigator.userAgent.match('Safari/...') &&
110
+ !navigator.userAgent.match('Chrome/...') &&
111
+ !navigator.userAgent.match('Chromium/...') &&
112
+ !navigator.userAgent.match('Epiphany/...');
98
113
  }
99
114
 
100
115
  export function isFirefox() {
101
- return navigator && !!(/firefox/i).exec(navigator.userAgent);
116
+ return !!navigator.userAgent.match('Firefox/...') &&
117
+ !navigator.userAgent.match('Seamonkey/...');
118
+ }
119
+
120
+ export function isChrome() {
121
+ return !!navigator.userAgent.match('Chrome/...') &&
122
+ !navigator.userAgent.match('Chromium/...') &&
123
+ !navigator.userAgent.match('Edg/...') &&
124
+ !navigator.userAgent.match('OPR/...');
125
+ }
126
+
127
+ export function isChromium() {
128
+ return !!navigator.userAgent.match('Chromium/...');
102
129
  }
103
130
 
131
+ export function isOpera() {
132
+ return !!navigator.userAgent.match('OPR/...');
133
+ }
134
+
135
+ export function isEdge() {
136
+ return !!navigator.userAgent.match('Edg/...');
137
+ }
138
+
139
+ /* Engine */
140
+
141
+ export function isGecko() {
142
+ return !!navigator.userAgent.match('Gecko/...');
143
+ }
144
+
145
+ export function isWebKit() {
146
+ return !!navigator.userAgent.match('AppleWebKit/...') &&
147
+ !navigator.userAgent.match('Chrome/...');
148
+ }
149
+
150
+ export function isBlink() {
151
+ return !!navigator.userAgent.match('Chrome/...');
152
+ }
@@ -18,6 +18,10 @@ export default class Cursor {
18
18
  this._canvas.style.position = 'fixed';
19
19
  this._canvas.style.zIndex = '65535';
20
20
  this._canvas.style.pointerEvents = 'none';
21
+ // Safari on iOS can select the cursor image
22
+ // https://bugs.webkit.org/show_bug.cgi?id=249223
23
+ this._canvas.style.userSelect = 'none';
24
+ this._canvas.style.WebkitUserSelect = 'none';
21
25
  // Can't use "display" because of Firefox bug #1445997
22
26
  this._canvas.style.visibility = 'hidden';
23
27
  }