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. +

read unsanitized format

+ + : 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|. + +
+ +

write unsanitized format

+ + : 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|. + +
+