演示 API

W3C 候选推荐标准草案

更多关于此文档的详细信息
此版本:
https://www.w3.org/TR/2025/CRD-presentation-api-20250212/
最新发布的版本:
https://www.w3.org/TR/presentation-api/
最新编辑草案:
https://w3c.github.io/presentation-api/
历史:
https://www.w3.org/standards/history/presentation-api/
提交历史
实现报告:
https://www.w3.org/wiki/Second_Screen/Implementation_Status#Tests
编辑:
(谷歌)
前任编辑:
Dominik Röttsches (英特尔) (截至 2015年4月)
反馈:
GitHub w3c/presentation-api (拉取请求新问题开放问题)
测试套件
GitHub web-platform-tests/presentation-api
wpt.live/presentation-api/

摘要

本规范定义了一个 API,使 Web 内容能够访问演示显示器,并使用它们来展示 Web 内容。

本文档状态

本节描述了本文档在发布时的状态。当前 W3C 出版物列表和本技术报告的最新修订版本可在 W3C 技术报告索引 中找到,网址为 https://www.w3.org/TR/。

本文档由 第二屏工作组 作为候选推荐标准草案发布,采用 推荐标准路线

自 2017年6月1日发布候选推荐标准以来,工作组更新了构建 PresentationRequest 的步骤,以忽略不支持方案的 URL,并进一步限制接收浏览上下文的自我导航方式,取消了 BinaryType 枚举的定义,转而使用 HTML 规范中的定义。除 WebIDL 更新外,本文档中定义的其他接口没有改变。此外,还进行了各种澄清和编辑更新。详细信息请参见 变更列表

没有任何功能被标识为有风险

第二屏工作组将在候选推荐标准期间完善 Presentation API 的 测试套件,并更新 初步实现报告。要推进到建议标准,必须展示每个功能的两个独立且互操作的实现,具体见 候选推荐标准退出标准 部分。

发布为候选推荐标准并不意味着 W3C 及其成员的认可。候选推荐标准草案集成了工作组打算包含在后续候选推荐标准快照中的更改。

这是一个草案文件,可能随时被更新、替换或废弃。将本文档作为除正在进行的工作外的其他用途引用是不合适的。

本文档由一个依据 W3C 专利政策 运营的工作组产生。 W3C 维护着一份 与本工作组交付物相关的专利披露列表; 该页面还包括披露专利的说明。任何人如果知道某个专利包含必要声明,则必须根据 W3C 专利政策第 6 节 披露该信息。

本文档受 2023年11月3日 W3C 流程文档 管辖。

1. 引言

本节为非规范性内容。

演示 API 旨在使诸如演示显示器(如投影仪、连接的显示器和联网的电视)对 Web 可用。它考虑了使用有线(HDMI、DVI 或类似)和无线技术(Miracast、Chromecast、DLNA、AirPlay 或类似技术)连接的显示器。

屏幕尺寸有限的设备无法将 Web 内容展示给更大范围的观众,例如会议室中的同事或家中的朋友和家人。在更大的 演示显示器上展示的 Web 内容具有更高的感知质量、可读性和影响力。

演示 API 的核心是使 控制器 页面能够在 演示 页面上展示并与 演示显示器 交换消息。演示页面如何传输到显示器以及如何在其与控制器页面之间交换消息由实现决定;这允许使用各种显示技术。

例如,如果 演示显示器 通过 HDMI 或 Miracast 连接,它们仅允许传输音频和视频,则承载 控制器 的用户代理(UA)还将渲染 演示。 然后,它使用操作系统将生成的图形和音频输出发送到演示显示器。我们称这种情况为 1-UA 模式 的演示 API 实现。唯一的要求是用户代理能够从渲染的演示中发送图形和音频到演示显示器,并在控制器页面与演示页面之间内部交换消息。

如果 演示显示器 能够原生渲染 HTML 并通过网络与 控制器 通信,则承载控制器的用户代理无需渲染 演示。相反,用户代理作为代理,请求演示显示器自行加载并渲染演示页面。消息交换通过用户代理与演示显示器之间的网络连接进行。我们称这种情况为 2-UA 模式 的演示 API 实现。

演示 API 旨在与连接到 演示显示器 的用户代理一起使用,支持 1-UA 模式2-UA 模式,以及可能的其他未列出的方式。为了提高用户代理与演示显示器之间的互操作性,浏览器与显示器之间的网络通信标准化正在由 第二屏社区组 考虑。

2. 用例和需求

本节为非规范性内容。

用例和需求记录在一个单独的 演示 API 用例和需求 文档中。

3. 一致性

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

本文档中的关键词 可以(MAY)必须(MUST)不得(MUST NOT)可选(OPTIONAL)应(SHOULD)不应(SHOULD NOT) 应按照 BCP 14 [RFC2119] [RFC8174] 的描述进行解释,仅在这些词汇全部大写时才应按此解释。

作为算法一部分的要求(例如“删除任何前导空格字符”或“返回 false 并终止这些步骤”)应根据引入算法时使用的关键词(“必须(MUST)”、“应(SHOULD)”、“可以(MAY)”等)进行解释。

以算法或具体步骤表述的一致性要求可以以任何方式实现,只要结果是等效的。(特别是,本规范中定义的算法旨在易于理解,而非性能最佳。)

3.1 一致性类别

本规范描述了两类用户代理的一致性标准。

控制用户代理

符合 控制用户代理 规范的 Web 浏览器必须能够通过提供 控制浏览上下文 来启动和控制演示,具体如本规范所述。此上下文实现了 PresentationPresentationAvailabilityPresentationConnectionPresentationConnectionAvailableEventPresentationConnectionCloseEvent、 和 PresentationRequest 接口。

接收用户代理

符合 接收用户代理 规范的 Web 浏览器必须能够通过提供 接收浏览上下文 来渲染演示,具体如本规范所述。此上下文实现了 PresentationPresentationConnectionPresentationConnectionAvailableEventPresentationConnectionCloseEventPresentationConnectionList、 和 PresentationReceiver 接口。

一个用户代理可以同时充当 控制用户代理接收用户代理,如果它提供了两种浏览上下文并实现了它们所需的所有接口。当同一用户代理能够承载 控制浏览上下文接收浏览上下文 时,例如在 API 的 1-UA 模式 实现中,就会发生这种情况。

针对 用户代理 的一致性要求适用于 控制用户代理接收用户代理 或两者,具体取决于上下文。

4. 术语

术语 JavaScript 领域当前领域的用法与 [ECMASCRIPT] 中的定义一致。在 Promise 对象的上下文中,术语 已解决已拒绝的用法与 [ECMASCRIPT] 中的定义一致。

术语 Accept-LanguageHTTP 身份验证 按照 [RFC9110] 中的定义使用。

术语 cookie 存储 按照 [RFC6265] 中的定义使用。

术语 UUID 按照 [RFC4122] 中的定义使用。

术语 DIAL 按照 [DIAL] 中的定义使用。

术语 重新加载文档 指的是在 [HTML] 中调用 reload() 方法时运行的步骤。

术语 本地存储区域 指的是由 localStorage 属性公开的存储区域,术语 会话存储区域 指的是由 [HTML] 中的 sessionStorage 属性公开的存储区域。

本规范引用了其他规范中导出的术语,参见 B.2 引用定义的术语。它还引用了以下其他规范中的内部概念:

5. 示例

本节为非规范性内容。

本节展示了演示 API 主要功能的示例代码。在这些示例中,controller.html 实现了控制器,presentation.html 实现了演示。两个页面均从域名 https://example.org 提供服务(https://example.org/controller.htmlhttps://example.org/presentation.html)。这些示例假设控制页面一次只管理一个演示。有关更多详细信息,请参阅代码示例中的注释。

5.1 监控演示显示器的可用性

该代码渲染了一个按钮,当至少有一个兼容的 演示显示器 可以展示 https://example.com/presentation.html https://example.net/alternate.html 时,该按钮可见。

通过首先使用您要展示的 URL 创建一个 PresentationRequest 来监控显示器的可用性,然后调用 getAvailability 来获取一个 PresentationAvailability 对象,当演示可用性状态更改时,其 change 事件将被触发。

<!-- controller.html -->
    <button id="presentBtn" style="display: none;">开始演示</button>
    <script>
      // 当至少有一个演示显示器可用时,显示“开始演示”按钮
      var presentBtn = document.getElementById("presentBtn");
      // 也可以使用相对演示 URL,例如 "presentation.html"
      var presUrls = ["https://example.com/presentation.html",
                      "https://example.net/alternate.html"];
      // 根据显示器的可用性显示或隐藏“开始演示”按钮
      var handleAvailabilityChange = function(available) {
        presentBtn.style.display = available ? "inline" : "none";
      };
      // 一旦知道演示显示器的可用性,Promise 就会被解析。
      var request = new PresentationRequest(presUrls);
      request.getAvailability().then(function(availability) {
        // 只要可用性对象存在,availability.value 可能会由控制用户代理保持更新。建议 Web 开发人员在对象不再需要时将其丢弃。
        handleAvailabilityChange(availability.value);
        availability.onchange = function() { handleAvailabilityChange(this.value); };
      }).catch(function() {
        // 平台不支持可用性监控,因此只有在调用 request.start() 后才会发现演示显示器。
        // 为简单起见,假装设备可用;或者,也可以为按钮实现第三种状态。
        handleAvailabilityChange(true);
      });
    </script>

5.2 启动新演示

当用户点击 presentBtn 时,此代码将请求展示 PresentationRequest 中的一个 URL。调用 start 时,浏览器通常会显示一个对话框,允许用户选择一个可用的兼容显示器。与所选显示器兼容的第一个 PresentationRequest URL 将在该显示器上展示。

start 方法将返回一个 PresentationConnection 对象,用于跟踪演示的状态,并在演示页面加载到显示器后与其交换消息。

<!-- controller.html -->
    <script>
      presentBtn.onclick = function () {
        // 启动新演示。
        request.start()
          // 演示连接将在成功时传递给 setConnection。
          .then(setConnection);
          // 否则,用户取消了选择对话框,或未找到显示器。
      };
    </script>

5.3 重新连接到现有演示

即使启动演示的原始页面关闭其 PresentationConnection、导航或关闭,演示仍会继续运行。另一个页面可以使用 id 重新连接到现有演示并恢复对其的控制。此功能仅保证在启动演示的同一浏览器中可用。

<!-- controller.html -->
    <button id="reconnectBtn" style="display: none;">重新连接</button>
    <script>
      var reconnect = function () {
        // 如果存在,则从 localStorage 读取 presId
        var presId = localStorage["presId"];
        // presId 是重新连接到演示时的必需项。
        if (!!presId) {
          request.reconnect(presId)
            // 新的演示连接将在成功时传递给 setConnection。
            .then(setConnection);
            // 未找到 presUrl 和 presId 的连接,或发生错误。
        }
      };
      // 在控制器导航时,自动重新连接。
      document.addEventListener("DOMContentLoaded", reconnect);
      // 或允许手动重新连接。
      const reconnectBtn = document.querySelector("#reconnectBtn");
      reconnectBtn.onclick = reconnect;
    </script>

5.4 由控制用户代理启动演示

某些浏览器提供了一种方式,允许用户在不直接与控制页面交互的情况下启动演示。控制页面可以通过设置 defaultRequest 属性来选择这种行为,并监听当以这种方式启动演示时触发的 connectionavailable 事件。事件传递的 PresentationConnection 行为与页面调用 start 时相同。

<!-- controller.html -->
    <!-- 设置 presentation.defaultRequest 允许页面指定在控制用户代理启动演示时使用的 PresentationRequest。 -->
    <script>
      navigator.presentation.defaultRequest = new PresentationRequest(presUrls);
      navigator.presentation.defaultRequest.onconnectionavailable = function(evt) {
        setConnection(evt.connection);
      };
    </script>

5.5 监控连接状态和交换数据

一旦演示开始,返回的 PresentationConnection 用于监控其状态并与之交换消息。通常用户会在控制页面中选择断开或终止演示。

由于控制页面在其生命周期内可能会连接和断开多个演示,跟踪当前的 PresentationConnection 及其状态非常有帮助。仅当连接处于 connected 状态时,消息才能发送和接收。

<!-- controller.html -->
    <button id="disconnectBtn" style="display: none;">断开连接</button>
    <button id="stopBtn" style="display: none;">停止</button>
    <script>
      let connection;
    
      // 如果有已连接的演示,显示断开连接和停止按钮
      const stopBtn = document.querySelector("#stopBtn");
      const disconnectBtn = document.querySelector("#disconnectBtn");
    
      stopBtn.onclick = _ => {
        connection && connection.terminate();
      };
    
      disconnectBtn.onclick = _ => {
        connection && connection.close();
      };
    
      function setConnection(newConnection) {
        // 如果不是尝试重新连接,则断开现有演示连接
        if (connection && connection != newConnection && connection.state != 'closed') {
          connection.onclose = undefined;
          connection.close();
        }
    
        // 设置新连接并保存演示 ID
        connection = newConnection;
        localStorage["presId"] = connection.id;
    
        function showConnectedUI() {
          // 允许用户断开或终止演示
          stopBtn.style.display = "inline";
          disconnectBtn.style.display = "inline";
          reconnectBtn.style.display = "none";
        }
    
        function showDisconnectedUI() {
          disconnectBtn.style.display = "none";
          stopBtn.style.display = "none";
          reconnectBtn.style.display = localStorage["presId"] ? "inline" : "none";
        }
    
        // 监控连接状态
        connection.onconnect = _ => {
          showConnectedUI();
    
          // 注册消息处理程序
          connection.onmessage = message => {
            console.log(`收到消息: ${message.data}`);
          };
    
          // 向演示页面发送初始消息
          connection.send("Say hello");
        };
    
        connection.onclose = _ => {
          connection = null;
          showDisconnectedUI();
        };
    
        connection.onterminate = _ => {
          // 如果存在,则从 localStorage 中删除 presId
          delete localStorage["presId"];
          connection = null;
          showDisconnectedUI();
        };
      };
    </script>

5.6 监听传入的演示连接

此代码运行在演示页面上(https://example.org/presentation.html)。多个控制页面可能会连接到演示,因此演示页面必须在 connectionList 对象上监听传入的连接。

<!-- presentation.html -->
    <script>
      var addConnection = function(connection) {
        connection.onmessage = function (message) {
          if (message.data == "Say hello")
            connection.send("hello");
        };
      };
    
      navigator.presentation.receiver.connectionList.then(function (list) {
        list.connections.map(function (connection) {
          addConnection(connection);
        });
        list.onconnectionavailable = function (evt) {
          addConnection(evt.connection);
        };
      });
    </script>

5.7 使用消息传递区域信息

<!-- controller.html -->
    <script>
      connection.send('{"string": "你好,世界!", "lang": "zh-CN"}');
      connection.send('{"string": "こんにちは、世界!", "lang": "ja"}');
      connection.send('{"string": "안녕하세요, 세계!", "lang": "ko"}');
      connection.send('{"string": "Hello, world!", "lang": "en-US"}');
    </script>
    
    <!-- presentation.html -->
    <script>
      connection.onmessage = function (message) {
        var messageObj = JSON.parse(message.data);
        var spanElt = document.createElement("SPAN");
        spanElt.lang = messageObj.lang;
        spanElt.textContent = messageObj.string;
        document.body.appendChild(spanElt);
      };
    </script>

5.8 从同一控制页面创建第二个演示

控制页面可以在两个不同的演示显示器上启动和控制两个独立的演示。此代码展示了如何在前面的示例中添加第二个演示。

<!-- controller.html -->
    <!-- 同一控制页面可以通过多次调用 start() 创建和管理多个演示。 -->
    <button id="secondPresentBtn" style="display: none;">再次演示</button>
    <script>
      var secondPresentBtn = document.getElementById("secondPresentBtn");
      var secondPresUrl = "https://example.com/second-presentation.html";
      var secondRequest = new PresentationRequest(secondPresUrl);
      // 为简便起见,省略了处理 secondRequest 屏幕可用性并更新 secondPresentBtn 状态的逻辑。
      secondPresentBtn.onclick = function () {
      // 启动新演示,可能会在与原始请求不同的屏幕上。
        secondRequest.start().then(setSecondConnection);
      };
      function setSecondConnection(newConnection) {
        // 处理 second-presentation.html 的消息逻辑。
      };
    </script>

6. API

6.1 常见惯用法

演示显示器 指通过实现特定的连接技术,用户代理可以访问的图形和/或音频输出设备。

演示连接是一个将控制浏览上下文与其接收浏览上下文关联起来的对象,并启用它们之间的双向消息传递。每个演示连接都有一个演示连接状态、一个唯一的演示标识符(用于将其与其他演示区分开来),以及一个演示 URL,该 URL 是一个用于创建或重新连接到演示URL有效的演示标识符仅包含字母数字 ASCII 字符,并且至少有 16 个字符长。

由于功能、安全或硬件限制,某些演示显示器可能只能显示 Web 内容的子集。例如机顶盒、智能电视或仅能呈现音频的网络扬声器。如果控制用户代理可以合理地保证在该显示器上演示 URL 会成功,我们就说这样的显示器是演示 URL可用演示显示器

控制浏览上下文(或简称控制器)是一个通过调用 startreconnect 连接到演示浏览上下文,或者通过 connectionavailable 事件接收到演示连接的浏览上下文。在 PresentationRequest 的算法中,控制浏览上下文是其 JavaScript 领域用于构造 PresentationRequest浏览上下文

接收浏览上下文(或简称演示文稿)是负责向演示显示器呈现内容的浏览上下文。接收浏览上下文可以与控制浏览上下文位于同一个用户代理中,也可以位于不同的用户代理中。通过执行创建接收浏览上下文的步骤来创建接收浏览上下文

在一个过程中,当过程在控制浏览上下文处启动时,目标浏览上下文接收浏览上下文;如果过程在接收浏览上下文处启动,则目标浏览上下文是控制浏览上下文

受控演示集(初始为空)包含由控制用户代理(或该用户代理中的特定用户配置文件)的控制浏览上下文创建的演示连接受控演示集由表示底层演示连接PresentationConnection 对象列表表示。该集合中的多个 PresentationConnection 对象可能共享相同的演示 URL演示标识符,但对于给定的控制浏览上下文,只能有一个具有特定演示 URL演示标识符PresentationConnection

演示控制器集(初始为空)包含由接收用户代理接收浏览上下文创建的演示连接演示控制器集由表示底层演示连接PresentationConnection 对象列表表示。此集合中的所有演示连接共享相同的演示 URL演示标识符

接收浏览上下文中,演示控制器监视器(初始设置为 null)向接收应用程序公开当前的演示控制器集演示控制器监视器PresentationConnectionList 表示。

接收浏览上下文中,演示控制器承诺(初始设置为 null)在建立初始演示连接后提供演示控制器监视器演示控制器承诺由一个 Promise 表示,该 Promise 解析为演示控制器监视器

控制浏览上下文中,默认演示请求(初始设置为 null)表示当用户希望从浏览器界面启动演示连接时要使用的请求。

本规范中提到的任务的任务源演示任务源

当算法将演示 API 任务 T 排队时,用户代理 必须使用当前领域全局对象演示任务源将全局任务 T 排队。

除非另有说明,算法步骤构造的脚本对象的 JavaScript 域当前域

6.2 接口 Presentation

WebIDLpartial interface Navigator {
  [SecureContext, SameObject] readonly attribute Presentation presentation;
};

[SecureContext, Exposed=Window]
interface Presentation {
};

presentation 属性用于检索 Presentation 接口的实例。它 必须 返回 Presentation 实例。

6.2.1 控制用户代理

控制用户代理 必须 实现以下部分接口:

WebIDLpartial interface Presentation {
      attribute PresentationRequest? defaultRequest;
    };

defaultRequest 属性 必须 返回 默认演示请求(如果有),否则返回 null。当设置时,默认 演示请求 必须 被设置为新值。

控制用户代理 仅在用户通过用户手势明确表达意图时(例如点击浏览器界面的按钮)使用 默认演示请求 来启动演示。

要使用 默认演示请求 启动演示,控制用户代理 必须 遵循 从默认演示请求启动演示 的步骤。

支持使用 默认演示请求 启动演示是 可选的

注意
如果 控制用户代理 不支持 从默认演示请求启动演示,那么该用户代理应忽略为 defaultRequest 设置的任何值。

6.2.2 接收用户代理

接收用户代理 必须 实现以下部分接口:

WebIDLpartial interface Presentation {
      readonly attribute PresentationReceiver? receiver;
    };

receiver 属性必须返回与接收浏览上下文关联的 PresentationReceiver 实例,该实例由接收用户代理接收浏览上下文创建时创建。在任何其他浏览上下文(包括接收浏览上下文子可导航对象)中,它必须返回 null

注意

Web 开发者可以使用 navigator.presentation.receiver 来检测文档是否作为演示加载。

6.3 接口 PresentationRequest

WebIDL[SecureContext, Exposed=Window]
interface PresentationRequest : EventTarget {
  constructor(USVString url);
  constructor(sequence<USVString> urls);
  Promise<PresentationConnection> start();
  Promise<PresentationConnection> reconnect(USVString presentationId);
  Promise<PresentationAvailability> getAvailability();

  attribute EventHandler onconnectionavailable;
};

PresentationRequest 对象与由 控制浏览上下文 发起的演示请求或重新连接相关联。PresentationRequest 对象 必须 在由 控制浏览上下文 提供的 控制用户代理 中实现。

当构造 PresentationRequest 时,提供的 urls 必须 用作 演示请求 URL 的列表, 它们每一个都是该 PresentationRequest 实例的可能 演示 URL

6.3.1 构造 PresentationRequest

当调用 PresentationRequest 构造函数时, 控制用户代理 必须 执行以下步骤:

输入
urlurls,即 演示请求 URL
输出
一个新的 PresentationRequest 对象
  1. 如果文档对象的活动沙箱标志集设置了沙箱化演示文稿浏览上下文标志,则抛出 SecurityError 并中止这些步骤。
  2. 如果 urls 是一个空序列,则抛出 NotSupportedError 并中止所有剩余步骤。
  3. 如果提供了一个 url,则令 urls 为一个包含 url 的单项数组。
  4. presentationUrls 为一个空的 URL 列表。
  5. 对于 urls 中的每个 URL U
    1. A 为一个绝对 URL,它是相对于当前设置对象指定的 API 基本 URL 解析 U 的结果。
    2. 如果解析 URL 算法失败,则抛出 SyntaxError 异常并中止所有剩余步骤。
    3. 如果 A 的方案受控制用户代理支持,则将 A 添加到 presentationUrls
  6. 如果 presentationUrls 是一个空列表,则抛出 NotSupportedError 并中止所有剩余步骤。
  7. 如果 presentationUrls 的任何成员不是潜在可信 URL,则抛出 SecurityError 并中止这些步骤。
  8. 构造一个新的 PresentationRequest 对象,其演示文稿请求 URLpresentationUrls,并返回它。

6.3.2 选择一个演示显示设备

当调用 start 方法时, 用户代理 必须 按以下步骤来 选择一个演示显示设备

输入
presentationRequest,接收到 start 调用的 PresentationRequest 对象
输出
一个新的 Promise
  1. 如果文档的活动窗口没有瞬时激活,则返回一个因InvalidAccessError 异常而被拒绝的 Promise,并中止这些步骤。
  2. topContext控制浏览上下文顶层浏览上下文
  3. 如果在 topContexttopContext 的任何后代可导航对象中的浏览上下文中,已存在先前调用 start 时产生的未解决的 Promise,则返回一个因 OperationError 异常而被拒绝的新 Promise,并中止所有剩余步骤。
  4. P 为一个新的 Promise
  5. 返回 P,但并行继续运行这些步骤。
  6. 如果用户代理监视可用演示显示列表,则并行运行监视可用演示显示列表的步骤。
  7. presentationUrlspresentationRequest演示请求 URL
  8. 请求用户允许使用演示显示器并选择一个演示显示器。
  9. 如果以下任一情况为真:
    1. 在请求用户权限完成之前,可用演示显示器列表为空并将保持为空。
    2. 可用演示显示器列表中的任何成员都不是 presentationUrls 中任何成员的可用演示显示器
    则运行以下步骤:
    1. 将演示 API 任务排队以使用 NotFoundError 异常拒绝 P
    2. 中止所有剩余步骤。
  10. 如果用户拒绝使用显示器的权限,则将演示 API 任务排队以使用NotAllowedError 异常拒绝 P,并中止所有剩余步骤。
  11. 否则,用户授予使用显示器的权限;令 D 为该显示器。
  12. 使用 presentationRequestDP 运行启动演示连接的步骤。
实现许可请求和显示选择的详细信息留给用户代理;例如,它可能显示对话框并允许用户选择可用的显示设备(授予许可),或取消选择(拒绝许可)。建议实现者显示当前是否正在使用某个可用的显示设备,以便支持能够使用多个显示设备的演示。
建议接收用户代理为演示显示设备提供一个用户友好的名称,例如“客厅电视”,以帮助用户选择所需的显示设备。建议接收用户代理的实现者提供该用户友好名称的区域设置和预期的文本方向信息。建议控制用户代理的实现者在已知的情况下,以用户友好名称的区域设置和文本方向显示该名称。

6.3.3 从默认演示请求开始演示

当用户通过浏览器界面(通过专用按钮、用户手势或其他信号)表达在演示显示设备上开始演示文档的意图时,用户代理 必须 执行以下步骤来 从默认演示请求开始演示。 如果文档上未设置 默认演示请求,则不得运行这些步骤。

输入
W,用户表达意图要开始演示的文档
presentationRequest,非 null 的值,由 navigator.presentation.defaultRequestW 上设置
D,作为演示目标的 演示显示设备
  1. 并行运行以下步骤 并行
  2. presentationUrlspresentationRequest演示请求 URL
  3. 如果没有适合 D 作为 可用演示显示设备presentationRequest 对应的 演示请求 URL,则中止这些步骤。
  4. 运行 启动演示连接 的步骤,使用 presentationRequestD
从默认演示请求开始演示 时, 控制用户代理 可能允许用户请求演示并使用相同的用户手势选择目标 演示显示设备。 例如,浏览器界面可以允许用户从菜单中选择显示设备,或允许用户点击支持 近场通信 (NFC) 的显示设备。

6.3.4 启动演示连接

用户代理启动演示连接 时,它 必须 执行以下步骤:

输入
presentationRequest,用于启动演示连接的 PresentationRequest
D,所选的 演示显示设备
P,一个可选的 Promise,它将被解析为一个新的演示连接
  1. 断言:此操作正在并行运行。
  2. I 设置为一个新的 有效演示标识符, 在所有已知的 演示连接受控演示集 中唯一。 为避免指纹识别,演示标识符 设置为遵循 [rfc4122] 第 4.4 或 4.5 形式生成的 UUID
  3. 创建一个新的 PresentationConnection S
  4. S演示标识符 设置为 I
  5. presentationUrls 成为 presentationRequest演示请求 URL
  6. S演示 URL 设置为第一个存在于 可用演示显示设备列表 中的 (presentationUrl, D)presentationUrl
  7. S演示连接状态 设置为 connecting
  8. S 添加到 受控演示集
  9. 如果提供了 P,则 排队一个演示 API 任务解决 PS
  10. 将演示 API 任务排队以在 presentationRequest触发一个名为 connectionavailable 的事件, 该事件使用 PresentationConnectionAvailableEvent 接口,并将 connection 属性初始化为 S。 该事件不得冒泡且不得取消。
  11. U 成为连接到 D 的用户代理。
  12. 如果下一步失败,取消所有剩余步骤并 关闭演示连接 S, 将 closeReason 设置为 error,并设置 closeMessage 为描述失败的可读信息。
  13. 使用特定实现机制,告知 U 使用 DpresentationUrlI 作为参数 创建接收浏览上下文
  14. 建立演示连接S
presentationUrl 应该命名一个本地或远程用户代理可以访问的资源。本规范定义了使用 httphttps 协议的 presentationUrl 的行为;对于其他协议的行为,本规范未定义。

6.3.5 重新连接到演示

当调用 reconnect 方法时,用户代理 必须 运行以下步骤来重新连接到演示:

输入
presentationRequest,调用了 reconnectPresentationRequest 对象
presentationId,一个有效的演示标识符
输出
P,一个新的 Promise
  1. P 为一个新的 Promise
  2. 返回 P,但并行继续运行这些步骤。
  3. 受控演示集中搜索满足以下条件的 PresentationConnection
  4. 如果存在这样的 PresentationConnection,则运行以下步骤:
    1. existingConnection 为该 PresentationConnection
    2. 将演示 API 任务排队以使用 existingConnection 解析 P
    3. 如果 existingConnection演示连接状态connectingconnected,则中止所有剩余步骤。
    4. existingConnection演示连接状态设置为 connecting
    5. 使用 existingConnection 建立演示连接
    6. 中止所有剩余步骤。
  5. 受控演示集中搜索第一个满足以下条件的 PresentationConnection
  6. 如果存在这样的 PresentationConnection,则运行以下步骤:
    1. existingConnection 为该 PresentationConnection
    2. 创建一个新的 PresentationConnection newConnection
    3. newConnection演示标识符设置为 presentationId
    4. newConnection演示 URL 设置为 existingConnection演示 URL
    5. newConnection演示连接状态设置为 connecting
    6. newConnection 添加到受控演示集
    7. 将演示 API 任务排队以使用 newConnection 解析 P
    8. 将演示 API 任务排队以在 presentationRequest触发一个名为 connectionavailable 的事件,该事件使用 PresentationConnectionAvailableEvent 接口,并将 connection 属性初始化为 newConnection。该事件不得冒泡且不得取消。
    9. 使用 newConnection 建立演示连接
    10. 中止所有剩余步骤。
  7. 将演示 API 任务排队以使用 NotFoundError 异常拒绝 P

6.3.6 事件处理程序

以下是对象实现 PresentationRequest 接口时必须支持的事件处理程序(及其对应的事件处理程序事件类型),作为事件处理程序 IDL 属性:

事件处理程序 事件处理程序事件类型
onconnectionavailable connectionavailable

6.4 接口 PresentationAvailability

WebIDL[SecureContext, Exposed=Window]
interface PresentationAvailability : EventTarget {
  readonly attribute boolean value;

  attribute EventHandler onchange;
};
一个 PresentationAvailability 对象暴露了一个演示请求的 演示显示可用性。 一个演示请求的 演示显示可用性 记录是否当前至少有一个 可用的演示显示设备 适用于该请求的 演示请求 URL

当 ECMASCript 代码无法观察到 PresentationAvailability 对象时,该演示请求的 演示显示可用性 适用于垃圾回收。

如果 控制用户代理 能够在后台 监控可用演示显示设备列表(无挂起的 start 请求), 那么 PresentationAvailability 对象必须控制浏览上下文中实现。

value 属性必须 返回它的最后一次设置值。 该值由 监控可用演示显示设备列表 算法初始化和更新。

onchange 属性是一个事件处理程序,其对应的事件处理程序事件类型change

6.4.1 演示可用性对象集合

用户代理 必须 跟踪由 getAvailability 方法创建的 演示可用性对象集合演示可用性对象集合 由一组元组 (A, availabilityUrls) 表示,初始为空,其中:

  1. A 是一个活动的 PresentationAvailability 对象。
  2. availabilityUrls 是当 getAvailabilityPresentationRequest 上被调用时的 演示请求 URL 列表。

6.4.2 可用演示显示设备列表

用户代理 必须 保持一个 可用演示显示设备列表可用演示显示设备列表 由元组列表 (availabilityUrl, display) 表示。此列表中的条目意味着 display 当前是 availabilityUrl 的一个 可用演示显示设备。 此 演示显示设备 列表可以用于启动新的演示,并根据具体实现的发现机制进行填充。 它设置为用于 监控可用演示显示设备列表 算法的最新结果。

演示可用性对象集合不为空时,用户代理可以持续监视可用演示显示器列表,以便页面可以使用 PresentationAvailability 对象的 value 属性仅在有可用显示器时提供演示。但是,用户代理可能不支持后台持续可用性监视;例如,由于平台或功耗限制。在这种情况下,getAvailability 返回的 Promise 将被拒绝,并且监视可用演示显示器列表的算法将仅作为选择演示显示器算法的一部分运行。

演示可用性对象集合 为空时(即没有被监控的 availabilityUrls),用户代理 不应 监控可用演示显示设备列表 以满足 节能非功能性要求。 为了进一步节省电力,用户代理 可以 还跟踪持有 PresentationAvailability 对象的页面是否处于前台。 使用这些信息,可以恢复或暂停实现特定的演示显示设备发现。

6.4.3 获取演示显示设备的可用性信息

当调用 getAvailability 方法时,用户代理必须 运行以下步骤:

输入
presentationRequest,接收到 getAvailability 调用的 PresentationRequest 对象
输出
一个新的 Promise
  1. P 为在 presentationRequestJavaScript 领域中构造的一个新的 Promise
  2. 返回 P,但并行继续运行这些步骤。
  3. 如果用户代理无法在后台持续监视可用演示显示器列表,但稍后可以找到演示显示器以启动连接,则:
    1. 将演示 API 任务排队以使用 NotSupportedError 异常拒绝 P
    2. 中止所有剩余步骤。
  4. 如果 presentationRequest演示显示设备可用性 不是 null,则:
    1. 排队一个 Presentation API 任务解析 P,并将请求的 演示显示设备可用性 作为结果返回。
    2. 中止所有剩余的步骤。
  5. presentationRequest演示显示设备可用性 设置为一个在 presentationRequestJavaScript 域中构造的新的 PresentationAvailability 对象,并将 A 设置为该对象。
  6. 创建一个元组 (ApresentationUrls),并将其添加到 演示可用性对象集合
  7. 运行监控可用演示显示设备列表 算法。
    注意
    监控算法在执行前一步后,至少还需要运行一次,以获取刚刚添加到 演示可用性对象集合的元组。
  8. 排队一个 Presentation API 任务解析 P 并返回 A

6.4.4 监控可用演示显示设备列表

如果 演示可用性对象集合不为空,或有一个挂起的请求用于 选择演示显示设备,则 用户代理 必须通过运行以下步骤来 监控可用演示显示设备列表

  1. 断言:此操作正在并行运行。
  2. availabilitySet演示可用性对象集合的浅拷贝。
  3. 如果有一个挂起的请求用于选择演示显示设备,并且 PresentationRequest演示显示设备可用性null,则运行以下子步骤:
    1. 创建一个新的 PresentationAvailability 对象,并将其命名为 A
    2. 创建一个元组(A, presentationUrls),其中 presentationUrlsPresentationRequest演示请求 URL,并将其添加到 availabilitySet
  4. newDisplays 为一个空列表。
  5. 如果 用户代理 无法检索 演示显示设备(例如,用户已禁用此功能),则跳过以下步骤。
  6. 检索 演示显示设备(使用实现特定的机制),并将 newDisplays 设置为此列表。
  7. 可用演示显示设备列表 设置为空列表。
  8. 对于 availabilitySet 中的每个成员(AavailabilityUrls),运行以下步骤:
    1. Avalue 属性的值设置为 previousAvailability
    2. newAvailability 设置为 false
    3. 对于 availabilityUrls 中的每个 availabilityUrl,运行以下步骤:
      1. 对于 newDisplays 中的每个 display,如果 displayavailabilityUrl可用演示显示设备,则运行以下步骤:
        1. 将元组(availabilityUrl, display) 插入到 可用演示显示设备列表 中,前提是没有相同的元组已存在。
        2. newAvailability 设置为 true
    4. 如果 Avalue 属性尚未初始化,则将其设置为 newAvailability,并跳过以下步骤。
    5. 如果 previousAvailability 不等于 newAvailability,则 排队一个 Presentation API 任务来运行以下步骤:
      1. Avalue 属性设置为 newAvailability
      2. 触发一个事件,名为 change,目标为 A
注意
控制用户代理 可以选择如何频繁地 监控可用演示显示设备列表, 包括将来自 startgetAvailability 的请求分组,并在多个浏览上下文之间聚合它们。

演示显示设备可用性对象符合垃圾回收条件时, 用户代理 应该运行以下步骤:

  1. A 为刚刚被回收的 PresentationAvailability 对象。
  2. 演示可用性对象集合中查找并移除任何 (A, availabilityUrl)的条目。
  3. 如果演示可用性对象集合现在为空,并且没有挂起的 选择演示显示设备 请求,则取消任何挂起的用于监控可用演示显示设备列表的任务以节省电力, 并将可用演示显示设备列表设置为空列表。
注意
用于监控演示显示设备可用性的机制以及确定某个 演示显示设备与给定 URL 的兼容性由用户代理决定。

6.4.5 接口 PresentationConnectionAvailableEvent

WebIDL[SecureContext, Exposed=Window]
interface PresentationConnectionAvailableEvent : Event {
  constructor(DOMString type, PresentationConnectionAvailableEventInit eventInitDict);
  [SameObject] readonly attribute PresentationConnection connection;
};

dictionary PresentationConnectionAvailableEventInit : EventInit {
  required PresentationConnection connection;
};

当与PresentationRequest对象关联的连接被创建时,控制用户代理会在该对象上触发一个名为 connectionavailable 的事件。该事件在 PresentationRequest 实例上触发,使用 PresentationConnectionAvailableEvent 接口,并将 connection 属性设置为被创建的 PresentationConnection 对象。 该事件针对为控制器创建的每个连接触发,无论是通过控制器调用 startreconnect,还是由控制用户代理通过 defaultRequest 代表控制器创建连接。

当传入连接被创建时,接收用户代理会在 PresentationReceiver触发一个名为 connectionavailable 的事件。 该事件在演示控制器监视器上触发,使用 PresentationConnectionAvailableEvent 接口,并将 connection 属性设置为被创建的 PresentationConnection 对象。 该事件针对在监视传入演示连接时创建的所有连接触发。

connection 属性必须返回在创建 PresentationConnection 对象时设置的值。

当调用 PresentationConnectionAvailableEvent 构造函数时,用户代理 必须 构建一个新的 PresentationConnectionAvailableEvent 对象,并将其 connection 属性设置为传递给构造函数的 connection 成员的值。

6.5 接口 PresentationConnection

每个 展示连接 都由一个 PresentationConnection 对象表示。控制用户代理接收用户代理 必须 实现 PresentationConnection

WebIDLenum PresentationConnectionState { "connecting", "connected", "closed", "terminated" };

[SecureContext, Exposed=Window]
interface PresentationConnection : EventTarget {
  readonly attribute USVString id;
  readonly attribute USVString url;
  readonly attribute PresentationConnectionState state;
  undefined close();
  undefined terminate();
  attribute EventHandler onconnect;
  attribute EventHandler onclose;
  attribute EventHandler onterminate;

  // Communication
  attribute BinaryType binaryType;
  attribute EventHandler onmessage;
  undefined send (DOMString message);
  undefined send (Blob data);
  undefined send (ArrayBuffer data);
  undefined send (ArrayBufferView data);
};

id 属性指定了 展示连接展示标识符

url 属性指定了 展示连接展示 URL

state 属性表示 展示连接 的当前状态。它可以是 PresentationConnectionState 中的值,具体取决于连接状态:

connected 状态并不意味着发送或接收消息一定会成功,因为通信通道可能会随时突然关闭。希望尽早检测到此类情况的应用程序应实现自己的保活机制。

当调用 close 方法时,PresentationConnection S用户代理 必须 开始关闭展示连接 ScloseReason 设置为 closedcloseMessage 为空。

当调用 terminate 方法时, PresentationConnection S控制浏览上下文中,用户代理 必须 运行算法以 终止控制浏览上下文中的展示

当调用 terminate 方法时, PresentationConnection S接收浏览上下文中,用户代理 必须 运行算法以 终止接收浏览上下文中的展示

binaryType 属性可以取 BinaryType 中的一个值。当创建 PresentationConnection 对象时,其 binaryType 属性必须设置为字符串 "arraybuffer"。获取时,它必须返回最后设置的值。设置时,用户代理必须将属性设置为新值。

备注
binaryType 属性允许开发者控制二进制数据如何暴露给脚本。通过将该属性设置为 "blob",二进制数据以 Blob 形式返回;通过将其设置为 "arraybuffer",则以 ArrayBuffer 形式返回。 该属性默认为 "arraybuffer"。此属性对以字符串形式发送的数据没有影响。

当在 PresentationConnection S 上调用 send 方法时, 用户代理必须运行通过 S 发送消息的算法。

PresentationConnection 对象 S 被丢弃(因为拥有它的文档正在导航或已关闭),而 S演示连接状态connectingconnected 时,用户代理 必须wentaway 作为 closeReason 并使用空的 closeMessage开始关闭演示连接 S

如果用户代理目标浏览上下文接收到要关闭 PresentationConnection S 的信号,它必须closedwentaway 作为 closeReason 并使用空的 closeMessage关闭演示连接 S

6.5.1 建立展示连接

用户代理 需要使用 展示连接建立展示连接 时,它 必须 执行以下步骤:

输入
presentationConnection,即需要连接的 PresentationConnection 对象
  1. 断言:此操作正在并行运行。
  2. 如果 presentationConnection展示连接状态 不是 connecting,则终止所有后续步骤。
  3. 请求将 presentationConnection 连接到 接收浏览上下文。该请求必须包含 presentationConnection展示标识符
  4. 如果连接成功完成,将展示 API 任务排队,执行以下步骤:
    1. presentationConnection展示连接状态 设置为 connected
    2. 触发一个事件,名为 connect,目标为 presentationConnection
  5. 如果连接无法完成,则 关闭展示连接 S,将 error 作为 closeReason,并提供描述故障的可读消息作为 closeMessage
用于在远程显示上展示并连接 控制浏览上下文 与展示文档的机制由用户代理实现决定。连接必须提供一种双向消息传递抽象,能够按可靠且有序的方式传递 DOMString 和二进制有效负载,如以下 发送消息接收消息 步骤中所述。

6.5.2 通过 PresentationConnection 发送消息

对于控制浏览上下文接收浏览上下文之间的连接,没有强制规定特定的传输方式,只是对于多次调用 send,必须确保消息能够可靠且按顺序地传递到另一端。 该传输应具有与可靠模式下的 RTCDataChannel 等效的功能。

展示消息数据 为在两个浏览上下文之间传输的有效数据。设 展示消息类型 为该数据的类型,可以是 textbinary

用户代理 需要通过 展示连接 发送消息 时,它 必须 执行以下步骤:

输入
presentationConnection,即与其他浏览上下文连接的 展示连接
messageOrData,即要发送到其他浏览上下文的 展示消息数据
  1. 如果 presentationConnectionstate 属性不是 connected,则抛出一个 InvalidStateError 异常。
  2. 如果 presentationConnection关闭过程已开始,则中止这些步骤。
  3. 如果 messageOrData 的类型是 ArrayBufferArrayBufferViewBlob,则令演示消息类型 messageTypebinary。如果 messageOrData 的类型是 DOMString,则令 messageTypetext
  4. 使用实现特定的机制,将 messageOrData 的内容作为演示消息数据,并将 messageType 作为演示消息类型传输到目标浏览上下文
  5. 如果上一步遇到不可恢复的错误,则以 error 作为 closeReason,并使用描述所遇到错误的 closeMessage 立即关闭演示连接 presentationConnection

为了帮助应用程序从通过 展示连接 发送消息时发生的错误中恢复,用户代理应在 closeMessage 中包括有关哪个尝试失败的详细信息,并提供一个可读字符串来解释失败的原因。closeMessage 的示例:

  • Unable to send text message (network_error): "hello" 对于 DOMString 消息,其中 "hello" 是失败消息的前 256 个字符。
  • 对于 ArrayBufferArrayBufferViewBlob 消息,提示“无法发送二进制消息 (invalid_message)”。
当通过 展示连接 发送用户可见的字符串时,页面作者应确保传播语言环境信息,以便目标用户代理知道如何最好地呈现字符串。请参阅 示例 以了解一种解决方案。

6.5.3 通过 PresentationConnection 接收消息

用户代理 从远程端接收到包含 展示消息数据展示消息类型 的传输内容时,它 必须 按以下步骤通过 PresentationConnection 接收消息

输入
presentationConnection,接收消息的 展示连接
messageType,消息的 展示消息类型
messageData,消息的 展示消息数据
  1. 断言:此操作正在并行运行。
  2. 如果 presentationConnectionstate 属性不是 connected,则中止这些步骤。
  3. event 为使用 MessageEvent 接口创建事件的结果,事件类型为 message,该事件不冒泡且不可取消。
  4. 按如下方式初始化 event 的 data 属性:
    1. 如果 messageTypetext,则将 eventdata 属性初始化为类型为 DOMStringmessageData
    2. 如果 messageTypebinary,并且 binaryType 属性设置为 "blob",则将 eventdata 属性初始化为一个新的 Blob 对象,其原始数据为 messageData
    3. 如果 messageTypebinary,并且 binaryType 属性设置为 "arraybuffer",则将 eventdata 属性初始化为一个新的 ArrayBuffer 对象,其内容为 messageData
  5. 将演示 API 任务排队以在 presentationConnection触发 event

如果 用户代理 在通过 presentationConnection 接收消息 时遇到不可恢复的错误,它 必须 立即 关闭展示连接 presentationConnection,并将 error 作为 closeReason。它 应该 使用人类可读的描述来解释遇到的错误,作为 closeMessage

6.5.4 接口 PresentationConnectionCloseEvent

WebIDLenum PresentationConnectionCloseReason { "error", "closed", "wentaway" };

[SecureContext, Exposed=Window]
interface PresentationConnectionCloseEvent : Event {
  constructor(DOMString type, PresentationConnectionCloseEventInit eventInitDict);
  readonly attribute PresentationConnectionCloseReason reason;
  readonly attribute DOMString message;
};

dictionary PresentationConnectionCloseEventInit : EventInit {
  required PresentationConnectionCloseReason reason;
  DOMString message = "";
};

当一个 PresentationConnectionCloseEvent 在展示连接进入 closed 状态时触发。 reason 属性提供了连接关闭的原因。它可以取以下值之一 PresentationConnectionCloseReason:

  • error 表示连接或通信机制遇到了不可恢复的错误。
  • closed 表示连接的 控制浏览上下文接收浏览上下文 调用了 close()
  • wentaway 表示浏览器关闭了连接,例如,因为拥有连接的浏览上下文已导航或被丢弃。

reason 属性为 error 时,用户代理 SHOULD 设置 message 属性为一个人类可读的描述,说明通信通道如何遇到错误。

PresentationConnectionCloseEvent 构造函数被调用时,用户代理 MUST 构造一个新的 PresentationConnectionCloseEvent 对象,其 reason 属性应设置为传递给构造函数的 PresentationConnectionCloseEventInit 对象的 reason 成员,其 message 属性应设置为该对象的 message 成员(如果已设置),否则应为空字符串。

6.5.5 关闭 PresentationConnection

用户代理开始关闭展示连接 时,它 MUST 执行以下步骤:

输入
presentationConnection,要关闭的 展示连接
closeReason,描述为什么要关闭连接的 PresentationConnectionCloseReason
closeMessage,包含关闭连接详细原因的可读消息
  1. 如果 展示连接状态 不是 connectingconnected,则中止剩余步骤。
  2. 展示连接状态 设置为 closed
  3. 开始向 目标浏览上下文 发出信号,传达关闭相应 PresentationConnection 的意图,并将 closeReason 传递给该上下文。用户代理不需要等待确认相应的 PresentationConnection 实际关闭即可继续下一步。
  4. 如果 closeReason 不是 wentaway,则本地执行关闭展示连接的步骤,使用 presentationConnectioncloseReasoncloseMessage

用户代理关闭展示连接 时,它 MUST 执行以下步骤:

输入
presentationConnection,要关闭的 展示连接
closeReason,描述关闭连接原因的 PresentationConnectionCloseReason
closeMessage,包含关闭连接原因的可读消息。
  1. 如果有待处理的 关闭展示连接 任务,或者 关闭展示连接 任务已经为 presentationConnection 运行过,则中止剩余步骤。
  2. 排队执行展示 API 任务,执行以下步骤:
    1. 如果 展示连接状态 不是 connectingconnectedclosed,则中止剩余步骤。
    2. 如果 presentationConnection 状态还不是 closed,则将其设置为 closed
    3. 如果 presentationConnection 是作为 监控传入展示连接 的结果在 接收浏览上下文 中创建的,请执行以下子步骤:
      1. 展示控制器集合 中移除 presentationConnection
      2. 使用 展示控制器集合 填充 展示控制器监控器
    4. 触发一个事件,名为 close, 该事件使用 PresentationConnectionCloseEvent 接口,并将 reason 属性初始化为 closeReason,并将 message 属性初始化为 closeMessage,目标为 presentationConnection。该事件不得冒泡且不得取消。

6.5.6 在控制浏览上下文中终止展示

控制用户代理在控制浏览上下文中终止展示 时使用 connection,它 MUST 执行以下步骤:

  1. 如果 connection展示连接状态 不是 connectedconnecting,则中止这些步骤。
  2. 否则,对于 控制用户代理 中的 受控展示集合 中的每个 known connection
    1. 如果 known connectionconnection演示标识符相等,并且 known connection演示连接状态connectedconnecting,则在给定 known connection相关全局对象演示任务源将全局任务排队以运行以下步骤:
      1. known connection演示连接状态设置为 terminated
      2. known connection触发一个名为 terminate 的事件。
  3. 并行地,使用实现特定的机制,向其接收用户代理发送该演示的终止请求

6.5.7 在接收浏览上下文中终止展示

当以下任意情况发生时,接收用户代理 MUST 终止在接收浏览上下文中的展示

  1. 接收用户代理将要卸载接收浏览上下文对应的文档,例如,响应将该上下文导航到新资源的请求。
  2. 用户通过 接收用户代理 请求终止展示。
    Note

    这可能是用户的显式操作,也可能是用户代理的策略。例如,接收用户代理 可能配置为终止所有 PresentationConnection 对象已关闭 30 分钟的展示。

  3. 控制用户代理 发送终止请求 给该展示的 接收用户代理

接收用户代理终止在接收浏览上下文中的展示 时,它 MUST 执行以下步骤:

  1. P 为要终止的展示,allControllers 为为 P 创建的 展示控制器集合,并将 connectedControllers 设置为空列表。
  2. 对于 allControllers 中的每个 connection,运行以下步骤:
    1. 如果 connection展示连接状态connected,则将 connection 添加到 connectedControllers
    2. connection展示连接状态 设置为 terminated
  3. 如果 P 存在一个接收浏览上下文,并且它有一个用于 P 的尚未卸载的文档,则卸载与该浏览上下文对应的文档,从用户界面中移除该浏览上下文并将其丢弃。
  4. 对于 connectedControllers 中的每个 connection,使用特定实现机制向拥有 connection控制用户代理 发送展示的终止确认。
    Note

    每个 控制用户代理 只需发送一次终止确认。

6.5.8 在控制用户代理中处理终止确认

接收用户代理 发送展示 P 的终止确认,并且该确认由 控制用户代理 接收时,控制用户代理 MUST 运行以下步骤:

  1. 对于受控演示集中连接到 P 的每个 connection,在给定 connection相关全局对象演示任务源将全局任务排队以运行以下步骤:
    1. 如果 connection演示连接状态不是 connectedconnecting,则中止以下步骤。
    2. connection演示连接状态设置为 terminated
    3. connection触发一个名为 terminate 的事件。

6.5.9 事件处理程序

以下是必须支持的事件处理程序(及其对应的事件处理程序事件类型),作为实现 PresentationConnection 接口的对象的事件处理程序 IDL 属性:

事件处理程序 事件处理程序事件类型
onmessage message
onconnect connect
onclose close
onterminate terminate

6.6 接口 PresentationReceiver

WebIDL[SecureContext, Exposed=Window]
interface PresentationReceiver {
  readonly attribute Promise<PresentationConnectionList> connectionList;
};

PresentationReceiver 接口允许 接收浏览上下文 访问 控制浏览上下文 并与其通信。PresentationReceiver 接口 MUST 实现在由 接收用户代理 提供的 接收浏览上下文 中。

当获取时,connectionList 属性 MUST 返回运行以下步骤的结果:

  1. 如果 展示控制器的 promise 不为 null,则返回 展示控制器的 promise 并中止所有剩余步骤。
  2. 否则,令 演示控制器 promise 为一个新的 Promise,它在此 PresentationReceiver 对象的JavaScript 领域中构造。
  3. 返回 展示控制器的 promise
  4. 如果 展示控制器监视器 不为 null,则使用 展示控制器监视器 解析 展示控制器的 promise

6.6.1 创建接收浏览上下文

用户代理创建一个接收浏览上下文 时,它 MUST 运行以下步骤:

输入
D,由用户选择的 展示设备
presentationUrl展示请求 URL
presentationId展示标识符
  1. 创建一个新的顶层浏览上下文 C,设置为在 D 上显示内容。
  2. C会话历史设置为空列表。
  3. C 上设置沙盒模态框标志沙盒辅助导航浏览上下文标志
  4. 如果接收用户代理实现了 [PERMISSIONS],则将 C 的所有权限描述符类型权限状态设置为 "denied"
  5. C 创建一个新的空 Cookie 存储
  6. C 创建一个新的空存储,用于保存HTTP 身份验证状态。
  7. C 创建一个新的空存储,用于会话存储区域本地存储区域
  8. 如果接收用户代理实现了 [INDEXEDDB],则为 C 的 IndexedDB 数据库创建一个新的空存储。
  9. 如果接收用户代理实现了 [SERVICE-WORKERS],则为 C 创建一个新的空服务工作者注册列表和一个新的空Cache 对象集合。
  10. C 导航presentationUrl
  11. 使用 presentationIdpresentationUrlC 开始监视传入的演示连接

由呈现的文档创建的所有子可导航对象,即以接收浏览上下文作为其顶层浏览上下文的子可导航对象,必须也具有上述限制 2-4。此外,它们必须设置无用户激活的沙盒顶层导航浏览上下文标志。所有这些浏览上下文必须也为上面列出的功能 5-10 共享相同的浏览状态(存储)。

顶层浏览上下文尝试导航到新资源并运行导航步骤时,它必须遵循步骤 1 来确定是否允许导航。此外,它不得允许将自身导航到新资源,除非通过导航到片段标识符或通过重新加载其文档

注意

这允许用户根据选择演示显示器时显示的演示 URL 的来源授予权限。

如果顶层浏览上下文不允许导航,则它不应提供在新顶层浏览上下文中打开资源的功能,但否则导航步骤保持一致。

接收浏览上下文及其后代可导航对象关联的窗口客户端工作者客户端不得暴露给相互关联的服务工作者

接收浏览上下文终止时,与其关联的任何服务工作者以及其后代可导航对象中的浏览上下文必须被注销和终止。与接收浏览上下文及其后代可导航对象中的浏览上下文关联的任何浏览状态,包括会话历史Cookie 存储、任何HTTP 身份验证状态、任何数据库会话存储区域本地存储区域服务工作者注册列表和Cache 对象必须被丢弃,并且不得用于任何其他浏览上下文

注意

此算法旨在创建一个明确定义的环境,以便为单用户代理双用户代理演示提供可互操作的行为,并最大限度地减少用于双用户代理演示的演示显示器上剩余的状态量。

接收用户代理接收浏览上下文中使用反映控制用户代理语言首选项的 HTTP Accept-Language 标头获取资源(即,使用控制用户代理本应发送的相同 Accept-Language)。这将有助于接收用户代理使用反映用户首选项的字体和特定于区域设置的属性来呈现演示。

注意

鉴于演示显示器的操作环境,某些 Web API 将因设计原因(例如,需要用户输入)而无法工作,或者将过时(例如,尝试窗口管理);接收用户代理应意识到这一点。此外,任何模态用户界面都需要仔细处理。沙盒模态框标志设置在接收浏览上下文上,以防止大多数这些操作。

注意

一致性中所述,既是控制用户代理又是接收用户代理的用户代理可以允许接收浏览上下文创建其他演示(从而也成为控制浏览上下文)。Web 开发人员可以使用 navigator.presentation.receiver 来检测文档何时作为接收浏览上下文加载。

6.7 接口 PresentationConnectionList

WebIDL[SecureContext, Exposed=Window]
interface PresentationConnectionList : EventTarget {
  readonly attribute FrozenArray<PresentationConnection> connections;
  attribute EventHandler onconnectionavailable;
};

connections 属性 MUST 返回在 展示控制器集合 中的非终止 展示连接 集合。

6.7.1 监控传入的展示连接

接收用户代理 开始在 接收浏览上下文监控传入的展示连接 来自 控制浏览上下文 时,必须 监听并接受来自 控制浏览上下文 的传入连接请求,使用实现特定的机制。当收到来自 控制浏览上下文 的新连接请求时,接收用户代理 必须 运行以下步骤:

输入
I,由 控制浏览上下文 随传入连接请求一起传递的 展示标识符
presentationId,用于 创建接收浏览上下文展示标识符
presentationUrl,用于 创建接收浏览上下文展示请求URL
  1. 断言:此操作正在并行运行。
  2. 如果 presentationIdI 不相等,拒绝连接并终止所有剩余步骤。
  3. 创建一个新的 PresentationConnection S
  4. S展示标识符 设置为 I
  5. S展示URL 设置为 presentationUrl
  6. 使用实现特定的机制在控制和 接收浏览上下文 之间建立连接。
  7. 如果连接建立成功,设置 S展示连接状态connected。否则,设置 S展示连接状态closed,并终止所有剩余步骤。
  8. S 添加到 展示控制器集合 中。
  9. 如果演示控制器监视器null,则并行运行以下步骤。
    1. 展示控制器监视器 为一个新构建的 PresentationConnectionList,构建在 JavaScript 环境 中的 PresentationReceiver 对象。
    2. 使用 展示控制器集合 填充 展示控制器监视器
    3. 如果 展示控制器承诺 不为 null队列一个展示API任务,并 解决 展示控制器承诺展示控制器监视器
    4. 终止所有剩余步骤。
  10. 否则,并行运行以下步骤。
    1. 使用演示控制器集合填充演示控制器监视器
    2. 将演示 API 任务排队,以在演示控制器监视器触发一个名为 connectionavailable 的事件,该事件使用 PresentationConnectionAvailableEvent 接口,并将 connection 属性初始化为 S。该事件不得冒泡且不得取消。

6.7.2 事件处理程序

以下是由实现 PresentationConnectionList 接口的对象支持的事件处理程序(及其对应的事件处理程序事件类型),作为事件处理程序的IDL属性:

事件处理程序 事件处理程序事件类型
onconnectionavailable connectionavailable

7. 安全性和隐私考虑

本节是非规范性的。

7.1 个人身份信息

change 事件在 PresentationAvailability 对象上触发,揭示了关于通过浏览器的局域网发现的 演示显示器 存在或不存在的一个信息位。 这可以与其他信息结合使用来对用户进行指纹识别。然而,该信息也依赖于用户的本地网络环境,因此风险最小化。

API 允许 监视可用演示显示器的列表。用户代理如何确定给定 URL 与 演示显示器 的兼容性和可用性是实现细节。如果 控制用户代理演示请求 URLDIAL 应用程序匹配以确定其可用性,则此功能可以用来探测关于用户未同意的情况下在 演示显示器 上安装的哪些 DIAL 应用程序的信息。

7.2 跨域访问

一个 演示 允许跨域访问; 演示 URL演示标识符 是唯一需要的信息,可用于从控制用户代理的任何源重新连接到演示。 换句话说,演示并不与特定的打开源绑定。

这种设计允许来自不同来源的控制上下文连接到共享的演示资源。 演示标识符的安全性防止任意来源连接到现有的演示。

本规范还允许 接收用户代理 发布有关其 受控演示集 的信息,并允许 控制用户代理 重新连接到从其他设备启动的演示。 这在控制浏览上下文通过用户、本地存储或服务器获取正在运行的演示的 演示 URL演示标识符 后成为可能, 然后通过 reconnect 连接到演示。

本规范不保证任何连接到演示的方的身份。 一旦连接,演示可以通过应用程序特定的方式进一步验证连接方的身份。 例如,演示可以要求控制器通过 send 提供一个令牌, 演示将使用该令牌来验证身份和授权。

7.3 用户界面指南

来源显示

当在选择演示显示器的步骤中,要求用户允许使用演示显示器时,控制用户代理应明确指出哪个来源正在请求演示以及将呈现哪个来源。

显示请求演示的来源将帮助用户理解是哪个内容发出的请求,尤其是在请求由子可导航对象发起时。例如,嵌入式内容可能会试图诱使用户点击以触发启动不需要的演示的请求。

无用户激活的沙盒顶层导航浏览上下文标志设置在接收浏览上下文上,以强制演示的顶层来源在演示的整个生命周期内保持不变。

跨设备访问

当用户启动演示时,用户最初将独占控制演示。然而,Presentation API 允许其他设备(可能属于不同用户)连接并因此也控制演示。当第二台设备连接到演示时,建议所有已连接的控制用户代理通过浏览器通知其用户,原始用户已失去独占访问权限,现在有多个控制器控制演示。

此外,接收用户代理可能能够接收用户输入并充当演示显示。在这种情况下,当接收浏览上下文受远程方控制时(即有一个或多个已连接的控制器),接收用户代理应通过浏览器通知其用户。

7.4 设备访问

Presentation API 抽象了显示设备的“本地”含义,这意味着它将网络可访问的显示设备暴露为仿佛它们直接连接到用户的设备。Presentation API 要求页面访问任何显示设备时必须获得用户许可,以减轻可能出现的问题,例如在他人可见的显示设备上显示不需要的内容。

7.5 临时标识符和浏览器状态

演示 URL演示标识符可用于从另一个浏览上下文连接到演示。如果攻击者能够向控制页面注入内容,则这些信息可能会被拦截。

7.6 私密浏览模式和清除浏览数据

演示中显示的内容与控制器不同。特别是,如果用户在两个上下文中都登录,然后从控制浏览上下文中注销,他们不会自动从接收浏览上下文中注销。使用身份验证的应用程序在设备之间通信时应格外小心。

当用户请求“清除浏览数据”时,用户代理已知的演示集应被清除。

当处于私密浏览模式(“隐身”)时,该浏览会话中的初始受控演示集必须为空。会话终止时,添加到其中的任何演示连接必须被丢弃。

7.7 演示连接之间的消息传递

本规范不会规定控制浏览上下文接收浏览上下文之间的通信协议,但应为相应的演示连接之间的消息机密性和真实性提供某些保证。

A. IDL 索引

WebIDLpartial interface Navigator {
  [SecureContext, SameObject] readonly attribute Presentation presentation;
};

[SecureContext, Exposed=Window]
interface Presentation {
};

partial interface Presentation {
  attribute PresentationRequest? defaultRequest;
};

partial interface Presentation {
  readonly attribute PresentationReceiver? receiver;
};

[SecureContext, Exposed=Window]
interface PresentationRequest : EventTarget {
  constructor(USVString url);
  constructor(sequence<USVString> urls);
  Promise<PresentationConnection> start();
  Promise<PresentationConnection> reconnect(USVString presentationId);
  Promise<PresentationAvailability> getAvailability();

  attribute EventHandler onconnectionavailable;
};

[SecureContext, Exposed=Window]
interface PresentationAvailability : EventTarget {
  readonly attribute boolean value;

  attribute EventHandler onchange;
};

[SecureContext, Exposed=Window]
interface PresentationConnectionAvailableEvent : Event {
  constructor(DOMString type, PresentationConnectionAvailableEventInit eventInitDict);
  [SameObject] readonly attribute PresentationConnection connection;
};

dictionary PresentationConnectionAvailableEventInit : EventInit {
  required PresentationConnection connection;
};

enum PresentationConnectionState { "connecting", "connected", "closed", "terminated" };

[SecureContext, Exposed=Window]
interface PresentationConnection : EventTarget {
  readonly attribute USVString id;
  readonly attribute USVString url;
  readonly attribute PresentationConnectionState state;
  undefined close();
  undefined terminate();
  attribute EventHandler onconnect;
  attribute EventHandler onclose;
  attribute EventHandler onterminate;

  // Communication
  attribute BinaryType binaryType;
  attribute EventHandler onmessage;
  undefined send (DOMString message);
  undefined send (Blob data);
  undefined send (ArrayBuffer data);
  undefined send (ArrayBufferView data);
};

enum PresentationConnectionCloseReason { "error", "closed", "wentaway" };

[SecureContext, Exposed=Window]
interface PresentationConnectionCloseEvent : Event {
  constructor(DOMString type, PresentationConnectionCloseEventInit eventInitDict);
  readonly attribute PresentationConnectionCloseReason reason;
  readonly attribute DOMString message;
};

dictionary PresentationConnectionCloseEventInit : EventInit {
  required PresentationConnectionCloseReason reason;
  DOMString message = "";
};

[SecureContext, Exposed=Window]
interface PresentationReceiver {
  readonly attribute Promise<PresentationConnectionList> connectionList;
};

[SecureContext, Exposed=Window]
interface PresentationConnectionList : EventTarget {
  readonly attribute FrozenArray<PresentationConnection> connections;
  attribute EventHandler onconnectionavailable;
};

B. 索引

B.1 本规范定义的术语

B.2 引用定义的术语

C. 致谢

感谢 Addison Phillips、Anne Van Kesteren、Anssi Kostiainen、Anton Vayvod、Chris Needham、Christine Runnegar、Daniel Davis、Domenic Denicola、Erik Wilde、François Daoust、闵洪波 (Hongbo Min)、Hongki CHA、Hubert Sablonnière、Hyojin Song、Hyun June Kim、Jean-Claude Dufourd、Joanmarie Diggs、Jonas Sicking、Louay Bassbouss、Mark Watson、Martin Dürst、Matt Hammond、Mike West、Mounir Lamouri、Nick Doty、Oleg Beletski、Philip Jägenstedt、Richard Ishida、Shih-Chiang Chien、Takeshi Kanai、Tobie Langel、Tomoyuki Shimizu、Travis Leithead、Wayne Carr 对本草案的编辑、审阅和反馈的帮助。

AirPlayHDMIChromecastDLNAMiracast 分别是 Apple Inc.、HDMI Licensing LLC.、Google Inc.、数字生活网络联盟 (Digital Living Network Alliance) 和 Wi-Fi 联盟的注册商标。它们仅作为背景信息引用,且实施此规范时不需要使用这些商标。

D. 候选推荐退出标准

要将此规范推进到建议推荐状态,每个符合性类别(控制用户代理接收用户代理)至少需要两个独立的、可互操作的实现。每个功能的实现可以来自不同的产品,并且不要求所有功能由单个产品实现。此外,控制用户代理的实现必须包括至少一个1-UA 模式的实现,以及一个2-UA 模式的实现。2-UA 模式的实现可能只支持非 http/https 的演示 URL。接收用户代理的实现可能不包括2-UA 模式的实现。

API 最近限制为仅适用于安全上下文。在早期实现中,弃用非安全上下文中的 API 需要时间。如果存在计划在未来限制这些实现,工作组可能会请求在非安全上下文中暴露 API 的实现转变为建议推荐状态。

为了符合这些标准,我们定义了以下术语:

独立
每个实现必须由不同的实体开发,并且不能共享、重用或派生自另一个合格实现中使用的代码。与规范的实现无关的代码部分不受此要求的限制。
可互操作
通过官方测试套件中的相应测试用例。
实现
一个用户代理,它:
  1. 实现了规范的符合性类别之一。
  2. 可供公众使用。实现可以是发货产品或其他公开可用的版本(即,Beta 版本、预览发布或“夜间构建”)。非发货产品发布必须至少在一个月内实现该功能以证明其稳定性。
  3. 不是实验性的(即专为通过测试套件设计且不用于未来正常使用的版本)。

E. 变更记录

本节为非规范性内容。

本节列出了自2016年7月首次发布为候选推荐以来对规范所做的更改,并提供了与工作组问题跟踪器上的相关问题的链接。

E.1 自2017年6月1日以来的更改

E.2 自2016年7月14日以来的更改

F. 参考文献

F.1 规范性引用

[DIAL]
发现与启动协议规范。 Netflix;YouTube。Netflix。网址:http://www.dial-multiscreen.org/dial-protocol-specification
[dom]
DOM 标准。Anne van Kesteren。WHATWG。现行标准。网址:https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范。Ecma International。网址:https://tc39.es/ecma262/multipage/
[fileapi]
文件 API。Marijn Kruisselbrink。W3C。2024年12月4日。W3C 工作草案。URL:https://www.w3.org/TR/FileAPI/
[HTML]
HTML 标准。Anne van Kesteren;Domenic Denicola;Dominic Farolino;Ian Hickson;Philip Jägenstedt;Simon Pieters。WHATWG。现行标准。网址:https://html.spec.whatwg.org/multipage/
[INDEXEDDB]
索引数据库 API。Nikunj Mehta;Jonas Sicking; Eliot Graff;Andrei Popescu;Jeremy Orlow;Joshua Bell。W3C。2015年1月8日。W3C 推荐标准。网址:https://www.w3.org/TR/IndexedDB/
[infra]
基础设施标准。Anne van Kesteren;Domenic Denicola。 WHATWG。现行标准。网址:https://infra.spec.whatwg.org/
[PERMISSIONS]
权限。Marcos Caceres;Mike Taylor。W3C。2024年12月20日。W3C 工作草案。URL:https://www.w3.org/TR/permissions/
[RFC2119]
在 RFC 中使用关键字以指示要求级别。S. Bradner。 IETF。1997年3月。最佳当前实践。网址:https://www.rfc-editor.org/rfc/rfc2119
[RFC4122]
通用唯一标识符 (UUID) URN 命名空间。P. Leach; M. Mealling;R. Salz。IETF。2005年7月。提议标准。网址:https://www.rfc-editor.org/rfc/rfc4122
[RFC6265]
HTTP 状态管理机制。A. Barth。IETF。2011年4月。 提议标准。网址:https://httpwg.org/specs/rfc6265.html
[RFC8174]
RFC 2119 中大写与小写的歧义。B. Leiba。 IETF。2017年5月。最佳当前实践。网址:https://www.rfc-editor.org/rfc/rfc8174
[RFC9110]
HTTP 语义。R. Fielding 编辑; M. Nottingham 编辑;J. Reschke 编辑。IETF。2022年6月。互联网标准。URL:https://httpwg.org/specs/rfc9110.html
[secure-contexts]
安全上下文。Mike West。W3C。 2023年11月10日。CRD。URL:https://www.w3.org/TR/secure-contexts/
[SERVICE-WORKERS]
服务工作线程。Jake Archibald; Marijn Kruisselbrink。W3C。2022年7月12日。CRD。URL:https://www.w3.org/TR/service-workers/
[url]
URL 标准。Anne van Kesteren。WHATWG。现行标准。网址:https://url.spec.whatwg.org/
[WEBIDL]
Web IDL 标准。Edgar Chen;Timothy Gu。WHATWG。 现行标准。网址:https://webidl.spec.whatwg.org/
[websockets]
WebSockets 标准。Adam Rice。WHATWG。现行标准。网址:https://websockets.spec.whatwg.org/

F.2 参考性引用

[webrtc]
WebRTC:浏览器中的实时通信。Cullen Jennings; Jan-Ivar Bruaroey;Henrik Boström;Florent Castelli。W3C。2024年10月8日。W3C 推荐标准。网址:https://www.w3.org/TR/webrtc/