Skip to content

Commit b0e87a3

Browse files
fix(web): fix issue when clipboard monitoring treats clipboard update from server as the local update (#857)
When the server sends the clipboard update, we write it to our clipboard. But this new clipboard data was then processed as a new one, so we sent it back to the server. This commit fixes this behavior by tracking the data that we received from the server.
1 parent 9c99133 commit b0e87a3

File tree

1 file changed

+35
-12
lines changed

1 file changed

+35
-12
lines changed

web-client/iron-remote-desktop/src/iron-remote-desktop.svelte

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@
6666
const CLIPBOARD_MONITORING_INTERVAL = 100; // ms
6767
6868
let isClipboardApiSupported = false;
69-
let lastClientClipboardItems = new Map<string, string | Uint8Array>();
70-
let lastClientClipboardData: ClipboardData | null = null;
69+
let lastClientClipboardItems: Record<string, string | Uint8Array> = {};
70+
let lastReceivedClipboardData: Record<string, string | Uint8Array> = {};
71+
let lastSentClipboardData: ClipboardData | null = null;
7172
let lastClipboardMonitorLoopError: Error | null = null;
7273
7374
/* Firefox-specific BEGIN */
@@ -130,7 +131,7 @@
130131
return (evt.ctrlKey && evt.code === 'KeyV') || evt.code == 'Paste';
131132
}
132133
133-
// This function is required to convert `ClipboardData` to a object that can be used
134+
// This function is required to convert `ClipboardData` to an object that can be used
134135
// with `ClipboardItem` API.
135136
function clipboardDataToRecord(data: ClipboardData): Record<string, Blob> {
136137
let result = {} as Record<string, Blob>;
@@ -145,11 +146,23 @@
145146
return result;
146147
}
147148
149+
function clipboardDataToClipboardItemsRecord(data: ClipboardData): Record<string, string | Uint8Array> {
150+
let result = {} as Record<string, string | Uint8Array>;
151+
152+
for (const item of data.items()) {
153+
let mime = item.mimeType();
154+
result[mime] = item.value();
155+
}
156+
157+
return result;
158+
}
159+
148160
// This callback is required to send initial clipboard state if available.
149161
function onForceClipboardUpdate() {
162+
// TODO(Fix): lastSentClipboardData is nullptr.
150163
try {
151-
if (lastClientClipboardData) {
152-
remoteDesktopService.onClipboardChanged(lastClientClipboardData);
164+
if (lastSentClipboardData) {
165+
remoteDesktopService.onClipboardChanged(lastSentClipboardData);
153166
} else {
154167
remoteDesktopService.onClipboardChangedEmpty();
155168
}
@@ -163,6 +176,7 @@
163176
try {
164177
const mime_formats = clipboardDataToRecord(data);
165178
const clipboard_item = new ClipboardItem(mime_formats);
179+
lastReceivedClipboardData = clipboardDataToClipboardItemsRecord(data);
166180
navigator.clipboard.write([clipboard_item]);
167181
} catch (err) {
168182
console.error('Failed to set client clipboard: ' + err);
@@ -192,7 +206,7 @@
192206
return;
193207
}
194208
195-
var values = new Map<string, string | Uint8Array>();
209+
var values: Record<string, string | Uint8Array> = {};
196210
var sameValue = true;
197211
198212
// Sadly, browsers build new `ClipboardItem` object for each `read` call,
@@ -221,14 +235,22 @@
221235
);
222236
};
223237
224-
const previousValue = lastClientClipboardItems.get(kind);
238+
const previousValue = lastClientClipboardItems[kind];
225239
226240
if (!is_equal(previousValue, value)) {
241+
// When the local clipboard updates, we need to compare it with the last data received from the server.
242+
// If it's identical, the clipboard was updated with the server's data, so we shouldn't send this data
243+
// to the server.
244+
if (is_equal(lastReceivedClipboardData[kind], value)) {
245+
lastClientClipboardItems[kind] = lastReceivedClipboardData[kind];
246+
}
227247
// One of mime types has changed, we need to update the clipboard cache
228-
sameValue = false;
248+
else {
249+
sameValue = false;
250+
}
229251
}
230252
231-
values.set(kind, value);
253+
values[kind] = value;
232254
}
233255
234256
// Clipboard has changed, we need to acknowledge remote side about it.
@@ -238,7 +260,7 @@
238260
let clipboardData = new module.ClipboardData();
239261
240262
// Iterate over `Record` type
241-
values.forEach((value: string | Uint8Array, key: string) => {
263+
Object.entries(values).forEach(([key, value]: [string, string | Uint8Array]) => {
242264
// skip null/undefined values
243265
if (value == null || value == undefined) {
244266
return;
@@ -252,8 +274,9 @@
252274
});
253275
254276
if (!clipboardData.isEmpty()) {
255-
lastClientClipboardData = clipboardData;
256-
remoteDesktopService.onClipboardChanged(clipboardData);
277+
lastSentClipboardData = clipboardData;
278+
// TODO(Fix): onClipboardChanged takes an ownership over clipboardData, so lastSentClipboardData will be nullptr.
279+
await remoteDesktopService.onClipboardChanged(clipboardData);
257280
}
258281
}
259282
} catch (err) {

0 commit comments

Comments
 (0)