DRAFT
Proposal for a modern, asynchronous clipboard interface.
-
Introduce a new object:
navigator.clipboard
-
Have
read()
andwrite()
methods that return aPromise
-
Add a new event listener to detect clipboard change events.
- API for safe, asynchronous clipboard access
- Eliminate all usage of
document.execCommand()
An API for clipboard actions (cut, copy and paste) has long been desired for webpages, but agreement has been slow because of various security and usability concerns with the feature. In summary, giving web pages access to the clipboard introduces problems with clobbering (overwriting) and sniffing (surreptitiously reading) the clipboard.
This lack of a consistent API has forced web developers who need this feature to rely on alternate methods (e.g., Flash, via ZeroClipboard) which simply trades one set of problems for another.
Recently, however, all major browsers have converged on support for clipboard access
using document.execCommand()
:
| cut | copy | paste
-------------- | ---- | ---- | -----
IE ¹ | 9 | 9 | 9
Chrome ² | 43 | 43 | no
Firefox ² | 41 | 41 | ?
Opera ² | 29 | 29 | 29
Safari ² | yes ³ | yes ³ | ?
Edge ² | 12 | 12 | 14 <sup>4</sup>
¹ A permission prompt to the user when the feature is used.
² Only permitted when executing code in response to a user action or gesture.
³ The Safari Technology Preview (announced on 2016-Mar-30) has support for cut and copy.
4 Edge 14 supports paste only through an offline-managed Allow List.
However, even with this increased support, there are still many inconsistencies across browser implementations. See this blog post for a description of the problems Javascript programmers face, and note the existence of various Javascript clipboard libraries to help address these issues.
As noted earlier, the current Clipboard API
specification that the browsers are implementing assumes that the API should be
based on document.execCommand()
. However, there have recently been
discussions on public-webapps
questioning whether this is appropriate in the long term.
The specific issues that motivate this proposal are:
-
The current model is synchronous, so it blocks the main page.
- This makes permission prompts more irritating (since the page is blocked).
- This prevents sanitizing data types (like images) that need to be transcoded.
- Transcoding is sometimes required to guard against exploits in external parsers.
- It's not reasonable to block page while large images are being sanitized.
-
The code required for programmatic clipboard actions is... bizarre:
- To modify the clipboard, you need to add an event listener for 'copy'. This listener will
fire when you call
execCommand('copy')
and also for any user initiated copies.
- To modify the clipboard, you need to add an event listener for 'copy'. This listener will
fire when you call
-
There is a need for a notification event when the clipboard is mutated.
- Apps that synchronize the clipboard (like remote access apps) need this to know when to send the updated clipboard contents to the secondary system.
-
execCommand
is old API originally designed for editing the DOM and it has a large number of interoperability bugs. The latest version of the execCommand spec states that it is incomplete and not expected to advance beyond draft status. -
In practice, many developers load a library to use
execCommand
properly. That shouldn't be necessary for something as basic clipboard cut/copy/paste.
And finally,
- Make it easier to support alternate permission models.
- For example, to enable web apps to copy to the clipboard without requiring a user gesture.
- Enable use of the Permissions API or a consent dialog that doesn't block the browser.
This section provides more details about the proposal.
The main clipboard object is part of the navigator
because it is a
"system" level object that is not tied to the current window or document.
Note: Having this on the navigator
means that it would be accessible
from workers.
These methods would return a Promise
that is resolved when the reading or
writing operation is complete.
write()
would take aDataTransfer
and return aPromise<>
read()
would return aPromise<DataTransfer>
It is also desireable to have some convenience methods like writeText()
or writeHtml()
.
This event would fire whenever the clipboard contents are changed. If the clipboard contents are changed outside the browser, then this event would fire when the browser regains focus.
Example of writing to the clipboard:
// Using convenience function to write a specific MIME type.
navigator.clipboard.writeText("Howdy, partner!");
// Multiple MIME types.
var data = new DataTransfer();
data.items.add("text/plain", "Howdy, partner!");
data.items.add("text/html", "<b>Howdy</b>, partner!");
navigator.clipboard.write(data);
// Use the Promise outcome to perform an action.
navigator.clipboard.writeText("some text").then(function() {
console.log(“Copied to clipboard successfully!”);
}, function() {
console.error(“Unable to write to clipboard. :-(”);
});
Example of reading from the clipboard:
// Reading data from the clipboard.
navigator.clipboard.read().then(function(data) {
for (var i = 0; i < data.items.length; i++) {
if (data.items[i].type == "text/plain") {
console.log(“Your string:”, data.items[i].getAs(“text/plain”))
} else {
console.error(“No text/plain data on clipboard.”);
}
}
})
Example of detecting clipboard changes:
function listener(event) {
// Do stuff with navigator.clipboard
}
navigator.clipboard.addEventListener(“clipboardchange”, listener);
The current Clipboard API describes events that are fired when either:
- the user selects one of the standard clipboard actions via the browser's UI or keyboard shortcuts (these are "trusted" events), or
- javascript code sends one of these events (in which case, they are "synthetic" and "untrusted").
With this proposal, these events would still be present, but the recommended way
to access the clipboard would be through the Promise-based APIs rather than
via execCommand
(although the current execCommand
-based API would stick
around for compatibility reasons).
The current model of requiring some sort of permission or opt-in before allowing untrusted access to the clipboard would be retained.
Basically, the intent (if there's agreement) is to merge this into the current Clipboard API document so that all the clipboard-related APIs live in the same document.
Note that with this proposal, the ClipboardEvent
type could be replaced with
a simple Event
and all clipboard access could be through the
navigator.clipboard
object.
Detect clipboard change example:
function listener(event) {
navigator.clipboard.read().then(function(data) {
// Do something with clipboard data.
});
}
navigator.clipboard.addEventListener(“clipboardchange”, listener);
There are a few avenues for abuse that are not specific to this proposal, but are applicable to any API that provides clipboard access.
It is one of these abuse vectors in particular, pasting images, that motivates this proposal. In order to clean up malicious images, they would need to be decoded and it is not appropriate to do this on the main thread (large images could lock the browser while the image is being processed).
Sniffing the clipboard contents. Of concern not just because of the possibility of PII, but also because it is not uncommon for users to copy/paste passwords (e.g., from a password manager to a website).
Inject malicious content onto the clipboard.
Note, that it is already possible to clobber the clipboard contents:
document.addEventListener('copy', function(e) {
// Modify the document selection or call e.clipboardData.setData()
}
Malicious text can be in the form of commands (e.g., 'rm -rf /\n') or script (Self-XSS).
Images can be crafted to exploit bugs in the image-handling code, so they need to be scrubbed as well. Transcoding large images can be computationally expensive, so care must be taken to avoid processing them on the main thread.
Currently, user agents mitigate abuse by untrusted actions by either requiring a user gesture (e.g., clicking on a button) or with a permission dialog. These approaches suffer from the following issues:
User gestures provide defense against "drive-by" clipboard access, but the user receives no notifications if the clipboard is accessed as part of an unrelated user gesture. An example or this would be tricking user to click on innocous "OK" button and then silently writing to the clipboard. In this situation, the user grants no permission and receives no notification.
Pop-up permission dialogs can be problematic because clipboard events are
cancelable, so the browser needs to wait until the event handler is done (to know
whether or not it was canceled) before continuing. If the event handler
directly calls execCommand
(which is also synchronous), then the browser is
blocked until the command (including any permission dialogs) is complete.
Note that replacing execCommand
with an asychronous clipboard API would
make these permission dialogs more user-friendly.
For this feature, we should consider some combination of the following:
- Require a user gesture. To protect against drive-by access, although this may not be necessary with the right set of permissions.
- Only allow clipboard access from code running in the front tab.
- Pop-up Notifications. A post-facto notification similar to what is done for fullscreen. Display something like: "New data pasted to clipboard" or "Data read from clipboard".
- Permission Dialog. With an async clipboard API, this would be more acceptable since it wouldn't block the main process.
- Permissions API. Add a "clipboard" name to the registry
This proposal is motivated by the desire to support image types in a safe manner, but there is a desire to handle all types safely.
To do: Need to agree on set of required mimetypes. Can we safely allow any
mimetype and binary data? What alternatives are there for application-specific
data on the clipboard? DataTransfer
would need to be updated for this since
it currently only supports text and files.
Thanks to the following people for the discussions that lead to the creation of this proposal:
Daniel Cheng (Google), Lucas Garron (Google), Gary Kacmarcik (Google), Hallvord R. M. Steen (Mozilla),