-
Notifications
You must be signed in to change notification settings - Fork 37
/
index.bs
2265 lines (1483 loc) · 86.8 KB
/
index.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<h1>Clipboard API and events</h1>
<pre class="metadata">
Shortname: clipboard-apis
Level: none
Group: webediting
Status: w3c/ED
TR: https://www.w3.org/TR/clipboard-apis/
ED: https://w3c.github.io/clipboard-apis/
Previous Version: from biblio
Repository: w3c/clipboard-apis
!Explainer: <a href="https://github.com/w3c/clipboard-apis/blob/master/explainer.adoc">Async Clipboard API Explainer</a>
Editor: Gary Kacmarcik, Google, [email protected], w3cid 59482
Editor: Anupam Snigdha, Microsoft, [email protected], w3cid 126950
Former Editor: Hallvord R. M. Steen, Mozilla
Former Editor: Grisha Lyukshin, Microsoft, [email protected], w3cid 78883
Abstract:
This document describes APIs for accessing data on the system clipboard. It provides
operations for overriding the default clipboard actions (cut, copy and paste), and
for directly accessing the clipboard contents.
Status Text: <p> <strong>Changes to this document may be tracked at <a href="https://github.com/w3c/clipboard-apis">https://github.com/w3c/clipboard-apis</a>.</strong> </p>
</pre>
<pre class="anchors">
urlPrefix: https://html.spec.whatwg.org/multipage/interaction.html#; type: dfn;
text: drag data store mode
text: drag data item kind
text: drag data item type string
text: concept dnd rw
text: concept dnd ro
text: concept dnd p
text: focusable area
text: dom anchor
urlPrefix: http://www.w3.org/TR/html5/browsers.html#; type: dfn;
text: allowed to show a popup
urlPrefix: https://dom.spec.whatwg.org/#; type: dfn;
text: constructing events
url: https://w3c.github.io/permissions/#permissions-task-source; type: dfn;
text: permission task source
</pre>
<pre class="biblio">
{
"MICROSOFT-CLIP-OP": {
"title": "About DHTML Data Transfer. Microsoft Developer Network.",
"href": "https://msdn.microsoft.com/en-us/ie/ms537658(v=vs.94)"
}
}
</pre>
<h2 id="secintro">Introduction</h2>
<i>This section is non-normative.</i>
This specification defines how the system clipboard is exposed to web applications.
There are two general APIs described in this specification:
* Clipboard Event API - This API provides a way to hook into the common clipboard
operations of cutting, copying and pasting so that web application can
adjust the clipboard data as required.
* Async Clipboard API - This API provides direct access to read and write the
clipboard data. Since this is considered to be a <a>powerful feature</a>,
access to this API is controlled by a permission.
<h2 id="Cases">Use Cases</h2>
<i>This section is non-normative.</i>
<h3 id="change-default-clipboard-ops">Changing Default Clipboard Operations</h3>
There are many scenarios where it is desireable to change the default clipboard
operations (cut/copy/paste). Here are a few examples:
<ul>
<li><strong>Metadata</strong>
When copying text from a repository of documents, it can be useful for the
copied text to include metadata about the source of the copied content.
<li><strong>Rich content editing</strong>
When copying text which contains hyperlinks or other structure,
it is often useful to be able to reformat the content to preserve
important information.
</li>
<li><strong>Graphics with built-in semantics</strong>
In order to make web applications which allow the manipulation of rich
text, or of graphic content such as [[SVG11]], it is useful to provide a
mechanism that allows for copying more than just the rendered content.
</li>
<li><strong>Mathematical information</strong>
With content such as mathematics, simply copying rendered text and pasting
it into another application generally leads to most of the semantics being
lost. MathML often needs to be transformed to be copied as plain text, for
example to make sure "to the power of" is shown with the caret "^" sign
in a formula plain-text input. The XML source could also be placed in the
clipboard with the appropriate transformation occurring at paste time.
</li>
</ul>
<h3 id="remote-clipboard-sync">Remote Clipboard Synchronization</h3>
For web applications that communicate with remote devices (e.g., remote access
or remote shell applications), there is often a need for the clipboard data
to be kept in sync between the two devices.
One important aspect of this use case is that it requires access to the
clipboard in the absense of a user gesture or interaction.
<div class="example">
To copy the clipboard data from a remote device to the local clipboard, a web
application would take the remote clipboard data and then {{Clipboard/write()}}
the data to the local clipboard.
</div>
<div class="example">
To copy the local clipboard data to a remote device, a web application would
listen for {{GlobalEventHandlers/clipboardchange}} events, {{Clipboard/read()}} from the clipboard
whenever it is updated, and then send the new clipboard data to the remote device.
</div>
<h3 id="trigger-clipboard-actions">Trigger Clipboard Actions</h3>
Applications that provide an alternate interface to a user agent sometimes need
to be able to trigger clipboard actions in the user agent.
As an example, consider a screen reader application that provides a more accessible
interface to a standard web browser. While the reader can display content and
allow the user to interact with it, actions like clipboard copy need
to occur in the underlying browser to ensure that the clipboard content is set
correctly (with any metadata added by the browser during copy).
<h2 id="terminolofy">Terminology</h2>
The term <dfn>editable context</dfn> means any element that is either an
[=editing host=], a textarea element, or an input element with its type
attribute set to any of "text", "search", "tel",
"url", "email", "password" or "number".
<h2 id="model">Model</h2>
The platform provides a <dfn>system clipboard</dfn>.
The [=system clipboard=] has a list of [=system clipboard item=]s that are collectively called the <dfn>system clipboard data</dfn>.
Each <dfn>system clipboard item</dfn> has a list of [=system clipboard representation=]s.
Each <dfn>system clipboard representation</dfn> has a <dfn for="system clipboard representation">name</dfn>, which is a string, and <dfn for="system clipboard representation">data</dfn>, which is a sequence of bytes.
<h2 id="clipboard-events-and-interfaces">Clipboard Events</h2>
<h3 id="clipboard-event-interfaces">Clipboard event interfaces</h3>
The ClipboardEvent interface extends the {{Event}} interface.
<div id="clipboardeventinit-idl">
<pre class="idl" data-highlight="webidl">
dictionary ClipboardEventInit : EventInit {
DataTransfer? clipboardData = null;
};
</pre>
: <dfn for="ClipboardEventInit" data-export>clipboardData</dfn>
:: A {{DataTransfer}} object to hold data and meta data related to the event.
</div>
<div id="clipboardevent-idl">
<pre class="idl" data-highlight="webidl">
[Exposed=Window]
interface ClipboardEvent : Event {
constructor(DOMString type, optional ClipboardEventInit eventInitDict = {});
readonly attribute DataTransfer? clipboardData;
};
</pre>
: <dfn for="ClipboardEvent" data-export>clipboardData</dfn>
:: The clipboardData attribute is an instance of the {{DataTransfer}}
interface which lets a script read and manipulate values on the system
clipboard during user-initiated copy, cut and paste operations. The
associated drag data store is a live but filtered view of the system
clipboard, exposing [=mandatory data types=] the implementation knows
the script can safely access. For synthetic events, the drag data store
contains the data added by the script that created the event.
:: The clipboardData object's {{DataTransfer/items}} and
{{DataTransfer/files}} properties enable processing of multi-part or
non-textual data from the clipboard.
</div>
The interface can be used to <a lt="constructing events">construct events</a>.
An example is given below:
<pre class="example javascript">
var pasteEvent = new ClipboardEvent('paste');
pasteEvent.clipboardData.items.add('My string', 'text/plain');
document.dispatchEvent(pasteEvent);
</pre>
Note: Synthetic clipboard events will not actually modify the clipboard or
the document. In other words, while the script above will fire a paste event,
the data will not be pasted into the document.
<!-- Issue: There should be a way a script can check if a clipboard format
is registered and whether the implementation allows writing this format to
the clipboard! event.clipboardData.isTypeSupported('mime/type')</em>?
-->
<h3 id="clipboard-event-definitions">Clipboard events</h3>
<h4 id="clipboard-event-clipboardchange">The <dfn event for=GlobalEventHandlers>clipboardchange</dfn> event</h4>
The {{GlobalEventHandlers/clipboardchange}} event fires whenever the contents of the
[=system clipboard=] are changed. These changes could be due to any of the
following (non-exhaustive):
* User-initiated cut or copy actions
* Scripts that use the [[#async-clipboard-api]] to write to the clipboard
* Actions that update the clipboard outside the user agent
If the clipboard contents are changed outside the user agent, then the
{{GlobalEventHandlers/clipboardchange}} event MUST fire when the user agent regains focus.
Since synthetic {{GlobalEventHandlers/cut}} and {{GlobalEventHandlers/copy}} events do not update the [=system clipboard=],
they will not trigger a "clipboardchange" event.
<h4 id="clipboard-event-copy">The <dfn event for=GlobalEventHandlers>copy</dfn> event</h4>
When the user initiates a copy action, the user agent
[=fire a clipboard event|fires a clipboard event=] named
{{GlobalEventHandlers/copy}}.
If the event is not canceled, the currently selected
data will be copied to the [=system clipboard=].
The current document selection is not affected.
The {{GlobalEventHandlers/copy}} event bubbles, is cancelable, and is composed.
See [[#copy-action]] for a detailed description of the processing model for
this event.
A synthetic {{GlobalEventHandlers/copy}} event can be manually constructed and dispatched, but it
will not affect the contents of the [=system clipboard=].
<h4 id="clipboard-event-cut">The <dfn event for=GlobalEventHandlers>cut</dfn> event</h4>
When the user initiates a cut action, the user agent
[=fire a clipboard event|fires a clipboard event=] named
{{GlobalEventHandlers/cut}}.
In an [=editable context=], if the event is not
canceled the action will place the currently selected data on the
[=system clipboard=] and remove the selection from the document.
The {{GlobalEventHandlers/cut}} event fires before the selected data is removed. When
the cut operation is completed, the selection is collapsed.
In a non-[=editable context=], the {{ClipboardEvent/clipboardData}} will
be an empty list. Note that the {{GlobalEventHandlers/cut}} event will still be fired
in this case.
The {{GlobalEventHandlers/cut}} event bubbles, is cancelable, and is composed.
See [[#cut-action]] for a detailed description of the processing model for
this event.
A synthetic {{GlobalEventHandlers/cut}} event can be manually constructed and dispatched, but it
will not affect the contents of the document or of the [=system clipboard=].
<h4 id="clipboard-event-paste">The <dfn event for=GlobalEventHandlers>paste</dfn> event</h4>
When a user initiates a paste action, the user agent
[=fire a clipboard event|fires a clipboard event=] named
{{GlobalEventHandlers/paste}}. The event fires before any clipboard data is inserted
into the document.
If the cursor is in an [=editable context=], the paste action will
insert clipboard data in the most suitable format (if any) supported
for the given context.
The paste action has no effect in a non-[=editable context=],
but the {{GlobalEventHandlers/paste}} event fires regardless.
The {{GlobalEventHandlers/paste}} event bubbles, is cancelable, and is composed.
See [[#paste-action]] for a detailed description of the processing model for
this event.
A synthetic {{GlobalEventHandlers/paste}} event can be manually constructed and dispatched, but it
will not affect the contents of the document.
<h3 id="integration-with-other-scripts-and-events">Integration with other scripts and events</h3>
<h4 id="allow-modify-clipboard">Event handlers that are <dfn export>allowed to modify the clipboard</dfn></h4>
Event handlers may write to the clipboard if any of the following is
true:
* The action which triggers the event is invoked from the user-agent's
own user interface, e.g. from a "Copy" menu entry or shortcut key.
* The action which triggers the event is invoked from a scripting thread
which is [=allowed to show a popup=].
The implementation <em>may</em> allow other trusted event types to
modify the clipboard if the implementation authors believe that those
event types are likely to express user intention. The implementation
<em>may</em> also support configuration that trusts specific sites or
apps to modify the clipboard regardless of the origin of the scripting
thread.
Synthetic {{GlobalEventHandlers/cut}} and {{GlobalEventHandlers/copy}} events <em>must not</em> modify data on the
system clipboard.
<h4 id="allow-read-clipboard">Event handlers that are <dfn>allowed to read from clipboard</dfn></h4>
Event handlers may read data from the system clipboard if either of the
following is true
* The action that triggers the event is invoked from the user-agent's
own user interface, e.g. a "Paste" menu entry or shortcut key.
* The script that triggers the action is running on a site which through
an implementation-dependant mechanism is given permission to read
data from the clipboard.
* The action that triggers the event is triggered in an app with
permissions to read the clipboard.
Synthetic {{GlobalEventHandlers/paste}} events <em>must not</em> give a script access to data on
the real system clipboard.
<h4 id="integration-with-rich-text-editing">Integration with rich text editing APIs</h4>
If an implementation supports ways to execute clipboard commands through
scripting, for example by calling the
<code>document.execCommand()</code> method with the commands "cut",
"copy" and "paste", the implementation <em>must</em> trigger the
corresponding action, which again will dispatch the associated clipboard
event.
These are the steps to follow when triggering copy, cut or paste actions
through a scripting API:
1. Execute the corresponding action synchronously.
1. Use the action's return value as the return value for the API call.
Note: Copy and cut commands triggered through a scripting API will only
affect the contents of the real clipboard if the event is dispatched
from an event that is trusted and triggered by the user, or if the
implementation is configured to allow this. Paste commands triggered
through a scripting API will only fire paste events and give access to
clipboard contents if the implementation is configured to allow this.
How implementations can be configured to allow read or write access to
the clipboard is outside the scope of this specification.
<h4 id="otherevents">Interaction with other events</h4>
If the clipboard operation is triggered by keyboard input, the
implementation <em>must</em> fire the corresponding event that initiates
the clipboard operation. The event is asynchronous but <em>must</em> be
dispatched before <b>keyup</b> events for the relevant keys.
The cut and paste actions <em>may</em> cause the implementation to
dispatch other supported events, such as textInput, input, change,
validation events, DOMCharacterDataModified and DOMNodeRemoved /
DOMNodeInserted. Any such events are queued up to fire after processing
of the cut/paste event is finished.
The implementation <em>must not</em> dispatch other input-related events
like textInput, input, change, and validation events in response to the
copy operation.
<h4 id="selection-mod">Event listeners that modify selection or focus</h4>
If the event listener modifies the selection or [=focusable area=], the clipboard
action <em>must</em> be completed on the modified selection.
<h2 id="clipboard-event-api">Clipboard Event API</h2>
The Clipboard Event API allows you to override the default cut, copy
and paste behavior of the user agent.
Access to the clipboard is performed using the standard
{{DataTransfer}} methods to mutate the {{DataTransfer/items}} on a
{{ClipboardEvent}}'s {{ClipboardEvent/clipboardData}} attribute.
One consequence of this is that these clipboard APIs can only
access clipboard data in the context of a {{ClipboardEvent}} handler.
Note: If you need to access the clipboard outside of a clipboard event
handler, see [[#async-clipboard-api]].
Note: The Clipboard Event APIs are synchronous, so they are limited in what they can
do. Actions which are potentially blocking (like asking for permission or
transcoding a image) are not supported by these APIs. See [[#async-clipboard-api]]
for a more powerful API that can support blocking or other time-consuming
actions.
<h3 id="override-copy">Overriding the copy event</h3>
To override the default {{GlobalEventHandlers/copy}} event behavior, a {{GlobalEventHandlers/copy}} event
handler must be added and this event handler must call
{{Event/preventDefault()}} to cancel the event.
Canceling the event is required in order for the [=system clipboard=] to be
updated with the data in {{ClipboardEvent/clipboardData}}.
If the {{ClipboardEvent}} is not canceled, then the data from the
current document selection will be copied instead.
<pre class="example javascript">
// Overwrite what is being copied to the clipboard.
document.addEventListener('copy', function(e) {
// e.clipboardData is initially empty, but we can set it to the
// data that we want copied onto the clipboard.
e.clipboardData.setData('text/plain', 'Hello, world!');
e.clipboardData.setData('text/html', '<b>Hello, world!</b>');
// This is necessary to prevent the current document selection from
// being written to the clipboard.
e.preventDefault();
});
</pre>
<h3 id="override-cut">Overriding the cut event</h3>
To override the default {{GlobalEventHandlers/cut}} event behavior, a {{GlobalEventHandlers/cut}} event
handler must be added and this event handler must call
{{Event/preventDefault()}} to cancel the event.
Canceling the event is required in order for the [=system clipboard=] to be
updated with the data in {{ClipboardEvent/clipboardData}}.
If the {{ClipboardEvent}} is not canceled, then the data from the
current document selection will be copied instead.
Note that canceling the {{GlobalEventHandlers/cut}} event will also prevent the document from
being updated (i.e., the current selection will not be removed). The event
handler will need to manually update the document to remove the currently
selected text.
<pre class="example javascript">
// Overwrite what is copied to the clipboard.
document.addEventListener('cut', function(e) {
// e.clipboardData is initially empty, but we can set it to the
// data that we want copied onto the clipboard as part of the cut.
// Write the data that we want copied onto the clipboard.
e.clipboardData.setData('text/plain', 'Hello, world!');
e.clipboardData.setData('text/html', '<b>Hello, world!</b>');
// Since we will be canceling the cut operation, we need to manually
// update the document to remove the currently selected text.
deleteCurrentDocumentSelection();
// This is necessary to prevent the document selection from being
// written to the clipboard.
e.preventDefault();
});
</pre>
<h3 id="override-paste">Overriding the paste event</h3>
To override the default {{GlobalEventHandlers/paste}} event behavior, a {{GlobalEventHandlers/paste}} event
handler must be added and this event handler must call
{{Event/preventDefault()}} to cancel the event.
Canceling the event is required so that the user agent does not update the
document with data from the [=system clipboard=].
Note that canceling the {{GlobalEventHandlers/paste}} event will also prevent the document from
being updated (i.e., nothing will be pasted into the document). The event
handler will need to manually paste the data into the document.
Also note that, when pasting, the [=drag data store mode=] flag is
<a lt="concept dnd ro">read-only</a>, hence calling {{DataTransfer/setData()}} from a
{{GlobalEventHandlers/paste}} event handler will not modify the data that is
inserted, and not modify the data on the clipboard.
<pre class="example javascript">
// Overwrite what is being pasted onto the clipboard.
document.addEventListener('paste', function(e) {
// e.clipboardData contains the data that is about to be pasted.
if (e.clipboardData.types.indexOf('text/html') > -1) {
var oldData = e.clipboardData.getData('text/html');
var newData = '<b>Ha Ha!</b> ' + oldData;
// Since we are canceling the paste operation, we need to manually
// paste the data into the document.
pasteClipboardData(newData);
// This is necessary to prevent the default paste action.
e.preventDefault();
}
});
</pre>
<h3 id="mandatory-data-types-x"><dfn>Mandatory data types</dfn></h3>
The implementation must recognize the native OS clipboard format description
for the following data types, to be able to populate the
{{DataTransferItemList}} and {{ClipboardItem}} with the correct description for
paste events, and set the correct data format on the OS clipboard in response to
copy and cut events.
<h4 id="reading-from-clipboard">Reading from the clipboard</h4>
These data types must be exposed by <em>paste</em> events if a
corresponding native type exists on the clipboard:
* text/plain
* text/html
* image/png
<h4 id="writing-to-clipboard">Writing to the clipboard</h4>
These data types must be placed on the clipboard with a corresponding
native type description if added to a {{DataTransfer}} object during
<em>copy</em> and <em>cut</em> events.
* text/plain
* text/html
* image/png
Advisement: Warning! The data types that untrusted scripts are allowed to write to the
clipboard are limited as a security precaution. Untrusted scripts can
attempt to exploit security vulnerabilities in local software by placing
data known to trigger those vulnerabilities on the clipboard.
<h3 id="optional-data-types-x"><dfn>Optional data types</dfn></h3>
The implementation MAY recognize the native OS clipboard format description
for the following data types, to be able to populate the
{{ClipboardItem}} with the correct description for paste events, and
set the correct data format on the OS clipboard in response to copy and cut
events.
These data types MAY be exposed by UAs if a corresponding native
type exists on the clipboard:
* text/uri-list
* image/svg+xml
* Custom format [=string/starts with=] `"web "`("web" followed by U+0020 SPACE) prefix
and suffix (after stripping out `"web "`) passes the [=parsing a MIME type=] check.
<h3 id="unsanitized-data-types-x"><dfn>Unsanitized data types</dfn></h3>
<em>This section is non-normative.</em>
These data types MUST NOT be sanitized by UAs:
* image/png
These data types MAY NOT be sanitized by UAs:
* [=optional unsanitized data types=]
<dfn>Optional unsanitized data types</dfn> are [=representation/mime type=]s specified by the web authors that MAY NOT be sanitized by the user agent.
The valid [=optional unsanitized data types=] are listed below:
* text/html
<p class=note> [=optional unsanitized data types=] may not be supported by a user agent due to their privacy requirements.</p>
<h2 id="async-clipboard-api">Asynchronous Clipboard API</h2>
<h3 id="navigator-interface">Navigator Interface</h3>
<pre class="idl" data-highlight="webidl">
partial interface Navigator {
[SecureContext, SameObject] readonly attribute Clipboard clipboard;
};
</pre>
<div id="navigator-idl" dfn-for="Navigator">
<div class="algorithm" data-algorithm="navigator-clipboard">
<h4 id="h-navigator-clipboard"><dfn>clipboard</dfn></h4>
It is used to execute read/write operations from/to the [=system clipboard=].
The {{Navigator/clipboard}} getter steps are to return [=this=]'s [=clipboard=] object.
</div>
</div><!-- dfn-for Navigator -->
<h3 id="clipboard-item-interface">ClipboardItem Interface</h3>
<pre class="idl" data-highlight="webidl">
typedef Promise<(DOMString or Blob)> ClipboardItemData;
[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);
static boolean supports(DOMString type);
};
enum PresentationStyle { "unspecified", "inline", "attachment" };
dictionary ClipboardItemOptions {
PresentationStyle presentationStyle = "unspecified";
};
</pre>
<dl class=note>
<dt><code><var>clipboardItem</var> = new ClipboardItem([<var>items</var>, <var>options</var>])</code></dt>
<dd>
Creates a new {{ClipboardItem}} object. <var>items</var> denote [=list of representations=], each [=representation=] has a [=representation/mime type=] and a {{Promise}} to {{Blob}} or {{DOMString}} corresponding to the [=representation/mime type=], <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},
{presentationStyle: "unspecified"});
</pre>
</dd>
<dt><code><var>clipboardItem</var>.getType(<var>type</var>)</code></dt>
<dd>Returns a {{Promise}} to the {{Blob}} corresponding to the [=representation/mime type=] <var>type</var>.</dd>
<dt><code><var>clipboardItem</var>.<var>types</var></code></dt>
<dd>Returns the list of [=representation/mime type=]s contained in the [=/clipboard item=] object.</dd>
<dt><code>{{ClipboardItem}}.supports(<var>type</var>)</code></dt>
<dd>Returns true if <var>type</var> is in [=mandatory data types=] or [=optional data types=], else, returns false.</dd>
</dl>
A <dfn>clipboard item</dfn> is conceptually data that the user has expressed a desire to make shareable by invoking a "cut" or "copy" command. A [=/clipboard item=] serves two purposes. First, it allows a website to read data copied by a user to the [=system clipboard=]. Second, it allows a website to write data to the [=system clipboard=].
<p class=note>
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.
</p>
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 <dfn>list of representations</dfn>, each <dfn>representation</dfn> with an associated <dfn for=representation>mime type</dfn> (a [=/MIME type=]), an <dfn for="representation">isCustom</dfn> flag, initially |false|, that indicates if this [=representation=] should be treated as a [=web custom format=] (as opposed to a well-known format of the [=system clipboard=]), and <dfn for=representation>data</dfn> (a {{ClipboardItemData}}).
A <dfn>web custom format</dfn> has [=representation/isCustom=] set to |true|.
<p class=note>
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), or a web custom format (web text/csv).
</p>
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.
<p class=note>
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.
</p>
A [=/clipboard item=] has a <dfn>presentation style</dfn> (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=].
<p class=note>
{{Clipboard/write()}} chooses the last [=/clipboard item=].
</p>
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 <dfn for="ClipboardItem">clipboard item</dfn>, which is a [=/clipboard item=].
A {{ClipboardItem}} object has an associated <dfn for="ClipboardItem">types array</dfn>, which is a {{FrozenArray}}<{{DOMString}}>.
To <dfn>create a {{ClipboardItem}} object</dfn>, 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|.
The <dfn constructor for="ClipboardItem" lt="ClipboardItem(items, options)"><code>new ClipboardItem(<var>items</var>, <var>options</var>)</code></dfn> 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 |representation| be a new [=representation=].
1. Let |isCustom| be |false|.
1. If |key| [=string/starts with=] `"web "` prefix, then
1. Remove `"web "` prefix and assign the remaining string to |key|.
1. Set |isCustom| |true|
1. Set |representation|'s [=representation/isCustom=] flag to |isCustom|.
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| and whose [representation/isCustom] is |isCustom|, then throw a {{TypeError}}.
<p class=note>The step above prevents collision between mime-types well-known by the user agent and those that are intended by the author to be treated as a custom type. For example, it would be possible for the author's list of items to contain representations for "text/html" as well as "web text/html".</p>
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. If |isCustom| is |true|, prefix |mimeTypeString| with `"web "`.
1. Add |mimeTypeString| to |types|.
1. Set [=this=]'s [=ClipboardItem/types array=] to the result of running [=create a frozen array=] from |types|.
<h4 attribute for=ClipboardItem lt=presentationStyle>presentationStyle</h4>
<p>
The {{ClipboardItem/presentationStyle}} getter steps are to return [=this=]'s [=ClipboardItem/clipboard item=]'s [=presentation style=].
</p>
<h4 attribute for=ClipboardItem lt=types>types</h4>
<p>
The {{ClipboardItem/types}} getter steps are to return [=this=]'s [=ClipboardItem/types array=].
</p>
<h4 method for=ClipboardItem lt=getType(type)>getType(type)</h4>
This method must run the below steps:
1. Let |realm| be [=this=]'s [=relevant realm=].
1. Let |isCustom| be |false|.
1. If |type| [=string/starts with=] `"web "` prefix, then:
1. Remove `"web "` prefix and assign the remaining string to |type|.
1. Set |isCustom| to |true|.
1. Let |mimeType| be the result of [=parsing a MIME type=] given |type|.
1. If |mimeType| is failure, then throw a {{TypeError}}.
1. Let |itemTypeList| be [=this=]'s [=ClipboardItem/clipboard item=]'s [=list of representations=].
1. Let |p| be [=a new promise=] in |realm|.
1. For each |representation| in |itemTypeList|:
1. If |representation|'s [=representation/MIME type=] is |mimeType| and |representation|'s [=representation/isCustom=] is |isCustom|, 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. Let |dataAsBytes| be the result of [=UTF-8 encoding=] |v|.
1. Let |blobData| be a {{Blob}} created using |dataAsBytes| with its {{Blob/type}} set to |mimeType|, [=serialize a MIME type|serialized=].
1. Resolve |p| with |blobData|.
1. If |v| is a {{Blob}}, then follow the below steps:
1. Resolve |p| with |v|.
1. If |representationDataPromise| was rejected, then:
1. [=Reject=] |p| with {{"NotFoundError"}} {{DOMException}} in |realm|.
Issue: Web developers might be interested in the underlying rejection reason.
1. Return |p|.
1. [=Reject=] |p| with {{"NotFoundError"}} {{DOMException}} in |realm|.
1. Return |p|.
<h4 method for=ClipboardItem lt=supports(type)>supports(type)</h4>
This method must run the below steps:
1. If |type| is in [=mandatory data types=] or [=optional data types=], then return true.
1. If not, then return false.
<h3 id="clipboard-interface">Clipboard Interface</h3>
<pre class="idl" data-highlight="webidl">
typedef sequence<ClipboardItem> ClipboardItems;
[SecureContext, Exposed=Window]
interface Clipboard : EventTarget {
Promise<ClipboardItems> read(optional ClipboardUnsanitizedFormats formats = {});
Promise<DOMString> readText();
Promise<undefined> write(ClipboardItems data);
Promise<undefined> writeText(DOMString data);
};
dictionary ClipboardUnsanitizedFormats {
sequence<DOMString> unsanitized;
};
</pre>
Some methods of the {{Clipboard}} interface take or return multiple {{ClipboardItem}} objects. However, not all platforms support more than one [=/clipboard item=]; on such platforms, the algorithms below will ignore any {{ClipboardItem}} objects beyond the first one that are passed to {{Clipboard/write()}}, and {{Clipboard/read()}} and {{Clipboard/readText()}} only get one clipboard item from the OS.
A <dfn>clipboard items</dfn> object is a [=sequence=] of [=/clipboard item=]s.
<p class="note">
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 data=].
</p>
{{ClipboardUnsanitizedFormats/unsanitized}} is a [=sequence=] of {{DOMString}}s corresponding to the [=representation/mime type=] that the author wants to be treated as [=optional unsanitized data types=].
<p class=note> The {{ClipboardUnsanitizedFormats/unsanitized}} option MAY NOT be supported by the user agent. The web authors shouldn't assume that the content for the MIME types listed in {{ClipboardUnsanitizedFormats/unsanitized}} would be unsanitized as there could be privacy modes where this option may not be allowed. </p>
The <dfn>clipboard task source</dfn> is triggered in response to reading or writing of [=system clipboard data=].
<div id="clipboard-idl" dfn-for="Clipboard">
<div class="algorithm" data-algorithm="clipboard-read">
<h4 method for="Clipboard">read(|formats|)</h4>
The {{Clipboard/read(formats)}} method must run these steps:
1. Let |realm| be [=this=]'s [=relevant realm=].
1. Let |p| be [=a new promise=] in |realm|.
1. If |formats| is not empty, then:
1. For each |format| in |formats|["{{ClipboardUnsanitizedFormats/unsanitized}}"]:
1. If |format| is not in [=optional unsanitized data types=], then [=reject=] |p| with |format| {{"NotAllowedError"}} {{DOMException}} in |realm|.
1. Run the following steps [=in parallel=]:
1. Let |r| be the result of running [=check clipboard read permission=].
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. Let |data| be a copy of the [=system clipboard data=].
1. Let |items| be a [=sequence=]<[=/clipboard item=]>.
1. For each |systemClipboardItem| in |data|:
1. Let |item| be a new [=/clipboard item=].
1. For each |systemClipboardRepresentation| in |systemClipboardItem|:
1. Let |mimeType| be the result of running the [=well-known mime type from os specific format=] algorithm given |systemClipboardRepresentation|'s [=system clipboard representation/name=].
1. If |mimeType| is null, continue this loop.
1. Let |representation| be a new [=representation=].
1. Set |representation|'s [=representation/MIME type=] to |mimeType|.
1. Let |isUnsanitized| be |false|.
1. If |formats| is not empty, then:
1. For each |format| in |formats|["{{ClipboardUnsanitizedFormats/unsanitized}}"]:
1. If |format| is equal to [=representation/MIME type=], set |isUnsanitized| to true.
1. Resolve |representation|'s [=representation/data=] with |systemClipboardRepresentation|'s [=system clipboard representation/data=].
Issue: It should be possible to read the data asynchronously from the system clipboard after the author calls getType, however, this set of steps implies that data will be provided at the time of read.
1. The user agent, MAY sanitize |representation|'s [=representation/data=], unless |representation|'s [=representation/MIME type=]'s essence is "image/png", which should remain unsanitized to preserve meta data, or if it satisfies the below conditions:
1. |representation|'s [=representation/MIME type=] is in optional [=unsanitized data types=] list.
1. |isUnsanitized| is true.
1. Append |representation| to |item|'s [=list of representations=].
1. Set |isUnsanitized| to |false|.
1. If |item|'s [=list of representations=] size is greater than 0, append |item| to |items|.
1. If |items| has a size > 0, then:
1. Let |firstItem| be |items|[0]
1. Run the [=read web custom format=] algorithm given |firstItem|.
1. Else:
1. Let |customItem| be a new [=/clipboard item=].
1. Run the [=read web custom format=] algorithm given |customItem|.
1. If |customItem|'s [=list of representations=] size is greater than 0, append |customItem| to |items|.
1. [=Queue a global task=] on the [=clipboard task source=], given |realm|'s [=realm/global object=], to perform the below steps:
1. Let |clipboardItems| be a [=sequence=]<{{ClipboardItem}}>.
1. For each [=/clipboard item=] |underlyingItem| of |items|:
1. Let |clipboardItem| be the result of running the steps of [=create a ClipboardItem object=] given |underlyingItem| and |realm|.
1. Append |clipboardItem| to |clipboardItems|.
1. Resolve |p| with |clipboardItems|.
1. Return |p|.
<div></div>
<pre class="example javascript" highlight=js>
const items = await navigator.clipboard.read();
const textBlob = await items[0].getType("text/plain");
const text = await (new Response(textBlob)).text();
</pre>
</div><!-- read() -->
<div class="algorithm" data-algorithm="clipboard-readtext">
<h4 method for="Clipboard">readText()</h4>
The {{Clipboard/readText()}} method must run these steps:
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=].
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. Let |data| be a copy of the [=system clipboard data=].
Issue: Some OSs contain multiple clipboard (e.g. Linux, "primary", "secondary", "selection"). Define from which of those data is read.
Issue: Add definition of sanitized copy.
1. [=Queue a global task=] on the [=clipboard task source=], given |realm|'s [=realm/global object=], to perform the below steps:
1. For each |systemClipboardItem| in |data|:
1. For each |systemClipboardRepresentation| in |systemClipboardItem|:
1. Let |mimeType| be the result of running the [=well-known mime type from os specific format=] algorithm given |systemClipboardRepresentation|'s [=system clipboard representation/name=].
1. If |mimeType| is null, continue this loop.
1. Let |representation| be a new [=representation=].
1. If |representation|'s [=representation/MIME type=] [=MIME type/essence=] is "text/plain", then:
1. Set |representation|'s [=representation/MIME type=] to |mimeType|.
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. Return |p|.
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. Return |p|.
1. If |representationDataPromise| was rejected, then:
1. [=Reject=] |p| with {{"NotFoundError"}} {{DOMException}} in |realm|.
1. Return |p|.