Web 分享 API

W3C 推荐

关于此文档的更多细节
此版本
https://w3org.cn/TR/2023/REC-web-share-20230530/
最新发布版本
https://w3org.cn/TR/web-share/
最新编辑草案
https://w3c.github.io/web-share/
历史
https://w3org.cn/standards/history/web-share
提交历史
测试套件
https://wpt.live/web-share/
实现报告
https://w3c.github.io/web-share/imp-report/
编辑
Matt Giuca (Google 公司)
Eric Willigers (Google 公司)
Marcos Cáceres (Apple Inc.)
反馈
GitHub w3c/web-share (拉取请求, 新问题, 打开的问题)
勘误表
存在勘误表.
浏览器支持
caniuse.com

另请参见 译本


摘要

本规范定义了一套用于将文本、链接及其他内容分享至用户自行选择的任意目标的 API。

可用的分享目标在此未作指定;它们由用户代理提供。例如,可能是应用、网站或联系人。

本文档状态

本节描述本文档在发布时的状态。当前的 W3C 出版物列表以及本技术报告的最新修订版可在 https://w3org.cn/TR/ 的 W3C 技术报告索引 中找到。

本文件由 Web Applications Working Group 作为推荐路径的 Recommendation(推荐)发布。

W3C 建议广泛部署本规范作为 Web 标准。

W3C Recommendation(推荐)是一份在广泛共识达成后,由 W3C 及其成员认可,并获得工作组成员对实现的免版税许可承诺的规范。对本 Recommendation 的未来更新可能会加入新功能

本文件由遵循 W3C 专利政策 的小组制作。W3C 维护一份 公开的专利披露列表,列出与小组交付物相关的专利披露;该页面还包括披露专利的指南。任何实际了解其认为包含 必要权利要求 的专利的个人,必须依据 W3C 专利政策第 6 节进行披露。

本文档受 2021 年 11 月 2 日的 W3C 流程文档管辖。

实现状态

本节是非规范性的。

内容分享的能力往往依赖底层操作系统提供“分享”功能以及操作系统的 UI 约定。例如,有的系统呈现“分享表单”,而有的依赖弹出菜单。由于上述依赖,实施者正持续努力让 Web Share API 在所有操作系统上可用。该工作在实现报告中表现为失败记录,因为报告只在有限的操作系统集合上运行测试。然而,工作组对 Web Share API 随着时间推移将在所有操作系统上更广泛可用持乐观态度,且它已在主流操作系统和多种设备上得到广泛支持。

1. 使用示例

本节是非规范性的。

1.2 共享文件

此示例展示了如何共享文件。请注意,files 成员是一个数组,允许一次共享多个文件。

示例 2: 共享文件
shareButton.addEventListener("click", async () => {
  const file = new File(data, "some.png", { type: "image/png" });
  try {
    await navigator.share({
      title: "Example File",
      files: [file]
    });
  } catch (err) {
    console.error("Share failed:", err.message);
  }
});

1.3 验证共享

调用 canShare() 方法并传入 ShareData 字典会验证共享数据。与 share() 不同,它可以在没有瞬时激活的情况下调用。

const file = new File([], "some.png", { type: "image/png" });

// Check if files are supported
if (navigates.canShare({files: [file]})) {
  // Sharing a png file would probably be ok...
}

// Check if a URL is ok to share...
if (navigates.canShare({ url: someURL })) {
  // The URL is valid and can probably be shared...
}

1.4 检查成员是否受支持

由于 WebIDL 字典的工作方式,传递给 share() 的、用户代理不认识的成员会被忽略。当共享多个成员而用户代理不支持其中某个成员时,这可能会成为问题。若要确保传入的每个成员都被用户代理支持,可将它们单独传给 canShare() 检查是否受支持。

示例 4: 使共享具备未来兼容性
const data = {
  title: "Example Page",
  url: "https://example.com",
  text: "This is a text to share",
  someFutureThing: "some future thing",
};
const allSupported = Object.entries(data).every(([key, value]) => {
  return navigator.canShare({ [key]: value });
});
if (allSupported) {
  await navigator.share(data);
}

或者,您可以调整应用的 UI,使其不显示不受支持成员的 UI 组件。

示例 5: 过滤掉不受支持的成员
const data = {
  title: "Example Page",
  url: "https://example.com",
  text: "This is a text to share",
  someFutureThing: "some future thing",
};

// Things that are not supported...
const unsupported = Object.entries(data).filter(([key, value]) => {
  return !navigator.canShare({ [key]: value });
});

1.5 在第三方上下文中启用 API

默认允许列表的默认允许列表'self',这使得 Web Share API 默认仅在第一方上下文中可用。

第三方可以通过 iframeallow 属性来使用此 API。

或者,也可以在第一方上下文中通过指定 HTTP 响应头来禁用此 API。

有关详细信息以及如何对每个来源单独控制权限策略,请参阅 Permissions Policy 规范。

2. API 定义

2.1 Navigator 接口的扩展

WebIDLpartial interface Navigator {
  [SecureContext] Promise<undefined> share(optional ShareData data = {});
  [SecureContext] boolean canShare(optional ShareData data = {});
};

2.1.1 内部槽位

此 API 向 Navigator 接口添加以下内部槽位。

Promise? [[sharePromise]]
this.[[sharePromise]] 为一个 Promise,表示用户当前希望将某些数据分享至共享目标的意图。它初始为 null

2.1.2 share() 方法

当调用 share() 且传入参数 data 时,按照下列步骤执行,同时考虑以下安全影响。

Web Share 使网站能够将数据发送到一个共享目标,该目标可以是本地应用。虽然此能力并非 Web Share 独有,但它带来多种潜在安全风险,风险程度取决于底层平台。

传递给 share() 的数据可能被用于利用共享目标中的缓冲区溢出或其他远程代码执行漏洞。没有通用方法可以防范此类问题,但实现者应意识到其可能性(尤其是在共享文件时)。

共享目标 若对共享的 URL 进行解析并转发,可能不经意间泄露本应保密的信息。如果共享的内容仅在该应用、运行所在主机或其网络位置可访问,则可能导致意外的信息泄漏。

恶意站点可能利用会泄露信息的共享目标,提供最终指向本地资源的 URL,包括但不限于 “file:” URL 或本应不可访问的本地服务。即使此 API 将共享的 URL 限制在一组可共享方案之内,使用重定向到其他 URL 或对这些 URL 所在主机的 DNS 记录进行篡改,也可能导致应用获取内容。

为避免被用于上述攻击,共享目标可以仅消费 URL、检索内容并在不共享的情况下处理信息。例如,照片编辑应用可能检索“共享”给它的图像。共享目标也可以只共享 URL 而不获取任何被引用的内容。

为了提供预览或进行内容共享而获取内容的共享目标存在信息泄漏风险。用户预览并授权的内容可能安全转发,但用户并不总能判断何时信息应当保密,因此转发任何内容都存在风险。特别是title 可能被攻击者用来欺骗用户误解内容的性质(另见5. 可访问性考虑)。

与任何使用DOMException 的代码一样,实现者需谨慎考虑在share() 被拒绝时错误信息中透露了哪些信息。即使仅区分“没有可用的共享目标”与“用户取消”,也可能泄露用户设备上已安装的共享目标信息。

  1. globalthis相关全局对象
  2. documentglobal关联的 Document
  3. 如果 document 不是完全激活,则返回一个被拒绝的 Promise,其原因为 InvalidStateError DOMException
  4. 如果 document 不被允许使用 "web-share",则返回一个被拒绝的 Promise,其原因为 NotAllowedError DOMException
  5. 如果 this.[[sharePromise]] 不是 null,则返回一个被拒绝的 Promise,其原因为 InvalidStateError DOMException
  6. 如果 global 没有瞬时激活,则返回一个被拒绝的 Promise,其原因为 NotAllowedError DOMException
  7. 消费 global 的用户激活。
  8. basethis相关设置对象API 基础 URL
  9. 如果使用 database 进行验证共享数据返回 false,则返回一个被拒绝的 Promise,其原因为 TypeError
  10. 如果 dataurl 成员存在
    1. url 为对 dataurl 使用 base 进行URL 解析器后的结果。
    2. 断言urlURL
    3. data 设为其自身的副本,并将其url 成员设为对 url 使用URL 序列化器后的结果。
  11. 如果因安全考虑而阻止某种文件类型,返回一个被拒绝的 Promise,其原因为 “NotAllowedErrorDOMException
  12. this.[[sharePromise]] 设为一个新 Promise
  13. 返回 this.[[sharePromise]]并行执行。
    1. 如果没有可用的共享目标,则在用户交互任务源上使用 global 排入全局任务,以
      1. 拒绝 this.[[sharePromise]],并使用 “AbortErrorDOMException
      2. this.[[sharePromise]] 设为 null
      3. 终止此算法。
    2. 向用户展示一个选择界面,列出一个或多个共享目标以及中止操作的选项。此 UI 界面充当安全确认,确保网站无法静默将数据发送至本地应用。用户代理 应当 显示中间 UI,以便用户核实共享内容(如果操作系统层面的 UI 未提供此功能)。
    3. 等待用户的选择。
    4. 如果用户选择中止共享操作,则在用户交互任务源上使用 global 排入全局任务,以
      1. 拒绝 this.[[sharePromise]],并使用 “AbortErrorDOMException
      2. this.[[sharePromise]] 设为 null
      3. 终止此算法。
    5. 激活已选中的共享目标data 转换为适合目标摄取的格式,并将转换后的数据传输给目标。
    6. 如果启动目标或传输数据时出现错误,则在用户交互任务源上使用 global 排入全局任务,以
      1. 拒绝 this.[[sharePromise]],并使用 “DataErrorDOMException
      2. this.[[sharePromise]] 设为 null
      3. 终止此算法。
    7. 一旦数据已成功传输至共享目标,或已成功传输至操作系统(若无法确认已传输至共享目标),则在用户交互任务源上使用 global 排入全局任务,以
      1. 解析 this.[[sharePromise]]undefined
      2. this.[[sharePromise]] 设为 null

2.1.3 canShare(data) 方法

canShare() 方法以 ShareData data 为参数被调用时,执行以下步骤

  1. documentthis相关全局对象关联的 Document
  2. 如果 document 不是完全激活,返回 false
  3. 如果 document 不被允许使用 "web-share",返回 false
  4. 返回使用 datathis相关设置对象API 基础 URL进行验证共享数据的结果。
注意: canShare() 并非未来兼容

2.1.4 验证共享数据

一个可共享方案指以下任意URL 方案

使用 database 验证共享数据,执行以下步骤

  1. 如果 data 的成员 titletexturlfiles 均不存在,返回 false
  2. titleTextOrUrltrue,若 titletexturl 任一存在。
  3. 如果 datafiles 成员存在
    1. 如果 titleTextOrUrlfalsedatafiles 成员为空,返回 false

      这会导致 { files: [] } 字典被视作空字典。然而,传入如 {text: "text", files: []} 的字典是可以的,因为 files 将被忽略。

    2. 如果实现不支持文件共享,返回 false
    3. 如果用户代理判断共享 files 中的任意文件可能导致敌对共享(例如因内容、大小或其他特性判定文件为恶意),返回 false
  4. 如果 dataurl 成员存在
    1. url 为对 dataurl 成员使用 base(且不覆写编码)进行URL 解析器后的结果。
    2. 如果 url 为失败,返回 false
    3. 如果 url方案本地方案,或 filejavascriptwswss,返回 false
    4. 如果 url方案不是可共享方案,返回 false
  5. 返回 true。

2.2 ShareData 字典

WebIDLdictionary ShareData {
  sequence<File> files;
  USVString title;
  USVString text;
  USVString url;
};

ShareData 字典由若干可选成员组成

files 成员
要共享的文件。
text 成员
任意构成共享消息主体的文本。
title 成员
被共享文档的标题,目标可能会忽略。
url 成员
指向被共享资源的 URL 字符串。

3. 共享目标

一个 share target 是用户代理将共享数据发送到的目标的抽象概念。构成 share target 的内容由用户代理自行决定。

共享目标可能无法直接接受一个 ShareData(因为它并未针对该 API 编写)。然而,它 MUST 必须具备接收与 ShareData 中公开的某些或全部概念相匹配的数据的能力。要 将数据转换为适合摄入目标的格式,用户代理 SHOULDShareData 的成员映射到目标中的等价概念。必要时它 MAY 丢弃或合并成员。负载中每个成员的含义由共享目标自行决定。

ShareData 映射到共享目标(或操作系统)的原生格式可能比较棘手,因为某些平台并不存在等价的成员集合。例如,如果目标拥有 “text” 成员但没有 “URL” 成员(如 Android 所示),一种解决方案是将 texturl 成员拼接起来,并将结果放入目标的 “text” 成员中。

每个 share target MAY 根据提供给 share() 方法的 ShareData 载荷有条件地变为可用。

3.1 共享目标示例

本节是非规范性的。

共享目标列表可以从多种来源填充,这取决于用户代理和宿主操作系统。例如:

已尝试对网站进行标准化注册,以便在上述最终使用场景中接收共享数据;详情请参阅 Web Share Target

在某些情况下,宿主操作系统会提供类似于 Web Share 的共享或 intent 系统。此时,用户代理可以简单地将共享数据转发给操作系统,而无需直接与本地应用程序交互。

4. 权限策略集成

本规范定义了一个由字符串 "web-share" 标识的受策略控制的权限。其默认允许列表'self',这意味着第三方上下文默认 不被允许使用 此 API。

用户代理 OPTIONAL 支持 Permissions PolicyPermissions-Policy HTTP 头部。

开发者可以使用 Permissions Policy 规范提供的手段,来控制第三方上下文何时以及是否 被允许使用 此 API。

: 权限策略实现状态

5. 可访问性考虑

本节是非规范性的。

当本规范用于在用户界面中呈现信息时,实现者应遵循平台的操作系统层面的可访问性指南。此外,正如在 share() 方法的安全性考虑部分所述,分享 UI 必须以可访问的方式呈现,同时兼顾平台对用户界面的安全指南。关键的考虑因素包括:

上述要点共同帮助视力、运动或认知障碍的用户更好地理解网页所分享内容的本质。

6. 隐私考虑

7. 一致性

除了标记为非规范性的章节外,本规范中的所有创作指南、图表、示例和注释均为非规范性内容。本规范中的其他所有内容均为规范性内容。

本文档中的关键词 MAYMUSTOPTIONALSHOULD 应按照 BCP 14 [RFC2119] [RFC8174] 的定义进行解释,仅当且仅当它们全部以大写形式出现时才生效,如本页所示。

8. IDL 索引

WebIDLpartial interface Navigator {
  [SecureContext] Promise<undefined> share(optional ShareData data = {});
  [SecureContext] boolean canShare(optional ShareData data = {});
};

dictionary ShareData {
  sequence<File> files;
  USVString title;
  USVString text;
  USVString url;
};

9. 变更日志

本节是非规范性的。

以下规范性更改在 Proposed Recommendation 阶段完成。完整的更改列表请参阅 提交日志

以下规范性更改自从作为 First Public Working Draft 发布后,直至 Candidate Recommendation。完整的更改列表请参阅 提交日志

A. 致谢

编辑们想要感谢以下 W3C 组织对本规范提供的宝贵反馈,这极大地改进了本规范:Accessible Platform Architectures Working GroupInternationalization Working GroupPrivacy Interest Group,以及 Technical Architecture Group

感谢 Web Intents 团队,他们为 Web 应用的互操作性用例奠定了基础。特别是 Paul Kinlan,他为 Web Share 做了大量早期倡导工作。

B. 参考资料

B.1 规范性参考资料

[fetch]
获取 (Fetch) 标准. Anne van Kesteren. WHATWG. 活标准. URL: https://fetch.spec.whatwg.org/
[fileapi]
File API。Marijn Kruisselbrink。W3C。2023 年 2 月 6 日。W3C 工作草案。URL:https://w3org.cn/TR/FileAPI/
[html]
HTML Standard. Anne van Kesteren; Domenic Denicola; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.whatwg.cn/multipage/
[PERMISSIONS-POLICY]
Permissions Policy。Ian Clelland。W3C。2023 年 3 月 22 日。W3C 工作草案。URL:https://w3org.cn/TR/permissions-policy-1/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. 1997年3月. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
RFC 2119 关键词中大小写的歧义. B. Leiba. IETF. 2017年5月. 最佳当前实践. URL: https://www.rfc-editor.org/rfc/rfc8174
[url]
URL 标准. Anne van Kesteren. WHATWG. 活标准. URL: https://url.spec.whatwg.org/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/

B.2 参考性参考资料

[编码]
编码标准. Anne van Kesteren. WHATWG. 活标准. URL: https://encoding.spec.whatwg.org/