diff --git a/index.bs b/index.bs index 04171d6..18c366d 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 [=/clipboard item=] objects that mirrors the contents of the clipboard.
- typedef sequence<ClipboardItem> ClipboardItems; + typedef Promise<(DOMString or Blob)> ClipboardItemData; - [SecureContext, Exposed=Window] interface Clipboard : EventTarget { - Promise<ClipboardItems> read(); - Promise<DOMString> readText(); - Promise<undefined> write(ClipboardItems data); - Promise<undefined> writeText(DOMString data); - }; + [SecureContext, Exposed=Window] + interface ClipboardItem { + constructor(record<DOMString, ClipboardItemData> items, + optional ClipboardItemOptions options = {}); + + readonly attribute PresentationStyle presentationStyle; + readonly attribute FrozenArray<DOMString> types; + + Promise<Blob> getType(DOMString type); + }; - typedef (DOMString or Blob) ClipboardItemDataType; - typedef Promise<ClipboardItemDataType> ClipboardItemData; + enum PresentationStyle { "unspecified", "inline", "attachment" }; - callback ClipboardItemDelayedCallback = ClipboardItemData (); + dictionary ClipboardItemOptions { + PresentationStyle presentationStyle = "unspecified"; + }; +- [Exposed=Window] interface ClipboardItem { - constructor(record<DOMString, ClipboardItemData> items, - optional ClipboardItemOptions options = {}); - static ClipboardItem createDelayed( - record<DOMString, ClipboardItemDelayedCallback> items, - optional ClipboardItemOptions options = {}); +
clipboardItem = new ClipboardItem([items, options])
+ + const format1 = 'text/plain'; + const promise_text_blob = Promise.resolve(new Blob(['hello'], {type: format1})); + const clipboardItemInput = new ClipboardItem( + {[format1]: promise_text_blob}, + {presentationStyle: "unspecified"}); +- readonly attribute FrozenArray<DOMString> types; +
clipboardItem.getType(type)
+ Returns a [=Promise=] to the {{Blob}} corresponding to the [=representation/mime type=] type.
- Promise<Blob> getType(DOMString type); - }; +clipboardItem.types
+ Returns the list of [=representation/mime type=]s contained in the [=/clipboard item=] object. + +
+ For example, if a user copies a range of cells from a spreadsheet of a native application, it will result in one [=/clipboard item=]. If a user copies a set of files from their desktop, that list of files will be represented by multiple [=/clipboard item=]s. +
+ + Some platforms may support having more than one [=/clipboard item=] at a time on the [=clipboard=], while other platforms replace the previous [=/clipboard item=] with the new one. + + A [=/clipboard item=] has a list of representations, each representation with an associated mime type (a [=/MIME type=]) and data (a {{ClipboardItemData}}). + ++ 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 type=]s describe a different [=representation=] of the same [=/clipboard item=] at different levels of fidelity and make the [=/clipboard item=] 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 a photo editing app, while the text/plain format can be used by text editor apps. +
+ + A [=/clipboard item=] has a presentation style (a {{PresentationStyle}}). It helps distinguish whether apps "pasting" a [=/clipboard item=] should insert the contents of an appropriate [=representation=] inline at the point of paste or if it should be treated as an attachment. + + Web apps that support pasting only a single [=/clipboard item=] should use the first [=/clipboard item=]. + ++ {{Clipboard/write()}} chooses the last [=/clipboard item=]. +
+ + Web apps that support pasting more than one [=/clipboard item=] could, for example, provide a user interface that previews the contents of each [=/clipboard item=] and allow the user to choose which one to paste. + Further, apps are expected to enumerate the [=/MIME type=]s of the [=/clipboard item=] 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 [=/clipboard item=], e.g. "paste as image" or "paste formatted text", etc. + + A {{ClipboardItem}} object has an associated clipboard item, which is a [=/clipboard item=]. + + A {{ClipboardItem}} object has an associated types array, which is a {{FrozenArray}}<{{DOMString}}>. + + To create a {{ClipboardItem}} object, given a [=/clipboard item=] |clipboardItem| and a Realm |realm|, run these steps: + 1. Let |clipboardItemObject| be a [=new=] {{ClipboardItem}} with |realm|. + + 1. Set |clipboardItemObject|'s [=/clipboard item=] to |clipboardItem|. + + Thenew ClipboardItem(items, options)
constructor steps are:
+ 1. If |items| is empty, then throw a {{TypeError}}.
+
+ 1. If |options| is empty, then set options["presentationStyle"] = "unspecified".
+
+ 1. Set [=this=]'s [=ClipboardItem/clipboard item=] to a new [=/clipboard item=].
+
+ 1. Set [=this=]'s [=ClipboardItem/clipboard item=]'s [=presentation style=] to |options|["{{ClipboardItemOptions/presentationStyle}}"].
+
+ 1. Let |types| be a list of {{DOMString}}.
+
+ 1. For each (|key|, |value|) in |items|:
+
+ 1. Let |mimeType| be the result of [=parsing a MIME type=] given |key|.
+
+ 1. If |mimeType| is failure, then throw a {{TypeError}}.
+
+ 1. If [=this=]'s [=ClipboardItem/clipboard item=]'s [=list of representations=] [=list/contains=] a [=representation=] whose [=representation/MIME type=] is |mimeType|, then throw a {{TypeError}}.
+
+ 1. Let |representation| be a new [=representation=].
+
+ 1. Set |representation|'s [=representation/MIME type=] to |mimeType|.
+
+ 1. Set |representation|'s [=representation/data=] to |value|.
+
+ 1. Append |representation| to [=this=]'s [=ClipboardItem/clipboard item=]'s [=list of representations=].
+
+ 1. Let |mimeTypeString| be the result of [=serializing a MIME type=] with |mimeType|.
+
+ 1. Add |mimeTypeString| to |types|.
+
+ 1. Set [=this=]'s [=ClipboardItem/types array=] to the result of running [=create a frozen array=] from |types|.
+
+ + The {{ClipboardItem/presentationStyle}} getter steps are to return [=this=]'s [=ClipboardItem/clipboard item=]'s [=presentation style=]. +
+ ++ The {{ClipboardItem/types}} getter steps are to return [=this=]'s [=ClipboardItem/types array=]. +
+ ++ 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 web author needs to create a |data| which is an array of {{ClipboardItem}}s in order to write content to [=system clipboard=] using the {{Clipboard/write(data)}} method. + {{Clipboard/read()}} returns a [=Promise=] to [=clipboard items=] object that represents contents of [=system clipboard=]. +
+ + The clipboard task source is triggered in response to reading or writing of [=system clipboard data=]. ++const items = await navigator.clipboard.read(); const textBlob = await items[0].getType("text/plain"); const text = await (new Response(textBlob)).text(); @@ -644,31 +824,69 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;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. Let |r| be the result of running [=check clipboard read permission=]. - 1. If |r| is not "granted", then reject |p| with a "NotAllowedError" DOMException + 1. If |r| is false, then: + + 1. [=Queue a global task=] on the [=permission task source=], given |realm|'s [=Realm/global object=], to [=reject=] |p| with {{"NotAllowedError"}} {{DOMException}} in |realm|. - 1. Let |data| be a copy of the [=system clipboard data=]. + 1. Abort these steps. - 1. Let |textData| be an empty string. + 1. Let |data| be a copy of the [=system clipboard data=] represented as [=clipboard items=]. For the MIME types defined in the [=mandatory data types=] list, |data| contains the sanitized copy of text/plain format. - 1. If |data| contains a "text/plain" item |textItem|, then: + Issue: Some OSs contain multiple clipboard (e.g. Linux, "primary", "secondary", "selection"). Define from which of those data is read. - 1. Set |textData| to be a copy of |textItem|'s string data + Issue: Add definition of sanitized copy. - 1. Resolve |p| with |textData|. + 1. [=Queue a global task=] on the [=clipboard task source=], given |realm|'s [=Realm/global object=], to perform the below steps: - 1. Return |p|. + 1. Let |dataList| be a [=sequence=] of [=/clipboard item=]s. + + 1. If |data|'s [=list/size=] is greater than 1, then add |data|[0] to |dataList|. + + Issue: Reading multiple clipboard items should be fixed in https://github.com/w3c/clipboard-apis/issues/166. + + 1. Let |itemTypeList| be |dataList|'s [=ClipboardItem/clipboard item=]'s [=list of representations=]. + + 1. For each |representation| in |itemTypeList|: + + 1. If |representation|'s [=representation/MIME type=] [=MIME type/essence=] is "text/plain", then: + + 1. Let |representationDataPromise| be the |representation|'s [=representation/data=]. + + 1. [=promise/React=] to |representationDataPromise|: + + 1. If |representationDataPromise| was fulfilled with value |v|, then: + + 1. If |v| is a {{DOMString}}, then follow the below steps: + + 1. Resolve |p| with |v|. + + 1. If |v| is a {{Blob}}, then follow the below steps: + + 1. Let |string| be the result of [=UTF-8 decoding=] |v|'s underlying byte sequence. + + 1. Resolve |p| with |string|. + + 1. If |representationDataPromise| was rejected, then: + + 1. [=Reject=] |p| with {{"NotFoundError"}} {{DOMException}} in |realm|. + + 1. [=Reject=] |p| with {{"NotFoundError"}} {{DOMException}} in |realm|. + + 1. Return |p|. -+navigator.clipboard.readText().then(function(data) { - console.log(“Your string: ”, data); + console.log("Your string: ", data); });@@ -678,34 +896,88 @@ 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. Let |r| be the result of running [=check clipboard write permission=]. + + Issue: clipboard-write was removed in https://github.com/w3c/clipboard-apis/pull/164. + + 1. If |r| is false, then: + + 1. [=Queue a global task=] on the [=permission task source=], given |realm|'s [=Realm/global object=], to [=reject=] |p| with {{"NotAllowedError"}} {{DOMException}} in |realm|. + + 1. Abort these steps. + + 1. [=Queue a global task=] on the [=clipboard task source=], given |realm|'s [=Realm/global object=], to perform the below steps: + + 1. Let |itemList| and |cleanItemList| be an empty [=sequence=]<{{Blob}}>. + + 1. Let |dataList| be a [=sequence=]<{{ClipboardItem}}>. + + 1. If |data|'s [=list/size=] is greater than 1, and the current operating system does not support multiple native clipboard items on the [=system clipboard=], then add |data|[0] to |dataList|, else, set |dataList| to |data|. + + Issue: when |data| contains multiple items and the operating system supports multiple native clipboard items, the current algorithm writes the items in sequence to the system clipboard instead of writing them collectively. + + 1. For each |clipboardItem| in |dataList|: + + 1. For each |representation| in |clipboardItem|'s [=ClipboardItem/clipboard item=]'s [=list of representations=]: + + 1. Let |representationDataPromise| be the |representation|'s [=representation/data=]. + + 1. [=promise/React=] to |representationDataPromise|: + + 1. If |representationDataPromise| was fulfilled with value |v|, then: + + 1. If |v| is a {{DOMString}}, then follow the below steps: + + 1. Let |dataAsBytes| be the result of [=UTF-8 encoding=] |v|. - 1. If |r| is not "granted", then reject |p| with a "NotAllowedError" DOMException + 1. Let |blobData| be a {{Blob}} created using |dataAsBytes| with its {{Blob/type}} set to |representation|'s [=representation/MIME type=]. - 1. Let |cleanItemList| be an empty sequence<{{Blob}}>. + 1. Add |blobData| to |itemList|. - 1. For each {{Blob}} |item| in |data|: + 1. If |v| is a {{Blob}}, then add |v| to |itemList|. - 1. Let |cleanItem| be a sanitized copy of |item|. + 1. If |representationDataPromise| was rejected, then: - 1. If unable to create a sanitized copy, then reject |p|. + 1. [=Reject=] |p| with {{"NotAllowedError"}} {{DOMException}} in |realm|. - 1. Add |cleanItem| to |cleanItemList|. + 1. Abort these steps. - 1. Replace the [=system clipboard data=] with |cleanItemList|. + 1. For each |blob| in |itemList|: - 1. Resolve |p|. + 1. Let |type| be the |blob|'s {{Blob/type}}. + + 1. If |type| is not in the [=mandatory data types=] list, then [=reject=] |p| with {{"NotAllowedError"}} {{DOMException}} in |realm| and abort these steps. + + 1. Let |cleanItem| be a sanitized copy of |blob|. + + Issue: Add definition of sanitized copy. + + 1. If unable to create a sanitized copy, then follow the below steps: + + 1. [=Reject=] |p| with {{"NotAllowedError"}} DOMException in |realm|. + + 1. Abort these steps. + + 1. Append |cleanItem| to |cleanItemList|. + + 1. Let |option| be |clipboardItem|'s [=ClipboardItem/clipboard item=]'s [=presentation style=]. + + 1. [=Write blobs and option to the clipboard=] with |cleanItemList| and |option|. + + 1. Resolve |p|. 1. Return |p|. -- 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() { @@ -719,36 +991,45 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;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. Let |r| be the result of running [=check clipboard write permission=]. + + Issue: clipboard-write was removed in https://github.com/w3c/clipboard-apis/pull/164. + + 1. If |r| is false, then: + + 1. [=Queue a global task=] on the [=permission task source=], given |realm|'s [=Realm/global object=], to [=reject=] |p| with {{"NotAllowedError"}} DOMException in |realm|. - 1. If |r| is not "granted", then reject |p| with a "NotAllowedError" DOMException + 1. Abort these steps. - 1. Let |newItemList| be an empty sequence<{{ClipboardItem}}>. + 1. [=Queue a global task=] on the [=clipboard task source=], given |realm|'s [=Realm/global object=], to perform the below steps: - 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 |itemList| be an empty [=sequence=]<{{Blob}}>. - 1. Let |newItem| be a new {{ClipboardItem}} created with a single - representation: - [=type=] attribute set to text/plain8, and - [=data=] set to |textBlob|. + 1. Let |textBlob| be a new {{Blob}} created with: + {{Blob/type}} attribute set to "text/plain;charset=utf-8
", and + its underlying byte sequence set to the [=UTF-8 encoding=] of |data|. + + Note: On Windows replace `\n` characters with `\r\n` in |data| before creating |textBlob|. + + 1. Add |textBlob| to |itemList|. - 1. Add |newItem| to |newItemList|. + 1. Let |option| be set to "unspecified". - 1. Replace the [=system clipboard data=] with |newItemList|. + 1. [=Write blobs and option to the clipboard=] with |itemList| and |option|. - 1. Resolve |p|. + 1. Resolve |p|. 1. Return |p|. -+await navigator.clipboard.writeText("Howdy, partner!");@@ -1245,6 +1526,8 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn; Ojan Vafai, Tarquin Wilton-Jones, Tom Wlodkowski, + Bo Cupp, + mbrodesser and Boris Zbarsky. @@ -1321,6 +1604,125 @@ urlPrefix: https://w3c.github.io/FileAPI/#dfn-; type: dfn;
+ For Windows see https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats and + https://docs.microsoft.com/en-us/windows/win32/dataxchg/about-atom-tables?redirectedfrom=MSDN + For Mac see https://developer.apple.com/documentation/appkit/nspasteboardtype +
+ + 1. Let |wellKnownFormat| be a string. + + 1. If |mimeType|'s [=MIME type/essence=] is "text/plain", then + + On Windows, follow the convention described below: + + 1. Assign CF_UNICODETEXT to |wellKnownFormat|. + + 1. Return |wellKnownFormat|. + + On MacOS, follow the convention described below: + + 1. Assign NSPasteboardTypeString to |wellKnownFormat|. + + 1. Return |wellKnownFormat|. + + On Linux, ChromeOS, and Android, follow the convention described below: + + 1. Assign "text/plain" to |wellKnownFormat|. + + 1. Return |wellKnownFormat|. + + 1. Else, if |mimeType|'s [=MIME type/essence=] is "text/html", then + + On Windows, follow the convention described below: + + 1. Assign CF_HTML to |wellKnownFormat|. + + 1. Return |wellKnownFormat|. + + On MacOS, follow the convention described below: + + 1. Assign NSHTMLPboardType to |wellKnownFormat|. + + 1. Return |wellKnownFormat|. + + On Linux, ChromeOS, and Android, follow the convention described below: + + 1. Assign "text/html" to |wellKnownFormat|. + + 1. Return |wellKnownFormat|. + + 1. Else, if |mimeType|'s [=MIME type/essence=] is "image/png", then + + On Windows, follow the convention described below: + + 1. Assign "PNG" to |wellKnownFormat|. + + 1. Return |wellKnownFormat|. + + On MacOS, follow the convention described below: + + 1. Assign NSPasteboardTypePNG to |wellKnownFormat|. + + 1. Return |wellKnownFormat|. + + On Linux, ChromeOS, and Android, follow the convention described below: + + 1. Assign "image/png" to |wellKnownFormat|. + + 1. Return |wellKnownFormat|. + + 1. Else + + 1. This is left to the implementation... + + Issue: It's not good to leave things up to the + implementation. What should happen here? + + Note: Due to limitations in the implementation of operating + system clipboards, scripts should not assume that custom + formats will be available to other applications on the + system. For example, there is a limit to how many custom + clipboard formats can be registered in Microsoft Windows. + While it is possible to use any string for + setData()'s type argument, sticking to the + [=mandatory data types=] is strongly recommended. + +