diff --git a/index.bs b/index.bs
index 7f4a346..bd9014d 100644
--- a/index.bs
+++ b/index.bs
@@ -168,7 +168,7 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;
exposed as a {{DataTransfer}} object that mirrors the contents of the clipboard.
* For the [[#async-clipboard-api]], the [=system clipboard data=] is
- exposed as a sequence of {{ClipboardItem}} objects that mirrors the contents of
+ exposed as a sequence of [=ClipboardItem=] objects that mirrors the contents of
the clipboard.
Clipboard Events
@@ -577,69 +577,170 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;
clipboard
The [=clipboard=] attribute must return the {{Navigator}}'s [=clipboard=] object.
+ It is used to execute read/write operations from/to the [=system clipboard=].
- Clipboard Interface
-
+ ClipboardItem Interface
- typedef sequence<ClipboardItem> ClipboardItems;
+ typedef (DOMString or Blob) ClipboardItemDataType;
+ typedef Promise<ClipboardItemDataType> ClipboardItemData;
- [SecureContext, Exposed=Window] interface Clipboard : EventTarget {
- Promise<ClipboardItems> read();
- Promise<DOMString> readText();
- Promise<undefined> write(ClipboardItems data);
- Promise<undefined> writeText(DOMString data);
- };
+ callback ClipboardItemDelayedCallback = ClipboardItemData ();
- typedef (DOMString or Blob) ClipboardItemDataType;
- typedef Promise<ClipboardItemDataType> ClipboardItemData;
+ [SecureContext, Exposed=Window] interface ClipboardItem {
+ constructor(record<DOMString, ClipboardItemData> items,
+ optional ClipboardItemOptions options = {});
- callback ClipboardItemDelayedCallback = ClipboardItemData ();
+ readonly attribute PresentationStyle presentationStyle;
+ readonly attribute FrozenArray<DOMString> types;
- [Exposed=Window] interface ClipboardItem {
- constructor(record<DOMString, ClipboardItemData> items,
- optional ClipboardItemOptions options = {});
- static ClipboardItem createDelayed(
- record<DOMString, ClipboardItemDelayedCallback> items,
- optional ClipboardItemOptions options = {});
+ Promise<Blob> getType(DOMString type);
+ };
- readonly attribute PresentationStyle presentationStyle;
- readonly attribute long long lastModified;
- readonly attribute boolean delayed;
+ enum PresentationStyle { "unspecified", "inline", "attachment" };
- readonly attribute FrozenArray<DOMString> types;
+ dictionary ClipboardItemOptions {
+ PresentationStyle presentationStyle = "unspecified";
+ sequence<DOMString> unsanitized;
+ };
+
+ ClipboardItem
+ A [=ClipboardItem=] is conceptually data that the user has expressed a desire to make shareable by invoking a "cut" or "copy" command.
+ For example, if a user copies a range of cells from a spreadsheet, it will result in one [=ClipboardItem=]. If a user copies a set of files from their desktop, that list of files will be a different single [=ClipboardItem=].
+ Some platforms may support having more than one [=ClipboardItem=] at a time on the [=Clipboard=], while other platforms replace the previous [=ClipboardItem=] with the new one.
- Promise<Blob> getType(DOMString type);
- };
+ Each [=ClipboardItem=] can be represented as multiple mime-types. In the example where the user copies a range of cells from a spreadsheet, it may be represented as an image (image/png), an HTML table (text/html), or plain text (text/plain).
+ Each of these mime-types describe the same [=ClipboardItem=] at different levels of fidelity and make the [=ClipboardItem=] more consumable by target applications during paste.
+ Making the range of cells available as an image will allow the user to paste the cells into Photoshop, while the text/plain format can be used by applications like Windows Notepad.
+
+ Apps that support pasting only a single [=ClipboardItem=] should use the first [=ClipboardItem=].
+ Apps that support pasting more than one [=ClipboardItem=] could, for example, provide a user interface that previews the contents of each [=ClipboardItem=] and allow the user to choose which one to paste.
+ Further, apps are expected to enumerate the mime-types of the [=ClipboardItem=] they are pasting and select the one best-suited for the app according to some app-specific algorithm.
+ Alternatively, an app can present the user with options on how to paste a [=ClipboardItem=], e.g. “paste as image” or “paste formatted text”, etc.
+
+ A [=ClipboardItem=] contains multiple representations. Each representation consists of a MIME type and a corresponding {{DOMString}} or [=Blob=].
+
+
+ clipboardItem = new ClipboardItem([items, options])
+ -
+ Creates a new [=ClipboardItem=] object. items are used to fill its MIME types and [=Promise=]s to [=Blob=]s corresponding to the MIME types, options can be used to fill its {{ClipboardItemOptions}},
+ as per the example below.
+
+
+ const format1 = 'text/plain';
+ const promise_text_blob = Promise.resolve(new Blob(['hello'], {type: format1}));
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: promise_text_blob},
+ {options: "unspecified"});
+
+
+ clipboardItem.getType(type)
+ Returns a [=Promise=] to the [=Blob=] corresponding to type.
+
+ clipboardItem.types
+ Returns the list of types contained in the clipboardItem object.
+
+
+
+ The ClipboardItem step for new ClipboardItem(items, options)
is to set [=this=]'s items to items and options to options
+
- enum PresentationStyle { "unspecified", "inline", "attachment" };
+ {{ClipboardItem/presentationStyle}} getter steps are to return [=this=]'s {{ClipboardItem/presentationStyle}}
- dictionary ClipboardItemOptions {
- PresentationStyle presentationStyle = "unspecified";
+ types
+ Returns the MIME types used to create the [=ClipboardItem=] object.
+ {{ClipboardItem/types}} getter steps are to return [=this=]'s {{ClipboardItem/types}}
+
+ getType(type) must run the below steps:
+
+ 1. Let |realm| be [=this=]'s [=relevant realm=].
+
+ 1. If |type| is not listed in the [=mandatory data types=] list, then [=a promise rejected with=] "The type was not found" DOMException in |realm|.
+
+ 1. Let |p| be [=a new promise=] in |realm|.
+
+ 1. Let |blobData| be a [=Blob=] corresponding to the |type|.
+
+ 1. Resolve |p| with |blobData|.
+
+ 1. Return |p|.
+
+ ClipboardItemDataType
+
+ It is a {{DOMString}} or [=Blob=] type. This contains the payload corresponding to the MIME type while creating a [=ClipboardItem=] object.
+
+ ClipboardItemData
+
+ A [=Promise=] to a {{ClipboardItemDataType}}.
+
+
+ presentationStyle
+ It is an enumerated attribute whose keywords are the string
, unspecified
, inline
and attachment
.
+
+ Note: {{ClipboardItemOptions/presentationStyle}} helps distinguish between "inline" data(e.g. selecting text on a web page and copying), vs. file-like data (e.g. copying a plain text file).
+ This difference is used to provide a hint when writing to the system pasteboard on iOS/iPadOS. This allows apps like Notes to insert a file attachment when copying a plain text file from the Files app, but insert text inline when
+ copying a selected piece of plain text from another app. In both cases, the MIME type in the pasteboard would be text/plain. This is used for both reading and writing.
+
+
+ unsanitized
+
+ [=sequence=] of {{DOMString}} that contains the MIME types that don't go through the sanitization process during clipboard read/write operation.
+
+
+ Clipboard Interface
+
+
+ typedef sequence<ClipboardItem> ClipboardItems;
+
+ [SecureContext, Exposed=Window] interface Clipboard : EventTarget {
+ Promise<ClipboardItems> read();
+ Promise<DOMString> readText();
+ Promise<undefined> write(ClipboardItems data);
+ Promise<undefined> writeText(DOMString data);
};
-
+ A [=Clipboard=] may contain one or more [=ClipboardItem=]s. Multiple [=ClipboardItem=]s are only supported on platforms such as iOS/iPadOS.
+ A web author needs to create a |data| which is a [=ClipboardItems=] object in order to write content to clipboard using the {{Clipboard/write(data)}} method.
+ {{Clipboard/read()}} returns a [=Promise=] to [=ClipboardItems=] object that represents contents of clipboard.
+ A [=ClipboardItem=] can be read from [=ClipboardItems=], which then can be used to read a specific |type| using {{ClipboardItem/getType(type)}}.
+
ClipboardItems
+
A [=sequence=] of [=ClipboardItem=] objects
+
+
read()
The {{Clipboard/read()}} method must run these steps:
- 1. Let |p| be a new [=Promise=].
+ 1. Let |realm| be [=this=]'s [=relevant realm=].
+
+ 1. Let |p| be [=a new promise=] in |realm|.
1. Run the following steps [=in parallel=]:
1. Let |r| be the result of running [=check clipboard read permission=] [=in parallel=]
- 1. If |r| is not "granted", then reject |p| with a "NotAllowedError" DOMException
+ Note: Clipboard permission is not supported on Safari. However, the read() method must be called inside
+ a user gesture event and the user must select the paste option from the native context menu that pops up
+ when read() is called from JS, otherwise, the promise will be rejected.
+
+ 1. If |r| is not "granted", then [=a promise rejected with=] "NotAllowedError" DOMException in |realm|.
- 1. Let |data| be a copy of the [=system clipboard data=] represented as
- a sequence of {{ClipboardItem}}s.
+ 1. Let |data| be a copy of the [=system clipboard data=] represented as [=ClipboardItems=].
+
+ 1. For the MIME types defined in the [=mandatory data types=] list, |data| contains sanitized copy of text/html format, but image/png format has unsanitized payload to preserve meta data.
+
+ Note: Currently in Chromium, only one [=ClipboardItem=] object is supported for reading and writing via the [=clipboard=] object.
- Note: As further described in [[#image-transcode]] this explicitly does not transcode images.
+ Note: As further described in [[#image-transcode]] this explicitly does not transcode images.
Rather the original unmodified image data should be exposed to the website.
+
+ 1. For the MIME types defined in the {{ClipboardItemOptions/unsanitized}}, |data| contains copy of the unsanitized payload of the MIME types.
+
+ Note: See [=read unsanitized format=] for more details.
1. Resolve |p| with |data|.
@@ -653,19 +754,39 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;
const text = await (new Response(textBlob)).text();
+
+ // Pickling read example. ClipboardItems returned by clipboard.read() may
+ // contain pickled formats.
+ const clipboardItems = await navigator.clipboard.read(
+ {unsanitized: ['text/custom']} /* This new list specifies the pickled format
+ 'text/custom' for all read ClipboardItems. */
+ );
+ const clipboardItem = clipboardItems[0];
+ const textBlob = await clipboardItem.getType('text/plain');
+ // This format reads as a pickled format, only if it is included in the unsanitized
+ // format list.
+ const customTextBlob = await clipboardItem.getType('text/custom');
+
+
readText()
The {{Clipboard/readText()}} method must run these steps:
- 1. Let |p| be a new [=Promise=].
+ 1. Let |realm| be [=this=]'s [=relevant realm=].
+
+ 1. Let |p| be [=a new promise=] in |realm|.
1. Run the following steps [=in parallel=]:
1. Let |r| be the result of running [=check clipboard read permission=] [=in parallel=]
- 1. If |r| is not "granted", then reject |p| with a "NotAllowedError" DOMException
+ Note: Clipboard permission is not supported on Safari. However, the read() method must be called inside
+ a user gesture event and the user must select the paste option from the native context menu that pops up
+ when read() is called from JS, otherwise, the promise will be rejected.
+
+ 1. If |r| is not "granted", then [=a promise rejected with=] "NotAllowedError" DOMException in |realm|.
1. Let |data| be a copy of the [=system clipboard data=].
@@ -693,25 +814,68 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;
write(|data|)
The {{Clipboard/write(data)}} method must run these steps:
- 1. Let |p| be a new [=Promise=].
+ 1. Let |realm| be [=this=]'s [=relevant realm=].
+
+ 1. Let |p| be [=a new promise=] in |realm|.
1. Run the following steps [=in parallel=]:
1. Let |r| be the result of running [=check clipboard write permission=] [=in parallel=]
- 1. If |r| is not "granted", then reject |p| with a "NotAllowedError" DOMException
+ Note: Clipboard permission is not supported on Safari. However, the read() method must be called inside
+ a user gesture event and the user must select the paste option from the native context menu that pops up
+ when read() is called from JS, otherwise, the promise will be rejected.
+
+ 1. If |r| is not "granted", then [=a promise rejected with=] "NotAllowedError" DOMException in |realm|.
+
+ 1. Let |itemList|, |cleanItemList| and |unsanitizedItemList| be empty sequence<{{Blob}}>.
+
+ 1. Let |u| be the [=sequence=] of string in the {{ClipboardItemOptions/unsanitized}} list.
+
+ 1. Let |clipboardItemList| be an empty [=ClipboardItems=].
- 1. Let |cleanItemList| be an empty sequence<{{Blob}}>.
+ 1. For each |clipboardItem| in |data|:
- 1. For each {{Blob}} |item| in |data|:
+ 1. For each |item| in |clipboardItem|:
- 1. Let |cleanItem| be a sanitized copy of |item|.
+ 1. Let |p1| be [=a new promise=] to {{Blob}} in |realm|.
- 1. If unable to create a sanitized copy, then reject |p|.
+ 1. If |p1| is [=reject=]d, then throw "Promises to Blobs were rejected." DOMException in |realm|.
- 1. Add |cleanItem| to |cleanItemList|.
+ 1. Add the {{Blob}} in |item| to |itemList| after |p1| has been resolved.
- 1. Replace the [=system clipboard data=] with |cleanItemList|.
+ 1. For each {{Blob}} |blob| in |itemList|:
+
+ 1. Let |type| be the |blob|'s {{Blob/type}}.
+
+ 1. If |type| is not in the [=mandatory data types=] list:
+
+ 1. If |type| is not in |u| then throw [=a promise rejected with=] "Type |type| not supported on write." DOMException in |realm|.
+
+ 1. Let |unsanitizedItem| be an unsanitized copy of |blob|.
+
+ 1. If |type| is in the [=mandatory data types=] list:
+
+ 1. Let |cleanItem| be a sanitized copy of |blob|.
+
+ Issue: Add definition of sanitized copy.
+
+ 1. Add |cleanItem| to |cleanItemList|.
+
+ 1. If unable to create a sanitized copy, then [=reject=] |p|.
+
+ 1. Add |unsanitizedItem| to |unsanitizedItemList|.
+
+ 1. Add |unsanitizedItemList| to |clipboardItemList|.
+
+ 1. Add |cleanItemList| to |clipboardItemList|.
+
+ 1. Replace the [=system clipboard data=] with |clipboardItemList|.
+
+ Note: For writing |type| to the [=system clipboard=] that are in the {{ClipboardItemOptions/unsanitized}} list, please see [=write unsanitized format=].
+
+ Note: Writing multiple [=ClipboardItem=]s to the clipboard is only supported on MacOS.
+ For other platforms, only the first [=ClipboardItem=] should be written to the clipboard.
1. Resolve |p|.
@@ -720,7 +884,7 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;
- var data = [new ClipboardItem({ "text/plain": new Blob(["Text data"], { type: "text/plain" }) })];
+ var data = [new ClipboardItem({ "text/plain": Promise.resolve(new Blob(["Text data"], { type: "text/plain" }) }))];
navigator.clipboard.write(data).then(function() {
console.log("Copied to clipboard successfully!");
}, function() {
@@ -728,27 +892,59 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;
});
+
+ // Pickling write example.
+ // This format 'text/plain' is recognized by the Clipboard API, so will be
+ // written as usual.
+ const text = new Blob(['text'], {type: 'text/plain'});
+ // This format 'text/custom' is not sanitized by the Clipboard API. It will be
+ // pickled if the format is specified in the {unsanitized: []} formats list.
+ const customText = new Blob(
+ ['<custom_markup> pickled_text</custom_markup>'], {type: 'text/custom'});
+
+ // Clipboard format ordering: Pickled formats will be written before sanitized
+ // formats by the browser, since they're more "custom" and likely more targeted
+ // towards this use case.
+ const clipboardItem = new ClipboardItem({
+ 'text/plain': text, /* Sanitized format. */
+ 'text/custom': customText /* Pickled format. This new format will be accepted
+ and written without rejection, as long as the new
+ unsanitized list contains this format. */
+ },
+ {unsanitized: ['text/custom']} /* This new list specifies the pickled format
+ 'text/custom'. */
+ );
+ navigator.clipboard.write([clipboard_item]);
+
+
+
writeText(|data|)
The {{Clipboard/writeText(data)}} method must run these steps:
- 1. Let |p| be a new [=Promise=].
+ 1. Let |realm| be [=this=]'s [=relevant realm=].
+
+ 1. Let |p| be [=a new promise=] in |realm|.
1. Run the following steps [=in parallel=]:
1. Let |r| be the result of running [=check clipboard write permission=] [=in parallel=]
- 1. If |r| is not "granted", then reject |p| with a "NotAllowedError" DOMException
+ Note: Clipboard permission is not supported on Safari. However, the read() method must be called inside
+ a user gesture event and the user must select the paste option from the native context menu that pops up
+ when read() is called from JS, otherwise, the promise will be rejected.
+
+ 1. If |r| is not "granted", then [=a promise rejected with=] "NotAllowedError" DOMException in |realm|.
- 1. Let |newItemList| be an empty sequence<{{ClipboardItem}}>.
+ 1. Let |newItemList| be empty [=ClipboardItems=].
1. Let |textBlob| be a new {{Blob}} created with:
[=type=] attribute set to text/plain;charset=utf-8, and
[=blobParts=] set to a sequence containing the string |data|.
- 1. Let |newItem| be a new {{ClipboardItem}} created with a single
+ 1. Let |newItem| be a new [=ClipboardItem=] created with a single
representation:
[=type=] attribute set to text/plain8, and
[=data=] set to |textBlob|.
@@ -1260,6 +1456,7 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;
Ojan Vafai,
Tarquin Wilton-Jones,
Tom Wlodkowski,
+ Bo Cupp,
and Boris Zbarsky.
@@ -1582,3 +1779,115 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;
+
+
+ This section is non-normative.
+
+
+ : Input
+ :: |mimeType|, a string
+
+ : Output
+ :: |item|, a {{Blob}}
+
+ 1. Let |webCustomFormatMap| be the [=os specific custom map name=].
+
+ 1. Read |webCustomFormatMap| from the [=system clipboard=].
+
+ 1. Let |webCustomFormatMapString| be the JSON string deserialized from |webCustomFormatMap|.
+
+ Note: Need a JSON reader to deserialize the content from the |webCustomFormatMap|.
+
+ 1. Let |webCustomFormat| be the value corresponding to the |mimeType| key in |webCustomFormatMapString|.
+
+ 1. Let |item| be a {{Blob}}.
+
+ 1. Read |webCustomFormat| from the [=system clipboard=] and store the resulting data into |item|.
+
+ 1. Return |item|.
+
+
+
+
+
+ : Input
+ :: |items|, {{Blob}}s
+ :: |mimeType|, a string
+
+ 1. Let |idx| be a number initialized to 0.
+
+ 1. Let |webCustomFormatMap| be the [=os specific custom map name=].
+
+ 1. For each |item| in |items|:
+
+ 1. Let |webCustomFormat| be the [=os specific custom name=].
+
+ 1. Let |webCustomFormatIdx| be the result of appending |idx| to |webCustomFormat|.
+
+ 1. Insert |mimeType| as key and |webCustomFormatIdx| as value into the |webCustomFormatMap|.
+
+ Note: Need a JSON writer to serialize the content into the |webCustomFormatMap|.
+
+ 1. Insert the |item| into the [=system clipboard=] using |webCustomFormatIdx| as the format.
+
+ 1. Increment |idx|.
+
+ 1. Insert the |webCustomFormatMap| into the [=system clipboard=].
+
+
+
+
+
+ This section is non-normative.
+
os specific custom map name
+
+ : Output
+ :: |webCustomFormatMap|, a string
+
+ On Windows, follow the convention described below:
+
+ 1. Assign "Web Custom Format Map" to |webCustomFormatMap|.
+
+ 1. Return |webCustomFormatMap|.
+
+ On MacOS, follow the convention described below:
+
+ 1. Assign "com.web.custom.format.map" to |webCustomFormatMap|.
+
+ 1. Return |webCustomFormatMap|.
+
+ On Linux, ChromeOS, and Android, follow the convention described below:
+
+ 1. Assign "application/web;type=\"custom/formatmap\"" to |webCustomFormatMap|.
+
+ 1. Return |webCustomFormatMap|.
+
+
+
+
+ This section is non-normative.
+
os specific custom name
+
+ : Output
+ :: |webCustomFormat|, a string
+
+ On Windows, follow the convention described below:
+
+ 1. Assign "Web Custom Format" to |webCustomFormat|.
+
+ 1. Return |webCustomFormat|.
+
+ On MacOS, follow the convention described below:
+
+ 1. Assign "com.web.custom.format" to |webCustomFormat|.
+
+ 1. Return |webCustomFormat|.
+
+ On Linux, ChromeOS, and Android, follow the convention described below:
+
+ 1. Assign "application/web;type="custom/format" to |webCustomFormat|.
+
+ 1. Return |webCustomFormat|.
+
+
+