Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions core/rfb.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export default class RFB extends EventTargetMixin {
this._pendingLatencyRender = null;

this._clipboardText = null;
this._clipboardWrite = null;
this._clipboardServerCapabilitiesActions = {};
this._clipboardServerCapabilitiesFormats = {};

Expand Down Expand Up @@ -1106,10 +1107,8 @@ export default class RFB extends EventTargetMixin {
if (this.clipboardUp && this.clipboardSeamless && this._resendClipboardNextUserDrivenEvent) {
this._resendClipboardNextUserDrivenEvent = false;
if (this.clipboardBinary) {
navigator.clipboard.read().then((data) => {
this.clipboardPasteDataFrom(data);
}, (err) => {
Log.Debug("No data in clipboard: " + err);
this._sendLocalClipboard().catch((err) => {
Log.Debug("Failed to read system clipboard: " + err);
});
} else {
if (navigator.clipboard && navigator.clipboard.readText) {
Expand All @@ -1123,6 +1122,28 @@ export default class RFB extends EventTargetMixin {
}
}

// A ClipboardItem from clipboard.read() reads the system clipboard when
// getType() is called, and Chrome throws InvalidStateError if the clipboard
// changed after read(): a copy on the client, a clipboard manager, or our
// own write of the server's clipboard. Wait for our write, read once more
// if it changes anyway, and failing that try again on the next
// user-driven event.
async _sendLocalClipboard() {
for (let attempt = 0; ; attempt++) {
await this._clipboardWrite;
try {
await this.clipboardPasteDataFrom(await navigator.clipboard.read());
return;
} catch (err) {
if (err.name !== 'InvalidStateError') { throw err; }
if (attempt > 0) {
this._resendClipboardNextUserDrivenEvent = true;
throw err;
}
}
}
}

clipboardPasteFrom(text) {
if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
if (!(typeof text === 'string' && text.length > 0)) { return; }
Expand Down Expand Up @@ -1177,8 +1198,6 @@ export default class RFB extends EventTargetMixin {
if (h === this._clipHash) {
Log.Debug('No clipboard changes');
return;
} else {
this._clipHash = h;
}
}

Expand Down Expand Up @@ -1211,6 +1230,9 @@ export default class RFB extends EventTargetMixin {


if (dataset.length > 0) {
// Only once every type has been read: a getType() that throws
// must not leave the hash claiming this clipboard was sent.
this._clipHash = h;
if (this._isPrimaryDisplay) {
RFB.messages.sendBinaryClipboard(this._sock, dataset, mimes);
} else {
Expand Down Expand Up @@ -3798,7 +3820,8 @@ export default class RFB extends EventTargetMixin {
}

_write_binary_clipboard(clipItemData, textdata) {
navigator.clipboard.write([new ClipboardItem(clipItemData)]).then(
// _sendLocalClipboard() waits on this so it doesn't read mid-write
this._clipboardWrite = navigator.clipboard.write([new ClipboardItem(clipItemData)]).then(
() => {
if (textdata) {
this._clipHash = hashUInt8Array(textdata);
Expand All @@ -3808,7 +3831,7 @@ export default class RFB extends EventTargetMixin {
Log.Error("Error writing to client clipboard: " + err);
// Lets try writeText
if (textdata.length > 0) {
navigator.clipboard.writeText(textdata).then(
return navigator.clipboard.writeText(textdata).then(
() => {
this._clipHash = hashUInt8Array(textdata);
},
Expand Down