@@ -44,15 +44,24 @@ so that:
44
44
2 . When a user copies something on the remote machine and switches away from the VDI app, they can paste the copied
45
45
content locally.
46
46
47
- Without ` contentsId() ` , there are two primary ways to achieve the first scenario:
47
+ Without ` contentsId() ` , there are three primary ways to achieve the first scenario:
48
48
49
+ - Expose an additional "synchronize clipboard" button to the user - which, on click, would send the current clipboard to
50
+ the remote machine.
49
51
- Upon refocusing the VDI app, automatically send the content from the local clipboard to the remote machine.
50
52
- Upon refocusing the VDI app, read the clipboard contents, compare them with the last known state, and send to the
51
53
remote machine only if they have changed.
52
54
53
- Neither of these approaches is optimal (especially with large clipboard contents), and additional challenges related to
54
- sanitization and encoding make it difficult to directly compare the clipboard contents byte-by-byte with previously
55
- received data.
55
+ Neither of these approaches is both optimal (especially with large clipboard contents) and user-friendly, and additional
56
+ challenges related to sanitization and encoding make it difficult to directly compare the clipboard contents
57
+ byte-by-byte with previously received data.
58
+
59
+ Moreover, only the first option is viable in browsers that do not implement persistent clipboard permissions. There, the
60
+ user would have to consciously remember to click on a button every time they have before copied something - which is
61
+ problematic, as the system clipboard is a tool that most people use intuitively and intensively. A good example of this
62
+ challenge could be [ Cameyo] ( https://cameyo.com/ ) , which essentially streams individual applications from the remote
63
+ servers as Progressive Web Apps. Alt-tabbing from one like-native application to another and having to remember whether
64
+ you have copied anything or not significantly degrades user experience.
56
65
57
66
### What is the optimal solution then?
58
67
64
73
[ Wayland] ( https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/platform/wayland/host/wayland_data_device.cc;drc=d815f515138991af2aa5b1d07c64906fd8a7366b;bpv=1;bpt=1;l=182?gsn=OnSelection&gs=KYTHE%3A%2F%2Fkythe%3A%2F%2Fchromium.googlesource.com%2Fcodesearch%2Fchromium%2Fsrc%2F%2Fmain%3Flang%3Dc%252B%252B%3Fpath%3Dui%2Fozone%2Fplatform%2Fwayland%2Fhost%2Fwayland_data_device.cc%23KBIABXwYhD42mocIlezMjghFMtoChm0IKDja7p09J9o ) ,
65
74
[ ChromeOS] ( https://source.chromium.org/chromium/chromium/src/+/main:ui/base/clipboard/clipboard_non_backed.cc;drc=65c747e508657f16ca3d0905ab1e11115f5a5ff1;l=286 ) ,
66
75
[ Android] ( https://developer.android.com/reference/android/content/ClipboardManager.OnPrimaryClipChangedListener ) and
67
- [ iOS] ( https://developer.apple.com/documentation/uikit/uipasteboard/changecount ) ) offer efficient ways to track clipboard content changes
68
- without directly reading the data. This is often achieved through clipboard sequence numbers or change notifications.
69
- The ` navigator.clipboard.contentsId() ` API aims to leverage these capabilities. It allows websites to request a numeric
70
- token (a 128-bit integer) representing the current clipboard state. If this token differs from a previously retrieved
71
- one, it indicates that the clipboard contents have changed between the two calls. Importantly, this operation has a
72
- constant time complexity (O(1)), independent of the clipboard's size. Therefore, even frequent checks (e.g., on window
73
- refocus) remain efficient, even when dealing with large amounts of copied data.
76
+ [ iOS] ( https://developer.apple.com/documentation/uikit/uipasteboard/changecount ) ) offer efficient ways to track clipboard
77
+ content changes without directly reading the data. This is often achieved through clipboard sequence numbers or change
78
+ notifications. The ` navigator.clipboard.contentsId() ` API aims to leverage these capabilities. It allows websites to
79
+ request a numeric token (a 128-bit integer) representing the current clipboard state. If this token differs from a
80
+ previously retrieved one, it indicates that the clipboard contents have changed between the two calls. Importantly, this
81
+ operation has a constant time complexity (O(1)), independent of the clipboard's size. Therefore, even frequent checks
82
+ (e.g., pasting quickly many times in an online document editor or checking on every refocus whether to show "synchronize
83
+ clipboard" button) remain efficient, even when dealing with large amounts of copied data.
84
+
85
+ This could help greatly in making web VDI clients work smoothly in browsers that base clipboard access on user
86
+ activation - as the site could without activation check _ whether_ the clipboard has changed and only then display a
87
+ "click ctrl+v to synchronize clipboard" notification or a "click here to synchronize clipboard" button, taking the
88
+ burden of tracking this from the user while still not having too much access to the clipboard.
89
+
90
+ Moreover, above approach at enabling apps to work better without broad persistent clipboard permissions could be
91
+ extended to a lot more applications, for example online editors, that could using this minize the number of ` read() `
92
+ calls (as they prompt user to make additional action) and call it only if the clipboard has changed from the last time.
74
93
75
94
## Goals
76
95
@@ -86,9 +105,11 @@ refocus) remain efficient, even when dealing with large amounts of copied data.
86
105
87
106
## Token stability across tabs or app windows
88
107
89
- One of the goals of this API is to enable cross-app synchronization of clipboard \- so this should be as close to the
90
- stability of the clipboard itself as possible. So, every site under the same browser process should get the same token
91
- from calling ` contentsId() ` .
108
+ One of the goals of this API is to enable cross-tab synchronization of clipboard on the scope of one origin \- so this
109
+ should be as close to the stability of the clipboard itself as possible without providing cross-site fingerprinting
110
+ surface. So, every tab of the same origin under the same
111
+ [ partition] ( https://w3ctag.github.io/privacy-principles/#dfn-partition ) should get the same token from calling
112
+ ` contentsId() ` .
92
113
93
114
## How to use it?
94
115
@@ -109,7 +130,8 @@ window.addEventListener("focus", () => {
109
130
navigator .clipboard .contentsId ().then ((token ) => {
110
131
if (token !== lastToken) {
111
132
// Clipboard contents have changed!
112
- // Send to remote machine
133
+ // Display the "synchronize clipboard" button or "press ctrl+v" notification to the user,
134
+ // or just read the clipboard if you have the persistent clipboard permission.
113
135
}
114
136
lastToken = token;
115
137
});
@@ -144,17 +166,16 @@ enable heuristics to make this invisible in most cases, but will not fix it comp
144
166
145
167
## Security & Privacy considerations
146
168
147
- This should be under the same restrictions as the ` navigator.clipboard.read() ` :
169
+ This in of itself does not provide the website with any new substantial information about the user. The only potential
170
+ danger is a new fingerprinting surface. To remediate this:
148
171
149
- - It should require ` clipboard-read ` permissions and request them on call.
150
- - It should be available only while the tab has focus.
172
+ - This should be available only when the document is in focus (same as ` navigator.clipboard.read() ` ).
173
+ - The ID returned by this should be unique to the origin calling the method and change every time the site data for it
174
+ is deleted.
151
175
152
- Thus, it doesn’t expose any new not-available-before security-sensitive information. The only potential attack vector
153
- would be correlating different sessions with the same user based on the token, which provides a more precise way of
154
- ensuring across sessions that those to clipboards are in fact the same user. In practice however, this could be done by
155
- just re-reading the clipboard contents and comparing them, especially across changes \- which is possible already.
156
- Correlating users across sites by the origins that have clipboard permissions is already trivially easy and existence of
157
- this API does not change this state significantly.
176
+ In this way, correlation of users cross-site should be impossible based on either the number itself or the exact timing
177
+ of this number changing. Hence, this API should not provide any substantially new information to the site except a hint
178
+ when to best call ` read() ` so that it's optimal and user-friendly.
158
179
159
180
## Alternatives
160
181
0 commit comments