This commit is contained in:
Pierre Ossman 2022-12-27 13:13:48 +01:00
commit 1ff2ecd9f0
12 changed files with 623 additions and 350 deletions

View File

@ -26,10 +26,13 @@
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"indent": ["error", 4, { "SwitchCase": 1,
"VariableDeclarator": "first",
"FunctionDeclaration": { "parameters": "first" },
"FunctionExpression": { "parameters": "first" },
"CallExpression": { "arguments": "first" },
"ArrayExpression": "first",
"ObjectExpression": "first",
"ImportDeclaration": "first",
"ignoreComments": true }],
"comma-spacing": ["error"],
"comma-style": ["error"],

View File

@ -8,7 +8,8 @@
import * as Log from '../core/util/logging.js';
import _, { l10n } from './localization.js';
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold }
import { isTouchDevice, isMac, isIOS, isAndroid, isChromeOS, isSafari,
hasScrollbarGutter, dragThreshold }
from '../core/util/browser.js';
import { setCapture, getPointerEvent } from '../core/util/events.js';
import KeyTable from "../core/input/keysym.js";
@ -1049,6 +1050,7 @@ const UI = {
UI.rfb.addEventListener("serververification", UI.serverVerify);
UI.rfb.addEventListener("credentialsrequired", UI.credentials);
UI.rfb.addEventListener("securityfailure", UI.securityFailed);
UI.rfb.addEventListener("clippingviewport", UI.updateViewDrag);
UI.rfb.addEventListener("capabilities", UI.updatePowerButton);
UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
UI.rfb.addEventListener("bell", UI.bell);
@ -1325,13 +1327,25 @@ const UI = {
const scaling = UI.getSetting('resize') === 'scale';
// Some platforms have overlay scrollbars that are difficult
// to use in our case, which means we have to force panning
// FIXME: Working scrollbars can still be annoying to use with
// touch, so we should ideally be able to have both
// panning and scrollbars at the same time
let brokenScrollbars = false;
if (!hasScrollbarGutter) {
if (isIOS() || isAndroid() || isMac() || isChromeOS()) {
brokenScrollbars = true;
}
}
if (scaling) {
// Can't be clipping if viewport is scaled to fit
UI.forceSetting('view_clip', false);
UI.rfb.clipViewport = false;
} else if (!hasScrollbarGutter) {
// Some platforms have scrollbars that are difficult
// to use in our case, so we always use our own panning
} else if (brokenScrollbars) {
UI.forceSetting('view_clip', true);
UI.rfb.clipViewport = true;
} else {
@ -1362,7 +1376,8 @@ const UI = {
const viewDragButton = document.getElementById('noVNC_view_drag_button');
if (!UI.rfb.clipViewport && UI.rfb.dragViewport) {
if ((!UI.rfb.clipViewport || !UI.rfb.clippingViewport) &&
UI.rfb.dragViewport) {
// We are no longer clipping the viewport. Make sure
// viewport drag isn't active when it can't be used.
UI.rfb.dragViewport = false;
@ -1379,6 +1394,8 @@ const UI = {
} else {
viewDragButton.classList.add("noVNC_hidden");
}
viewDragButton.disabled = !UI.rfb.clippingViewport;
},
/* ------^-------

View File

@ -32,7 +32,7 @@ export function initLogging(level) {
export function getQueryVar(name, defVal) {
"use strict";
const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = ''.concat(document.location.href, window.location.hash).match(re);
match = ''.concat(document.location.href, window.location.hash).match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {
@ -46,7 +46,7 @@ export function getQueryVar(name, defVal) {
export function getHashVar(name, defVal) {
"use strict";
const re = new RegExp('.*[&#]' + name + '=([^&]*)'),
match = document.location.hash.match(re);
match = document.location.hash.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {

View File

@ -81,7 +81,7 @@
const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];
const z = 0x0;
let a,b,c,d,e,f;

View File

@ -287,6 +287,7 @@ export default class RFB extends EventTargetMixin {
this._viewOnly = false;
this._clipViewport = false;
this._clippingViewport = false;
this._scaleViewport = false;
this._resizeSession = false;
@ -318,6 +319,16 @@ export default class RFB extends EventTargetMixin {
get capabilities() { return this._capabilities; }
get clippingViewport() { return this._clippingViewport; }
_setClippingViewport(on) {
if (on === this._clippingViewport) {
return;
}
this._clippingViewport = on;
this.dispatchEvent(new CustomEvent("clippingviewport",
{ detail: this._clippingViewport }));
}
get touchButton() { return 0; }
set touchButton(button) { Log.Warn("Using old API!"); }
@ -749,6 +760,10 @@ export default class RFB extends EventTargetMixin {
const size = this._screenSize();
this._display.viewportChangeSize(size.w, size.h);
this._fixScrollbars();
this._setClippingViewport(size.w < this._display.width ||
size.h < this._display.height);
} else {
this._setClippingViewport(false);
}
// When changing clipping we might show or hide scrollbars.

View File

@ -77,27 +77,76 @@ export const hasScrollbarGutter = _hasScrollbarGutter;
* It's better to use feature detection than platform detection.
*/
/* OS */
export function isMac() {
return navigator && !!(/mac/i).exec(navigator.platform);
return !!(/mac/i).exec(navigator.platform);
}
export function isWindows() {
return navigator && !!(/win/i).exec(navigator.platform);
return !!(/win/i).exec(navigator.platform);
}
export function isIOS() {
return navigator &&
(!!(/ipad/i).exec(navigator.platform) ||
return (!!(/ipad/i).exec(navigator.platform) ||
!!(/iphone/i).exec(navigator.platform) ||
!!(/ipod/i).exec(navigator.platform));
}
export function isAndroid() {
/* Android sets navigator.platform to Linux :/ */
return !!navigator.userAgent.match('Android ');
}
export function isChromeOS() {
/* ChromeOS sets navigator.platform to Linux :/ */
return !!navigator.userAgent.match(' CrOS ');
}
/* Browser */
export function isSafari() {
return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
navigator.userAgent.indexOf('Chrome') === -1);
return !!navigator.userAgent.match('Safari/...') &&
!navigator.userAgent.match('Chrome/...') &&
!navigator.userAgent.match('Chromium/...') &&
!navigator.userAgent.match('Epiphany/...');
}
export function isFirefox() {
return navigator && !!(/firefox/i).exec(navigator.userAgent);
return !!navigator.userAgent.match('Firefox/...') &&
!navigator.userAgent.match('Seamonkey/...');
}
export function isChrome() {
return !!navigator.userAgent.match('Chrome/...') &&
!navigator.userAgent.match('Chromium/...') &&
!navigator.userAgent.match('Edg/...') &&
!navigator.userAgent.match('OPR/...');
}
export function isChromium() {
return !!navigator.userAgent.match('Chromium/...');
}
export function isOpera() {
return !!navigator.userAgent.match('OPR/...');
}
export function isEdge() {
return !!navigator.userAgent.match('Edg/...');
}
/* Engine */
export function isGecko() {
return !!navigator.userAgent.match('Gecko/...');
}
export function isWebKit() {
return !!navigator.userAgent.match('AppleWebKit/...') &&
!navigator.userAgent.match('Chrome/...');
}
export function isBlink() {
return !!navigator.userAgent.match('Chrome/...');
}

View File

@ -16,61 +16,12 @@ protocol stream.
### Properties
`viewOnly`
- Is a `boolean` indicating if any events (e.g. key presses or mouse
movement) should be prevented from being sent to the server.
Disabled by default.
`focusOnClick`
- Is a `boolean` indicating if keyboard focus should automatically be
moved to the remote session when a `mousedown` or `touchstart`
event is received. Enabled by default.
`clipViewport`
- Is a `boolean` indicating if the remote session should be clipped
to its container. When disabled scrollbars will be shown to handle
the resulting overflow. Disabled by default.
`dragViewport`
- Is a `boolean` indicating if mouse events should control the
relative position of a clipped remote session. Only relevant if
`clipViewport` is enabled. Disabled by default.
`scaleViewport`
- Is a `boolean` indicating if the remote session should be scaled
locally so it fits its container. When disabled it will be centered
if the remote session is smaller than its container, or handled
according to `clipViewport` if it is larger. Disabled by default.
`resizeSession`
- Is a `boolean` indicating if a request to resize the remote session
should be sent whenever the container changes dimensions. Disabled
by default.
`showDotCursor`
- Is a `boolean` indicating whether a dot cursor should be shown
instead of a zero-sized or fully-transparent cursor if the server
sets such invisible cursor. Disabled by default.
`background`
- Is a valid CSS [background](https://developer.mozilla.org/en-US/docs/Web/CSS/background)
style value indicating which background style should be applied
to the element containing the remote session screen. The default value is `rgb(40, 40, 40)`
(solid gray color).
`qualityLevel`
- Is an `int` in range `[0-9]` controlling the desired JPEG quality.
Value `0` implies low quality and `9` implies high quality.
Default value is `6`.
`compressionLevel`
- Is an `int` in range `[0-9]` controlling the desired compression
level. Value `0` means no compression. Level 1 uses a minimum of CPU
resources and achieves weak compression ratios, while level 9 offers
best compression but is slow in terms of CPU consumption on the server
side. Use high levels with very slow network connections.
Default value is `2`.
`capabilities` *Read only*
- Is an `Object` indicating which optional extensions are available
on the server. Some methods may only be called if the corresponding
@ -80,71 +31,122 @@ protocol stream.
| -------- | --------- | -----------
| `power` | `boolean` | Machine power control is available
`clippingViewport` *Read only*
- Is a `boolean` indicating if the remote session is currently being
clipped to its container. Only relevant if `clipViewport` is
enabled.
`clipViewport`
- Is a `boolean` indicating if the remote session should be clipped
to its container. When disabled scrollbars will be shown to handle
the resulting overflow. Disabled by default.
`compressionLevel`
- Is an `int` in range `[0-9]` controlling the desired compression
level. Value `0` means no compression. Level 1 uses a minimum of CPU
resources and achieves weak compression ratios, while level 9 offers
best compression but is slow in terms of CPU consumption on the server
side. Use high levels with very slow network connections.
Default value is `2`.
`dragViewport`
- Is a `boolean` indicating if mouse events should control the
relative position of a clipped remote session. Only relevant if
`clipViewport` is enabled. Disabled by default.
`focusOnClick`
- Is a `boolean` indicating if keyboard focus should automatically be
moved to the remote session when a `mousedown` or `touchstart`
event is received. Enabled by default.
`qualityLevel`
- Is an `int` in range `[0-9]` controlling the desired JPEG quality.
Value `0` implies low quality and `9` implies high quality.
Default value is `6`.
`resizeSession`
- Is a `boolean` indicating if a request to resize the remote session
should be sent whenever the container changes dimensions. Disabled
by default.
`scaleViewport`
- Is a `boolean` indicating if the remote session should be scaled
locally so it fits its container. When disabled it will be centered
if the remote session is smaller than its container, or handled
according to `clipViewport` if it is larger. Disabled by default.
`showDotCursor`
- Is a `boolean` indicating whether a dot cursor should be shown
instead of a zero-sized or fully-transparent cursor if the server
sets such invisible cursor. Disabled by default.
`viewOnly`
- Is a `boolean` indicating if any events (e.g. key presses or mouse
movement) should be prevented from being sent to the server.
Disabled by default.
### Events
[`connect`](#connect)
- The `connect` event is fired when the `RFB` object has completed
the connection and handshaking with the server.
[`disconnect`](#disconnect)
- The `disconnect` event is fired when the `RFB` object disconnects.
[`serververification`](#serververification)
- The `serververification` event is fired when the server identity
must be confirmed by the user.
[`credentialsrequired`](#credentialsrequired)
- The `credentialsrequired` event is fired when more credentials must
be given to continue.
[`securityfailure`](#securityfailure)
- The `securityfailure` event is fired when the security negotiation
with the server fails.
[`clipboard`](#clipboard)
- The `clipboard` event is fired when clipboard data is received from
the server.
[`bell`](#bell)
- The `bell` event is fired when a audible bell request is received
from the server.
[`desktopname`](#desktopname)
- The `desktopname` event is fired when the remote desktop name
changes.
[`capabilities`](#capabilities)
- The `capabilities` event is fired when `RFB.capabilities` is
updated.
### Methods
[`clipboard`](#clipboard)
- The `clipboard` event is fired when clipboard data is received from
the server.
[`RFB.disconnect()`](#rfbdisconnect)
- Disconnect from the server.
[`clippingviewport`](#clippingviewport)
- The `clippingviewport` event is fired when `RFB.clippingViewport` is
updated.
[`connect`](#connect)
- The `connect` event is fired when the `RFB` object has completed
the connection and handshaking with the server.
[`credentialsrequired`](#credentialsrequired)
- The `credentialsrequired` event is fired when more credentials must
be given to continue.
[`desktopname`](#desktopname)
- The `desktopname` event is fired when the remote desktop name
changes.
[`disconnect`](#disconnect)
- The `disconnect` event is fired when the `RFB` object disconnects.
[`securityfailure`](#securityfailure)
- The `securityfailure` event is fired when the security negotiation
with the server fails.
[`serververification`](#serververification)
- The `serververification` event is fired when the server identity
must be confirmed by the user.
### Methods
[`RFB.approveServer()`](#rfbapproveserver)
- Proceed connecting to the server. Should be called after the
[`serververification`](#serververification) event has fired and the
user has verified the identity of the server.
[`RFB.sendCredentials()`](#rfbsendcredentials)
- Send credentials to server. Should be called after the
[`credentialsrequired`](#credentialsrequired) event has fired.
[`RFB.blur()`](#rfbblur)
- Remove keyboard focus from the remote session.
[`RFB.sendKey()`](#rfbsendkey)
- Send a key event.
[`RFB.clipboardPasteFrom()`](#rfbclipboardpastefrom)
- Send clipboard contents to server.
[`RFB.sendCtrlAltDel()`](#rfbsendctrlaltdel)
- Send Ctrl-Alt-Del key sequence.
[`RFB.disconnect()`](#rfbdisconnect)
- Disconnect from the server.
[`RFB.focus()`](#rfbfocus)
- Move keyboard focus to the remote session.
[`RFB.blur()`](#rfbblur)
- Remove keyboard focus from the remote session.
[`RFB.machineShutdown()`](#rfbmachineshutdown)
- Request a shutdown of the remote machine.
[`RFB.getImageData()`](#rfbgetimagedata)
- Return the current content of the screen as an ImageData array.
[`RFB.machineReboot()`](#rfbmachinereboot)
- Request a reboot of the remote machine.
@ -152,18 +154,25 @@ protocol stream.
[`RFB.machineReset()`](#rfbmachinereset)
- Request a reset of the remote machine.
[`RFB.clipboardPasteFrom()`](#rfbclipboardpastefrom)
- Send clipboard contents to server.
[`RFB.machineShutdown()`](#rfbmachineshutdown)
- Request a shutdown of the remote machine.
[`RFB.getImageData()`](#rfbgetimagedata)
- Return the current content of the screen as an ImageData array.
[`RFB.sendCredentials()`](#rfbsendcredentials)
- Send credentials to server. Should be called after the
[`credentialsrequired`](#credentialsrequired) event has fired.
[`RFB.toDataURL()`](#rfbtodataurl)
- Return the current content of the screen as data-url encoded image file.
[`RFB.sendCtrlAltDel()`](#rfbsendctrlaltdel)
- Send Ctrl-Alt-Del key sequence.
[`RFB.sendKey()`](#rfbsendkey)
- Send a key event.
[`RFB.toBlob()`](#rfbtoblob)
- Return the current content of the screen as Blob encoded image file.
[`RFB.toDataURL()`](#rfbtodataurl)
- Return the current content of the screen as data-url encoded image file.
### Details
#### RFB()
@ -216,12 +225,48 @@ connection to a specified VNC server.
- An `Array` of `DOMString`s specifying the sub-protocols to use
in the WebSocket connection. Empty by default.
#### bell
The `bell` event is fired when the server has requested an audible
bell.
#### capabilities
The `capabilities` event is fired whenever an entry is added or removed
from `RFB.capabilities`. The `detail` property is an `Object` with the
property `capabilities` containing the new value of `RFB.capabilities`.
#### clippingviewport
The `clippingviewport` event is fired whenever `RFB.clippingViewport`
changes between `true` and `false`. The `detail` property is a `boolean`
with the new value of `RFB.clippingViewport`.
#### clipboard
The `clipboard` event is fired when the server has sent clipboard data.
The `detail` property is an `Object` containing the property `text`
which is a `DOMString` with the clipboard data.
#### credentialsrequired
The `credentialsrequired` event is fired when the server requests more
credentials than were specified to [`RFB()`](#rfb-1). The `detail`
property is an `Object` containing the property `types` which is an
`Array` of `DOMString` listing the credentials that are required.
#### connect
The `connect` event is fired after all the handshaking with the server
is completed and the connection is fully established. After this event
the `RFB` object is ready to recieve graphics updates and to send input.
#### desktopname
The `desktopname` event is fired when the name of the remote desktop
changes. The `detail` property is an `Object` with the property `name`
which is a `DOMString` specifying the new name.
#### disconnect
The `disconnect` event is fired when the connection has been
@ -230,27 +275,6 @@ property `clean`. `clean` is a `boolean` indicating if the termination
was clean or not. In the event of an unexpected termination or an error
`clean` will be set to false.
#### serververification
The `serververification` event is fired when the server provides
information that allows the user to verify that it is the correct server
and protect against a man-in-the-middle attack. The `detail` property is
an `Object` containing the property `type` which is a `DOMString`
specifying which type of information the server has provided. Other
properties are also available, depending on the value of `type`:
`"RSA"`
- The server identity is verified using just a RSA key. The property
`publickey` is a `Uint8Array` containing the public key in a unsigned
big endian representation.
#### credentialsrequired
The `credentialsrequired` event is fired when the server requests more
credentials than were specified to [`RFB()`](#rfb-1). The `detail`
property is an `Object` containing the property `types` which is an
`Array` of `DOMString` listing the credentials that are required.
#### securityfailure
The `securityfailure` event is fired when the handshaking process with
@ -271,37 +295,19 @@ thus the language of the string is not known. However most servers will
probably send English strings. The server can choose to not send a
reason and in these cases the `reason` property will be omitted.
#### clipboard
#### serververification
The `clipboard` event is fired when the server has sent clipboard data.
The `detail` property is an `Object` containing the property `text`
which is a `DOMString` with the clipboard data.
The `serververification` event is fired when the server provides
information that allows the user to verify that it is the correct server
and protect against a man-in-the-middle attack. The `detail` property is
an `Object` containing the property `type` which is a `DOMString`
specifying which type of information the server has provided. Other
properties are also available, depending on the value of `type`:
#### bell
The `bell` event is fired when the server has requested an audible
bell.
#### desktopname
The `desktopname` event is fired when the name of the remote desktop
changes. The `detail` property is an `Object` with the property `name`
which is a `DOMString` specifying the new name.
#### capabilities
The `capabilities` event is fired whenever an entry is added or removed
from `RFB.capabilities`. The `detail` property is an `Object` with the
property `capabilities` containing the new value of `RFB.capabilities`.
#### RFB.disconnect()
The `RFB.disconnect()` method is used to disconnect from the currently
connected server.
##### Syntax
RFB.disconnect( );
`"RSA"`
- The server identity is verified using just a RSA key. The property
`publickey` is a `Uint8Array` containing the public key in a unsigned
big endian representation.
#### RFB.approveServer()
@ -313,6 +319,94 @@ and that the connection can continue.
RFB.approveServer( );
#### RFB.blur()
The `RFB.blur()` method remove keyboard focus on the remote session.
Keyboard events will no longer be sent to the remote server after this
point.
##### Syntax
RFB.blur( );
#### RFB.clipboardPasteFrom()
The `RFB.clipboardPasteFrom()` method is used to send clipboard data
to the remote server.
##### Syntax
RFB.clipboardPasteFrom( text );
###### Parameters
**`text`**
- A `DOMString` specifying the clipboard data to send.
#### RFB.disconnect()
The `RFB.disconnect()` method is used to disconnect from the currently
connected server.
##### Syntax
RFB.disconnect( );
#### RFB.focus()
The `RFB.focus()` method sets the keyboard focus on the remote session.
Keyboard events will be sent to the remote server after this point.
##### Syntax
RFB.focus( [options] );
###### Parameters
**`options`** *Optional*
- A `object` providing options to control how the focus will be
performed. Please see [`HTMLElement.focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus)
for available options.
#### RFB.getImageData()
The `RFB.getImageData()` method is used to return the current content of the
screen encoded as [`ImageData`](https://developer.mozilla.org/en-US/docs/Web/API/ImageData).
##### Syntax
RFB.getImageData();
#### RFB.machineReboot()
The `RFB.machineReboot()` method is used to request a clean reboot of
the remote machine. The capability `power` must be set for this method
to have any effect.
##### Syntax
RFB.machineReboot( );
#### RFB.machineReset()
The `RFB.machineReset()` method is used to request a forced reset of
the remote machine. The capability `power` must be set for this method
to have any effect.
##### Syntax
RFB.machineReset( );
#### RFB.machineShutdown()
The `RFB.machineShutdown()` method is used to request to shut down the
remote machine. The capability `power` must be set for this method to
have any effect.
##### Syntax
RFB.machineShutdown( );
#### RFB.sendCredentials()
The `RFB.sendCredentials()` method is used to provide the missing
@ -328,6 +422,16 @@ credentials after a `credentialsrequired` event has been fired.
- An `Object` specifying the credentials to provide to the server
when authenticating. See [`RFB()`](#rfb-1) for details.
#### RFB.sendCtrlAltDel()
The `RFB.sendCtrlAltDel()` method is used to send the key sequence
*left Control*, *left Alt*, *Delete*. This is a convenience wrapper
around [`RFB.sendKey()`](#rfbsendkey).
##### Syntax
RFB.sendCtrlAltDel( );
#### RFB.sendKey()
The `RFB.sendKey()` method is used to send a key event to the server.
@ -353,115 +457,6 @@ The `RFB.sendKey()` method is used to send a key event to the server.
- A `boolean` specifying if a press or a release event should be
sent. If omitted then both a press and release event are sent.
#### RFB.sendCtrlAltDel()
The `RFB.sendCtrlAltDel()` method is used to send the key sequence
*left Control*, *left Alt*, *Delete*. This is a convenience wrapper
around [`RFB.sendKey()`](#rfbsendkey).
##### Syntax
RFB.sendCtrlAltDel( );
#### RFB.focus()
The `RFB.focus()` method sets the keyboard focus on the remote session.
Keyboard events will be sent to the remote server after this point.
##### Syntax
RFB.focus( [options] );
###### Parameters
**`options`** *Optional*
- A `object` providing options to control how the focus will be
performed. Please see [`HTMLElement.focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus)
for available options.
#### RFB.blur()
The `RFB.blur()` method remove keyboard focus on the remote session.
Keyboard events will no longer be sent to the remote server after this
point.
##### Syntax
RFB.blur( );
#### RFB.machineShutdown()
The `RFB.machineShutdown()` method is used to request to shut down the
remote machine. The capability `power` must be set for this method to
have any effect.
##### Syntax
RFB.machineShutdown( );
#### RFB.machineReboot()
The `RFB.machineReboot()` method is used to request a clean reboot of
the remote machine. The capability `power` must be set for this method
to have any effect.
##### Syntax
RFB.machineReboot( );
#### RFB.machineReset()
The `RFB.machineReset()` method is used to request a forced reset of
the remote machine. The capability `power` must be set for this method
to have any effect.
##### Syntax
RFB.machineReset( );
#### RFB.clipboardPasteFrom()
The `RFB.clipboardPasteFrom()` method is used to send clipboard data
to the remote server.
##### Syntax
RFB.clipboardPasteFrom( text );
###### Parameters
**`text`**
- A `DOMString` specifying the clipboard data to send.
#### RFB.getImageData()
The `RFB.getImageData()` method is used to return the current content of the
screen encoded as [`ImageData`](https://developer.mozilla.org/en-US/docs/Web/API/ImageData).
##### Syntax
RFB.getImageData();
#### RFB.toDataURL()
The `RFB.toDataURL()` method is used to return the current content of the
screen encoded as a data URL that could for example be put in the `src` attribute
of an `img` tag.
##### Syntax
RFB.toDataURL();
RFB.toDataURL(type);
RFB.toDataURL(type, encoderOptions);
###### Parameters
**`type`** *Optional*
- A string indicating the requested MIME type of the image
**`encoderOptions`** *Optional*
- A number between 0 and 1 indicating the image quality.
#### RFB.toBlob()
The `RFB.toBlob()` method is used to return the current content of the
@ -484,3 +479,23 @@ screen encoded as [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob
**`encoderOptions`** *Optional*
- A number between 0 and 1 indicating the image quality.
#### RFB.toDataURL()
The `RFB.toDataURL()` method is used to return the current content of the
screen encoded as a data URL that could for example be put in the `src` attribute
of an `img` tag.
##### Syntax
RFB.toDataURL();
RFB.toDataURL(type);
RFB.toDataURL(type, encoderOptions);
###### Parameters
**`type`** *Optional*
- A string indicating the requested MIME type of the image
**`encoderOptions`** *Optional*
- A number between 0 and 1 indicating the image quality.

244
tests/test.browser.js Normal file
View File

@ -0,0 +1,244 @@
/* eslint-disable no-console */
const expect = chai.expect;
import { isMac, isWindows, isIOS, isAndroid, isChromeOS,
isSafari, isFirefox, isChrome, isChromium, isOpera, isEdge,
isGecko, isWebKit, isBlink } from '../core/util/browser.js';
describe('OS detection', function () {
let origNavigator;
beforeEach(function () {
// window.navigator is a protected read-only property in many
// environments, so we need to redefine it whilst running these
// tests.
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
});
afterEach(function () {
Object.defineProperty(window, "navigator", origNavigator);
});
it('should handle macOS', function () {
const platforms = [
"MacIntel",
"MacPPC",
];
navigator.userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15";
platforms.forEach((platform) => {
navigator.platform = platform;
expect(isMac()).to.be.true;
expect(isWindows()).to.be.false;
expect(isIOS()).to.be.false;
expect(isAndroid()).to.be.false;
expect(isChromeOS()).to.be.false;
});
});
it('should handle Windows', function () {
const platforms = [
"Win32",
"Win64",
];
navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36";
platforms.forEach((platform) => {
navigator.platform = platform;
expect(isMac()).to.be.false;
expect(isWindows()).to.be.true;
expect(isIOS()).to.be.false;
expect(isAndroid()).to.be.false;
expect(isChromeOS()).to.be.false;
});
});
it('should handle iOS', function () {
const platforms = [
"iPhone",
"iPod",
"iPad",
];
navigator.userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1";
platforms.forEach((platform) => {
navigator.platform = platform;
expect(isMac()).to.be.false;
expect(isWindows()).to.be.false;
expect(isIOS()).to.be.true;
expect(isAndroid()).to.be.false;
expect(isChromeOS()).to.be.false;
});
});
it('should handle Android', function () {
let userAgents = [
"Mozilla/5.0 (Linux; Android 13; SM-G960U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.128 Mobile Safari/537.36",
"Mozilla/5.0 (Android 13; Mobile; LG-M255; rv:108.0) Gecko/108.0 Firefox/108.0",
];
navigator.platform = "Linux x86_64";
userAgents.forEach((ua) => {
navigator.userAgent = ua;
expect(isMac()).to.be.false;
expect(isWindows()).to.be.false;
expect(isIOS()).to.be.false;
expect(isAndroid()).to.be.true;
expect(isChromeOS()).to.be.false;
});
});
it('should handle ChromeOS', function () {
let userAgents = [
"Mozilla/5.0 (X11; CrOS x86_64 15183.59.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.75 Safari/537.36",
"Mozilla/5.0 (X11; CrOS aarch64 15183.59.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.75 Safari/537.36",
];
navigator.platform = "Linux x86_64";
userAgents.forEach((ua) => {
navigator.userAgent = ua;
expect(isMac()).to.be.false;
expect(isWindows()).to.be.false;
expect(isIOS()).to.be.false;
expect(isAndroid()).to.be.false;
expect(isChromeOS()).to.be.true;
});
});
});
describe('Browser detection', function () {
let origNavigator;
beforeEach(function () {
// window.navigator is a protected read-only property in many
// environments, so we need to redefine it whilst running these
// tests.
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
});
afterEach(function () {
Object.defineProperty(window, "navigator", origNavigator);
});
it('should handle Chrome', function () {
navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36";
expect(isSafari()).to.be.false;
expect(isFirefox()).to.be.false;
expect(isChrome()).to.be.true;
expect(isChromium()).to.be.false;
expect(isOpera()).to.be.false;
expect(isEdge()).to.be.false;
expect(isGecko()).to.be.false;
expect(isWebKit()).to.be.false;
expect(isBlink()).to.be.true;
});
it('should handle Chromium', function () {
navigator.userAgent = "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Raspbian Chromium/74.0.3729.157 Chrome/74.0.3729.157 Safari/537.36";
expect(isSafari()).to.be.false;
expect(isFirefox()).to.be.false;
expect(isChrome()).to.be.false;
expect(isChromium()).to.be.true;
expect(isOpera()).to.be.false;
expect(isEdge()).to.be.false;
expect(isGecko()).to.be.false;
expect(isWebKit()).to.be.false;
expect(isBlink()).to.be.true;
});
it('should handle Firefox', function () {
navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0";
expect(isSafari()).to.be.false;
expect(isFirefox()).to.be.true;
expect(isChrome()).to.be.false;
expect(isChromium()).to.be.false;
expect(isOpera()).to.be.false;
expect(isEdge()).to.be.false;
expect(isGecko()).to.be.true;
expect(isWebKit()).to.be.false;
expect(isBlink()).to.be.false;
});
it('should handle Seamonkey', function () {
navigator.userAgent = "Mozilla/5.0 (Windows NT 6.1; rv:36.0) Gecko/20100101 Firefox/36.0 Seamonkey/2.33.1";
expect(isSafari()).to.be.false;
expect(isFirefox()).to.be.false;
expect(isChrome()).to.be.false;
expect(isChromium()).to.be.false;
expect(isOpera()).to.be.false;
expect(isEdge()).to.be.false;
expect(isGecko()).to.be.true;
expect(isWebKit()).to.be.false;
expect(isBlink()).to.be.false;
});
it('should handle Safari', function () {
navigator.userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15";
expect(isSafari()).to.be.true;
expect(isFirefox()).to.be.false;
expect(isChrome()).to.be.false;
expect(isChromium()).to.be.false;
expect(isOpera()).to.be.false;
expect(isEdge()).to.be.false;
expect(isGecko()).to.be.false;
expect(isWebKit()).to.be.true;
expect(isBlink()).to.be.false;
});
it('should handle Edge', function () {
navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.34";
expect(isSafari()).to.be.false;
expect(isFirefox()).to.be.false;
expect(isChrome()).to.be.false;
expect(isChromium()).to.be.false;
expect(isOpera()).to.be.false;
expect(isEdge()).to.be.true;
expect(isGecko()).to.be.false;
expect(isWebKit()).to.be.false;
expect(isBlink()).to.be.true;
});
it('should handle Opera', function () {
navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 OPR/91.0.4516.20";
expect(isSafari()).to.be.false;
expect(isFirefox()).to.be.false;
expect(isChrome()).to.be.false;
expect(isChromium()).to.be.false;
expect(isOpera()).to.be.true;
expect(isEdge()).to.be.false;
expect(isGecko()).to.be.false;
expect(isWebKit()).to.be.false;
expect(isBlink()).to.be.true;
});
it('should handle Epiphany', function () {
navigator.userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Safari/605.1.15 Epiphany/605.1.15";
expect(isSafari()).to.be.false;
expect(isFirefox()).to.be.false;
expect(isChrome()).to.be.false;
expect(isChromium()).to.be.false;
expect(isOpera()).to.be.false;
expect(isEdge()).to.be.false;
expect(isGecko()).to.be.false;
expect(isWebKit()).to.be.true;
expect(isBlink()).to.be.false;
});
});

View File

@ -71,18 +71,10 @@ describe('Helpers', function () {
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
window.navigator.platform = "Mac x86_64";
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
Object.defineProperty(window, "navigator", origNavigator);
});
it('should respect ContextMenu on modern browser', function () {
@ -196,19 +188,11 @@ describe('Helpers', function () {
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
window.navigator.platform = "Windows";
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
Object.defineProperty(window, "navigator", origNavigator);
});
const keys = { 'Zenkaku': 0xff2a, 'Hankaku': 0xff2a,

View File

@ -144,18 +144,10 @@ describe('Key Event Handling', function () {
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
window.navigator.platform = "Mac x86_64";
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
Object.defineProperty(window, "navigator", origNavigator);
});
it('should change Alt to AltGraph', function () {
@ -267,17 +259,10 @@ describe('Key Event Handling', function () {
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
Object.defineProperty(window, "navigator", origNavigator);
});
it('should toggle caps lock on key press on iOS', function () {
@ -334,19 +319,11 @@ describe('Key Event Handling', function () {
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
window.navigator.platform = "Windows";
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
Object.defineProperty(window, "navigator", origNavigator);
});
const keys = { 'Zenkaku': 0xff2a, 'Hankaku': 0xff2a,
@ -375,20 +352,12 @@ describe('Key Event Handling', function () {
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
window.navigator.platform = "Windows x86_64";
this.clock = sinon.useFakeTimers();
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
Object.defineProperty(window, "navigator", origNavigator);
if (this.clock !== undefined) {
this.clock.restore();
}
@ -520,20 +489,12 @@ describe('Key Event Handling', function () {
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
window.navigator.platform = "Windows x86_64";
this.clock = sinon.useFakeTimers();
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
Object.defineProperty(window, "navigator", origNavigator);
if (this.clock !== undefined) {
this.clock.restore();
}

View File

@ -13,18 +13,10 @@ describe('Localization', function () {
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.languages !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
window.navigator.languages = [];
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
Object.defineProperty(window, "navigator", origNavigator);
});
it('should use English by default', function () {

View File

@ -66,11 +66,6 @@ describe('WebUtil', function () {
origLocalStorage = Object.getOwnPropertyDescriptor(window, "localStorage");
Object.defineProperty(window, "localStorage", {value: {}});
if (window.localStorage.setItem !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
window.localStorage.setItem = sinon.stub();
window.localStorage.getItem = sinon.stub();
@ -79,9 +74,7 @@ describe('WebUtil', function () {
return WebUtil.initSettings();
});
afterEach(function () {
if (origLocalStorage !== undefined) {
Object.defineProperty(window, "localStorage", origLocalStorage);
}
Object.defineProperty(window, "localStorage", origLocalStorage);
});
describe('writeSetting', function () {