Re: [w3c/clipboard-apis] Add clipboard IDL description. (#158)

@domenic commented on this pull request.

I got further this time. Let's try to get ClipboardItem solid and then we can tackle Clipboard itself... I suspect the hardest part will be getType().

In general I'd encourage you to adopt the mindset: if I knew nothing about the current implementation of this in Chromium, could I read this spec from top to bottom and implement the result from scratch, in a way that behaves identical to the Chromium implementation? That's the bar we need to meet; see e.g. the specifications section of the [Blink values in Practice](https://www.chromium.org/blink/guidelines/web-platform-changes-guidelines) document.

>  
-  typedef (DOMString or Blob) ClipboardItemDataType;
-  typedef Promise<ClipboardItemDataType> ClipboardItemData;
+   [SecureContext, Exposed=Window]
+   interface ClipboardItem {
+    constructor(record<DOMString, ClipboardItemData> items,
+       optional ClipboardItemOptions options = {});

These indents still don't quite line up in the output. I suspect because of a mixing of tabs and spaces.

> +      {[format1]: promise_text_blob},
+      {options: "unspecified"});
+    </pre>
+
+   <dt><code><var>clipboardItem</var>.getType(<var>type</var>)</code>
+   <dd><p>Returns a [=Promise=] to the [=Blob=] corresponding to <var>type</var>.</p>
+
+   <dt><code><var>clipboardItem</var>.<var>types</var></code>
+   <dd><p>Returns the list of <var>types</var> contained in the <var>clipboardItem</var> object.
+
+  </dl>
+  <p>
+   A {{ClipboardItem}} object has an associated [=clipboard item=].
+  </p>
+  <p>
+   A {{ClipboardItem}} object's {{ClipboardItem/presentationStyle}} is its [=clipboard item=]'s [=presentation style=].

It's best to delete this paragraph; it's confusing because it seems like an alternate definition of the getter steps below.

> +
+   <dt><code><var>clipboardItem</var>.getType(<var>type</var>)</code>
+   <dd><p>Returns a [=Promise=] to the [=Blob=] corresponding to <var>type</var>.</p>
+
+   <dt><code><var>clipboardItem</var>.<var>types</var></code>
+   <dd><p>Returns the list of <var>types</var> contained in the <var>clipboardItem</var> object.
+
+  </dl>
+  <p>
+   A {{ClipboardItem}} object has an associated [=clipboard item=].
+  </p>
+  <p>
+   A {{ClipboardItem}} object's {{ClipboardItem/presentationStyle}} is its [=clipboard item=]'s [=presentation style=].
+  </p>
+  <p>
+   A {{ClipboardItem}} object's {{ClipboardItem/types}} is its [=clipboard item=]'s [=types=].

Same here

> +  </p>
+
+  <p>A [=clipboard item=] has an associated <dfn>types</dfn> which contains the MIME types.</p>
+
+  <dl class=note>
+   <dt><code><var>clipboardItem</var> = new ClipboardItem([<var>items</var>, <var>options</var>])</code>
+   <dd>
+   Creates a new {{ClipboardItem}} object. <var>items</var> are used to fill its MIME types and [=Promise=]s to [=Blob=]s or {{DOMString}}s corresponding to the MIME types, <var>options</var> can be used to fill its {{ClipboardItemOptions}},
+   as per the example below.
+
+    <pre class="example javascript" highlight=js>
+     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"});

I believe this should read `presentationStyle: "unspecified"`

> +
+   <dt><code><var>clipboardItem</var>.<var>types</var></code>
+   <dd><p>Returns the list of <var>types</var> contained in the <var>clipboardItem</var> object.
+
+  </dl>
+  <p>
+   A {{ClipboardItem}} object has an associated [=clipboard item=].
+  </p>
+  <p>
+   A {{ClipboardItem}} object's {{ClipboardItem/presentationStyle}} is its [=clipboard item=]'s [=presentation style=].
+  </p>
+  <p>
+   A {{ClipboardItem}} object's {{ClipboardItem/types}} is its [=clipboard item=]'s [=types=].
+  </p>
+  <p>
+   The <a constructor lt="ClipboardItem()">constructor</a> steps for <code>new ClipboardItem(<var>items</var>, <var>options</var>)</code> are to set [=this=]'s items to <var>items</var> and options to <var>options</var>.

So, you have defined a ClipboardItem to have exactly one associated value: its "clipboard item". You have not defined that it has associated "items" or "options", so you cannot set those on it.

I believe you want to instead do something like the following:

1. Set this's clipboard item to a new clipboard item.
2. Set this's clipboard item's presentation style to options["presentationStyle"].
3. ??? do something with items to store them somewhere ???

Step 3 is tricky because I don't know how to map a `record<DOMString, ClipboardItemData>` into any other concepts defined in this spec. A guess might be that each clipboard item has **representations**, which is a [map](https://infra.spec.whatwg.org/#maps) from [MIME types](https://mimesniff.spec.whatwg.org/#mime-type) to `ClipboardItemData` values. (If so, that should be stated and defined in the section on "clipboard item".) Then you would do something like the following:

3. For each (key, value) of items:
    1. Let mimeType be the result of [parsing a MIME type](https://mimesniff.spec.whatwg.org/#parse-a-mime-type) given key.
    2. If mimeType is failure, then throw a TypeError.
    3. Set this's clipboard item's representations[mimeType] to value.

but there are variants, e.g.: maybe you don't want to parse the MIME types and will accept any random string; maybe you want to unwrap the promises instead of storing them internally so that later, when you get the data, you don't have to wait for the promise; etc.

> +  <p>
+   A {{ClipboardItem}} object has an associated [=clipboard item=].
+  </p>
+  <p>
+   A {{ClipboardItem}} object's {{ClipboardItem/presentationStyle}} is its [=clipboard item=]'s [=presentation style=].
+  </p>
+  <p>
+   A {{ClipboardItem}} object's {{ClipboardItem/types}} is its [=clipboard item=]'s [=types=].
+  </p>
+  <p>
+   The <a constructor lt="ClipboardItem()">constructor</a> steps for <code>new ClipboardItem(<var>items</var>, <var>options</var>)</code> are to set [=this=]'s items to <var>items</var> and options to <var>options</var>.
+  </p>
+
+  <h4 attribute for=ClipboardItem lt=presentationStyle>presentationStyle</h4>
+  <p>
+   Each {{ClipboardItem}} has a {{ClipboardItem/presentationStyle}}, which is a {{PresentationStyle}}.

The first sentence here is redundant with the IDL.

> +  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 [=clipboard item=] should use the first [=clipboard item=].
+  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-types 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 [=clipboard item=] contains multiple representations. Each representation consists of a MIME type and a corresponding {{DOMString}} or [=Blob=].
+
+  <p>A [=clipboard item=] has an associated <dfn>presentation style</dfn> which is <code>"unspecified"</code> or <code>"inline"</code> or <code>"attachment"</code>.
+  [=presentation style=] 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.
+  </p>
+
+  <p>A [=clipboard item=] has an associated <dfn>types</dfn> which contains the MIME types.</p>

What is this? Below I suggested maybe it should be a map of representations? But I don't think that works for how you're using it... more suggestions in the getter section.

> +  </p>
+  <p>
+   A {{ClipboardItem}} object's {{ClipboardItem/types}} is its [=clipboard item=]'s [=types=].
+  </p>
+  <p>
+   The <a constructor lt="ClipboardItem()">constructor</a> steps for <code>new ClipboardItem(<var>items</var>, <var>options</var>)</code> are to set [=this=]'s items to <var>items</var> and options to <var>options</var>.
+  </p>
+
+  <h4 attribute for=ClipboardItem lt=presentationStyle>presentationStyle</h4>
+  <p>
+   Each {{ClipboardItem}} has a {{ClipboardItem/presentationStyle}}, which is a {{PresentationStyle}}.
+   The {{ClipboardItem/presentationStyle}} getter steps are to return [=this=]'s [=clipboard item=]'s [=presentation style=].
+  </p>
+
+  <h4 attribute for=ClipboardItem lt=types>types</h4>
+  <p> Each {{ClipboardItem}} has a {{ClipboardItem/types}}, which contains the MIME types.

Since a clipboard item's "types" doesn't have a defined type, it's hard to tell if this works. Also, nothing ever sets the types, so this isn't well-defined.

My suggestion is to add something to the above constructor steps which accumulates the MIME type strings into a [list](https://infra.spec.whatwg.org/#lists), and then [creates a frozen array](https://webidl.spec.whatwg.org/#dfn-create-frozen-array) from them. And then, store that on a "types" field of the `{{ClipboardItem}}` (not the "clipboard item"), so that the getter steps can be to "return this's types".

> +     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"});
+    </pre>
+
+   <dt><code><var>clipboardItem</var>.getType(<var>type</var>)</code>
+   <dd><p>Returns a [=Promise=] to the [=Blob=] corresponding to <var>type</var>.</p>
+
+   <dt><code><var>clipboardItem</var>.<var>types</var></code>
+   <dd><p>Returns the list of <var>types</var> contained in the <var>clipboardItem</var> object.
+
+  </dl>
+  <p>
+   A {{ClipboardItem}} object has an associated [=clipboard item=].

This is on the right track, but you should have separate `<dfn>`s: one for the "clipboard item" type (what you have), and one for the "`ClipboardItem`'s clipboard item" (which you don't have). Something like

```html
A {{ClipboardItem}} object has an associated <dfn for="ClipboardItem">clipboard item</dfn>, which is a [=clipboard item=]
```

Then, you can reference this field elsewhere using the syntax like `[=this=]'s [=ClipboardItem/clipboard item=]`.

> +
+  <h4 attribute for=ClipboardItem lt=presentationStyle>presentationStyle</h4>
+  <p>
+   Each {{ClipboardItem}} has a {{ClipboardItem/presentationStyle}}, which is a {{PresentationStyle}}.
+   The {{ClipboardItem/presentationStyle}} getter steps are to return [=this=]'s [=clipboard item=]'s [=presentation style=].
+  </p>
+
+  <h4 attribute for=ClipboardItem lt=types>types</h4>
+  <p> Each {{ClipboardItem}} has a {{ClipboardItem/types}}, which contains the MIME types.
+      {{ClipboardItem/types}} getter steps are to return [=this=]'s [=clipboard item=]'s [=types=].</p>
+
+  <h4 method for=ClipboardItem lt=getType(type)>getType(type)</a> must run the below steps:</h4>
+
+   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|.

This is not one of the defined DOMException names: https://webidl.spec.whatwg.org/#idl-DOMException-error-names


> +   The {{ClipboardItem/presentationStyle}} getter steps are to return [=this=]'s [=clipboard item=]'s [=presentation style=].
+  </p>
+
+  <h4 attribute for=ClipboardItem lt=types>types</h4>
+  <p> Each {{ClipboardItem}} has a {{ClipboardItem/types}}, which contains the MIME types.
+      {{ClipboardItem/types}} getter steps are to return [=this=]'s [=clipboard item=]'s [=types=].</p>
+
+  <h4 method for=ClipboardItem lt=getType(type)>getType(type)</a> must run the below steps:</h4>
+
+   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|.

I have no idea how to implement this. My best guess is that if you passed in `text/plain` as a type, this would return a promise for the blob created with `new Blob(["text/plain"])`, i.e. containing the string "text/plain" in UTF-8 encoding.

>  
-    Promise&lt;Blob> getType(DOMString type);
-  };
+   1. Return |p|.
+
+  <h4 typedef>ClipboardItemDataType</h4>

These two sections are just confusing and redundant with the IDL; it's best to leave them out.

> +
+  <h4 attribute for=ClipboardItem lt=presentationStyle>presentationStyle</h4>
+  <p>
+   Each {{ClipboardItem}} has a {{ClipboardItem/presentationStyle}}, which is a {{PresentationStyle}}.
+   The {{ClipboardItem/presentationStyle}} getter steps are to return [=this=]'s [=clipboard item=]'s [=presentation style=].
+  </p>
+
+  <h4 attribute for=ClipboardItem lt=types>types</h4>
+  <p> Each {{ClipboardItem}} has a {{ClipboardItem/types}}, which contains the MIME types.
+      {{ClipboardItem/types}} getter steps are to return [=this=]'s [=clipboard item=]'s [=types=].</p>
+
+  <h4 method for=ClipboardItem lt=getType(type)>getType(type)</a> must run the below steps:</h4>
+
+   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|.

This is short on detail. Do inexact matches count? E.g. the "mandatory data types" list contains `text/plain`, but does `text/plain;charset=utf-8` count? How about `text/PLAIN`?

I would suggest running [parse a MIME type](https://mimesniff.spec.whatwg.org/#parse-a-mime-type) on the input and comparing its [essence](https://mimesniff.spec.whatwg.org/#mime-type-essence) to the list of mandatory data types.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/clipboard-apis/pull/158#pullrequestreview-799309863

Received on Friday, 5 November 2021 21:08:52 UTC