本规范定义了一个 API,允许网站将自身声明为 Web 共享目标,从而可以接收来自 [[[Web-Share]]] 或系统事件(例如来自 原生应用的共享)的共享内容。
这是一种类似于 {{NavigatorContentUtils/registerProtocolHandler()}} 的机制,因为它通过向用户代理注册网站来工作, 以便稍后由另一个网站或原生应用通过用户代理(可能由用户酌情决定)调用。 不同之处在于,{{NavigatorContentUtils/registerProtocolHandler()}} 通过编程 API 注册处理器, 而 Web 共享目标是在 [[[appmanifest]]] 中声明的,并在用户代理或用户选择的时间注册。
这是 Web Share Target 规范的早期草案。
为了实现此 API,用户代理必须支持 [[[appmanifest]]]。本规范还复用了 [[[Web-Share]]] 规范中的一些定义。 但是,对 [[[Web-Share]]] 的支持是可选的。
要将站点注册为共享目标,需要向 [[[appmanifest]]] 添加一个 [=manifest/share_target=] 条目, 如下所示:
{
"name": "Includinator",
"share_target": {
"action": "share.html",
"params": {
"title": "name",
"text": "description",
"url": "link"
}
}
}
[=ShareTarget/params=] 键对应于 [[[Web-Share]]] 中 {{ShareData}} 的键名, 而值是共享目标启动时将用作查询参数的任意名称。
发生共享时,如果用户选择了此共享目标,用户代理会在 `action` URL 处打开一个新的浏览上下文, 查询参数值包含共享的数据,就像 HTML 表单提交一样。
在本示例中,我们假设清单位于 `https://example.org/includinator/manifest.webmanifest`。
<html>
<link rel="manifest" href="manifest.webmanifest">
<script>
window.addEventListener('load', () => {
const parsedUrl = new URL(window.location);
const { searchParams } = parsedUrl;
console.log("Title shared:", searchParams.get('name'));
console.log("Text shared:", searchParams.get('description'));
console.log("URL shared:", searchParams.get('link'));
});
</script>
如果传入的共享包含标题 "My News" 和 URL `http://example.com/news`,用户代理将打开一个新窗口或标签页并导航到:
https://example.org/includinator/share.html?name=My+News&link=http%3A%2F%2Fexample.com%2Fnews
由于使用了 [=`application\/x-www-form-urlencoded`=] 编码,U+0020 (SPACE) 字符会被编码为 "`+`", 而不是可能预期的 "`%20`"。处理器必须注意将 U+002B (+) 字符解码为 U+0020 (SPACE), 一些 URL 解码库,包括 ECMAScript 的 `decodeURIComponent` 函数,可能不会自动这样做。
查询参数会用正在共享的 {{ShareData}} 中的信息填充。如果 {{ShareData}} 不包含某个给定成员的信息, 则省略该查询参数。
共享目标可能只关注 {{ShareData}} 成员的一个子集。本示例还展示了一个以 `POST` 请求接收数据的共享目标; 如果请求会造成即时副作用,就应当采用这种方式。
{
"name": "Bookmark",
"share_target": {
"action": "/bookmark",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"url": "link"
}
}
}
共享的信息可以由 [=service worker=] 读取,而不是通过网络发送到服务器。
self.addEventListener("fetch", (event) => {
if (event.request.method !== "POST") {
event.respondWith(fetch(event.request));
return;
}
const formDataPromise = event.request.formData();
event.respondWith(
formDataPromise.then((formData) => {
const link = formData.get("link") || "";
saveBookmark(link);
return new Response(`Bookmark saved: ${link}`);
})
);
});
处理器如何处理共享数据由处理器自行决定,并且通常取决于应用的类型。下面是一些建议:
由于 [=manifest=] 是 JSON,本规范依赖于 [[JSON]] 规范中定义的类型:即 object 和 string。
将以下步骤添加到 [=processing extension-point of web manifest=]:
清单的 share_target
成员是一个 [=object=]。当存在时,它声明此应用为一个 Web 共享目标,
并描述应用如何接收共享数据。
Web 共享目标 是一个具有有效 [=manifest=] 且包含 [=manifest/share_target=] 成员的网站。
Web 共享目标是一种 share target(还可以有其他类型,例如某些系统应用)。
给定 [=object=] |json:JSON| 和 [=ordered map=] |manifest:ordered map|, 要处理 `share_target` 成员:
ShareTarget [=object=] 可以具有以下成员:
action 成员
method 成员
enctype 成员
params 成员
ShareTargetParams [=object=] 可以具有以下成员:
title
成员
text
成员
url 成员
Web 共享目标如何以及何时被“注册”,由用户代理和/或最终用户自行决定。事实上, “注册”是一个用户代理特有的概念,此处并未正式定义;用户代理完全不要求“注册”Web 共享目标; 它们只被要求提供某种机制,将共享数据传递给最终用户所选择的 Web 共享目标。 即使 Web 共享目标不是 [=installed web application|installed=],用户代理也可以认为它已“注册”。
用户代理可以在用户访问站点时自动注册所有 Web 共享目标,但建议采用更多酌情判断, 以避免让大量目标选项压倒用户。
用户代理可以采用的注册策略示例包括:
在向最终用户呈现 Web 共享目标 列表时,用户代理可以使用已预先索引清单的在线服务, 从而向用户显示他们从未访问或明确注册过的目标。
当最终用户正在共享一些面向通用应用的数据,并指明特定 Web 共享目标作为该数据的接收者时, Web 共享目标就会被调用。
数据来自何处,或最终用户如何指明 Web 共享目标作为接收者,并未被指定。不过,一个可能的来源是 在同一用户代理中调用 {{Navigator}} 的 {{Navigator/share()}} 方法。
Web 共享目标调用的其他可能来源示例包括:
当 Web 共享目标被调用时,数据可以采用未指定的格式。 用户代理必须首先将数据转换为 {{ShareData}} 对象(如果它还不是), 方法是从宿主系统中的等价概念映射到 `ShareData` 的成员。如果来源是对 {{Navigator/share()}} 的调用,用户代理应当原样使用 {{ShareData}} 实参 (但这并不总是可能,因为它可能必须以有损方式在某种其他格式中往返)。 用户代理可以采用启发式方法,尽可能好地将数据映射到 `ShareData` 字段。
例如,宿主共享系统可能没有专门的 URL 字段,而是采用一种约定: 纯文本和 URL 有时都会在 "text" 字段中传输。Android 就是这种情况。 用户代理可以检查 "text" 字段的全部或部分是否为 [=valid URL string=],如果是,则将该部分从 "text" 字段移动到 {{ShareData}} 的 {{ShareData/url}} 成员。
当具有 [=ordered map=] |manifest| 的 Web 共享目标 使用 {{ShareData}} |data| 被调用时,运行以下步骤:
此算法假设 |manifest| 已经对其运行过 [=process the `share_target` member=] 算法,并且之后仍具有 [=manifest/share_target=]。
本规范没有已知的无障碍注意事项。
感谢 [[[WEBINTENTS]]] 团队,他们为 Web 应用互操作用例奠定了基础。特别感谢 Paul Kinlan, 他为 Web Share 和 Web Share Target 做了大量早期倡导工作。
感谢 Connie Pyromallis,她撰写了本规范的早期草案,并帮助设计和原型化此 API。
感谢 Alex Russell 和 David Baron 对本规范早期草案的反馈。