|
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