内容索引

编辑草案,

此版本:
https://wicg.github.io/content-index/spec/
问题跟踪:
GitHub
规范内联
编辑:
(Google)
(Google)

摘要

一种用于让网站向浏览器注册其支持离线的内容的 API。

本文档的状态

本规范由 Web Platform Incubator Community Group 发布。 它不是 W3C 标准,也不在 W3C 标准轨道上。 请注意,根据 W3C Community Contributor License Agreement(CLA),存在有限的退出选择,并且还适用其他条件。 了解更多关于 W3C Community and Business Groups 的信息。

1. 引言

目前,用户并不容易发现高质量的离线可用 Web 内容。用户必须知道哪些网站可离线工作, 或者需要安装 PWA,才能在离线时浏览内容。这并不是良好的用户体验,因为没有入口点可用于发现 可用内容。为了解决这一点,本规范涵盖了一种新的 API,允许开发者将其特定内容告知浏览器。

内容索引允许网站向浏览器注册其离线可用内容。然后浏览器可以改进网站的离线能力, 并向用户提供可在离线时浏览的内容。此数据也可用于改进设备端搜索和增强浏览历史。

使用该 API 可帮助用户更轻松地发现以下示例用例中的内容:

1.1. 示例

在 service worker 中注册一篇离线新闻文章。
function deleteArticleResources(id) {
  return Promise.all([
    caches.open('offline-articles')
        .then(articlesCache => articlesCache.delete(`/article/${id}`)),
    // 如果该函数是因 `contentdelete` 事件而被调用的,
    // 则这是一个 no-op。
    self.registration.index.delete(id),
  ]);
}

self.addEventListener('activate', event => {
  // 当 service worker 被激活时,移除旧内容。
  event.waitUntil(async function() {
    const descriptions = await self.registration.index.getAll();
    const oldDescriptions =
        descriptions.filter(description => shouldRemoveOldArticle(description));
    await Promise.all(
        oldDescriptions.map(description => deleteArticleResources(description.id)));
  }());
});

self.addEventListener('push', event => {
  const payload = event.data.json();

  // 获取并存储文章,然后注册它。
  event.waitUntil(async function() {
    const articlesCache = await caches.open('offline-articles');
    await articlesCache.add(`/article/${payload.id}`);
    await self.registration.index.add({
      id: payload.id,
      title: payload.title,
      description: payload.description,
      category: 'article',
      icons: payload.icons,
      url: `/article/${payload.id}`,
    });

    // 如果紧急,则显示通知。
  }());
});

self.addEventListener('contentdelete', event => {
  // 在用户删除后清除底层内容。
  event.waitUntil(deleteArticleResources(event.id));
});    

在上面的示例中,shouldRemoveOldArticle 是开发者定义的函数。

2. 隐私考量

触发 contentdelete 事件可能会在用户离开页面后泄露用户的 IP 地址。利用这一点 可用于跟踪位置历史。用户代理应通过限制事件的持续时间来限制跟踪。

在触发 contentdelete 事件时,用户代理应阻止网站添加新内容。 这可防止垃圾网站重新添加相同内容,并防止用户看到其刚刚删除的内容。

显示所有已注册内容可能会导致 恶意网站用其内容向用户发送垃圾信息,以最大化曝光。强烈鼓励用户代理不要浮现所有内容, 而是基于一组用户代理定义的信号选择适当的内容来显示, 这些信号旨在改善用户体验。

3. 基础设施

3.1. 对 service worker registration 的扩展

一个 service worker registration 还具有:

3.2. 内容索引条目

一个内容索引 条目由以下部分组成:

3.2.1. 显示

用户代理可在任何时候显示一个 内容索引 条目(entry),只要 entry 存在于 一个 service worker registration 的内容索引条目中。

注: 用户代理应限制浮现的内容,以避免向用户显示过多条目。

要显示一个 内容索引 条目(entry),用户代理必须呈现一个遵循以下规则的用户界面:

3.2.2. 取消显示

要取消显示一个 内容索引条目(entry),用户代理必须移除 与在 entry 上运行显示相关联的所有 UI。

4. 算法

4.1. 删除一个内容 索引条目

要为 entry(一个 内容索引条目)删除一个 内容索引条目,运行以下步骤:
  1. 令 id 为 entry 的描述的 id。

  2. 令 contentIndexEntries 为 entry 的 service worker registration 的内容索引条目。

  3. 将以下步骤入队到 entry 的 service worker registration 的条目编辑队列:

    1. 取消显示 entry。

    2. 移除 contentIndexEntries[id]。

    3. 为 entry 触发一个 content delete 事件。

4.2. 激活一个 内容索引条目

要为 entry(一个 内容索引条目)激活一个 内容索引条目,运行以下步骤:
  1. 令 activeWorker 为 entry 的 service worker registration 的active worker。

  2. 如果 activeWorker 为 null,则中止这些步骤。

  3. 令 newContext 为一个新的顶级浏览上下文。

  4. 使用用户交互任务源,将运行以下步骤的任务排队到 newContext 的 Window 对象的环境设置对象的负责事件循环:

    1. HandleNavigate:以exceptions enabled 和 replacement enabled,将 newContext 导航到 entry 的启动 URL。

    2. 如果标记为 HandleNavigate 的步骤中调用的算法步骤抛出异常,则中止 这些步骤。

    3. 令 frameType 为 "`top-level`"。

    4. 令 visibilityState 为 newContext 的活动文档的 visibilityState 属性值。

    5. 令 focusState 为以 newContext 的活动文档作为参数运行has focus steps的结果。

    6. 令 ancestorOriginsList 为 newContext 的活动文档的相关全局对象的 Location 对象的祖先源列表的 关联列表。

    7. 令 serviceWorkerEventLoop 为 activeWorker 的全局对象的事件循环。

    8. 使用 DOM manipulation task source,将运行以下步骤的任务排队到 serviceWorkerEventLoop:

      1. 如果 newContext 的 Window 对象的环境设置对象的 创建 URL的源与 activeWorker 的源不是相同的,则中止这些步骤。

      2. 以 newContext 的 Window 对象的环境设置对象、 frameType、visibilityState、focusState 和 ancestorOriginsList 作为参数运行 Create Window Client。

创建新的 Browsing Context 是这里正确的做法吗? (issue)

4.3. 触发一个 content delete 事件

要为 entry(一个 内容索引条目)触发一个 content delete 事件,在 entry 的 service worker registration 上,使用 ContentIndexEvent 触发一个功能性事件,名称为 "contentdelete",并带有以下属性:
id

entry 的描述的 id。

5. API

5.1. 对 ServiceWorkerGlobalScope 的扩展

partial interface ServiceWorkerGlobalScope {
  attribute EventHandler oncontentdelete;
};

5.1.1. 事件

以下是必须由所有实现 ServiceWorker 接口的对象作为事件处理器 IDL 属性支持的事件处理器(及其对应的事件处理器事件类型):

事件处理器事件类型 事件处理器 接口
contentdelete oncontentdelete ContentIndexEvent

5.2. 对 ServiceWorkerRegistration 的扩展

partial interface ServiceWorkerRegistration {
  [SameObject] readonly attribute ContentIndex index;
};

一个 ServiceWorkerRegistration 具有一个内容索引(一个 ContentIndex), 初始为一个新的 ContentIndex, 其service worker registration 是上下文 对象的service worker registration。

index 属性的 getter 必须返回上下文对象的内容索引。

5.3. ContentIndex

ContentIndex

仅在一个当前引擎中。

Firefox无Safari无Chrome无
Opera无Edge无
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android84+Android WebView84+Samsung Internet无Opera Mobile60+
enum ContentCategory {
  "",
  "homepage",
  "article",
  "video",
  "audio",
};

dictionary ContentDescription {
  required DOMString id;
  required DOMString title;
  required DOMString description;
  ContentCategory category = "";
  sequence<ImageResource> icons = [];
  required USVString url;
};

[Exposed=(Window,Worker)]
interface ContentIndex {
  Promise<undefined> add(ContentDescription description);
  Promise<undefined> delete(DOMString id);
  Promise<sequence<ContentDescription>> getAll();
};

ContentIndex/getAll

仅在一个当前引擎中。

Firefox无Safari无Chrome无
Opera无Edge无
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android84+Android WebView84+Samsung Internet无Opera Mobile60+

一个 ContentIndex 具有一个service worker registration(一个 service worker registration)。

5.3.1. add()

ContentIndex/add

仅在一个当前引擎中。

Firefox无Safari无Chrome无
Opera无Edge无
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android84+Android WebView84+Samsung Internet无Opera Mobile60+
add(description) 方法在被调用时,必须 返回一个新的 promise promise,并并行运行 这些步骤:
  1. 令 registration 为上下文 对象的service worker registration。

  2. 如果 registration 的active worker 为 null,则用 TypeError 拒绝 promise 并中止这些步骤。

  3. 如果 description 的 id、 title、 description 或 url 中的任何一个是空字符串,则用 TypeError 拒绝 promise 并中止这些步骤。

  4. 令 launchURL 为用上下文对象的相关设置对象的API 基准 URL 对 description 的 url 进行解析的结果。

    注: 之后可能会引入一个具有更窄作用域的新的 service worker registration。

  5. 令 matchedRegistration 为以 launchURL 作为参数运行 Match Service Worker Registration 算法的结果。

  6. 如果 matchedRegistration 不等于 registration,则用 TypeError 拒绝 promise 并中止 这些步骤。

  7. 如果 registration 的active worker 的扩展事件集合不包含一个 FetchEvent, 则用 TypeError 拒绝 promise 并中止这些步骤。

  8. 令 icons 为空列表。

  9. 可选地,用户代理可从 description 的 icons 中选择要使用的图标。在这种情况下,在成功解析后,对 description 的 icons 的选定图标中的图像资源(resource)逐个 运行以下步骤:

    1. 令 response 为使用具有以下属性的新请求执行一次 fetch 并等待其结果:

      URL

      resource 的 src。

      客户端

      上下文对象的相关设置对象。

      Keepalive 标志

      设置。

      目的地

      "`image`"。

      模式

      "`no-cors`"。

      凭据模式

      "`include`"。

    2. 如果 response 是一个网络错误,则用 TypeError 拒绝 promise 并中止这些步骤。

    3. 如果 response 不能被解码为图像,则用 TypeError 拒绝 promise 并中止这些 步骤。

    4. 将 response 附加到 icons。

  10. 令 entry 为一个新的内容索引条目,其具有:

    描述

    description。

    启动 URL

    launchURL

    service worker registration

    registration。

    图标

    icons

  11. 令 id 为 description 的 id。

  12. 令 contentIndexEntries 为 registration 的内容索引 条目。

  13. 将以下步骤入队到 registration 的条目编辑队列:

    1. 将 contentIndexEntries[id] 设置为 entry。

    2. 可选地,用户代理可显示 entry。

    3. 用 undefined 兑现 promise。

注: 添加具有已有 ID 的描述会覆盖先前的值。

5.3.2. delete()

ContentIndex/delete

仅在一个当前引擎中。

Firefox无Safari无Chrome无
Opera无Edge无
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android84+Android WebView84+Samsung Internet无Opera Mobile60+
delete(id) 方法在被调用时,必须 返回一个新的 promise promise,并并行运行这些 步骤:
  1. 令 registration 为上下文 对象的service worker registration。

  2. 令 contentIndexEntries 为 registration 的内容索引 条目。

  3. 将以下步骤入队到 registration 的条目编辑队列:

    1. 取消显示 contentIndexEntries[id]。

    2. 移除 contentIndexEntries[id]。

    3. 用 undefined 兑现 promise。

5.3.3. getAll()

getAll() 方法在被调用时,必须返回一个新的 promise promise,并并行运行这些 步骤:
  1. 令 registration 为上下文 对象的service worker registration。

  2. 令 contentIndexEntries 为 registration 的内容索引 条目。

  3. 令 descriptions 为空列表。

  4. 将以下步骤入队到 registration 的条目编辑队列:

    1. 对 contentIndexEntries 的每个 id → entry 执行:

      1. 将 entry 的描述附加到 descriptions。

    2. 用 descriptions 兑现 promise。

5.4. ContentIndexEvent

ContentIndexEvent

仅在一个当前引擎中。

Firefox无Safari无Chrome无
Opera无Edge无
Edge (Legacy)无IE无
Firefox for Android无iOS Safari无Chrome for Android84+Android WebView84+Samsung Internet无Opera Mobile60+
dictionary ContentIndexEventInit : ExtendableEventInit {
  required DOMString id;
};

[Exposed=ServiceWorker]
interface ContentIndexEvent : ExtendableEvent {
  constructor(DOMString type, ContentIndexEventInit init);
  readonly attribute DOMString id;
};

一致性

文档 约定

一致性要求通过描述性断言 和 RFC 2119 术语的组合来表达。 规范性部分中的关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、 “MAY” 和 “OPTIONAL” 应按 RFC 2119 中所述进行解释。 但是,为了可读性, 这些词在本规范中并不全部以大写字母出现。

本规范的所有文本均为规范性内容, 但明确标记为非规范性的章节、示例和注释除外。 [RFC2119]

本规范中的示例会以 “for example” 一词引入, 或通过 class="example" 与规范性文本分隔开来, 如下所示:

这是一个信息性示例的例子。

信息性注释以 “Note” 一词开头, 并通过 class="note" 与规范性文本分隔开来, 如下所示:

注:这是一个信息性注释。

一致性 算法

作为算法一部分以祈使语气表述的要求 (例如 "strip any leading space characters" 或 "return false and abort these steps") 应按引入该算法时所使用的关键词 ("must"、"should"、"may" 等) 的含义来解释。

以算法或具体步骤形式表述的一致性要求 可以以任何方式实现, 只要最终结果等价即可。 特别是,本规范中定义的算法 旨在易于理解, 并不旨在具备性能优势。 鼓励实现者进行优化。

索引

由本 规范定义的术语

通过 引用定义的术语

参考文献

规范性参考文献

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[IMAGE-RESOURCE]
Aaron Gustafson; Rayan Kanso; Marcos Caceres. Image Resource. 2021年3月29日。WD。URL: https://www.w3.org/TR/image-resource/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PAGE-VISIBILITY]
Jatinder Mann; Arvind Jain. Page Visibility (Second Edition). 2013年10月29日。REC。URL: https://www.w3.org/TR/page-visibility/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997年3月。Best Current Practice。URL: https://tools.ietf.org/html/rfc2119
[SERVICE-WORKERS-1]
Alex Russell; et al. Service Workers 1. 2019年11月19日。 CR。URL: https://www.w3.org/TR/service-workers-1/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WebIDL]
Boris Zbarsky. Web IDL. 2016年12月15日。ED。URL: https://heycam.github.io/webidl/

IDL 索引

partial interface ServiceWorkerGlobalScope {
  attribute EventHandler oncontentdelete;
};

partial interface ServiceWorkerRegistration {
  [SameObject] readonly attribute ContentIndex index;
};

enum ContentCategory {
  "",
  "homepage",
  "article",
  "video",
  "audio",
};

dictionary ContentDescription {
  required DOMString id;
  required DOMString title;
  required DOMString description;
  ContentCategory category = "";
  sequence<ImageResource> icons = [];
  required USVString url;
};

[Exposed=(Window,Worker)]
interface ContentIndex {
  Promise<undefined> add(ContentDescription description);
  Promise<undefined> delete(DOMString id);
  Promise<sequence<ContentDescription>> getAll();
};

dictionary ContentIndexEventInit : ExtendableEventInit {
  required DOMString id;
};

[Exposed=ServiceWorker]
interface ContentIndexEvent : ExtendableEvent {
  constructor(DOMString type, ContentIndexEventInit init);
  readonly attribute DOMString id;
};

问题索引

创建新的 Browsing Context 是这里正确的做法吗? (issue) ↵