以下はClipboard API and eventsの2018/08/27時点での最新版、2017/09/29 Working Draft
の日本語訳です。
これは、これまで共通化されていなかったクリップボードへの読み書きを一貫するのが目的のAPIです。
現在このAPIのステータスは作業草案という初期段階ですが、既に多くのブラウザで一部実装されています。
最新の編集者草案はこちらにあります。
1 Introduction
このセクションは規定ではなく参考情報です。
この仕様は、カット、コピー、ペーストといったクリップボード操作を定義し、それらをWebアプリケーションから操作する高度な機能を提供します。
このドキュメントの目的は、既存の実装となるべく互換を持たせることです。
2 Use Cases
このセクションは規定ではなく参考情報です。
デフォルトのクリップボード操作(カット/コピー/ペースト)を変更したい多くのユースケースがあります。
どのような操作ができるかの事例を幾つか用意しましたが、全てがこの仕様書でサポートされているとは限りません。
2.1 Rich content editing
ハイパーリンクやその他のドキュメント構造を含むテキストをコピーする際、重要な情報を保持するためにコピー内容を再フォーマットできると役に立つでしょう。
2.2 Graphics with built-in semantics
リッチテキストやSVG11のようなグラフィックコンテンツを操作するWebアプリケーションは、レンダリングされたコンテンツだけではなく、それ以上の構造をコピーを可能にする機能があると便利になることでしょう。
2.3 Mathematical information
数式などのコンテンツは、テキストをコピーして別のアプリケーションに貼り付けるだけで、式のほとんどが失われてしまいます。
MathMLをコピーする際に、プレーンテキストとして意味が通るようにコピーされるように変換する需要があるでしょう。
例えば、累乗をプレーンテキストに渡したら^
に変換してほしいです。
XMLはクリップボードにコピーしたあと、ペースト時に適切な形式に変換できるようすることもできます。
3 Terminology
編集可能なテキスト(editable context)
とは、ContentEditable、textarea、input要素のうち"type"に"text"、"search"、"tel"、"url"、"email"、"password"、"number"のいずれかが指定されているものを示します。
4 Model
プラットフォームはシステムクリップボード(system clipboard)
を提供します。
システムクリップボードはシステムクリップボードデータ(system clipboard data)
と呼ばれる要素を置いておけるListです。
システムクリップボードデータは、クリップボードの内容を保持しているDataTransferオブジェクトです。
5 Clipboard Events
5.1 Clipboard event interfaces
ClipboardEvent
インターフェイスはEventインターフェイスを継承します。
dictionary ClipboardEventInit : EventInit {
DataTransfer? clipboardData = null;
};
DataTransferはイベントのデータとメタデータを保持するオブジェクトです。
[Constructor(DOMString type, optional ClipboardEventInit eventInitDict), Exposed=Window]
interface ClipboardEvent : Event {
readonly attribute DataTransfer? clipboardData;
};
clipboardData属性はDataTransferインターフェイスのインスタンスで、ユーザが行ったカット、コピー、ペーストの操作時にスクリプトがシステムクリップボードデータを操作することを可能にします。
"drag data store"は、システムクリップボードのフィルタリングされたビューで、スクリプトが安全にアクセスできるmandatory data typesデータ型のみが格納されます。
合成イベントの場合、"drag data store"にはそのイベントによって作られたデータが含まれています。
clipboardData
オブジェクトはitemsとfilesプロパティを持ち、マルチパートデータやテキスト以外の形式のデータをクリップボードで操作することができます。
このインターフェイスを使ってイベントを発火できます。
以下に例を示します。
var pasteEvent = new ClipboardEvent('paste');
pasteEvent.clipboardData.items.add('My string', 'text/plain');
document.dispatchEvent(pasteEvent);
注意:合成イベントは実際にクリップボードを変更することはありません。
つまり、上のスクリプトでは貼り付けイベントは発生しますが、データがドキュメントに貼り付けられることはありません。
5.2 Clipboard events
5.2.1 The clipboardchange event
clipboardchangeイベントは、システムクリップボードの内容が変化するたびに発生します。
全てを網羅してはいませんが、以下のような原因で発火します。
・ユーザがカットやコピー操作を行った
・スクリプトでAsynchronous Clipboard APIを使ってクリップボードに書き込んだ
・ブラウザ外でクリップボードの変更があった
ブラウザ外のでクリップボードの変更が行われた場合、clipboardchangeイベントはブラウザがアクティブに戻ったときに発生しなければなりません(MUST)。
合成イベントおよびコピーイベントはシステムクリップボードの内容を変更しないため、clipboardchangeイベントは発生しません。
5.2.2 The copy event
ユーザがコピー操作を行うと、ブラウザではcopy
というイベントが発生します。
イベントをキャンセルしないかぎり、現在選択中のデータがクリップボードにコピーされます。
現在選択している範囲には何の影響もありません。
コピーイベントはバブリング、キャンセルが可能です。
このイベントの動作の詳細については、8.1 The copy action
を参照してください。
合成コピーイベントを手動で作成して発火することはできますが、システムクリップボードに影響を与えることはできません。
5.2.3 The cut event
ユーザがカット操作を行うと、ブラウザではcut
というイベントが発生します。
編集可能なコンテキストを選択している場合、イベントをキャンセルしないかぎり、現在選択中のデータがクリップボードにコピーされます。
選択範囲のドキュメントは削除されます。
cutイベントは、選択したデータが削除される前に発生します。
イベントの完了時に選択範囲が削除されます。
編集不可能なコンテキストを選択している場合、clipboardDataは空のリストになります。
この場合でもイベント自体は発生することに注意してください。
カットイベントはバブリング、キャンセル可能です。
このイベントの動作の詳細については、8.2 The cut action
を参照してください。
合成カットイベントを手動で作成して発火することはできますが、ドキュメントおよびシステムクリップボードに影響を与えることはできません。
5.2.4 The paste event
ユーザがペースト操作を行うと、ブラウザではpaste
というイベントが発生します。
pasteイベントは、クリップボードのデータがドキュメントに挿入される前に発生します。
カーソルが編集可能なコンテキスト上にある場合、コンテキストがサポートしている中で最も適切なフォーマットでクリップボードデータを挿入します。
編集不可能なコンテキスト上にある場合、挿入はされませんが、ペーストイベント自体は発生します。
ペーストイベントはバブリング、キャンセル可能です。
このイベントの動作の詳細については、8.3 The paste action
を参照してください。
合成ペーストイベントを手動で作成して発火することはできますが、ドキュメントに影響を与えることはできません。
5.3 Integration with other scripts and events
5.3.1 Event handlers that are allowed to modify the clipboard
イベントハンドラは、次の何れかに該当する場合、クリップボードにデータを書き込むことができます。
・ユーザ自身の操作によってイベントが発動している。メニューのコピー項目、Ctrl+Cのショートカットキーなど。
・スクリプトが、ポップアップ表示可能なスレッドから呼び出されている。
実装は、イベントの発動元がユーザの意思による操作であると確信できる場合、クリップボードの中身を変更することが許されます。
実装はまた、特定のサイトおよびアプリケーションに対して、イベントの発動元にかかわらずクリップボードの中身を変更する許可を与えることができます。
合成カット、コピーイベントはシステムクリップボードデータを変更してはいけません(MUST NOT)。
5.3.2 Event handlers that are allowed to read from clipboard
イベントハンドラは、次の何れかに該当する場合、クリップボードからデータを読み込むことができます。
・ユーザ自身の操作によってイベントが発動している。メニューの貼り付け項目、Ctrl+Vのショートカットキーなど。
・スクリプトが、クリップボードの中身を読み取る権限を与えたサイト上で実行されている。
・アプリケーションが、クリップボードの中身を読み取る権限を与えたアプリ内で実行されている。
合成ペーストイベントはシステムクリップボードデータにアクセスしてはいけません(MUST NOT)。
5.3.3 Integration with rich text editing APIs
実装がdocument.execCommand()
コマンドなどのscripting APIを呼び出すことでカット・コピー・ペーストに対応した場合、コマンドが呼び出された際に対応したクリップボードイベントを発火しなければなりません(MUST)。
scripting APIからのコマンド呼び出しを対応する方法は以下のとおりです。
・対応するアクションを同期実行する。
・APIの返り値をコマンドの返り値とする。
注意:scripting APIから呼び出されたカット・コピーイベントは、ユーザが許可を与えたイベントから発火した、もしくは実装が許可を与えている場合にのみ、クリップボードを変更することができます。
ペーストイベントは、許可が与えられている場合にのみクリップボードにアクセスできます。
クリップボードに読み取り/書き込み許可を与える方法の実装については、この仕様の範囲外です。
5.3.4 Interaction with other events
キーボード入力によってクリップボード操作が行われた場合、実装は該当するクリップボードイベントを発生させる必要があります(MUST)。
イベントは非同期ですが、対応するキーのkeyupイベントより先に発生させなければなりません(MUST)。
カット・コピー操作は既存のtextInput・input・change・validation events・DOMCharacterDataModified・DOMNodeRemoved・DOMNodeInsertedといった多くの入力イベントに関連付いている場合があります。
それらのイベントは、カット・コピーのクリップボードイベントが終わるまでキューで待機します。
実装は、textInput・input・change・validation eventsなど他の入力関連イベントを発生させてはいけません(MUST NOT)。
5.3.5 Event listeners that modify selection or focus
イベントリスナーが選択範囲もしくはフォーカス可能領域を変更した場合、クリップボードアクションは変更された選択範囲を保たなければなりません(MUST)。
6 Synchronous Clipboard API
同期クリップボードAPIを使用すると、デフォルトのカット、コピー、ペーストの動作をオーバーライドすることができます。
クリップボードへのアクセスは標準のDataTransfer.itemsを使用し、ClipboardEvent.clipboardData属性を変更します。
結果として、同期クリップボードAPIはClipboardEventハンドラから見えるコンテキストのみアクセスできます。
注意:ClipboardEventハンドラの外にあるデータにアクセスしたい場合は、§7 Asynchronous Clipboard APIを参照してください。
6.1 Overriding the copy event
デフォルトのコピーイベントをオーバーライドする場合、copyイベントハンドラを追加し、デフォルト動作を防ぐためにpreventDefault()をコールする必要があります。
システムクリップボードをclipboardData内のデータで更新するには、イベントのキャンセルが必要です。
ClipboardEventがキャンセルされない場合、かわりに現在選択中のデータがコピーされます。
// コピーイベントの上書き
document.addEventListener('copy', function(e) {
// e.clipboardDataは最初は空だが、コピーしたい任意のデータをセットすることができる
e.clipboardData.setData('text/plain', 'Hello, world!');
e.clipboardData.setData('text/html', '<b>Hello, world!</b>');
// 入れないと現在選択中のテキストで上書きされる
e.preventDefault();
});
6.2 Overriding the cut event
デフォルトのカットイベントをオーバーライドする場合、cutイベントハンドラを追加し、デフォルト動作を防ぐためにpreventDefault()をコールする必要があります。
システムクリップボードをclipboardData内のデータで更新するには、イベントのキャンセルが必要です。
ClipboardEventがキャンセルされない場合、かわりに現在選択中のデータがコピーされます。
カットイベントをキャンセルすると、ドキュメントは更新されなくなります。
つまり現在選択中のテキストは削除されません。
必要であれば、現在選択されているテキストを削除するためにドキュメントを手動で更新する必要があります。
// カットイベントの上書き
document.addEventListener('cut', function(e) {
// e.clipboardDataは最初は空だが、コピーしたいデータをセットすることができる
e.clipboardData.setData('text/plain', 'Hello, world!');
e.clipboardData.setData('text/html', '<b>Hello, world!</b>');
// 現在選択中のドキュメントを手動で削除
deleteCurrentDocumentSelection();
// 入れないと現在選択中のテキストが上書きされる
e.preventDefault();
});
6.3 Overriding the paste event
デフォルトのペーストイベントをオーバーライドする場合、pasteイベントハンドラを追加し、デフォルト動作を防ぐためにpreventDefault()をコールする必要があります。
ドキュメントをシステムクリップボードのデータで上書きしないためには、イベントのキャンセルが必要です。
ペーストイベントをキャンセルすると、ドキュメントは何も更新されません。
必要であれば、ドキュメントを更新するために手動でデータを貼り付ける必要があります。
ペーストイベントの間、drag data store modeフラグはRead Onlyにされているため、setData()を呼んでもクリップボードのデータを更新することはできません。
// ペーストイベントの上書き
document.addEventListener('paste', function(e) {
// e.clipboardDataには貼り付けようとしているデータが入ってくる
if (e.clipboardData.types.indexOf('text/html') > -1) {
var oldData = e.clipboardData.getData('text/html');
var newData = '<b>Ha Ha!</b> ' + oldData;
// デフォルト動作のかわりに手動でデータを貼り付ける
pasteClipboardData(newData);
// デフォルト動作を停止
e.preventDefault();
}
7 Asynchronous Clipboard API
7.1 Navigator Interface
partial interface Navigator {
[SecureContext, SameObject] readonly attribute Clipboard clipboard;
};
7.1.1 clipboard
clipboard属性は、Navigator.Clipboardオブジェクトを返す必要があります(MUST)。
7.2 Clipboard Interface
[SecureContext, Exposed=Window] interface Clipboard : EventTarget {
Promise<DataTransfer> read();
Promise<DOMString> readText();
Promise<void> write(DataTransfer data);
Promise<void> writeText(DOMString data);
};
7.2.1 read()
read()メソッドは、以下を実装する必要があります(MUST)。
1 新しくPromisep
を生成する。
2 readパーミッションを確認し、falseであればpをrejectする。
3 以下を並列実行する。
3.1 システムクリップボードのデータをdataにコピーする。
3.2 pをresolveする。
4.pをreturnする。
navigator.clipboard.read().then(function(data) {
for (var i = 0; i < data.items.length; i++) {
if (data.items[i].type == "text/plain") {
console.log("Your string: ", data.items[i].getAs("text/plain"));
} else {
console.error("No text/plain data on clipboard.");
}
}
});
7.2.2 readText()
readText()メソッドは、以下を実装する必要があります(MUST)。
1 新しくPromisep
を生成する。
2 readパーミッションを確認し、falseであればpをrejectする。
3 以下を並列実行する。
3.1 システムクリップボードのデータをdataにコピーする。
3.2 textDataを空にする。
3.3 data.itemsが"text/plain"のtextItemを含んでいる場合
3.3.1 該当のtextItemをtextDataにコピーする。
3.4 pをresolveする。
4 pをreturnする。
navigator.clipboard.read().then(function(data) {
console.log("Your string: ", data);
});
7.2.3 write(data)
write(data)メソッドは、以下を実装する必要があります(MUST)。
1 新しくPromisep
を生成する。
2 writeパーミッションを確認し、falseであればpをrejectする。
3 以下を並列実行する。
3.1 cleanItemListを空のDataTransferItemListにする。
3.2 全てのdata.itemsについて以下を実行する。
3.2.1 itemをサニタイズしてcleanItemにコピーする。
3.2.2 コピーできなければpをrejectする。
3.2.3 cleanItemをcleanItemListに追加する。
3.3 cleanItemListをシステムクリップボードのitemsと入れ替える。
3.4 pをresolveする。
4 pをreturnする。
var data = new DataTransfer();
data.items.add("text/plain", "Howdy, partner!");
navigator.clipboard.write(data).then(function() {
console.log("Copied to clipboard successfully!");
}, function() {
console.error("Unable to write to clipboard. :-(");
});
7.2.4 writeText(data)
writeText(data)メソッドは、以下を実装する必要があります(MUST)。
1 新しくPromisep
を生成する。
2 writeパーミッションを確認し、falseであればpをrejectする。
3 以下を並列実行する。
3.1 newItemListを空のDataTransferItemListにする。
3.2 新たなDataTransferItemオブジェクトnewItemを作成し、drag data item kindをstringに、[drag data item type string]を"text/plain"にする。
3.3 newItem.dataにdataをセットする。
3.4 newItemをnewItemListに追加する。
3.5 newItemListをシステムクリップボードのitemsと入れ替える。
3.6 pをresolveする。
4 pをreturnする
・
・DataTransferItemを取り出しnewItemに入れ、テキストの形式を"text/plain"にする。
・newItemをnewItemListに追加する。
・システムクリップボードのデータをnewItemListに入れ替える
navigator.clipboard.writeText("Howdy, partner!").then(function() {
console.log("Copied to clipboard successfully!");
}, function() {
console.error("Unable to write to clipboard. :-(");
});
8 Clipboard Actions
このセクションでは、クリップボードアクションおよびイベントディスパッチの処理モデルを定義します。
各クリップボードアクションはscript-triggered
とscript-may-access-clipboard
のふたつのフラグを所有します。
script-triggeredフラグは、document.execCommand()
のようにアクションがスクリプトから実行された場合に設定されます。
クリップボードとやりとりするAPIは、今後実装されるものも含め、必要に応じてscript-triggeredフラグを設定しなければなりません(MUST)。
script-may-access-clipboardフラグは以下の場合に設定されます。
・アクションがcopyもしくはcutであり、クリップボードの変更が許可されている場合に設定される。
・アクションがpasteであり、クリップボードからの読み取りが許可されている場合に設定される。
8.1 The copy action
copyアクションは以下のステップで実行されます。
1 script-triggered
が設定されているならば、
1.1 script-may-access-clipboard
が設定されていないならば、
1.1.1 falseを返して処理を終了する。
2 copyのクリップボードイベントを発火する。
3 イベントがキャンセルされなかったならば、
3.1 選択している内容があるならばクリップボードにコピーする。Webページが選択されている場合はtext/html
とtext/plain
の両形式を作成する必要がある。
3.2 clipboardchangeのクリップボードイベントを発火する。
4 イベントがキャンセルされたのであれば、
4.1 クリップボードへの書き込みアルゴリズムを呼び出し、DataTransferItemList、clear-was-called
フラグ、types-to-clear
リストを渡す。
5 trueを返す。
8.2 The cut action
cutアクションは以下のステップで実行されます。
1 script-triggered
が設定されているならば、
1.1 script-may-access-clipboard
が設定されていないならば、
1.1.1 falseを返して処理を終了する。
2 copyのクリップボードイベントを発火する。
3 イベントがキャンセルされなかったならば、
3.1 選択している範囲が編集可能なコンテキストであるならば、
3.1.1 選択している内容があるならばクリップボードにコピーする。Webページが選択されている場合はtext/html
とtext/plain
の両形式を作成する必要がある。
3.1.2 選択範囲のコンテンツを削除する。
3.1.3 clipboardchangeのクリップボードイベントを発火する。
3.1.4 ドキュメントの変更によって発生するイベントを実行させるため、タスクをキューに入れる。詳細は5.3を参照。
3.2 選択している範囲が編集可能でなければ、
3.2.1 falseを返す。
4 イベントがキャンセルされたのであれば、
4.1 クリップボードへの書き込みアルゴリズムを呼び出し、DataTransferItemList、clear-was-called
フラグ、types-to-clear
リストを渡す。
4.2 clipboardchangeのクリップボードイベントを発火する。
5 trueを返す。
8.3 The paste action
サイトおよびアプリがクリップボードからデータを読み取ることができるscript-may-access-clipboard
フラグは、その許可メカニズムの実装に依存します。
pasteアクションがスクリプトから呼び出された場合、実装はユーザの許可なしにクリップボードを読み込み可能にしてはいけません(MUST NOT)。
アクセスがまだ許可されていない場合、許可を求めるプロンプトには、スクリプトの対象となるホスト名が含まれていなければなりません(MUST)。
pasteアクションは以下のステップで実行されます。
1 script-triggered
が設定されているならば、
1.1 script-may-access-clipboard
が設定されていないならば、
1.1.1 falseを返して処理を終了する。
2 pasteのクリップボードイベントを発火する。
3 イベントがキャンセルされなかったならば、
3.1 選択している範囲がペースト可能な編集可能なコンテキストであるならば、
3.1.1 クリップボードに存在するデータから最も適切な形式のコンテンツをペーストする。
3.1.2 ドキュメントの変更によって発生するイベントを実行させるため、タスクをキューに入れる。詳細は5.3を参照。
3.2 選択している範囲が編集可能でなければ、
3.2.1 falseを返す。
4 イベントがキャンセルされたのであれば、
4.1 falseを返す。
5 trueを返す。
9 Clipboard Permissions
このセクションは、クリップボードAPIへのアクセスをコントロールするclipboard permissionsについて定義します。
注意:現在、clipboard permissionsはAsync Clipboard APIにのみ適用されます。
今後この仕様は更新され、クリップボードにアクセスする全てのアクションに適用される予定です。
9.1 Clipboard read permission
クリップボードの読み取りパーミッションを確認する手順。
1 フルアクセス権限{ name: "clipboard", access: "full" }
をチェックする。
2 値が"denied"であればfalseを返す。
3 値が"granted"であればtrueを返す。
4 値が"prompt"であれば、
4.1 現在のスクリプトが、ユーザ操作もしくはOSによる貼り付け操作であれば、systemPasteをtrueにする。
4.2 systemPasteがtrueであればtrueを返す。
4.3 フルアクセス権限の許可をユーザに問い合わせる。
4.4 問い合わせの結果をフルアクセス権限{ name: "clipboard", access: "full" }
に書き込む。
5 フルアクセス権限の値が"granted"であればtrueを返す。
6 falseを返す。
9.2 Clipboard write permission
クリップボードの書き込みパーミッションを確認する手順。
1 フルアクセス権限{ name: "clipboard", access: "full" }
をチェックする。
2 書き込み権限{ name: "clipboard", access: "write" }
をチェックする。
3 フルアクセス権限の値が"granted"であればtrueを返す。
4 書き込み権限の値が"denied"であればfalseを返す。
5 現在のスクリプトが、ユーザ操作によって起動したものでなければfalseを返す。
6 値が"prompt"であれば、
6.1 書き込み権限の許可をユーザに問い合わせる。
6.2 問い合わせの結果を書き込み権限{ name: "clipboard", access: "write" }
に書き込む。
7 書き込み権限の値が"granted"であればtrueを返す。
8 falseを返す。
10 Mandatory data types
実装は、OSクリップボードで使用される以下のデータ型を認識し、pasteイベントにおいてDataTransferItemListを正しく読み込むことができなければなりません(MUST)。
またcopyおよびcutイベントにおいて、正しく書き込むことができなければなりません(MUST)。
10.1 Reading from the clipboard
クリップボードに以下のネイティブ型が存在した場合、pasteイベントは対応するデータ型で対象を読み込む必要があります。
・text/plain
・text/uri-list
・text/csv
・text/css
・text/html
・application/xhtml+xml
・image/png
・image/jpg, image/jpeg
・image/gif
・image/svg+xml
・application/xml, text/xml
・application/javascript
・application/json
・application/octet-stream
10.2 Writing to the clipboard
copyおよびcutイベント中に以下のデータ型がDataTransferに追加された場合、対応するネイティブ型としてクリップボードに書き込む必要があります。
・text/plain
・text/uri-list
・text/csv
・text/html
・image/svg+xml
・application/xml, text/xml
・application/json
注意:信頼できないスクリプトがクリップボードに書き込むことのできるデータ型は、セキュリティ上の理由から制限されています。
信頼できないスクリプトはローカルアプリの脆弱性を悪用しようとする可能性があります。
11 Security Considerations
Web制作者がユーザのコピーした内容を改変したり、選択していないものをクリップボードに入れたり、ペーストを無制限に許可したりすると、セキュリティとプライバシーに関する様々な懸念が生じます。
例として、ユーザがURLを選択してコピーしようとすると、コピーしようとした内容とは別のURLがクリップボードにコピーされたりといったことが起こります。
予期せぬ結果を与えるだけではなく、フィッシングに使われたりと、様々な問題が発生するでしょう。
11.1 Pasting HTML and multi-part data
このセクションは規定ではなく参考情報です。
リッチテキストやマルチパートデータをペーストすることには多くのセキュリティリスクが存在します。
・ユーザに気付かれないよう、<input type="hidden">
のような隠された領域に機密情報をペーストされる可能性があります。
・ユーザは、悪質なJavaScriptを信頼できるページにペーストすることがあります。
・実装が、ユーザが公開するつもりのないローカルファイルへのアクセス許可を与える場合があります。
どのアクセスポリシーを適用するかについては、以下のような要点を考える必要があります。
・ペーストされるデータの起源
・画像などマルチパートデータの参照先の起源
・実行中のスクリプトの起源
以下は実行シナリオとセキュリティポリシーの概要です。
データの起源 | スクリプトの起源 | 適用するルール |
---|---|---|
オンライン | 同一オリジン | HTMLをそのまま扱う。ローカルファイルにアクセスしない。 |
オンライン | 別のオリジン | HTMLをエスケープする。ローカルファイルにアクセスしない。 |
ローカルアプリ | 任意 | HTMLをそのまま扱う。ローカルファイルにアクセス可能。 |
リッチテキストを貼り付ける際に、script要素やJavaScriptなどの潜在的に問題のあるコンテンツを削除してペーストすることで、セキュリティリスクを緩和することができます。
デフォルトではそのように動作しますが、pasteイベントハンドラはサニタイズされていない元のデータを取得して処理することもできます。
11.2 General security policies
実装は、取得したDataTransferItemListに入っていたファイルへの参照について、リソースをダウンロードしたり内容を出力したりしてはいけません。
クリップボード上のデータがローカルアプリからのものでない場合、実装は参照されたローカルファイルへのアクセス権を与えてはいけません。
たとえばオンライン由来のデータに<img src="file://localhost/example.jpg">
と書かれていても、example.jpgのエントリをclipboardData.itemsに追加してはいけません。
11.3 Nuisance considerations
スクリプトがDataTransfer APIを使ってシステムクリップボード上のデータを引っかき回すことによって、ユーザを苛立たせ混乱させる可能性があります。
この仕様書ではそのような迷惑行為を防ぐ手立ては書式化しませんが、実装が独自に制限を追加する可能性はあります(MAY)。
スクリプトが非常に大量のデータをクリップボードに貼り付けようとしていたとしても、実装はその命令をハンドリングする必要があります(MUST)。
12 Privacy Considerations
信頼できないスクリプトには、クリップボードへの自由なアクセス許可を与えるべきではありません(SHOULD NOT)。
この仕様では、ユーザが明示的にペースト動作を行った場合には、クリップボードAPIからクリップボードデータへのアクセスが許可されます。
ただし、実装は注意深く行わなければならず、最低限以下の注意事項を守る必要があります(MUST)。
・クリップボードの内容を返すDataTransferオブジェクトは、ClipboardEventイベントハンドラの外部では使用できないようにしなければならない(MUST)。
・スクリプトがDataTransferオブジェクトをClipboardEventイベントハンドラ外で保持するようにしていた場合、全てのメソッドは何もしないようにしなければならない(MUST)。
・クリップボードにアクセスするために、スクリプト自らが合成イベントを作成してはならない(MUST NOT)。
・実装は、ユーザが明示的に許可しない限りdocument.execCommand("paste")
を呼び出さないようにすべきである(SHOULD NOT)。
実装は、DataTransferによって提供される機能をさらに制限することができます(MAY)。
たとえばこのAPI自体を無効にしたり、アクセスを許可するWebサイトを設定したりすることができます。
Appendix A: Algorithms
write content to the clipboard
クリップボード書き込み処理のアルゴリズム。
入力
・DataTransferItemList.itemsのリスト
・"clear-was-called"のフラグ
・"types-to-clear"のリスト
出力
・なし
1 itemsが空ではない場合、
1.1 クリップボードを空にする。
1.2 itemsの各アイテムごとに以下を実行する。
1.2.1 データ型が"text/plain"である場合、
1.2.1.1 OSとロケールの規則に従い、エンコードが正しいことを確認する。
1.2.1.2 プラットフォームの規則に従い、改行コードを整形する。
1.2.1.2 OSのクリップボード書式に従い、テキストをクリップボードにペーストする。
1.2.2 データが必須データ型に登録されたものであれば、
1.2.2.1 OSのクリップボード書式に従い、データをクリップボードにペーストする。
1.2.3 データがそれ以外のデータ型の場合
1.2.3.1 実装による。
2 itemsが空である場合、
2.1 "clear-was-called"フラグがtrueである場合、
2.1.1 "types-to-clear"リストが空である場合、
2.1.1.1 クリップボードを空にする。
2.1.2 "types-to-clear"リストが空ではない場合、
2.1.2.1 OSと実装が提供する方法で、クリップボードから"types-to-clear"の対象を削除する。
fire a clipboard event
クリップボードイベントのアルゴリズム。
入力
・ClipboardEventイベント
出力
・なし
1 "clear-was-called"フラグをfalseにする。
2 "types-to-clear"リストを空にする。
3 "clipboard-event-data"にitemsが空のDataTransferをセットする。
4 "clipboard-entry"に現在のクリップボードシーケンス番号をセットする。クリップボードシーケンス番号をサポートしていないOSであればnull。
5 イベントがユーザによって発火したものである場合"trusted"にtureを、そうでなければfalseをセットする。
6 以下の手順で"target"を設定する。
6.1 コンテキストが編集可能である場合、
6.1.1 現在選択している要素の開始点を含む要素を"target"にセットする。未選択であればbody要素をセットする。
6.2 コンテキストが編集不可能である場合、
6.2.1 現在選択している要素の開始点を含む要素を"target"にセットする。未選択であればbody要素をセットする。
7 以下の順序でイベントを処理する。
7.1 ClipboardEventが"paste"である場合、
7.1.1 "clipboard-event-data"内のdrag data store modeフラグをread/writeに設定する。
7.1.2 "trusted"がtrueであるか、実装がスクリプトにクリップボードへのアクセス許可を与えている場合、
7.1.2.1 クリップボード内の各"clipboard-part"について、以下を繰り返し実行する。
7.1.2.1.1 "clipboard-part"にプレーンテキストが含まれているならば、
7.1.2.1.1.1 スクリプトエンジンがテキストのエンコーディングに対応していることを確認する。
7.1.2.1.1.2 新たなDataTransferItem"new-data"を作成し、drag data item kindをstringに、drag data item type stringを"text/plain"にセットする。
7.1.2.1.1.3 "new-data"にプレーンテキストをセットする。
7.1.2.1.1.4 "new-data"を"clipboard-event-data".itemsに追加する。7.1.2.1.2 "clipboard-part"がファイルへの参照であるならば、
7.1.2.1.2.1 参照ファイルのMIME typeを取得する。
7.1.2.1.2.2 新たなDataTransferItem"new-data"を作成し、drag data item kindをfileに、drag data item type stringを該当のMIME typeにセットする。MIME typeが不明であれば"application/octet-stream"をセットする。
7.1.2.1.2.3 "new-data"にファイルへの参照をセットする。
7.1.2.1.2.4 "new-data"を"clipboard-event-data".itemsに追加する。7.1.2.1.3 "clipboard-part"に"HTML-"か"XHTML-"が含まれている場合、
7.1.2.1.3.1 実装がHTML貼り付けをサポートしている場合、HTMLペーストイベントに従って"clipboard-part"と"clipboard-event-data"のマークアップを処理する。
7.1.2.1.4 "clipboard-part"がそれ以外のサポートしているデータ型である場合、
7.1.2.1.4.1 データのMIME typeを取得する。
7.1.2.1.4.2 新たなDataTransferItem"new-data"を作成し、drag data item kindをfileに、drag data item type stringを該当のMIME typeにセットする。
7.1.2.1.1.3 "new-data"に対象のデータをセットする。
7.1.2.1.1.4 "new-data"を"clipboard-event-data".itemsに追加する。7.1.3 "clipboard-event-data".filesプロパティを、"clipboard-event-data".itemsプロパティと同じになるように更新する。
7.1.4 "clipboard-event-data".typesプロパティを、"clipboard-event-data".itemsプロパティと同じになるように更新する。
7.2 ClipboardEventが"copy"もしくは"cut"である場合、
7.2.1 対象のDataTransferオブジェクトについて、drag data store modeフラグにread/writeをセットする。
8 "clipboard-event-data"にclipboardDataをセットする。
9 "trusted"にisTrustedをセットする。
10 キャンセル可能なClipboardEventイベントeを発行する。
イベントのディスパッチ中にデータにアクセスするための要件はHTMLLSで定義されています。
process an HTML paste event
クリップボード読み込み処理のアルゴリズム。
入力
・"clipboard-part"、処理対象のクリップボード
・"clipboard-event-data"、対象となるDataTransferオブジェクト
出力
・なし
1 新たなDataTransferItem"new-data"を作成し、drag data item kindを"Plain Unicode string"に、drag data item type stringを"text/html"もしくは"application/xhtml+xml"にセットする。
2 "clipboard-part"からマークアップを抽出し、対象のパーサを使用してDOMツリーを構築する。
3 マークアップのソースURLが既知である場合は、hrefおよびsrc属性の全ての相対URLを絶対URLに書き換える。
4 マークアップのオリジンがローカルアプリケーションであった場合、ローカルファイルや他のクリップボードへの参照があるかをチェックする。存在していた場合、RFC2392に従って"content-id"参照へと書き換える。書き換え方法は以下に従う。
4.1 参照しようとしたファイルもしくはクリップボードが"clipboard-event-data".itemsに既に入っている場合、
4.1.1 "itemNumber"を既存エントリのインデックス番号に指定する。
4.2 入っていない場合、
4.2.1 新たなDataTransferItem"new-file-data"を作成し、drag data item kindをfileに、drag data item type stringを該当のMIME typeに、不明であれば"application/octet-stream"にセットする。
4.2.2 新たなFile"file-info"を作成し、nameをHTML属性のnameパートにセットする。lastModifiedは参照するファイルの時刻、もしくは参照先がクリップボードであれば0にセットする。
4.2.3 "new-file-data"内のFileオブジェクトに"file-info"をセットする。
4.2.4 "clipboard-event-data".itemに"new-file-data"をセットし、itemNumberをDataTransferItemListのインデックス番号にする。
4.3 ローカルファイルもしくはクリップボードを参照している部分のDOM属性を、'cid:'+itemNumberに変更する。
5 変更後のDOMをサニタイズし、結果のHTMLを"new-data"にセットする。
6 "clipboard-event-data".itemに"new-data"をセットする。
Conformance
Document conventions
各キーワード『しなければならない( MUST )』『してはならない( MUST NOT )』『要求されている( REQUIRED )』『することになる( SHALL )』『することはない( SHALL NOT )』『する必要がある( SHOULD )』『しないほうがよい( SHOULD NOT )』『推奨される( RECOMMENDED )』してもよい( MAY )』『選択できる( OPTIONAL )』はRFC2119に従います。
ただ読みやすさを優先し、各単語を大文字表示してはいません。
アルゴリズムの内で表現されている要件("falseを返す"など)は、RFC2119の意味に従います。
Conformant Algorithms
アルゴリズムおよび各ステップにおいて表示されている要件は、最終的な結果が同じであるかぎり、どのような方法でも実装することができます。
特にこのドキュメントで定義されているアルゴリズムは、流れの理解しやすさを優先しており、そのまま実行されることを意図していません。
従ってアルゴリズムの実装は最適化することが推奨されます。
References
Normative References
DOM https://dom.spec.whatwg.org/
ECMASCRIPT https://tc39.github.io/ecma262/
FileAPI https://www.w3.org/TR/FileAPI/
HTML https://html.spec.whatwg.org/multipage/
HTMLLS https://html.spec.whatwg.org/multipage/
RFC2119 https://tools.ietf.org/html/rfc2119
RFC2392 http://www.ietf.org/rfc/rfc2392.txt
WebIDL https://heycam.github.io/webidl/
Informative References
HTML5 https://www.w3.org/TR/html5/
MICROSOFT-CLIP-OP http://msdn.microsoft.com/en-us/library/ms537658.aspx
SVG11 https://www.w3.org/TR/SVG11/
感想
読み取り権限はどう考えても即刻抹殺すべき(MUST)。
百兆歩譲って、あらゆるサイトで一律に完全拒否して許可ダイアログも出さないオプションを用意するのであれば存在することを許そう(SHOULD NOT)。
・どこかの会員制サイトでログインダイアログが表示される
・パスワードマネージャからパスワードをコピーする
・別ウィンドウで別のサイトを閲覧してリンクを踏む
これで死亡だ。
少なくとも私はこの地雷を踏み抜かない自信は無い。
ドラフトではアクセス許可などと生温いことを言ってるが、そんなもの誰も読まないことはとっくに証明されている。
あとユーザアクションでしか起動できないと言ってるけど、ユーザアクションが具体的に指しているものがclickなのかinputなのかmouseoverなのかが書かれていない。
万一focusあたりで起動してしまおうものなら『別ウィンドウで別のサイトを閲覧』した時点で死ぬのだがどうか。
Clipboard APIを使わない方法として、本文でも出てきますがdocument.execCommand('copy')
というコマンドが存在し、現在選択しているテキストをクリップボードにコピーできます。
というか元々こちらがあって、しかしブラウザごとの互換性がいまいちだったためにこのAPIが策定されたという経緯があります。
そして、クリップボードから読み取りを行うdocument.execCommand('paste')
コマンドは、全てのブラウザにおいて禁止されています。
一律禁止することで安全を確保しているわけです。
………………禁止されていました。