@novnc/novnc 1.3.0-g42ec5f3 → 1.3.0-g4cfe0ff

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 (54) hide show
  1. package/core/display.js +12 -0
  2. package/core/input/keyboard.js +10 -0
  3. package/core/rfb.js +195 -86
  4. package/docs/API.md +61 -0
  5. package/lib/base64.js +19 -33
  6. package/lib/decoders/copyrect.js +0 -10
  7. package/lib/decoders/hextile.js +12 -44
  8. package/lib/decoders/jpeg.js +0 -42
  9. package/lib/decoders/raw.js +4 -20
  10. package/lib/decoders/rre.js +0 -14
  11. package/lib/decoders/tight.js +8 -76
  12. package/lib/decoders/tightpng.js +2 -24
  13. package/lib/decoders/zrle.js +2 -51
  14. package/lib/deflator.js +4 -21
  15. package/lib/des.js +19 -35
  16. package/lib/display.js +57 -105
  17. package/lib/encodings.js +1 -10
  18. package/lib/inflator.js +1 -17
  19. package/lib/input/domkeytable.js +77 -48
  20. package/lib/input/fixedkeys.js +8 -3
  21. package/lib/input/gesturehandler.js +81 -151
  22. package/lib/input/keyboard.js +57 -88
  23. package/lib/input/keysym.js +14 -270
  24. package/lib/input/keysymdef.js +5 -7
  25. package/lib/input/util.js +42 -84
  26. package/lib/input/vkeys.js +0 -3
  27. package/lib/input/xtscancodes.js +1 -168
  28. package/lib/ra2.js +37 -261
  29. package/lib/rfb.js +466 -1005
  30. package/lib/util/browser.js +16 -25
  31. package/lib/util/cursor.js +20 -64
  32. package/lib/util/element.js +3 -5
  33. package/lib/util/events.js +23 -30
  34. package/lib/util/eventtarget.js +0 -12
  35. package/lib/util/int.js +1 -2
  36. package/lib/util/logging.js +1 -19
  37. package/lib/util/md5.js +7 -27
  38. package/lib/util/strings.js +3 -5
  39. package/lib/vendor/pako/lib/utils/common.js +8 -17
  40. package/lib/vendor/pako/lib/zlib/adler32.js +3 -7
  41. package/lib/vendor/pako/lib/zlib/constants.js +2 -5
  42. package/lib/vendor/pako/lib/zlib/crc32.js +5 -12
  43. package/lib/vendor/pako/lib/zlib/deflate.js +212 -617
  44. package/lib/vendor/pako/lib/zlib/gzheader.js +1 -13
  45. package/lib/vendor/pako/lib/zlib/inffast.js +60 -176
  46. package/lib/vendor/pako/lib/zlib/inflate.js +397 -887
  47. package/lib/vendor/pako/lib/zlib/inftrees.js +62 -168
  48. package/lib/vendor/pako/lib/zlib/messages.js +1 -11
  49. package/lib/vendor/pako/lib/zlib/trees.js +245 -587
  50. package/lib/vendor/pako/lib/zlib/zstream.js +2 -18
  51. package/lib/websock.js +31 -84
  52. package/package.json +1 -1
  53. package/core/util/bigint-mod-arith.js +0 -283
  54. package/lib/util/bigint-mod-arith.js +0 -339
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
@@ -27,7 +27,6 @@ import XtScancode from "./input/xtscancodes.js";
27
27
  import { encodings } from "./encodings.js";
28
28
  import RSAAESAuthenticationState from "./ra2.js";
29
29
  import { MD5 } from "./util/md5.js";
30
- import { modPow } from "./util/bigint-mod-arith.js";
31
30
 
32
31
  import RawDecoder from "./decoders/raw.js";
33
32
  import CopyRectDecoder from "./decoders/copyrect.js";
@@ -55,6 +54,21 @@ const GESTURE_SCRLSENS = 50;
55
54
  const DOUBLE_TAP_TIMEOUT = 1000;
56
55
  const DOUBLE_TAP_THRESHOLD = 50;
57
56
 
57
+ // Security types
58
+ const securityTypeNone = 1;
59
+ const securityTypeVNCAuth = 2;
60
+ const securityTypeRA2ne = 6;
61
+ const securityTypeTight = 16;
62
+ const securityTypeVeNCrypt = 19;
63
+ const securityTypeXVP = 22;
64
+ const securityTypeARD = 30;
65
+
66
+ // Special Tight security types
67
+ const securityTypeUnixLogon = 129;
68
+
69
+ // VeNCrypt security types
70
+ const securityTypePlain = 256;
71
+
58
72
  // Extended clipboard pseudo-encoding formats
59
73
  const extendedClipboardFormatText = 1;
60
74
  /*eslint-disable no-unused-vars */
@@ -80,6 +94,12 @@ export default class RFB extends EventTargetMixin {
80
94
  throw new Error("Must specify URL, WebSocket or RTCDataChannel");
81
95
  }
82
96
 
97
+ // We rely on modern APIs which might not be available in an
98
+ // insecure context
99
+ if (!window.isSecureContext) {
100
+ Log.Error("noVNC requires a secure context (TLS). Expect crashes!");
101
+ }
102
+
83
103
  super();
84
104
 
85
105
  this._target = target;
@@ -397,7 +417,7 @@ export default class RFB extends EventTargetMixin {
397
417
 
398
418
  sendCredentials(creds) {
399
419
  this._rfbCredentials = creds;
400
- setTimeout(this._initMsg.bind(this), 0);
420
+ this._resumeAuthentication();
401
421
  }
402
422
 
403
423
  sendCtrlAltDel() {
@@ -470,16 +490,45 @@ export default class RFB extends EventTargetMixin {
470
490
  this._clipboardText = text;
471
491
  RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
472
492
  } else {
473
- let data = new Uint8Array(text.length);
474
- for (let i = 0; i < text.length; i++) {
475
- // FIXME: text can have values outside of Latin1/Uint8
476
- data[i] = text.charCodeAt(i);
493
+ let length, i;
494
+ let data;
495
+
496
+ length = 0;
497
+ // eslint-disable-next-line no-unused-vars
498
+ for (let codePoint of text) {
499
+ length++;
500
+ }
501
+
502
+ data = new Uint8Array(length);
503
+
504
+ i = 0;
505
+ for (let codePoint of text) {
506
+ let code = codePoint.codePointAt(0);
507
+
508
+ /* Only ISO 8859-1 is supported */
509
+ if (code > 0xff) {
510
+ code = 0x3f; // '?'
511
+ }
512
+
513
+ data[i++] = code;
477
514
  }
478
515
 
479
516
  RFB.messages.clientCutText(this._sock, data);
480
517
  }
481
518
  }
482
519
 
520
+ getImageData() {
521
+ return this._display.getImageData();
522
+ }
523
+
524
+ toDataURL(type, encoderOptions) {
525
+ return this._display.toDataURL(type, encoderOptions);
526
+ }
527
+
528
+ toBlob(callback, type, quality) {
529
+ return this._display.toBlob(callback, type, quality);
530
+ }
531
+
483
532
  // ===== PRIVATE METHODS =====
484
533
 
485
534
  _connect() {
@@ -917,8 +966,15 @@ export default class RFB extends EventTargetMixin {
917
966
  }
918
967
  }
919
968
  break;
969
+ case 'connecting':
970
+ while (this._rfbConnectionState === 'connecting') {
971
+ if (!this._initMsg()) {
972
+ break;
973
+ }
974
+ }
975
+ break;
920
976
  default:
921
- this._initMsg();
977
+ Log.Error("Got data while in an invalid state");
922
978
  break;
923
979
  }
924
980
  }
@@ -1327,6 +1383,21 @@ export default class RFB extends EventTargetMixin {
1327
1383
  this._rfbInitState = 'Security';
1328
1384
  }
1329
1385
 
1386
+ _isSupportedSecurityType(type) {
1387
+ const clientTypes = [
1388
+ securityTypeNone,
1389
+ securityTypeVNCAuth,
1390
+ securityTypeRA2ne,
1391
+ securityTypeTight,
1392
+ securityTypeVeNCrypt,
1393
+ securityTypeXVP,
1394
+ securityTypeARD,
1395
+ securityTypePlain,
1396
+ ];
1397
+
1398
+ return clientTypes.includes(type);
1399
+ }
1400
+
1330
1401
  _negotiateSecurity() {
1331
1402
  if (this._rfbVersion >= 3.7) {
1332
1403
  // Server sends supported list, client decides
@@ -1337,28 +1408,23 @@ export default class RFB extends EventTargetMixin {
1337
1408
  this._rfbInitState = "SecurityReason";
1338
1409
  this._securityContext = "no security types";
1339
1410
  this._securityStatus = 1;
1340
- return this._initMsg();
1411
+ return true;
1341
1412
  }
1342
1413
 
1343
1414
  const types = this._sock.rQshiftBytes(numTypes);
1344
1415
  Log.Debug("Server security types: " + types);
1345
1416
 
1346
- // Look for each auth in preferred order
1347
- if (types.includes(1)) {
1348
- this._rfbAuthScheme = 1; // None
1349
- } else if (types.includes(22)) {
1350
- this._rfbAuthScheme = 22; // XVP
1351
- } else if (types.includes(16)) {
1352
- this._rfbAuthScheme = 16; // Tight
1353
- } else if (types.includes(6)) {
1354
- this._rfbAuthScheme = 6; // RA2ne Auth
1355
- } else if (types.includes(2)) {
1356
- this._rfbAuthScheme = 2; // VNC Auth
1357
- } else if (types.includes(30)) {
1358
- this._rfbAuthScheme = 30; // ARD Auth
1359
- } else if (types.includes(19)) {
1360
- this._rfbAuthScheme = 19; // VeNCrypt Auth
1361
- } else {
1417
+ // Look for a matching security type in the order that the
1418
+ // server prefers
1419
+ this._rfbAuthScheme = -1;
1420
+ for (let type of types) {
1421
+ if (this._isSupportedSecurityType(type)) {
1422
+ this._rfbAuthScheme = type;
1423
+ break;
1424
+ }
1425
+ }
1426
+
1427
+ if (this._rfbAuthScheme === -1) {
1362
1428
  return this._fail("Unsupported security types (types: " + types + ")");
1363
1429
  }
1364
1430
 
@@ -1372,14 +1438,14 @@ export default class RFB extends EventTargetMixin {
1372
1438
  this._rfbInitState = "SecurityReason";
1373
1439
  this._securityContext = "authentication scheme";
1374
1440
  this._securityStatus = 1;
1375
- return this._initMsg();
1441
+ return true;
1376
1442
  }
1377
1443
  }
1378
1444
 
1379
1445
  this._rfbInitState = 'Authentication';
1380
1446
  Log.Debug('Authenticating using scheme: ' + this._rfbAuthScheme);
1381
1447
 
1382
- return this._initMsg(); // jump to authentication
1448
+ return true;
1383
1449
  }
1384
1450
 
1385
1451
  _handleSecurityReason() {
@@ -1429,7 +1495,7 @@ export default class RFB extends EventTargetMixin {
1429
1495
  this._rfbCredentials.username +
1430
1496
  this._rfbCredentials.target;
1431
1497
  this._sock.sendString(xvpAuthStr);
1432
- this._rfbAuthScheme = 2;
1498
+ this._rfbAuthScheme = securityTypeVNCAuth;
1433
1499
  return this._negotiateAuthentication();
1434
1500
  }
1435
1501
 
@@ -1487,49 +1553,66 @@ export default class RFB extends EventTargetMixin {
1487
1553
  subtypes.push(this._sock.rQshift32());
1488
1554
  }
1489
1555
 
1490
- // 256 = Plain subtype
1491
- if (subtypes.indexOf(256) != -1) {
1492
- // 0x100 = 256
1493
- this._sock.send([0, 0, 1, 0]);
1494
- this._rfbVeNCryptState = 4;
1495
- } else {
1496
- return this._fail("VeNCrypt Plain subtype not offered by server");
1556
+ // Look for a matching security type in the order that the
1557
+ // server prefers
1558
+ this._rfbAuthScheme = -1;
1559
+ for (let type of subtypes) {
1560
+ // Avoid getting in to a loop
1561
+ if (type === securityTypeVeNCrypt) {
1562
+ continue;
1563
+ }
1564
+
1565
+ if (this._isSupportedSecurityType(type)) {
1566
+ this._rfbAuthScheme = type;
1567
+ break;
1568
+ }
1497
1569
  }
1498
- }
1499
1570
 
1500
- // negotiated Plain subtype, server waits for password
1501
- if (this._rfbVeNCryptState == 4) {
1502
- if (this._rfbCredentials.username === undefined ||
1503
- this._rfbCredentials.password === undefined) {
1504
- this.dispatchEvent(new CustomEvent(
1505
- "credentialsrequired",
1506
- { detail: { types: ["username", "password"] } }));
1507
- return false;
1571
+ if (this._rfbAuthScheme === -1) {
1572
+ return this._fail("Unsupported security types (types: " + subtypes + ")");
1508
1573
  }
1509
1574
 
1510
- const user = encodeUTF8(this._rfbCredentials.username);
1511
- const pass = encodeUTF8(this._rfbCredentials.password);
1512
-
1513
- this._sock.send([
1514
- (user.length >> 24) & 0xFF,
1515
- (user.length >> 16) & 0xFF,
1516
- (user.length >> 8) & 0xFF,
1517
- user.length & 0xFF
1518
- ]);
1519
- this._sock.send([
1520
- (pass.length >> 24) & 0xFF,
1521
- (pass.length >> 16) & 0xFF,
1522
- (pass.length >> 8) & 0xFF,
1523
- pass.length & 0xFF
1524
- ]);
1525
- this._sock.sendString(user);
1526
- this._sock.sendString(pass);
1575
+ this._sock.send([this._rfbAuthScheme >> 24,
1576
+ this._rfbAuthScheme >> 16,
1577
+ this._rfbAuthScheme >> 8,
1578
+ this._rfbAuthScheme]);
1527
1579
 
1528
- this._rfbInitState = "SecurityResult";
1580
+ this._rfbVeNCryptState == 4;
1529
1581
  return true;
1530
1582
  }
1531
1583
  }
1532
1584
 
1585
+ _negotiatePlainAuth() {
1586
+ if (this._rfbCredentials.username === undefined ||
1587
+ this._rfbCredentials.password === undefined) {
1588
+ this.dispatchEvent(new CustomEvent(
1589
+ "credentialsrequired",
1590
+ { detail: { types: ["username", "password"] } }));
1591
+ return false;
1592
+ }
1593
+
1594
+ const user = encodeUTF8(this._rfbCredentials.username);
1595
+ const pass = encodeUTF8(this._rfbCredentials.password);
1596
+
1597
+ this._sock.send([
1598
+ (user.length >> 24) & 0xFF,
1599
+ (user.length >> 16) & 0xFF,
1600
+ (user.length >> 8) & 0xFF,
1601
+ user.length & 0xFF
1602
+ ]);
1603
+ this._sock.send([
1604
+ (pass.length >> 24) & 0xFF,
1605
+ (pass.length >> 16) & 0xFF,
1606
+ (pass.length >> 8) & 0xFF,
1607
+ pass.length & 0xFF
1608
+ ]);
1609
+ this._sock.sendString(user);
1610
+ this._sock.sendString(pass);
1611
+
1612
+ this._rfbInitState = "SecurityResult";
1613
+ return true;
1614
+ }
1615
+
1533
1616
  _negotiateStdVNCAuth() {
1534
1617
  if (this._sock.rQwait("auth challenge", 16)) { return false; }
1535
1618
 
@@ -1595,7 +1678,19 @@ export default class RFB extends EventTargetMixin {
1595
1678
  let exponentHex = "0x"+Array.from(exponent, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
1596
1679
  let modulusHex = "0x"+Array.from(modulus, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
1597
1680
 
1598
- let hexResult = modPow(BigInt(baseHex), BigInt(exponentHex), BigInt(modulusHex)).toString(16);
1681
+ let b = BigInt(baseHex);
1682
+ let e = BigInt(exponentHex);
1683
+ let m = BigInt(modulusHex);
1684
+ let r = 1n;
1685
+ b = b % m;
1686
+ while (e > 0) {
1687
+ if (e % 2n === 1n) {
1688
+ r = (r * b) % m;
1689
+ }
1690
+ e = e / 2n;
1691
+ b = (b * b) % m;
1692
+ }
1693
+ let hexResult = r.toString(16);
1599
1694
 
1600
1695
  while (hexResult.length/2<exponent.length || (hexResult.length%2 != 0)) {
1601
1696
  hexResult = "0"+hexResult;
@@ -1644,7 +1739,7 @@ export default class RFB extends EventTargetMixin {
1644
1739
  this._rfbCredentials.ardCredentials = encrypted;
1645
1740
  this._rfbCredentials.ardPublicKey = clientPublicKey;
1646
1741
 
1647
- setTimeout(this._initMsg.bind(this), 0);
1742
+ this._resumeAuthentication();
1648
1743
  }
1649
1744
 
1650
1745
  _negotiateTightUnixAuth() {
@@ -1754,12 +1849,12 @@ export default class RFB extends EventTargetMixin {
1754
1849
  case 'STDVNOAUTH__': // no auth
1755
1850
  this._rfbInitState = 'SecurityResult';
1756
1851
  return true;
1757
- case 'STDVVNCAUTH_': // VNC auth
1758
- this._rfbAuthScheme = 2;
1759
- return this._initMsg();
1760
- case 'TGHTULGNAUTH': // UNIX auth
1761
- this._rfbAuthScheme = 129;
1762
- return this._initMsg();
1852
+ case 'STDVVNCAUTH_':
1853
+ this._rfbAuthScheme = securityTypeVNCAuth;
1854
+ return true;
1855
+ case 'TGHTULGNAUTH':
1856
+ this._rfbAuthScheme = securityTypeUnixLogon;
1857
+ return true;
1763
1858
  default:
1764
1859
  return this._fail("Unsupported tiny auth scheme " +
1765
1860
  "(scheme: " + authType + ")");
@@ -1796,7 +1891,7 @@ export default class RFB extends EventTargetMixin {
1796
1891
  }).then(() => {
1797
1892
  this.dispatchEvent(new CustomEvent('securityresult'));
1798
1893
  this._rfbInitState = "SecurityResult";
1799
- this._initMsg();
1894
+ return true;
1800
1895
  }).finally(() => {
1801
1896
  this._rfbRSAAESAuthenticationState.removeEventListener(
1802
1897
  "serververification", this._eventHandlers.handleRSAAESServerVerification);
@@ -1810,33 +1905,32 @@ export default class RFB extends EventTargetMixin {
1810
1905
 
1811
1906
  _negotiateAuthentication() {
1812
1907
  switch (this._rfbAuthScheme) {
1813
- case 1: // no auth
1814
- if (this._rfbVersion >= 3.8) {
1815
- this._rfbInitState = 'SecurityResult';
1816
- return true;
1817
- }
1818
- this._rfbInitState = 'ClientInitialisation';
1819
- return this._initMsg();
1908
+ case securityTypeNone:
1909
+ this._rfbInitState = 'SecurityResult';
1910
+ return true;
1820
1911
 
1821
- case 22: // XVP auth
1912
+ case securityTypeXVP:
1822
1913
  return this._negotiateXvpAuth();
1823
1914
 
1824
- case 30: // ARD auth
1915
+ case securityTypeARD:
1825
1916
  return this._negotiateARDAuth();
1826
1917
 
1827
- case 2: // VNC authentication
1918
+ case securityTypeVNCAuth:
1828
1919
  return this._negotiateStdVNCAuth();
1829
1920
 
1830
- case 16: // TightVNC Security Type
1921
+ case securityTypeTight:
1831
1922
  return this._negotiateTightAuth();
1832
1923
 
1833
- case 19: // VeNCrypt Security Type
1924
+ case securityTypeVeNCrypt:
1834
1925
  return this._negotiateVeNCryptAuth();
1835
1926
 
1836
- case 129: // TightVNC UNIX Security Type
1927
+ case securityTypePlain:
1928
+ return this._negotiatePlainAuth();
1929
+
1930
+ case securityTypeUnixLogon:
1837
1931
  return this._negotiateTightUnixAuth();
1838
1932
 
1839
- case 6: // RA2ne Security Type
1933
+ case securityTypeRA2ne:
1840
1934
  return this._negotiateRA2neAuth();
1841
1935
 
1842
1936
  default:
@@ -1846,6 +1940,13 @@ export default class RFB extends EventTargetMixin {
1846
1940
  }
1847
1941
 
1848
1942
  _handleSecurityResult() {
1943
+ // There is no security choice, and hence no security result
1944
+ // until RFB 3.7
1945
+ if (this._rfbVersion < 3.7) {
1946
+ this._rfbInitState = 'ClientInitialisation';
1947
+ return true;
1948
+ }
1949
+
1849
1950
  if (this._sock.rQwait('VNC auth response ', 4)) { return false; }
1850
1951
 
1851
1952
  const status = this._sock.rQshift32();
@@ -1853,13 +1954,13 @@ export default class RFB extends EventTargetMixin {
1853
1954
  if (status === 0) { // OK
1854
1955
  this._rfbInitState = 'ClientInitialisation';
1855
1956
  Log.Debug('Authentication OK');
1856
- return this._initMsg();
1957
+ return true;
1857
1958
  } else {
1858
1959
  if (this._rfbVersion >= 3.8) {
1859
1960
  this._rfbInitState = "SecurityReason";
1860
1961
  this._securityContext = "security result";
1861
1962
  this._securityStatus = status;
1862
- return this._initMsg();
1963
+ return true;
1863
1964
  } else {
1864
1965
  this.dispatchEvent(new CustomEvent(
1865
1966
  "securityfailure",
@@ -2035,6 +2136,14 @@ export default class RFB extends EventTargetMixin {
2035
2136
  }
2036
2137
  }
2037
2138
 
2139
+ // Resume authentication handshake after it was paused for some
2140
+ // reason, e.g. waiting for a password from the user
2141
+ _resumeAuthentication() {
2142
+ // We use setTimeout() so it's run in its own context, just like
2143
+ // it originally did via the WebSocket's event handler
2144
+ setTimeout(this._initMsg.bind(this), 0);
2145
+ }
2146
+
2038
2147
  _handleSetColourMapMsg() {
2039
2148
  Log.Debug("SetColorMapEntries");
2040
2149
 
package/docs/API.md CHANGED
@@ -155,6 +155,15 @@ protocol stream.
155
155
  [`RFB.clipboardPasteFrom()`](#rfbclipboardpastefrom)
156
156
  - Send clipboard contents to server.
157
157
 
158
+ [`RFB.getImageData()`](#rfbgetimagedata)
159
+ - Return the current content of the screen as an ImageData array.
160
+
161
+ [`RFB.toDataURL()`](#rfbtodataurl)
162
+ - Return the current content of the screen as data-url encoded image file.
163
+
164
+ [`RFB.toBlob()`](#rfbtoblob)
165
+ - Return the current content of the screen as Blob encoded image file.
166
+
158
167
  ### Details
159
168
 
160
169
  #### RFB()
@@ -423,3 +432,55 @@ to the remote server.
423
432
 
424
433
  **`text`**
425
434
  - A `DOMString` specifying the clipboard data to send.
435
+
436
+ #### RFB.getImageData()
437
+
438
+ The `RFB.getImageData()` method is used to return the current content of the
439
+ screen encoded as [`ImageData`](https://developer.mozilla.org/en-US/docs/Web/API/ImageData).
440
+
441
+ ##### Syntax
442
+
443
+ RFB.getImageData();
444
+
445
+ #### RFB.toDataURL()
446
+
447
+ The `RFB.toDataURL()` method is used to return the current content of the
448
+ screen encoded as a data URL that could for example be put in the `src` attribute
449
+ of an `img` tag.
450
+
451
+ ##### Syntax
452
+
453
+ RFB.toDataURL();
454
+ RFB.toDataURL(type);
455
+ RFB.toDataURL(type, encoderOptions);
456
+
457
+ ###### Parameters
458
+
459
+ **`type`** *Optional*
460
+ - A string indicating the requested MIME type of the image
461
+
462
+ **`encoderOptions`** *Optional*
463
+ - A number between 0 and 1 indicating the image quality.
464
+
465
+ #### RFB.toBlob()
466
+
467
+ The `RFB.toBlob()` method is used to return the current content of the
468
+ screen encoded as [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
469
+
470
+ ##### Syntax
471
+
472
+ RFB.toDataURL(callback);
473
+ RFB.toDataURL(callback, type);
474
+ RFB.toDataURL(callback, type, quality);
475
+
476
+ ###### Parameters
477
+
478
+ **`callback`**
479
+ - A callback function which will receive the resulting [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
480
+ as the single argument
481
+
482
+ **`type`** *Optional*
483
+ - A string indicating the requested MIME type of the image
484
+
485
+ **`encoderOptions`** *Optional*
486
+ - A number between 0 and 1 indicating the image quality.