Skip to content

Commit

Permalink
Merge pull request #1 from zgroza/main
Browse files Browse the repository at this point in the history
Add explainer
  • Loading branch information
zgroza authored Dec 5, 2024
2 parents ecf24dd + 6b79e36 commit 1d2479c
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 124 deletions.
216 changes: 94 additions & 122 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,177 +1,149 @@
# Explainer for the TODO API
This proposal is an early design sketch by ChromeOS team to describe the problem below and solicit feedback on the proposed solution. It has not been approved to ship in Chrome.

**Instructions for the explainer author: Search for "todo" in this repository and update all the
instances as appropriate. For the instances in `index.bs`, update the repository name, but you can
leave the rest until you start the specification. Then delete the TODOs and this block of text.**
# Explainer: `navigator.clipboard.contentsID()`

This proposal is an early design sketch by [TODO: team] to describe the problem below and solicit
feedback on the proposed solution. It has not been approved to ship in Chrome.
## Authors:

TODO: Fill in the whole explainer template below using https://tag.w3.org/explainers/ as a
reference. Look for [brackets].

## Proponents

- [Proponent team 1]
- [Proponent team 2]
- [etc.]
- [Luke Klimek](mailto:[email protected])

## Participate
- https://github.com/explainers-by-googlers/[your-repository-name]/issues
- [Discussion forum]

## Table of Contents [if the explainer is longer than one printed page]
- [https://crbug.com/382270770](https://crbug.com/382270770)
- [https://github.com/explainers-by-googlers/clipboard-contents-id/issues](https://github.com/explainers-by-googlers/clipboard-contents-id/issues)

<!-- Update this table of contents by running `npx doctoc README.md` -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
## Table of Contents

- [Introduction](#introduction)
- [Why a new thing, aren’t other clipboard APIs enough?](#why-a-new-thing-arent-other-clipboard-apis-enough)
- [What is the optimal solution then?](#what-is-the-optimal-solution-then?)
- [Goals](#goals)
- [Non-goals](#non-goals)
- [User research](#user-research)
- [Use cases](#use-cases)
- [Use case 1](#use-case-1)
- [Use case 2](#use-case-2)
- [[Potential Solution]](#potential-solution)
- [How this solution would solve the use cases](#how-this-solution-would-solve-the-use-cases)
- [Use case 1](#use-case-1-1)
- [Use case 2](#use-case-2-1)
- [Detailed design discussion](#detailed-design-discussion)
- [[Tricky design choice #1]](#tricky-design-choice-1)
- [[Tricky design choice 2]](#tricky-design-choice-2)
- [Considered alternatives](#considered-alternatives)
- [[Alternative 1]](#alternative-1)
- [[Alternative 2]](#alternative-2)
- [Stakeholder Feedback / Opposition](#stakeholder-feedback--opposition)
- [Token stability across tabs or app windows](#token-stability-across-tabs-or-app-windows)
- [How to use it?](#how-to-use-it)
- [Security considerations](#security-considerations)
- [Alternatives](#alternatives)
- [Functionality itself](#functionality-itself)
- [Format of the token](#format-of-the-token)
- [References & acknowledgements](#references--acknowledgements)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Introduction

[The "executive summary" or "abstract".
Explain in a few sentences what the goals of the project are,
and a brief overview of how the solution works.
This should be no more than 1-2 paragraphs.]
### Why a new thing, aren’t other clipboard APIs enough?

## Goals
In short, without this there's no efficient way to detect clipboard changes.
To elaborate, let's consider a common use case: Virtual Desktop Infrastructure (VDI). Many Clipboard API use cases within VDI environments center around synchronizing the local clipboard with a remote machine, so that:

[What is the **end-user need** which this project aims to address? Make this section short, and
elaborate in the Use cases section.]
1. When a user copies something locally outside the VDI app and then switches to it, the new clipboard contents are seamlessly available in the remote session.
2. When a user copies something on the remote machine and switches away from the VDI app, they can paste the copied content locally.

## Non-goals
Without `contentsID()`, there are two primary ways to achieve the first scenario:

[If there are "adjacent" goals which may appear to be in scope but aren't,
enumerate them here. This section may be fleshed out as your design progresses and you encounter necessary technical and other trade-offs.]
* Upon refocusing the VDI app, automatically send the content from the local clipboard to the remote machine.
* Upon refocusing the VDI app, read the clipboard contents, compare them with the last known state, and send to the remote machine only if they have changed.

## User research
Neither of these approaches is optimal (especially with large clipboard contents), and additional challenges related to sanitization and encoding make it difficult to directly compare the clipboard contents byte-by-byte with previously received data.

[If any user research has been conducted to inform your design choices,
discuss the process and findings. User research should be more common than it is.]
### What is the optimal solution then?

## Use cases
Several platforms (ex. [MacOS](https://developer.apple.com/documentation/uikit/uipasteboard/1622103-changecount?language=objc), [Windows](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-addclipboardformatlistener), [X11](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/x/x11_clipboard_helper.cc;drc=d815f515138991af2aa5b1d07c64906fd8a7366b;bpv=1;bpt=1;l=68?gsn=SelectionChangeObserver&gs=KYTHE%3A%2F%2Fkythe%3A%2F%2Fchromium.googlesource.com%2Fcodesearch%2Fchromium%2Fsrc%2F%2Fmain%3Flang%3Dc%252B%252B%3Fpath%3Dui%2Fbase%2Fx%2Fx11_clipboard_helper.cc%238ndnC55hoYsX0PuoXruTyg4VFTFux3LU_qg9KPKIcTE) and [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), [Android](https://developer.android.com/reference/android/content/ClipboardManager.OnPrimaryClipChangedListener)) offer efficient ways to track clipboard content changes without directly reading the data. This is often achieved through clipboard sequence numbers or change notifications. The `navigator.clipboard.contentsID()` API aims to leverage these capabilities. It allows websites to request a numeric token (a 128-bit integer) representing the current clipboard state. If this token differs from a previously retrieved one, it indicates that the clipboard contents have changed between the two calls. Importantly, this operation has a constant time complexity (O(1)), independent of the clipboard's size. Therefore, even frequent checks (e.g., on window refocus) remain efficient, even when dealing with large amounts of copied data.

[Describe in detail what problems end-users are facing, which this project is trying to solve. A
common mistake in this section is to take a web developer's or server operator's perspective, which
makes reviewers worry that the proposal will violate [RFC 8890, The Internet is for End
Users](https://www.rfc-editor.org/rfc/rfc8890).]
## Goals

### Use case 1
* Provide a way to check if the clipboard changed between two points in time that is:
* Easy to use
* Efficient, no matter how big the clipboard contents are
* Usable across multiple windows/tabs under one browser process
* Improve potential current heuristics for clipboard synchronization…

### Use case 2
## Non-goals

<!-- In your initial explainer, you shouldn't be attached or appear attached to any of the potential
solutions you describe below this. -->
* …without providing a new fingerprinting surface.

## [Potential Solution]
## Token stability across tabs or app windows

[For each related element of the proposed solution - be it an additional JS method, a new object, a new element, a new concept etc., create a section which briefly describes it.]
One of the goals of this API is to enable cross-app synchronization of clipboard \- so this should be as close to the stability of the clipboard itself as possible. So, every site under the same browser process should get the same token from calling `contentsID()`.

```js
// Provide example code - not IDL - demonstrating the design of the feature.
## How to use it?

// If this API can be used on its own to address a user need,
// link it back to one of the scenarios in the goals section.
Frankly, quite straightforwardly. Signature of the method will look somewhat like this:

// If you need to show how to get the feature set up
// (initialized, or using permissions, etc.), include that too.
```javascript
Promise<BigInt> contentsID();
```

[Where necessary, provide links to longer explanations of the relevant pre-existing concepts and API.
If there is no suitable external documentation, you might like to provide supplementary information as an appendix in this document, and provide an internal link where appropriate.]

[If this is already specced, link to the relevant section of the spec.]

[If spec work is in progress, link to the PR or draft of the spec.]

[If you have more potential solutions in mind, add ## Potential Solution 2, 3, etc. sections.]

### How this solution would solve the use cases

[If there are a suite of interacting APIs, show how they work together to solve the use cases described.]

#### Use case 1

[Description of the end-user scenario]

```js
// Sample code demonstrating how to use these APIs to address that scenario.
So in the mentioned VDI case, the code could look somewhat like this:

```javascript
var lastToken = null;

// Handler called on every window refocus.
// It checks if it's necessary to sync clipboard contents to remote.
window.addEventListener("focus", () => {
navigator.clipboard.contentsID().then(token => {
if (token !== lastToken) {
// Clipboard contents have changed!
// Send to remote machine
}
lastToken = token;
});
});

// Function that is called by the client app when user copies something on remote.
async function onRemoteClipboardChanged(remoteClipboardItems) {
await navigator.clipboard.write(remoteClipboardItems);
lastToken = await navigator.clipboard.contentsID();
}
```

#### Use case 2

[etc.]
Then, all that remains is to call `onRemoteClipboardChanged` every time the clipboard changes remotely \- and provided that no changes occur locally while the window is in focus (which is usually the case, as clipboard changes mostly occur due to user actions \- especially in case of local clipboard and VDI), clipboard synchronization will look seamless.
In the unfortunate case of anticipated local changes to the clipboard done in the background, this can be improved in two ways:

## Detailed design discussion
* Regular polling of the token and invoking a similar handler to the `focus` [handler](?tab=t.0#bookmark=id.vsbj524ib3z1) in the snippet above: this is generally not the best solution, but this API should be lightweight enough that it doesn’t create much overhead.
* Integrating this with `clipboardchange` event in addition (or instead) or the `focus` event: this depends on whether `clipboardchange` event becomes a part of the web standard.

### [Tricky design choice #1]
Both however would require some synchronization of the handler and `onRemoteClipboardChanged` to prevent handlers getting between `write` and `contentsID`.

[Talk through the tradeoffs in coming to the specific design point you want to make.]
**Note:** In any case, this will be in some degree prone to inherent race conditions due to lack of clipboard atomic operations \- which will show themselves mostly in case of user switching apps very rapidly. This API exists in order to enable heuristics to make this invisible in most cases, but will not fix it completely.

```js
// Illustrated with example code.
```

[This may be an open question,
in which case you should link to any active discussion threads.]

### [Tricky design choice 2]
## Security considerations

[etc.]
This should be under the same restrictions as the `navigator.clipboard.read()`:

## Considered alternatives
* It should require `clipboard-read` permissions and request them on call.
* It should be available only while the tab has focus.

[This should include as many alternatives as you can,
from high level architectural decisions down to alternative naming choices.]
Thus, it doesn’t expose any new not-available-before security-sensitive information.
The only potential attack vector would be correlating different sessions with the same user based on the token, which provides a more precise way of ensuring across sessions that those to clipboards are in fact the same user. In practice however, this could be done by just re-reading the clipboard contents and comparing them, especially across changes \- which is possible already. Correlating users across sites by the origins that have clipboard permissions is already trivially easy and existence of this API does not change this state significantly.

### [Alternative 1]
## Alternatives

[Describe an alternative which was considered,
and why you decided against it.]
### Functionality itself

### [Alternative 2]
There is another proposed API for tracking clipboard changes \- a `clipboardchange` event. However, even if implemented and standardized, it operates differently. Instead of determining if a change has occurred between two points in time, it provides real-time notifications for every change, without detailed information about the cause. Therefore, if your app also writes to the clipboard, it can be challenging to determine whether you or another source caused the change (especially with multiple windows/tabs of the same app open), potentially leading to unnecessary data transfers or having to implement comparison anyway.
In case of `contentsID()`, you can save the new token just after writing \- and it will be irrelevant for all active tabs/windows irrelevant what caused the change, only that this change is already in sync with the remote and no action is needed.

[etc.]
### Format of the token

## Stakeholder Feedback / Opposition
There are several ways in which the token could look like, including:

[Implementors and other stakeholders may already have publicly stated positions on this work. If you can, list them here with links to evidence as appropriate.]
1. Sequence number that would increase with each change (or with each call that detected a change)
2. Timestamp of the last change (or call that detected it)
3. Hash of the clipboard contents
4. Random 128-bit number without any specified scheme or significance \- other than “after something is written to the clipboard, `contentsID()` should yield a different value than it did before the write”

- [Implementor A] : Positive
- [Stakeholder B] : No signals
- [Implementor C] : Negative
Preferred approach is 4, for the following reasons:

[If appropriate, explain the reasons given by other implementors for their concerns.]
* It doesn’t provide any information about the user’s action other than already available
* Randomness of this degree is enough to ensure the lack of false positives, conforming with [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) standards
* It’s implementationally and computationally the simplest
* It’s the simplest solution that is sufficient for the provided use case
* It’s trivial to compare and store

## References & acknowledgements

[Your design will change and be informed by many people; acknowledge them in an ongoing way! It helps build community and, as we only get by through the contributions of many, is only fair.]

[Unless you have a specific reason not to, these should be in alphabetical order.]

Many thanks for valuable feedback and advice from:

- [Person 1]
- [Person 2]
- [etc.]
- [Andrew Rayskiy](mailto:[email protected])
- [Ayu Ishii](mailto:[email protected])
- [Dominik Bylica](mailto:[email protected])
- [Robert Ferens](mailto:[email protected])
4 changes: 2 additions & 2 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Level: None
Status: w3c/UD
Repository: explainers-by-googlers/todo-your-repo-name
URL: https://explainers-by-googlers.github.io/todo-your-repo-name
Editor: TODO: Your Name, Google https://google.com, TODO@google.com
Abstract: TODO: A short description of your spec, one or two sentences.
Editor: Luke Klimek, Google https://google.com, zgroza@google.com
Abstract: An API for getting a unique ID of current clipboard contents.
Markup Shorthands: markdown yes, css no
Complain About: accidental-2119 yes, missing-example-ids yes
Assume Explicit For: yes
Expand Down

0 comments on commit 1d2479c

Please sign in to comment.