|
66 | 66 | const CLIPBOARD_MONITORING_INTERVAL = 100; // ms
|
67 | 67 |
|
68 | 68 | 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; |
71 | 72 | let lastClipboardMonitorLoopError: Error | null = null;
|
72 | 73 |
|
73 | 74 | /* Firefox-specific BEGIN */
|
|
130 | 131 | return (evt.ctrlKey && evt.code === 'KeyV') || evt.code == 'Paste';
|
131 | 132 | }
|
132 | 133 |
|
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 |
134 | 135 | // with `ClipboardItem` API.
|
135 | 136 | function clipboardDataToRecord(data: ClipboardData): Record<string, Blob> {
|
136 | 137 | let result = {} as Record<string, Blob>;
|
|
145 | 146 | return result;
|
146 | 147 | }
|
147 | 148 |
|
| 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 | +
|
148 | 160 | // This callback is required to send initial clipboard state if available.
|
149 | 161 | function onForceClipboardUpdate() {
|
| 162 | + // TODO(Fix): lastSentClipboardData is nullptr. |
150 | 163 | try {
|
151 |
| - if (lastClientClipboardData) { |
152 |
| - remoteDesktopService.onClipboardChanged(lastClientClipboardData); |
| 164 | + if (lastSentClipboardData) { |
| 165 | + remoteDesktopService.onClipboardChanged(lastSentClipboardData); |
153 | 166 | } else {
|
154 | 167 | remoteDesktopService.onClipboardChangedEmpty();
|
155 | 168 | }
|
|
163 | 176 | try {
|
164 | 177 | const mime_formats = clipboardDataToRecord(data);
|
165 | 178 | const clipboard_item = new ClipboardItem(mime_formats);
|
| 179 | + lastReceivedClipboardData = clipboardDataToClipboardItemsRecord(data); |
166 | 180 | navigator.clipboard.write([clipboard_item]);
|
167 | 181 | } catch (err) {
|
168 | 182 | console.error('Failed to set client clipboard: ' + err);
|
|
192 | 206 | return;
|
193 | 207 | }
|
194 | 208 |
|
195 |
| - var values = new Map<string, string | Uint8Array>(); |
| 209 | + var values: Record<string, string | Uint8Array> = {}; |
196 | 210 | var sameValue = true;
|
197 | 211 |
|
198 | 212 | // Sadly, browsers build new `ClipboardItem` object for each `read` call,
|
|
221 | 235 | );
|
222 | 236 | };
|
223 | 237 |
|
224 |
| - const previousValue = lastClientClipboardItems.get(kind); |
| 238 | + const previousValue = lastClientClipboardItems[kind]; |
225 | 239 |
|
226 | 240 | 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 | + } |
227 | 247 | // One of mime types has changed, we need to update the clipboard cache
|
228 |
| - sameValue = false; |
| 248 | + else { |
| 249 | + sameValue = false; |
| 250 | + } |
229 | 251 | }
|
230 | 252 |
|
231 |
| - values.set(kind, value); |
| 253 | + values[kind] = value; |
232 | 254 | }
|
233 | 255 |
|
234 | 256 | // Clipboard has changed, we need to acknowledge remote side about it.
|
|
238 | 260 | let clipboardData = new module.ClipboardData();
|
239 | 261 |
|
240 | 262 | // Iterate over `Record` type
|
241 |
| - values.forEach((value: string | Uint8Array, key: string) => { |
| 263 | + Object.entries(values).forEach(([key, value]: [string, string | Uint8Array]) => { |
242 | 264 | // skip null/undefined values
|
243 | 265 | if (value == null || value == undefined) {
|
244 | 266 | return;
|
|
252 | 274 | });
|
253 | 275 |
|
254 | 276 | 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); |
257 | 280 | }
|
258 | 281 | }
|
259 | 282 | } catch (err) {
|
|
0 commit comments