1. 简介
本节是非规范性的。
本规范定义了 Web 应用程序如何访问系统剪贴板。
本规范描述了两种通用的 API:
-
剪贴板事件 API - 该 API 提供了一种挂载到常见的剪切、复制和粘贴剪贴板操作的方法,以便 Web 应用程序可以根据需要调整剪贴板数据。
-
异步剪贴板 API - 该 API 提供对剪贴板数据的读写直接访问。由于这被视为一项强大功能 (powerful feature),因此对该 API 的访问受权限控制。
2. 用例
本节是非规范性的。
2.1. 更改默认剪贴板操作
在许多场景下,需要更改默认的剪贴板操作(剪切/复制/粘贴)。以下是一些例子:
- 元数据 当从文档库中复制文本时,如果复制的文本包含有关源内容的元数据,会非常有用。
- 富内容编辑 当复制包含超链接或其他结构的文本时,能够重新格式化内容以保留重要信息通常很有用。
- 内置语义的图形 为了使 Web 应用程序能够操作富文本或诸如 [SVG11] 等图形内容,提供一种允许复制不仅仅是渲染内容而包含更多信息的机制是非常有用的。
- 数学信息 对于数学等内容,简单地复制渲染后的文本并将其粘贴到另一个应用程序中,通常会导致大部分语义丢失。MathML 经常需要进行转换才能作为纯文本复制,例如为了确保在公式纯文本输入中,“幂”以插入符号 "^" 显示。XML 源码也可以放置在剪贴板中,并在粘贴时进行相应的转换。
2.2. 远程剪贴板同步
对于与远程设备通信的 Web 应用程序(例如远程访问或远程 Shell 应用程序),通常需要在两个设备之间保持剪贴板数据同步。
此用例的一个重要方面是,它需要在没有用户手势或交互的情况下访问剪贴板。
write() 将数据写入本地剪贴板。2.3. 触发剪贴板操作
为用户代理提供替代接口的应用程序有时需要能够触发用户代理中的剪贴板操作。
例如,考虑一个为标准 Web 浏览器提供更易访问接口的屏幕阅读器应用程序。虽然阅读器可以显示内容并允许用户与之交互,但剪贴板复制等操作需要在底层浏览器中执行,以确保剪贴板内容被正确设置(包括浏览器在复制时添加的任何元数据)。
3. 术语
术语 可编辑上下文 (editable context) 指的是任何属于 编辑宿主 (editing host) 的元素、textarea 元素,或者 type 属性设置为 "text"、"search"、"tel"、"url"、"email"、"password" 或 "number" 的 input 元素。
4. 模型
平台提供一个 系统剪贴板 (system clipboard)。
系统剪贴板拥有一个 系统剪贴板项目 (system clipboard item) 列表,统称为 系统剪贴板数据 (system clipboard data)。
每个 系统剪贴板项目 拥有一个 系统剪贴板表示 (system clipboard representation) 列表。
每个 系统剪贴板表示 都有一个 名称(字符串)和 数据(字节序列)。
5. 剪贴板事件
5.1. 剪贴板事件接口
ClipboardEvent 接口扩展了 Event 接口。
dictionary :ClipboardEventInit EventInit {DataTransfer ?=clipboardData null ; };
- clipboardData
-
一个用于保存与事件相关的数据和元数据的
DataTransfer对象。
[Exposed =Window ]interface :ClipboardEvent Event {(constructor DOMString ,type optional ClipboardEventInit = {});eventInitDict readonly attribute DataTransfer ?; };clipboardData
- clipboardData
-
clipboardData 属性是
DataTransfer接口的一个实例,它允许脚本在用户发起的复制、剪切和粘贴操作期间读取和操作系统剪贴板上的值。关联的拖拽数据存储是系统剪贴板的一个实时但经过过滤的视图,公开了实现认为脚本可以安全访问的 强制数据类型。对于合成事件,拖拽数据存储包含由创建该事件的脚本添加的数据。
该接口可用于 构建事件。下面给出一个示例:
var pasteEvent = new ClipboardEvent('paste');
pasteEvent.clipboardData.items.add('My string', 'text/plain');
document.dispatchEvent(pasteEvent);
注意:合成剪贴板事件实际上不会修改剪贴板或文档。换句话说,虽然上述脚本会触发粘贴事件,但数据不会粘贴到文档中。
5.2. 剪贴板事件
5.2.1. The clipboardchange 事件
clipboardchange 事件在 系统剪贴板的内容发生变化时触发。这些变化可能源于以下任何原因(不限于此):
-
用户发起的剪切或复制操作
-
使用 § 7 异步剪贴板 API 写入剪贴板的脚本
-
用户代理外部更新剪贴板的操作
5.2.1.1. 触发 clipboardchange 事件
给定 Document document,触发 clipboardchange 事件:
-
如果 document 没有 粘性激活 (sticky activation) 且 document 没有从剪贴板读取的权限,则返回。
-
令 global 为 document 的 相关全局对象。
-
如果 document 具有 系统焦点 (system focus)
-
令 changeId 为运行 生成 changeId(给定 document)的结果。
-
令 eventInit 为一个新的
ClipboardChangeEventInit字典,其types成员设置为 types,其changeId成员设置为 changeId。 -
在 global 上触发名为
clipboardchange的事件,使用ClipboardChangeEvent及 eventInit。
-
如果 document 不具有 系统焦点
-
将 document 的 clipboardchange 挂起标志 设置为 true。
-
用户代理可以选择跳过为那些在事件发送前已被更新的剪贴板变化触发 clipboardchange 事件。这种优化可以提高性能,特别是在剪贴板变化非常频繁的场景下,因为发送过时的变更通知对 Web 应用程序没有任何价值,且会消耗处理资源。
5.2.1.2. 文档焦点步骤
-
如果 document 的 clipboardchange 挂起标志 为 true
-
将 document 的 clipboardchange 挂起标志 设置为 false。
-
令 changeId 为运行 生成 changeId(给定 document)的结果。
-
令 eventInit 为一个新的
ClipboardChangeEventInit字典,其types成员设置为 types,其changeId成员设置为 changeId。 -
令 global 为 document 的 相关全局对象。
-
在 global 上触发名为
clipboardchange的事件,使用ClipboardChangeEvent及 eventInit。
-
对于嵌套浏览上下文中的文档,clipboardchange 事件根据各自的焦点状态在每个 Document 中独立触发。剪贴板变化将仅在拥有 系统焦点的文档中触发事件(如果该文档具有 粘性激活或持久性剪贴板权限)。
注意:`clipboardchange` 事件仅在粘性激活之后可用,除非文档具有从剪贴板读取的持久权限。在支持持久性剪贴板权限的用户代理中,拥有此类权限的站点可以在没有粘性激活的情况下接收 `clipboardchange` 事件,因为该权限已经授予了对更敏感剪贴板数据的访问权限。
changeId 为每个剪贴板变化操作提供唯一标识符。对于相同的剪贴板变化,所有具有相同 存储键 (storage key) 的窗口和标签页将收到具有相同 changeId 值的事件,使拥有多个窗口的应用程序能够对事件进行去重并避免冗余处理。该标识符特定于 存储键,不提供跨 存储键 的关联能力。
clipboardchange 事件不会冒泡且不可取消,因为它不是由用户操作触发的,而是由 系统剪贴板状态的变化触发的。
dictionary :ClipboardChangeEventInit EventInit {sequence <DOMString >= [];types bigint = 0; };changeId
[Exposed =Window ]interface :ClipboardChangeEvent Event {(constructor DOMString ,type optional ClipboardChangeEventInit = {});eventInitDict readonly attribute FrozenArray <DOMString >;types readonly attribute bigint ; };changeId
5.2.1.4. ChangeId 生成
给定 Document document,生成 changeId:
-
令 globalChangeId 为用户代理特定的唯一标识符,代表 系统剪贴板的当前状态。该标识符在 系统剪贴板每次被修改时改变,并在用户代理重启时重置。
-
令 storageKey 为运行 obtain a storage key for non-storage purposes(给定 document 的 相关设置对象)的结果。
-
令 storageKeyBytes 为 storageKey 的某种用户代理特定的二进制表示。
-
令 hashedValue 为对 globalChangeId(作为字节)和 storageKeyBytes 的连接应用加密哈希函数(例如 SHA-256)的结果。
-
返回一个从 hashedValue 衍生的 128 位整数(例如,取哈希输出的前 128 位)。
上述算法确保来自相同源且具有相同分区的文档,针对相同的剪贴板修改收到相同的更改 ID,从而实现跨多个窗口和标签页的正确事件去重,同时防止可能用于追踪的跨分区关联。随着客户端存储分区 (Client-side Storage Partitioning) 的变更被纳入存储键定义,此方法将自动在分区之间提供匿名化处理。
changeId 不会在浏览器重启后持续存在,因为 globalChangeId 计数器在用户代理重启时会重置。同样,当用户清除站点数据时,受影响的标签页应刷新,这会导致事件监听器被重新绑定,并仅在以后接收带有新更改 ID 的事件。
由于合成 cut 和 copy 事件不会更新 系统剪贴板,它们不会触发 "clipboardchange" 事件。
clipboardchange 事件使 Web 应用程序能够高效监控剪贴板变化,并根据可用数据格式提供动态用户界面。// For applications with multiple windows, track processed change IDs to avoid duplication
const processedChangeIds = new Set();
// Listen for clipboard changes
navigator.clipboard.addEventListener('clipboardchange', (e) => {
// Deduplicate events across multiple windows using changeId
if (processedChangeIds.has(e.changeId)) {
return; // This change has already been processed
}
processedChangeIds.add(e.changeId);
// Check what data types are available on the clipboard
const hasText = e.types.includes('text/plain');
const hasHTML = e.types.includes('text/html');
const hasImage = e.types.includes('image/png');
// Update UI based on available formats
document.getElementById('paste-text-btn').disabled = !hasText;
document.getElementById('paste-html-btn').disabled = !hasHTML;
document.getElementById('paste-image-btn').disabled = !hasImage;
// For remote desktop apps, sync clipboard to remote only once per change
if (hasText || hasHTML || hasImage) {
syncClipboardToRemote(e.changeId);
}
});
// Alternatively, you can use the onclipboardchange property
// navigator.clipboard.onclipboardchange = (e) => { ... };
这种基于事件的方法比使用 read() 或 readText() 等方法轮询剪贴板更高效,并且适用于那些需要用户激活才能访问剪贴板的浏览器,因为 UI 可以即时响应剪贴板变化,无需等待定时器触发。changeId 对于具有多个窗口的应用程序特别有用,确保每个剪贴板变化在所有具有相同 存储键 的窗口中仅被处理一次。
5.2.2. The copy 事件
当用户发起复制操作时,用户代理会触发一个名为 copy 的剪贴板事件。
如果该事件未被取消,当前选中的数据将被复制到 系统剪贴板。当前的文档选区不受影响。
copy 事件会冒泡,可取消,且是组合事件 (composed)。
有关此事件处理模型的详细说明,请参阅 § 8.1 copy 操作。
合成的 copy 事件可以手动构建和分发,但不会影响 系统剪贴板的内容。
5.2.3. The cut 事件
当用户发起剪切操作时,用户代理会触发一个名为 cut 的剪贴板事件。
在 可编辑上下文中,如果事件未被取消,该操作会将当前选中的数据放置在 系统剪贴板上,并从文档中删除该选区。cut 事件在选中数据被删除之前触发。当剪切操作完成时,选区将被折叠。
在非 可编辑上下文中,clipboardData 将是一个空列表。请注意,即使在这种情况下,cut 事件仍会被触发。
cut 事件会冒泡,可取消,且是组合事件。
有关此事件处理模型的详细说明,请参阅 § 8.2 cut 操作。
合成的 cut 事件可以手动构建和分发,但不会影响文档或 系统剪贴板的内容。
5.2.4. The paste 事件
当用户发起粘贴操作时,用户代理会触发一个名为 paste 的剪贴板事件。该事件在任何剪贴板数据被插入文档之前触发。
如果光标位于 可编辑上下文中,粘贴操作将以给定上下文支持的最合适格式(如有)插入剪贴板数据。
粘贴操作在非 可编辑上下文中无效,但 paste 事件仍会触发。
paste 事件会冒泡,可取消,且是组合事件。
有关此事件处理模型的详细说明,请参阅 § 8.3 paste 操作。
合成的 paste 事件可以手动构建和分发,但不会影响文档内容。
5.3. 与其他脚本和事件的集成
5.3.1. Event handlers that are 允许修改剪贴板
如果以下任何条件为真,事件处理程序可以向剪贴板写入数据:
-
触发该事件的操作是从用户代理自身的 UI 调用的,例如从 "复制" 菜单项或快捷键。
-
触发该事件的操作是从允许显示弹出窗口的脚本线程中调用的。
如果实现作者认为某些其他受信任的事件类型可能表达了用户意图,则实现可以允许这些事件类型修改剪贴板。实现还可以支持配置,即信任特定的站点或应用修改剪贴板,无论脚本线程的来源如何。
合成的 cut 和 copy 事件不得修改系统剪贴板上的数据。
5.3.2. Event handlers that are 允许从剪贴板读取
如果以下任一条件为真,事件处理程序可以从系统剪贴板读取数据:
-
触发该事件的操作是从用户代理自身的 UI 调用的,例如 "粘贴" 菜单项或快捷键。
-
触发该操作的脚本运行在某个站点上,该站点通过一种特定于实现的机制获得了从剪贴板读取数据的权限。
-
触发该事件的操作在拥有读取剪贴板权限的应用程序中触发。
合成的 paste 事件不得让脚本访问真实的系统剪贴板数据。
5.3.3. 与富文本编辑 API 的集成
如果实现支持通过脚本执行剪贴板命令的方式(例如调用带有 "cut"、"copy" 和 "paste" 命令的 document.execCommand() 方法),则实现必须触发相应的操作,这会再次分发相关的剪贴板事件。
通过脚本 API 触发复制、剪切或粘贴操作时,应遵循以下步骤:
-
同步执行相应的操作。
-
将操作的返回值作为 API 调用的返回值。
注意:只有在事件是从受信任且由用户触发的操作分发时,或者实现配置为允许时,通过脚本 API 触发的复制和剪切命令才会影响真实剪贴板的内容。只有在实现配置为允许时,通过脚本 API 触发的粘贴命令才会触发粘贴事件并提供对剪贴板内容的访问。如何配置实现以允许读取或写入剪贴板超出了本规范的范围。
5.3.4. 与其他事件的交互
如果剪贴板操作由键盘输入触发,实现必须触发启动剪贴板操作的对应事件。该事件是异步的,但必须在相关按键的 keyup 事件之前分发。
剪切和粘贴操作可能导致实现分发其他支持的事件,例如 textInput、input、change、验证事件、DOMCharacterDataModified 和 DOMNodeRemoved / DOMNodeInserted。任何此类事件都会被排队,并在剪切/粘贴事件的处理完成后触发。
实现不得在响应复制操作时分发其他输入相关的事件,如 textInput、input、change 和验证事件。
5.3.5. 修改选区或焦点的事件监听器
如果事件监听器修改了选区或 可聚焦区域,则剪贴板操作必须在修改后的选区上完成。
6. 剪贴板事件 API
剪贴板事件 API 允许您覆盖用户代理的默认剪切、复制和粘贴行为。
对剪贴板的访问是通过标准的 DataTransfer 方法来完成的,以改变 ClipboardEvent 的 clipboardData 属性上的 items。一个结果是,这些剪贴板 API 只能在 ClipboardEvent 处理程序的上下文中访问剪贴板数据。
注意:如果您需要在剪贴板事件处理程序之外访问剪贴板,请参阅 § 7 异步剪贴板 API。
注意:剪贴板事件 API 是同步的,因此它们能做的事情有限。这些 API 不支持潜在阻塞的操作(如请求权限或对图像进行转码)。请参阅 § 7 异步剪贴板 API,以了解支持阻塞或其他耗时操作的更强大 API。
6.1. 覆盖 copy 事件
要覆盖默认的 copy 事件行为,必须添加一个 copy 事件处理程序,并且该处理程序必须调用 preventDefault() 来取消该事件。
取消该事件是必要的,以便使用 clipboardData 中的数据更新 系统剪贴板。如果 ClipboardEvent 未被取消,则会改用当前文档选区中的数据进行复制。
// 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();
});
6.2. 覆盖 cut 事件
要覆盖默认的 cut 事件行为,必须添加一个 cut 事件处理程序,并且该处理程序必须调用 preventDefault() 来取消该事件。
取消该事件是必要的,以便使用 clipboardData 中的数据更新 系统剪贴板。如果 ClipboardEvent 未被取消,则会改用当前文档选区中的数据进行复制。
请注意,取消 cut 事件也会阻止文档被更新(即当前选区不会被移除)。事件处理程序需要手动更新文档以移除当前选中的文本。
// 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();
});
6.3. 覆盖 paste 事件
要覆盖默认的 paste 事件行为,必须添加一个 paste 事件处理程序,并且该处理程序必须调用 preventDefault() 来取消该事件。
取消该事件是必要的,以便用户代理不会使用 系统剪贴板中的数据更新文档。
请注意,取消 paste 事件也会阻止文档被更新(即文档中什么都不会被粘贴)。事件处理程序需要手动将数据粘贴到文档中。
另请注意,在粘贴时,拖拽数据存储模式标志为只读,因此从 paste 事件处理程序调用 setData() 不会修改插入的数据,也不会修改剪贴板上的数据。
// 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();
}
});
6.4. 强制数据类型
实现必须识别以下数据类型的原生 OS 剪贴板格式描述,以便能够为粘贴事件填充 DataTransferItemList 和 ClipboardItem,并响应复制和剪切事件在 OS 剪贴板上设置正确的数据格式。
6.4.1. Reading from the clipboard
如果剪贴板上存在相应的原生类型,这些数据类型必须由 paste 事件公开。
-
text/plain
-
text/html
-
image/png
6.4.2. Writing to the clipboard
如果这些数据类型在 copy 和 cut 事件期间添加到 DataTransfer 对象中,则必须将它们连同相应的原生类型描述一起放置在剪贴板上。
-
text/plain
-
text/html
-
image/png
警告!作为安全防范措施,不受信任的脚本被允许写入剪贴板的数据类型受到限制。不受信任的脚本可能试图通过将已知会触发漏洞的数据放置在剪贴板上来利用本地软件中的安全漏洞。
6.5. 可选数据类型
实现 MAY 识别以下数据类型的原生 OS 剪贴板格式描述,以便能够为粘贴事件用正确的描述填充 ClipboardItem,并响应复制和剪切事件在 OS 剪贴板上设置正确的数据格式。
如果剪贴板上存在相应的原生类型,用户代理 MAY 公开这些数据类型。
-
text/uri-list
-
image/svg+xml
-
自定义格式以 `"web "` ("web" 后跟 U+0020 SPACE) 为前缀,且后缀(剥离 `"web "` 后)通过解析 MIME 类型检查。
6.6. 未经净化的数据类型
本节是非规范性的。这些数据类型 MUST NOT 被用户代理净化。
-
image/png
这些数据类型 MAY NOT 被用户代理净化。
可选的未经净化的数据类型 是由 Web 作者指定的 MIME 类型,它们 MAY NOT 被用户代理净化。有效的 可选的未经净化的数据类型 列在下方。
-
text/html
可选的未经净化的数据类型可能由于用户代理的隐私要求而不受支持。
7. 异步剪贴板 API
7.1. Navigator 接口
partial interface Navigator { [SecureContext ,SameObject ]readonly attribute Clipboard ; };clipboard
7.2. ClipboardItem 接口
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 = "unspecified"; };presentationStyle
clipboardItem = new ClipboardItem([items, options])- 创建一个新的
ClipboardItem对象。items 表示表示列表,每个表示都有一个MIME 类型以及一个针对该 MIME 类型的Promise(解析为Blob或DOMString),options 可用于填充其ClipboardItemOptions,如下例所示。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" }); clipboardItem.getType(type)- 返回一个
Promise,解析为与 MIME 类型 type 对应的Blob。 clipboardItem.types- 返回包含在剪贴板项目对象中的 MIME 类型列表。
ClipboardItem.supports(type)- 如果 type 在 强制数据类型或 可选数据类型中,则返回 true,否则返回 false。
剪贴板项目在概念上是用户通过调用“剪切”或“复制”命令所表达的希望使其可共享的数据。一个剪贴板项目有两个目的。首先,它允许网站读取用户复制到系统剪贴板的数据。其次,它允许网站将数据写入系统剪贴板。
例如,如果用户从原生应用程序的电子表格中复制了一系列单元格,这将产生一个剪贴板项目。如果用户从桌面复制了一组文件,该文件列表将由多个剪贴板项目表示。
某些平台可能支持在剪贴板上同时存在多个剪贴板项目,而其他平台则用新的项目替换之前的剪贴板项目。
一个剪贴板项目拥有一个表示列表,每个表示都关联一个MIME 类型(MIME 类型)、一个初始为 false 的 isCustom 标志(指示此表示是否应被视为 Web 自定义格式,而不是系统剪贴板的已知格式),以及数据(一个 ClipboardItemData)。
Web 自定义格式 的 isCustom 设置为 true。
在用户从电子表格复制单元格范围的示例中,它可能表示为图像 (image/png)、HTML 表格 (text/html)、纯文本 (text/plain) 或 Web 自定义格式 (web text/csv)。
这些 MIME 类型中的每一个都描述了相同剪贴板项目在不同保真度下的不同表示,并使剪贴板项目在粘贴期间更易被目标应用程序使用。
将单元格范围作为图像提供将允许用户将单元格粘贴到照片编辑应用程序中,而 text/plain 格式可用于文本编辑器应用程序。
一个剪贴板项目拥有一个呈现样式 (presentation style)(一个 PresentationStyle)。它有助于区分“粘贴”剪贴板项目的应用程序应该在粘贴点内联插入相应表示的内容,还是应该将其视为附件处理。
仅支持粘贴单个剪贴板项目的 Web 应用程序应使用第一个剪贴板项目。
支持粘贴多个剪贴板项目的 Web 应用程序可以,例如,提供一个用户界面来预览每个剪贴板项目的内容,并允许用户选择要粘贴的项目。此外,应用程序预期会枚举它们正在粘贴的剪贴板项目的 MIME 类型,并根据某些特定于应用程序的算法选择最适合该应用程序的类型。或者,应用程序可以为用户提供关于如何粘贴剪贴板项目的选项,例如“粘贴为图像”或“粘贴格式化文本”等。
ClipboardItem 对象具有一个关联的剪贴板项目,它是一个剪贴板项目。
ClipboardItem 对象具有一个关联的类型数组,这是一个 FrozenArray<DOMString>。
给定一个剪贴板项目 clipboardItem 和一个 Realm realm,创建一个 ClipboardItem 对象的步骤为:
-
令 clipboardItemObject 为一个在 realm 中 新建 的
ClipboardItem。 -
设置 clipboardItemObject 的剪贴板项目为 clipboardItem。
new ClipboardItem(items, options) 构造函数的步骤为:
-
如果 items 为空,则抛出
TypeError。 -
如果 options 为空,则设置 options["presentationStyle"] = "unspecified"。
-
设置 this 的剪贴板项目的呈现样式为 options["
presentationStyle"]。 -
令 types 为
DOMString的列表。 -
对于 items 中的每个 (key, value)
-
令 representation 为一个新的表示。
-
令 isCustom 为 false。
-
如果 key 以 `"web "` 为前缀,则
-
移除 `"web "` 前缀并将剩余字符串分配给 key。
-
将 isCustom 设置为 true。
-
-
将 representation 的 isCustom 标志设置为 isCustom。
-
令 mimeType 为运行 解析 MIME 类型(给定 key)的结果。
-
如果 mimeType 失败,则抛出
TypeError。 -
如果 this 的剪贴板项目的表示列表包含一个表示,其 MIME 类型为 mimeType 且其 [representation/isCustom] 为 isCustom,则抛出
TypeError。
上述步骤防止了用户代理已知的 MIME 类型与作者打算视为自定义类型的类型之间的冲突。例如,作者的项目列表中可能包含 "text/html" 以及 "web text/html" 的表示。
-
7.2.1. presentationStyle
presentationStyle getter 的步骤是返回 this 的剪贴板项目的呈现样式。
7.2.2. types
types getter 的步骤是返回 this 的类型数组。
7.2.3. getType(type)
此方法必须运行以下步骤:
-
令 isCustom 为 false。
-
如果 type 以 `"web "` 为前缀,则
-
移除 `"web "` 前缀并将剩余字符串分配给 type。
-
将 isCustom 设置为 true。
-
-
令 mimeType 为运行 解析 MIME 类型(给定 type)的结果。
-
如果 mimeType 失败,则抛出
TypeError。 -
令 p 为 realm 中的一个新 promise。
-
对于 itemTypeList 中的每个 representation
-
如果 representation 的 MIME 类型为 mimeType 且 representation 的 isCustom 为 isCustom,则
-
令 representationDataPromise 为 representation 的 数据。
-
对 representationDataPromise 做出响应:
-
如果 representationDataPromise 以值 v 成功完成,则
-
如果 representationDataPromise 被拒绝,则
-
在 realm 中用
"NotFoundError"DOMException拒绝 p。
-
-
-
返回 p。
-
-
-
在 realm 中用
"NotFoundError"DOMException拒绝 p。 -
返回 p。
7.2.4. supports(type)
此方法必须运行以下步骤:
7.3. Clipboard 接口
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
Clipboard 接口的某些方法接收或返回多个 ClipboardItem 对象。然而,并非所有平台都支持多于一个的 剪贴板项;在这些平台上,下述算法将忽略传递给 write() 的第一个 ClipboardItem 对象之后的所有对象,且 read() 和 readText() 也只能从操作系统获取一个剪贴板项。
剪贴板项(clipboard items)对象是一个序列的剪贴板项。
Web 作者需要创建一个作为 ClipboardItem 数组的 data,以便使用 write(data) 方法将内容写入系统剪贴板。read() 返回一个 剪贴板项 对象的 Promise,该对象代表系统剪贴板数据的内容。
unsanitized 是一个 DOMString 的 序列,对应于作者希望被视为可选未清洗数据类型的 MIME 类型。
用户代理可能不支持 unsanitized 选项。Web 作者不应假定 unsanitized 中列出的 MIME 类型的内容会保持未清洗状态,因为在某些隐私模式下可能不允许此选项。
剪贴板任务源 是在响应读取或写入系统剪贴板数据时触发的。
7.3.1. read(formats)
read(formats) 方法必须运行以下步骤:-
设 realm 为 this 的 相关领域(relevant realm)。
-
设 p 为 realm 中的 新 Promise。
-
如果 formats 不为空,则:
-
对于 formats["
unsanitized"] 中的每个 format:-
如果 format 不在可选未清洗数据类型中,则在 realm 中以 format
"NotAllowedError"DOMException拒绝(reject) p。
-
-
-
并行运行以下步骤
-
设 r 为运行检查剪贴板读取权限的结果。
-
如果 r 为 false,则:
-
在权限任务源上排队一个全局任务,给定 realm 的全局对象,以
"NotAllowedError"DOMException在 realm 中拒绝 p。 -
中止这些步骤。
-
-
设 data 为系统剪贴板数据的副本。
-
对于 data 中的每个 systemClipboardItem:
-
设 item 为一个新的 剪贴板项。
-
对于 systemClipboardItem 中的每个 systemClipboardRepresentation:
-
设 mimeType 为在给定 systemClipboardRepresentation 的名称的情况下,运行从特定操作系统格式获取公认的 MIME 类型算法的结果。
-
如果 mimeType 为 null,则继续此循环。
-
设 representation 为一个新的表示(representation)。
-
将 representation 的MIME 类型设置为 mimeType。
-
设 isUnsanitized 为 false。
-
如果 formats 不为空,则:
-
对于 formats["
unsanitized"] 中的每个 format:-
如果 format 等于 MIME 类型,则将 isUnsanitized 设置为 true。
-
-
-
用户代理 MAY 对 representation 的数据进行清洗,除非 representation 的MIME 类型的本质是 "image/png"(应保持未清洗以保留元数据),或者满足以下条件:
-
将 representation 追加到 item 的表示列表中。
-
将 isUnsanitized 设置为 false。
-
-
如果 item 的表示列表大小大于 0,则将 item 追加到 items 中。
-
-
如果 items 的大小 > 0,则:
-
设 firstItem 为 items[0]。
-
运行给定 firstItem 的读取 Web 自定义格式算法。
-
-
否则:
-
设 customItem 为一个新的 剪贴板项。
-
运行给定 customItem 的读取 Web 自定义格式算法。
-
如果 customItem 的表示列表大小大于 0,则将 customItem 追加到 items 中。
-
-
在剪贴板任务源上排队一个全局任务,给定 realm 的全局对象,以执行以下步骤:
-
设 clipboardItems 为一个 序列<
ClipboardItem>。 -
对于 items 中的每个 剪贴板项 underlyingItem:
-
设 clipboardItem 为给定 underlyingItem 和 realm 运行创建 ClipboardItem 对象步骤的结果。
-
将 clipboardItem 追加到 clipboardItems 中。
-
-
以 clipboardItems 解析 p。
-
-
-
返回 p。
const items= await navigator. clipboard. read(); const textBlob= await items[ 0 ]. getType( "text/plain" ); const text= await ( new Response( textBlob)). text();
7.3.2. readText()
readText() 方法必须运行以下步骤:-
设 p 为 realm 中的 新 Promise。
-
以下步骤并行运行
-
设 r 为运行检查剪贴板读取权限的结果。
-
如果 r 为 false,则:
-
在权限任务源上排队一个全局任务,给定 realm 的全局对象,以
"NotAllowedError"DOMException在 realm 中拒绝 p。 -
中止这些步骤。
-
-
设 data 为系统剪贴板数据的副本。
某些操作系统包含多个剪贴板(例如 Linux 的 "primary"、"secondary"、"selection")。定义从中读取数据的剪贴板。
-
在剪贴板任务源上排队一个全局任务,给定 realm 的全局对象,以执行以下步骤:
-
对于 data 中的每个 systemClipboardItem:
-
对于 systemClipboardItem 中的每个 systemClipboardRepresentation:
-
设 mimeType 为在给定 systemClipboardRepresentation 的名称的情况下,运行从特定操作系统格式获取公认的 MIME 类型算法的结果。
-
如果 mimeType 为 null,则继续此循环。
-
设 representation 为一个新的表示。
-
如果 representation 的MIME 类型本质为 "text/plain",则:
-
将 representation 的MIME 类型设置为 mimeType。
-
设 representationDataPromise 为 representation 的数据。
-
响应 representationDataPromise:
-
如果 representationDataPromise 以值 v 成功完成,则
-
如果 representationDataPromise 被拒绝,则
-
在 realm 中以
"NotFoundError"DOMException拒绝 p。 -
返回 p。
-
-
-
-
-
-
在 realm 中以
"NotFoundError"DOMException拒绝 p。 -
返回 p。
-
-
navigator. clipboard. readText(). then( function ( data) { console. log( "Your string: " , data); });
7.3.3. write(data)
write(data) 方法必须运行以下步骤:-
设 p 为 realm 中的 新 Promise。
-
执行以下步骤 并行地
-
设 r 为运行检查剪贴板写入权限的结果。
clipboard-write 已在 https://github.com/w3c/clipboard-apis/pull/164 中移除。
-
如果 r 为 false,则:
-
在权限任务源上排队一个全局任务,给定 realm 的全局对象,以
"NotAllowedError"DOMException在 realm 中拒绝 p。 -
中止这些步骤。
-
-
在剪贴板任务源上排队一个全局任务,给定 realm 的全局对象,以执行以下步骤:
-
设 dataList 为一个 序列<
ClipboardItem>。 -
如果 data 的大小大于 1,且当前操作系统不支持系统剪贴板上的多个原生剪贴板项,则将 data[0] 添加到 dataList 中,否则,将 dataList 设置为 data。
-
对于 dataList 中的每个 clipboardItem:
-
对于 clipboardItem 的剪贴板项中的每个 representation 的表示列表:
-
设 representationDataPromise 为 representation 的数据。
-
响应 representationDataPromise:
-
如果 representationDataPromise 以值 v 成功完成,则
-
如果 representationDataPromise 被拒绝,则
-
在 realm 中以
"NotAllowedError"DOMException拒绝 p。 -
中止这些步骤。
-
-
-
-
对于 itemList 中的每个 blob:
-
设 type 为 blob 的
type。 -
如果 type 不在强制数据类型或可选数据类型列表中,则在 realm 中以
"NotAllowedError"DOMException拒绝 p 并中止这些步骤。 -
设 cleanItem 为 blob 的可选清洗副本。
-
如果尝试清洗但未成功完成,则遵循以下步骤:
-
在 realm 中以
"NotAllowedError"DOMException 拒绝 p。 -
中止这些步骤。
-
-
将 cleanItem 追加到 cleanItemList 中。
-
-
使用 cleanItemList 和 option 将 Blob 和选项写入剪贴板。
-
-
解析 p。
-
-
返回 p。
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 () { console. error( "Unable to write to clipboard. :-(" ); });
7.3.4. writeText(data)
writeText(data) 方法必须运行以下步骤:-
设 p 为 realm 中的 新 Promise。
-
在 并行中 运行以下步骤:
-
返回 p。
await navigator. clipboard. writeText( "Howdy, partner!" );
8. 剪贴板动作
本节定义剪贴板动作及事件分发的处理模型。
每个剪贴板动作都有两个名为 script-triggered 和 script-may-access-clipboard 的标志。
script-triggered 标志若动作由于脚本(例如 document.execCommand() 调用)而运行,则该标志被设置。未来与剪贴板交互的脚本 API 也应使用这些动作,且必须相应地设置 script-triggered 标志。
script-may-access-clipboard 标志的设置如下:
-
如果动作为 copy 或 cut 且脚本线程被允许修改剪贴板,则:
-
设置该动作的 script-may-access-clipboard 标志。
-
-
如果动作为 paste 且脚本线程被允许从剪贴板读取,则:
-
设置该动作的 script-may-access-clipboard 标志。
-
8.1. 复制(copy)动作
复制动作由以下步骤组成:
-
如果 script-triggered 标志已设置,则:
-
如果 script-may-access-clipboard 标志未设置,则:
-
从复制动作返回 false,终止此算法。
-
-
-
如果该事件未被取消,则:
-
将所选内容(如有)复制到剪贴板。在网页中选定内容时,实现应该创建替代的 text/html 和 text/plain 剪贴板格式。
-
触发一个名为
clipboardchange的剪贴板事件。
-
-
否则,如果该事件被取消,则:
-
调用将内容写入剪贴板算法,传递
DataTransferItemList列表 items、一个 clear-was-called 标志和一个 types-to-clear 列表。
-
-
从复制动作返回 true。
8.2. 剪切(cut)动作
剪切动作由以下步骤组成:
-
如果 script-triggered 标志已设置,则:
-
如果 script-may-access-clipboard 标志未设置,则:
-
从剪切动作返回 false,终止此算法。
-
-
-
如果该事件未被取消,则:
-
如果在一个启用了剪切的可编辑上下文中存在选区,则:
-
将所选内容(如有)复制到剪贴板。在网页中选定内容时,实现应该创建替代的 text/html 和 text/plain 剪贴板格式。
-
从文档中删除选区的内容并折叠选区。
-
触发一个名为
clipboardchange的剪贴板事件。 -
排队任务以触发由于修改而应触发的任何事件,详见 § 5.3 与其他脚本和事件的集成。
-
-
否则,如果不存在选区或者上下文不可编辑,则:
-
返回 false。
-
-
-
否则,如果该事件被取消,则:
-
调用将内容写入剪贴板算法,传递
DataTransferItemList列表 items、一个 clear-was-called 标志和一个 types-to-clear 列表。 -
触发一个名为
clipboardchange的剪贴板事件。
-
-
从剪切动作返回 true。
8.3. 粘贴(paste)动作
对于粘贴动作,script-may-access-clipboard 标志取决于特定于实现的权限机制,用于确定哪些网站或应用可以从剪贴板读取。当粘贴动作由脚本触发时,实现必须在未获得用户许可的情况下,不得提供剪贴板内容。如果尚未授予许可,权限提示必须包括与脚本线程关联的文档的主机名。
粘贴动作由以下步骤组成:
-
如果 script-triggered 标志已设置,则:
-
如果 script-may-access-clipboard 未设置,则:
-
从粘贴动作返回 false,终止此算法。
-
-
-
如果该事件未被取消,则:
-
如果在一个启用了粘贴的可编辑上下文中存在选区或光标,则:
-
将剪贴板上找到的最合适内容(如有)插入到该上下文中。
-
排队任务以触发由于修改而应触发的任何事件,详见 § 5.3 与其他脚本和事件的集成。
-
-
否则:
-
返回 false。
-
-
-
否则,如果事件被取消:
-
返回 false。
-
-
从该动作返回 true。
9. Permissions API 集成
[permissions] API 为网站访问强大功能(如剪贴板)提供了一种统一的方式。它允许网站向用户请求权限并查询它们拥有的权限。
对于剪贴板,定义了一个权限:"clipboard-write"
注意: 剪贴板权限目前仅适用于异步剪贴板 API。本规范的未来版本可能会更新,以将此权限应用于其他剪贴板交互。
这些剪贴板权限是强大功能,权限相关的算法和类型定义如下:
- 权限描述符类型
-
dictionary :ClipboardPermissionDescriptor PermissionDescriptor {boolean =allowWithoutGesture false ; };
共有 4 种剪贴板权限:
-
{ name: "clipboard-write", allowWithoutGesture: false }
-
{ name: "clipboard-write", allowWithoutGesture: true }
具有以下关系:
-
{ "clipboard-write" + true }比{ "clipboard-write" + false }更强。
虽然用户代理必须支持本规范中描述的 ClipboardPermissionDescriptor,但它们当然保留对默认设置以及如何(或是否)向用户公开它们的完全控制权。
-
{ "clipboard-write" + false }暴露给用户进行控制。 -
{ "clipboard-write" + true }始终被denied。
9.1. 剪贴板读取权限
9.2. 剪贴板写入权限
9.2.1. check clipboard write permission
-
设 writeWithoutGesture 为
{ name: "clipboard-write", allowWithoutGesture: true }权限的权限状态。 -
如果 writeWithoutGesture 为
granted,则返回 true。 -
如果 hasGesture 则:
-
设 systemCopy 为 true(如果当前脚本是由于用户与用户代理或操作系统创建的“剪切”或“复制”元素交互而运行的)。
-
如果 systemCopy 为 true,则返回 true。
-
返回请求使用
{ name: "clipboard-write", allowWithoutGesture: false }权限的结果。注意: 用户代理可以选择请求更强的权限,这将隐式更新此权限。
-
-
返回请求使用
{ name: "clipboard-write", allowWithoutGesture: true }权限的结果。
10. 安全考量
允许作者更改用户复制的内容,或对从未选择的内容进行自动复制并允许无限制地调用粘贴信息,可能会引发各种安全担忧。
一些示例场景包括:
-
用户选择并复制了一个链接,但剪贴板上却复制了另一个不同的链接。其影响可能从粘贴出乎意料的结果到尝试“网络钓鱼”攻击不等。
-
(Self-XSS) Shell 命令或可执行脚本可以被放置在剪贴板中,目的是让用户运行粘贴的内容。
-
专门为了利用核心操作系统图像处理代码中的漏洞而精心制作的图像,可以被写入剪贴板。
10.1. 粘贴 HTML 和多部分数据
本节是非规范性的。
粘贴格式化或多部分数据存在某些安全风险。
-
用户可能会在不知情的情况下粘贴隐藏数据。例如,如果标记包含 <input type="hidden"> 标签或 HTML 注释,可能会发生这种情况。此类隐藏数据可能包含敏感信息。
-
用户可能会将恶意 JavaScript 粘贴到受信任的页面中。
-
实现可能会授予脚本访问用户无意暴露的本地文件的权限。
为了确定使用哪些策略,我们考虑的因素是:
-
正在粘贴的数据来源
-
数据子部分(例如引用的图像)的来源
-
运行中脚本的来源
以下是场景和可能的安全策略概述:
| 数据来源 | 脚本来源 | 规则 |
|---|---|---|
| 源自在线资源 | 与数据相同 | 不清洗 HTML。不访问任何本地文件。 |
| 不同源 | 可选清洗内容。不访问任何本地文件。 | |
| 源自本地应用程序 | 任意 | 不清洗 HTML。授予访问本地文件的权限。 |
一些实现在粘贴富文本时,默认会剥离潜在的恶意内容(如 SCRIPT 元素和 javascript: 链接)来降低相关风险,但允许粘贴事件处理程序检索并处理原始的、未清洗的数据。
10.2. 常规安全策略
实现不得下载引用的在线资源,也不得在 files 列表或 DataTransferItemList 中暴露它们的内容。
如果剪贴板上的数据并非来自本地应用程序,实现不得授予对任何引用的本地文件的访问权限。例如,如果数据包含 <img src="file:///example.jpg"> 但数据来源是在线资源,实现不得在 clipboardData.items 列表中为 example.jpg 添加条目。
10.3. 图像转码
为防止将恶意图像数据放置到剪贴板,可以对图像数据进行转码以生成安全版本的图像。这可以防止网站尝试利用其他应用程序中的安全漏洞。
实现不应转码从剪贴板读取的图像。转码图像可能会丢失重要元数据(如图像的物理分辨率)。这与其他图像共享方式(如 `<input type=file>`)保持一致。
10.4. 干扰考量
脚本可能会使用 DataTransfer API,通过更改复制和剪切事件中系统剪贴板上的数据来干扰和迷惑用户。本规范不试图防止此类干扰,但实现可能会增加额外的限制。
实现必须优雅地处理尝试在剪贴板上放置过多数据的脚本。
11. 隐私考量
由于这些 API 提供了对用户剪贴板数据的访问权限,因此由于剪贴板可能包含姓名、地址或密码等个人身份信息 (PII),存在严重的隐私问题。
总体而言,用户代理必须确保不受信任的脚本无法通过这些 API 无限制地访问用户的剪贴板数据。
11.1. 隐私与剪贴板事件 API
剪贴板事件 API 允许在剪贴板事件处理程序上下文中运行的脚本访问系统剪贴板的副本,并可能修改正在写入剪贴板的数据。
用户代理应意识到有关保护剪贴板事件 API 所访问数据的以下要求:
-
实现
DataTransfer接口以返回剪贴板数据的对象不得在提供数据的 ClipboardEvent 事件处理程序之外可用。 -
如果脚本存储了对实现
DataTransfer接口的对象的引用以便在 ClipboardEvent 事件处理程序之外使用,则所有方法在预期上下文之外调用时必须为空操作。 -
实现不得允许脚本创建合成剪贴板事件以获取对真实剪贴板数据的访问权限(除非用户已配置为这样做)。
即使剪贴板事件 API 不在剪贴板权限的覆盖范围内,用户代理也可以选择提供一种供用户禁用此 API 或配置允许哪些站点访问它的方法。
11.2. 隐私与异步剪贴板 API
异步剪贴板 API 是一项强大功能,因为它可以允许任何脚本访问剪贴板数据(访问不受剪贴板事件处理程序限制),并且可以在没有用户提供手势的情况下访问数据。
为了帮助防止滥用,除非脚本在具有焦点的文档上下文中执行,否则此 API 必须不可用。
11.2.1. 隐私与剪贴板权限
剪贴板权限限制了对此 API 的访问,但用户代理可以选择权限默认值以及用户可以设置哪些权限设置。例如,用户代理可以选择仅在有用户手势时才允许访问异步剪贴板 API,并始终拒绝脚本在没有手势的情况下请求访问。
用户代理可以选择让此权限在用户授予权限后的一段时间自动过期,例如:
-
从首次授予权限算起的特定时间后
-
从用户上次访问站点算起的特定时间后
-
当用户导航离开页面时
11.3. 其他隐私考量
如果用户代理允许使用 document.execCommand("paste") 读取剪贴板数据,则用户代理必须确保用户已明确允许该访问发生。
12. 致谢
本节为非规范性内容
编辑们希望感谢前任编辑们的贡献,他们帮助引导本规范通过各种会议和邮件列表讨论,将其带到目前的状态。
-
Hallvord R. M. Steen
编辑们还希望感谢来自 Microsoft [MICROSOFT-CLIP-OP] 的 Data Transfer 功能文档以及 [HTML5] 规范早期草稿对他们的智力启发。我们也感谢 Paul Libbrecht 的“安全复制和粘贴”草稿(此草稿在 Web 上已不可用)。
最后,我们希望感谢以下人士的贡献:
Adam Barth, Shawn Carnell, Daniel Cheng, Daniel Dardailler, Domenic Denicola, Al Gilman, James Graham, James Greene, Ian Hickson, Darwin Huang, Lachlan Hunt, Philip Jägenstedt, Anne van Kesteren, Marijn Kruisselbrink, Aaron Leventhal, Jim Ley, Paul Libbrecht, "Martijn", Glenn Maynard, Chris Mills, ms2ger, Ryosuke Niwa, Robert O’Callahan, Dave Poehlman, "ROBO Design", Janina Sajka, Rich Schwerdtfeger, Jonas Sicking, Maciej Stachowiak, Mihai Sucan, Dmitry Titov, Ojan Vafai, Tarquin Wilton-Jones, Tom Wlodkowski, Bo Cupp, mbrodesser 和 Boris Zbarsky。
附录 A:算法
write content to the clipboard
- 输入:
-
items,一个要写入的
DataTransferItemList项列表 -
clear-was-called,一个布尔值
-
types-to-clear,一个列表
- 输出:
-
无
-
如果 items 列表不为空,则:
-
清除剪贴板。
-
对于列表中的每个部分:
-
如果数据类型是 text/plain,则:
-
根据操作系统和区域设置约定确保编码正确。
-
根据平台约定规范化行尾。
-
使用适当的操作系统剪贴板格式描述将文本置于剪贴板上。
-
-
否则,如果数据类型列在强制数据类型列表中,则:
-
使用适当的操作系统剪贴板格式描述将该部分置于剪贴板上。
-
-
否则:
-
-
-
否则,items 列表为空。遵循以下步骤以确定是否清除剪贴板:
write blobs and option to the clipboard
-
对于 items 中的每个 item:
-
设 formatString 为在给定 item 的
type的情况下,运行特定操作系统公认格式的结果。 -
如果 formatString 为空,则遵循以下步骤:
-
设 webCustomFormatString 为 item 的
type。 -
设 webCustomFormat 为一个空的
type。 -
如果 webCustomFormatString 以 `"web "` 前缀开头,则移除该前缀并将剩余字符串存储在 webMimeTypeString 中。
-
设 webMimeType 为在给定 webMimeTypeString 的情况下,解析 MIME 类型的结果。
-
如果 webMimeType 解析失败,则中止所有步骤。
-
将 item 的
type设置为 webCustomFormat。 -
将 webCustomFormat 追加到 webCustomFormats 中。
-
-
设 payload 为 UTF-8 解码 item 的底层字节序列的结果。
-
将 payload 和 presentationStyle 插入到系统剪贴板中,并使用 formatString 作为原生剪贴板格式。
某些操作系统包含多个剪贴板(例如 Linux 的 "primary"、"secondary"、"selection")。定义数据被写入其中的剪贴板。
-
-
在给定 webCustomFormats 的情况下写入 Web 自定义格式。
os specific well-known format
- 输入:
-
mimeType,一个
type - 输出:
-
wellKnownFormat,一个特定于平台的字符串类型。在 Mac 上是 NSPasteboardType,在 Windows 上是 LPCWSTR,在 Linux 上是 const char*。
对于 Windows,请参阅 https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats 和 https://docs.microsoft.com/en-us/windows/win32/dataxchg/about-atom-tables?redirectedfrom=MSDN。对于 Mac,请参阅 https://developer.apple.com/documentation/appkit/nspasteboardtype。
-
设 wellKnownFormat 为空字符串。
-
如果 mimeType 的本质是 "text/plain",则:
在 Windows 上,遵循以下描述的约定:
-
将 CF_UNICODETEXT 分配给 wellKnownFormat。
在 MacOS 上,遵循以下描述的约定:
-
将 NSPasteboardTypeString 分配给 wellKnownFormat。
在 Linux、ChromeOS 和 Android 上,遵循以下描述的约定:
-
将 "text/plain" 分配给 wellKnownFormat。
-
-
否则,如果 mimeType 的本质是 "text/html",则:
在 Windows 上,遵循以下描述的约定:
-
将 CF_HTML 分配给 wellKnownFormat。
在 MacOS 上,遵循以下描述的约定:
-
将 NSPasteboardTypeHTML 分配给 wellKnownFormat。
在 Linux、ChromeOS 和 Android 上,遵循以下描述的约定:
-
将 "text/html" 分配给 wellKnownFormat。
-
-
否则,如果 mimeType 的本质是 "image/png",则:
在 Windows 上,遵循以下描述的约定:
-
将 "PNG" 分配给 wellKnownFormat。
在 MacOS 上,遵循以下描述的约定:
-
将 NSPasteboardTypePNG 分配给 wellKnownFormat。
在 Linux、ChromeOS 和 Android 上,遵循以下描述的约定:
-
将 "image/png" 分配给 wellKnownFormat。
-
-
否则,如果 mimeType 的本质是 "image/svg+xml",则:
在 Windows 上,遵循以下描述的约定:
-
将 CFSTR_MIME_SVG_XML 分配给 wellKnownFormat。
在 MacOS 上,遵循以下描述的约定:
-
将 UTTypeSVG 分配给 wellKnownFormat。
在 Linux、ChromeOS 和 Android 上,遵循以下描述的约定:
-
将 "image/svg+xml" 分配给 wellKnownFormat。
-
-
返回 wellKnownFormat。
well-known mime type from os specific format
- 输入:
-
osFormatName,一个特定于平台的字符串类型。在 Mac 上是 NSPasteboardType,在 Windows 上是 LPCWSTR,在 Linux 上是 const char*。
- 输出:
-
mimeType,一个 MIME 类型
对于 Windows,请参阅 https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats 和 https://docs.microsoft.com/en-us/windows/win32/dataxchg/about-atom-tables?redirectedfrom=MSDN。对于 Mac,请参阅 https://developer.apple.com/documentation/appkit/nspasteboardtype。
在 Windows 上,遵循以下描述的约定:
-
如果 osFormatName 为 "UnicodeText",则设置 mimeTypeString 为 "text/plain"。
-
否则,如果 osFormatName 为 "HTML Format",则设置 mimeTypeString 为 "text/html"。
-
否则,如果 osFormatName 为 "PNG",则设置 mimeTypeString 为 "image/png"。
-
否则,如果 osFormatName 为 CFSTR_MIME_SVG_XML,则设置 mimeTypeString 为 "image/svg+xml"。
在 MacOS 上,遵循以下描述的约定:
-
如果 osFormatName 为 NSPasteboardTypeString,则设置 mimeTypeString 为 "text/plain"。
-
否则,如果 osFormatName 为 NSPasteboardTypeHTML,则设置 mimeTypeString 为 "text/html"。
-
否则,如果 osFormatName 为 NSPasteboardTypePNG,则设置 mimeTypeString 为 "image/png"。
-
否则,如果 osFormatName 为 UTTypeSVG,则设置 mimeTypeString 为 "image/svg+xml"。
在 Linux、ChromeOS 和 Android 上,遵循以下描述的约定:
-
如果 osFormatName 为 "text/plain",则设置 mimeTypeString 为 "text/plain"。
-
否则,如果 osFormatName 为 "text/html",则设置 mimeTypeString 为 "text/html"。
-
否则,如果 osFormatName 为 "image/png",则设置 mimeTypeString 为 "image/png"。
-
否则,如果 osFormatName 为 "image/svg+xml",则设置 mimeTypeString 为 "image/svg+xml"。
-
设 mimeType 为在给定 mimeTypeString 的情况下,解析 MIME 类型的结果。
-
返回 mimeType。
read web custom format
- 输入:
-
item,一个 剪贴板项
-
设 webCustomFormatMap 为特定操作系统自定义映射名称。
-
从系统剪贴板读取 webCustomFormatMap。
-
如果 webCustomFormatMap 为空,则返回 item。
-
设 webCustomFormatMapString 为从 webCustomFormatMap 反序列化的 JSON 字符串。
注意: 需要一个 JSON 读取器来反序列化来自 webCustomFormatMap 的内容。
-
对于 webCustomFormatMapString 中的每个 (key, value):
write web custom formats
-
设 idx 为初始化为 0 的数字。
-
设 webCustomFormatMap 为特定操作系统自定义映射名称。
-
设 webCustomFormatMapString 为一个空的 JSON 字符串。
-
对于 items 中的每个 item:
-
设 webCustomFormat 为特定操作系统自定义名称。
-
设 webCustomFormatIdx 为将 idx 追加到 webCustomFormat 的结果。
-
使用 JSON 序列化器将 item 的
type作为键,将 webCustomFormatIdx 作为值插入到 webCustomFormatMapString 中。注意: 需要一个 JSON 写入器来将内容序列化为 webCustomFormatMapString。
-
使用 webCustomFormatIdx 作为格式将 item 插入到系统剪贴板中。
-
递增 idx。
-
如果 idx 大于 100,则跳出此循环。
-
-
使用 webCustomFormatMap 作为格式将 webCustomFormatMapString 插入到系统剪贴板中。
os specific custom map name
- 输出:
-
webCustomFormatMap,一个字符串
在 Windows 上,遵循以下描述的约定:
-
将 "Web Custom Format Map" 分配给 webCustomFormatMap。
-
返回 webCustomFormatMap。
在 MacOS 上,遵循以下描述的约定:
-
将 "org.w3.web-custom-format.map" 分配给 webCustomFormatMap。
-
返回 webCustomFormatMap。
在 Linux、ChromeOS 和 Android 上,遵循以下描述的约定:
-
将 "application/web;type=\"custom/formatmap\"" 分配给 webCustomFormatMap。
-
返回 webCustomFormatMap。
os specific custom name
- 输出:
-
webCustomFormat,一个字符串
在 Windows 上,遵循以下描述的约定:
-
将 "Web Custom Format" 分配给 webCustomFormat。
-
返回 webCustomFormat。
在 MacOS 上,遵循以下描述的约定:
-
将 "org.w3.web-custom-format.type-" 分配给 webCustomFormat。
-
返回 webCustomFormat。
在 Linux、ChromeOS 和 Android 上,遵循以下描述的约定:
-
将 "application/web;type="custom/format" 分配给 webCustomFormat。
-
返回 webCustomFormat。
fire a clipboard event
- 输入:
-
e,要触发的
ClipboardEvent - 输出:
-
无
-
设 clear-was-called 为 false。
-
设 types-to-clear 为一个空列表。
-
设 clipboard-event-data 为一个新的
DataTransfer对象,具有空的items列表。 -
设 clipboard-entry 为当前剪贴板内容的序列号,如果操作系统剪贴板不支持序列号,则为 null。
-
设 trusted 为 true(如果事件由用户代理生成),否则为 false。
-
设置 target 如下:
-
处理事件如下:
-
如果 e 为 "paste",则:
-
如果 trusted 为 true,或者实现被配置为允许脚本生成的事件读取操作系统剪贴板:
-
对于操作系统剪贴板上的每个 clipboard-part,执行以下步骤:
-
如果 clipboard-part 包含纯文本,则:
-
确保文本处于脚本引擎内部使用的编码中。
-
设 new-data 为一个新的
DataTransferItem,拖拽数据项种类设置为 string,拖拽数据项类型字符串设置为 text/plain。 -
将 new-data 的数据设置为纯文本。
-
将 new-data 添加到 clipboard-event-data 的
items中。
-
-
如果 clipboard-part 表示文件引用,则对于每个文件引用:
-
确定引用文件的 MIME 类型。
-
设 new-data 为一个新的
DataTransferItem,拖拽数据项种类设置为 file,拖拽数据项类型字符串设置为相应的 MIME 类型,如果文件类型未知,则为application/octet-stream。 -
将 new-data 的数据设置为文件引用数据。
-
将 new-data 添加到 clipboard-event-data 的
items中。
-
-
如果 clipboard-part 包含 HTML 或 XHTML 格式的文本(根据操作系统描述此类剪贴板格式的约定),则:
-
如果实现支持粘贴 HTML,则实现必须通过调用处理 html 粘贴事件(传入 clipboard-part 和 clipboard-event-data)来处理标记。
-
-
如果 clipboard-part 包含其他受支持的二进制或基于文本的格式的数据(参见强制数据类型),则:
-
确定数据的 MIME 类型。
-
设 new-data 为一个新的
DataTransferItem,拖拽数据项种类设置为 file,拖拽数据项类型字符串设置为相应的 MIME 类型。 -
将 new-data 的数据设置为二进制或基于文本的数据。
-
将 new-data 添加到 clipboard-event-data 的
items中。
-
-
-
-
更新 clipboard-event-data 的
files属性,以匹配 clipboard-event-data 的items中的条目。 -
更新 clipboard-event-data 的
types属性,以匹配 clipboard-event-data 的items中的条目。
-
如果 e 为 "copy" 或 "cut",则:
-
将关联的
DataTransfer对象的内部拖拽数据存储模式标志设置为 读/写。
-
-
-
将 e 的
clipboardData设置为 clipboard-event-data。 -
将 e 的
isTrusted设置为 trusted。 -
将 e 的
composed设置为 true。 -
在该 target 上分发该事件 e,该事件冒泡且可取消,并使用
ClipboardEvent接口。事件分发期间访问数据的实现要求定义在 [HTML] 中。下面给出了一些额外的剪贴板事件特定的处理规则:
-
如果脚本调用 clearData() 或
clear()且DataTransfer对象的内部拖拽数据存储模式标志为 读/写,则:-
将 clear-was-called 标志设置为 true。如果给定了参数,则将该参数添加到 types-to-clear 列表中。
-
-
如果脚本调用 setData() 或修改了项,且 clear-was-called 标志为 true,则:
-
如果 types-to-clear 列表为空,则:
-
将 clear-was-called 标志设置为 false,然后……
-
-
否则,如果 setData() 的
type参数或新项的拖拽数据项类型字符串在 types-to-clear 列表中找到,则:-
从列表中删除它。如果列表现在为空,则将 clear-was-called 标志设置为 false。
-
-
-
如果脚本调用 getData() 或访问
DataTransferItemList中的项且 clipboard-entry 已设置,则:-
检查剪贴板数据的序列号是否匹配 clipboard-entry。如果剪贴板不再包含相同的条目,则将
DataTransferItemList对象的内部拖拽数据存储模式设置为 受保护。
-
警告!监听粘贴事件的恶意脚本可能会建立一个永无止境的循环,以便读取用户将来放入剪贴板的内容。在没有剪贴板序列号的平台上,应实施其他限制。
-
处理 HTML 粘贴事件
- 输入:
-
clipboard-part,要处理的剪贴板部分
-
clipboard-event-data,此事件的
DataTransfer对象 - 输出:
-
无
-
令 new-data 为一个新的
DataTransferItem,其 拖拽数据项类型 设置为“纯 Unicode 字符串”,拖拽数据项类型字符串 相应地设置为 text/html 或 application/xhtml+xml。 -
从 clipboard-part 中提取标记并使用相关解析器构建 DOM 树。
-
如果标记的源 URL 已知,则使用该源 URL 作为基础 URL 解析 HREF 和 SRC 属性中的所有相对 URL,并将相应属性设置为解析后的绝对 URL。
-
如果标记来源于本地应用程序,则检查是否存在对本地文件和/或其他 OS 剪贴板内容部分的引用。如果找到此类引用,则对子部分的引用必须替换为使用 cid: URL 方案的 content-id 引用 [RFC2392]。为此,请按照以下步骤处理引用本地文件或剪贴板部分的每个属性:
这些步骤是必要的吗?我们是否了解支持多个具有内部引用的部分的本机(平台)剪贴板实现?
此功能处于风险之中,因为尚不清楚是否需要它,而且难以以跨平台的方式进行测试。
-
如果 clipboard-event-data 的
items已包含所引用的文件或剪贴板部分的条目,则-
将 itemNumber 设置为现有条目的索引。
-
-
否则,
-
令 new-file-data 为一个新的
DataTransferItem,其 拖拽数据项类型 设置为“文件”,并且如果已知文件或剪贴板部分的 MIME 类型,则将 拖拽数据项类型字符串 设置为该 MIME 类型,如果文件类型未知,则设置为application/octet-stream。 -
令 file-info 为一个新的
File对象,其name设置为 HTML 属性内容的名称部分,并将lastModified设置为所引用文件的时间戳,如果条目引用的是剪贴板部分,则设置为 0。 -
将 new-file-data 的内部
File对象设置为 file-info。 -
将 new-file-data 添加到 clipboard-event-data 的
items中,并令 itemNumber 为该条目在DataTransferItemList中的索引。
-
-
更新引用本地文件或剪贴板部分的 DOM 属性,使其包含字符串 'cid:' 后跟 itemNumber。
-
-
序列化处理后的 DOM 并使用生成的 HTML 代码更新 new-data。
-
将 new-data 添加到 clipboard-event-data 的
items中。