From 8842f372b8cead623eabb5059237067ebf2769fb Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 1 Oct 2021 19:02:51 +0000 Subject: [PATCH] WIP, firefox support --- app/ui.js | 45 ++++++++------------------- core/rfb.js | 87 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 71 insertions(+), 61 deletions(-) diff --git a/app/ui.js b/app/ui.js index e98ff696..4803e9e6 100644 --- a/app/ui.js +++ b/app/ui.js @@ -1134,32 +1134,15 @@ const UI = { return; } if (UI.rfb && UI.rfb.clipboardUp && UI.rfb.clipboardSeamless) { - navigator.clipboard.read().then((data) => { - UI.rfb.clipboardPasteDataFrom(data); - UI.needToCheckClipboardChange = false; - }); - /*UI.readClipboard(function (text) { - console.log("clipboard read"); - var maximumBufferSize = 10000; - var clipVal = document.getElementById('noVNC_clipboard_text').value; - - if (clipVal != text) { - console.log("clipboard sent") - document.getElementById('noVNC_clipboard_text').value = text; // The websocket has a maximum buffer array size - - if (text.length > maximumBufferSize) { - UI.popupMessage("Clipboard contents too large. Data truncated", 2000); - UI.rfb.clipboardPasteFrom(text.slice(0, maximumBufferSize)); - } else { - //UI.popupMessage("Copied from Local Clipboard"); - UI.rfb.clipboardPasteFrom(text); - } - } // Reset flag to prevent checking too often - - - UI.needToCheckClipboardChange = false; - }); */ + if (UI.rfb.clipboardBinary) { + navigator.clipboard.read().then((data) => { + UI.rfb.clipboardPasteDataFrom(data); + UI.needToCheckClipboardChange = false; + }, (err) => { + console.log("No data in clipboard"); + }); + } } }, @@ -1300,15 +1283,13 @@ const UI = { UI.rfb.antiAliasing = UI.getSetting('anti_aliasing'); UI.rfb.clipboardUp = UI.getSetting('clipboard_up'); UI.rfb.clipboardDown = UI.getSetting('clipboard_down'); - UI.rfb.clipboardBinary = supportsBinaryClipboard(); UI.rfb.clipboardSeamless = UI.getSetting('clipboard_seamless'); - if (UI.rfb.clipboardSeamless) { + UI.rfb.clipboardBinary = supportsBinaryClipboard() && UI.rfb.clipboardSeamless; + + //Only explicitly request permission to clipboard on browsers that support binary clipboard access + if (supportsBinaryClipboard()) { // explicitly request permission to the clipboard - if (UI.rfb.clipboardBinary) { - navigator.permissions.query({ name: "clipboard-read" }).then((result) => { console.log('binary clipboard enabled') }); - } else { - navigator.permissions.query({ name: "clipboardRead" }).then((result) => { console.log('binary clipboard enabled') }); - } + navigator.permissions.query({ name: "clipboard-read" }).then((result) => { console.log('binary clipboard enabled') }); } // KASM-960 workaround, disable seamless on Safari if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) diff --git a/core/rfb.js b/core/rfb.js index 98c18616..4e2d7e2b 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -783,21 +783,28 @@ export default class RFB extends EventTargetMixin { clipboardPasteFrom(text) { if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; } + if (!(typeof text === 'string' && text.length > 0)) { return; } + this.sentEventsCounter+=1; - if (this._clipboardServerCapabilitiesFormats[extendedClipboardFormatText] && - this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) { - this._clipboardText = text; - RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]); - } else { - let data = new Uint8Array(text.length); - for (let i = 0; i < text.length; i++) { - // FIXME: text can have values outside of Latin1/Uint8 - data[i] = text.charCodeAt(i); - } - - RFB.messages.clientCutText(this._sock, data); + let data = new Uint8Array(text.length); + for (let i = 0; i < text.length; i++) { + data[i] = text.charCodeAt(i); } + + let h = hashUInt8Array(data); + if (h === this._clipHash) { + console.log('No clipboard changes'); + return; + } else { + this._clipHash = h; + } + + let dataset = []; + let mimes = [ 'text/plain' ]; + dataset.push(data); + + RFB.messages.sendBinaryClipboard(this._sock, dataset, mimes); } async clipboardPasteDataFrom(clipdata) { @@ -816,6 +823,9 @@ export default class RFB extends EventTargetMixin { case 'text/plain': case 'text/html': let blob = await clipdata[i].getType(mime); + if (!blob) { + continue; + } let buff = await blob.arrayBuffer(); let data = new Uint8Array(buff); @@ -2465,31 +2475,48 @@ export default class RFB extends EventTargetMixin { Log.Debug("HandleBinaryClipboard"); let num = this._sock.rQshift8(); // how many different mime types - let blobs = []; - let clipdata = []; let mimes = []; + let clipItemData = {}; console.log('Clipboard items recieved.'); for (let i = 0; i < num; i++) { let mimelen = this._sock.rQshift8(); - const mime = this._sock.rQshiftStr(mimelen); - + let mime = this._sock.rQshiftStr(mimelen); let len = this._sock.rQshift32(); - - const data = this._sock.rQshiftBytes(len); + let data = this._sock.rQshiftBytes(len); switch(mime) { case "image/png": case "text/html": case "text/plain": - if (mimes.includes(mime)){ + //if (mimes.includes(mime)){ + // continue; + //} + mimes.push(mime); + + if (!this.clipboardBinary) { + if (mime == "text/plain") { + + let textdata = new TextDecoder().decode(data); + + if ((textdata.length > 0) && "\0" === textdata.charAt(textdata.length - 1)) { + textdata = textdata.slice(0, -1); + } + + console.log('Clipboard item raw: ' + data); + console.log('Clipboard item decoded: ' + textdata); + this.dispatchEvent(new CustomEvent( + "clipboard", + { detail: { text: textdata } }) + ); + continue; + } continue; } - mimes.push(mime); + console.log("Mime " + mime + ", len ", len); console.log(data); - let blob = new Blob([data], { type: mime }); - clipdata.push(new ClipboardItem({ [mime]: blob })); + clipItemData[mime] = new Blob([data], { type: mime }); break; default: console.log('Mime type skipped: ' + mime); @@ -2497,13 +2524,15 @@ export default class RFB extends EventTargetMixin { } } - if (clipdata.length > 0) { - this._clipHash = 0; - navigator.clipboard.write(clipdata).then( - function() {}, - function(err) { - console.log("Error writing to client clipboard: " + err); - }); + if (Object.keys(clipItemData).length > 0) { + if (this.clipboardBinary) { + this._clipHash = 0; + navigator.clipboard.write([new ClipboardItem(clipItemData)]).then( + function() {}, + function(err) { + console.log("Error writing to client clipboard: " + err); + }); + } } return true;