内容索引

编辑草案,

此版本:
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. identry描述id

  2. contentIndexEntriesentryservice worker registration内容索引条目

  3. 将以下步骤入队entryservice worker registration条目编辑队列

    1. 取消显示 entry

    2. 移除 contentIndexEntries[id]。

    3. entry 触发一个 content delete 事件

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

要为 entry(一个 内容索引条目激活一个 内容索引条目,运行以下步骤:
  1. activeWorkerentryservice worker registrationactive worker

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

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

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

    1. HandleNavigate:以exceptions enabledreplacement enabled,将 newContext 导航entry启动 URL

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

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

    4. visibilityStatenewContext活动文档visibilityState 属性值。

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

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

    7. serviceWorkerEventLoopactiveWorker全局对象事件循环

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

      1. 如果 newContextWindow 对象的环境设置对象创建 URLactiveWorker不是相同的,则中止这些步骤。

      2. newContextWindow 对象的环境设置对象frameTypevisibilityStatefocusStateancestorOriginsList 作为参数运行 Create Window Client

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

4.3. 触发一个 content delete 事件

要为 entry(一个 内容索引条目触发一个 content delete 事件,在 entryservice 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

仅在一个当前引擎中。

FirefoxSafariChrome
OperaEdge
Edge (Legacy)IE
Firefox for AndroidiOS SafariChrome for Android84+Android WebView84+Samsung InternetOpera 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

仅在一个当前引擎中。

FirefoxSafariChrome
OperaEdge
Edge (Legacy)IE
Firefox for AndroidiOS SafariChrome for Android84+Android WebView84+Samsung InternetOpera Mobile60+

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

5.3.1. add()

ContentIndex/add

仅在一个当前引擎中。

FirefoxSafariChrome
OperaEdge
Edge (Legacy)IE
Firefox for AndroidiOS SafariChrome for Android84+Android WebView84+Samsung InternetOpera Mobile60+
add(description) 方法在被调用时,必须 返回一个新的 promise promise,并并行运行 这些步骤:
  1. registration上下文 对象service worker registration

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

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

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

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

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

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

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

  8. icons 为空列表

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

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

      URL

      resourcesrc

      客户端

      上下文对象相关设置对象

      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. iddescriptionid

  12. contentIndexEntriesregistration内容索引 条目

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

    1. contentIndexEntries[id] 设置entry

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

    3. 用 undefined 兑现 promise

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

5.3.2. delete()

ContentIndex/delete

仅在一个当前引擎中。

FirefoxSafariChrome
OperaEdge
Edge (Legacy)IE
Firefox for AndroidiOS SafariChrome for Android84+Android WebView84+Samsung InternetOpera Mobile60+
delete(id) 方法在被调用时,必须 返回一个新的 promise promise,并并行运行这些 步骤:
  1. registration上下文 对象service worker registration

  2. contentIndexEntriesregistration内容索引 条目

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

    1. 取消显示 contentIndexEntries[id]。

    2. 移除 contentIndexEntries[id]。

    3. 用 undefined 兑现 promise

5.3.3. getAll()

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

  2. contentIndexEntriesregistration内容索引 条目

  3. descriptions 为空列表

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

    1. contentIndexEntries 的每个 id → entry 执行

      1. entry描述附加descriptions

    2. descriptions 兑现 promise

5.4. ContentIndexEvent

ContentIndexEvent

仅在一个当前引擎中。

FirefoxSafariChrome
OperaEdge
Edge (Legacy)IE
Firefox for AndroidiOS SafariChrome for Android84+Android WebView84+Samsung InternetOpera 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