键盘映射

社区组报告草案

本版本:
https://wicg.github.io/keyboard-map/
问题跟踪:
GitHub
编辑者:
(Google)
说明文档:
键盘映射说明文档

摘要

本规范定义了一个 API,允许网站将给定的 code 值转换为可以展示给用户、用于标识该按键的有效 key 值。 从 codekey 的转换基于用户当前选择的键盘布局。 该功能主要用于那些希望将键盘视为一组按钮并需向用户描述这些按钮的 Web 应用。

本文档状态

本规范由 Web 平台孵化器社区组 发布。 它不是 W3C 标准,也未进入 W3C 标准流程。 请注意,根据 W3C 社区贡献者许可协议(CLA),有部分有限的选择退出权和其他条件适用。 了解更多 W3C 社区与商业组 信息。

本文档是作为首个公开工作草案提出的编辑者草稿。

1. 简介

KeyboardEvent 上,code 属性编码了一个值,表示被按下按键的物理位置。该值忽略当前的区域(如 "en-US")、布局(如 "dvorak")和修饰键状态(如 "Shift + Control"),因此非常适合那些希望将键盘作为通用按钮集来使用的应用(如游戏)。code 属性的理念是为每个物理键提供一个平台无关的 扫描码

key 属性则包含了按键按下后生成的值,会受到区域、布局和修饰键的影响。几乎每个 Unicode 字符都是有效的 `key` 属性值,同时还包含许多特殊的命名值(见 KeyboardEvent key 属性值),因此可能的 key 值有成千上万种。

由于大多数用户拥有与其区域及布局匹配的物理键盘,我们合理地假设 key 值可以很好地代表印在按键上的字符。当用户选择了不同的键盘布局时,这一假设虽然不再成立,但此时用户也很清楚实际输出字符和键帽印刷不符的情况。

本文描述的 API 提供了一种简单方法,从基础的 code 获取 key 的映射。

2. 键盘映射 API

键盘映射 API 扩展了 Keyboard 接口,增加了与当前键盘布局相关的属性和方法。

keyboard 属性在 Navigator 对象上的定义详见 Keyboard-Lock

2.2. KeyboardLayoutMap 接口

KeyboardLayoutMap

当前仅有一个引擎支持。

Firefox不支持Safari不支持Chrome69+
Opera55+Edge79+
Edge(旧版)不支持IE不支持
Firefox for Android不支持iOS Safari不支持Chrome for Android69+Android WebView69+Samsung Internet10.0+Opera Mobile48+
[Exposed=Window]
interface KeyboardLayoutMap {
  readonly maplike<DOMString, DOMString>;
};

KeyboardLayoutMap 是一个只读集合,用于将 code 值映射为 key 值。

2.3. Keyboard 接口

Keyboard

当前仅有一个引擎支持。

Firefox不支持Safari不支持Chrome68+
Opera55+Edge79+
Edge(旧版)不支持IE不支持
Firefox for Android不支持iOS Safari不支持Chrome for Android68+Android WebView68+Samsung Internet10.0+Opera Mobile48+
partial interface Keyboard {
  Promise<KeyboardLayoutMap> getLayoutMap();

  attribute EventHandler onlayoutchange;
};

注: 基础的 Keyboard 接口定义见 [Keyboard-Lock]

2.3.1. getLayoutMap()

Keyboard/getLayoutMap

当前仅有一个引擎支持。

Firefox不支持Safari不支持Chrome69+
Opera56+Edge79+
Edge(旧版)不支持IE不支持
Firefox for Android不支持iOS Safari不支持Chrome for Android69+Android WebView69+Samsung Internet10.0+Opera Mobile48+

调用 getLayoutMap() 时,用户代理必须执行以下步骤:

  1. p 为一个新的 Promise

  2. 如果 this相关全局对象关联文档不被允许使用名为 "keyboard-map" 的 策略控制特性

    1. 拒绝 p,错误为 "SecurityError" DOMException

    2. 返回 p

  3. 按下列步骤 并行执行

    1. map 为一个新的,初始为空的 KeyboardLayoutMap

    2. 遍历 书写系统按键表格的 "KeyboardEvent code" 列中的每个值 code

      1. layout 为最高优先级的 ASCII 支持布局; 或者如果没有此类布局,则选择最高优先级的布局。

      2. 如果 codelayout 中不是有效键,则跳过。

      3. key 为按下 code(无修饰键)时 layout 产生的 key 值。

      4. 如果 key死键,则:

        1. key 设置为与 死键对应的独立字符(见 死键单独字符对照表)。

      5. codekey 创建一个映射条目 e

      6. e 加入 map

    3. map 解决 p

  4. 返回 p

用户代理可自行选择对 map 进行缓存,只要在键盘布局更改时更新(或失效)缓存内容即可。

在游戏中展示该按哪个键进行操作的示例:
navigator.keyboard.getLayoutMap().then(function(map) {
  var keyUp = map.get("KeyW");
  showUserDialog("按 " + keyUp + " 进行上移。");
});
在 "US International" 键盘上,单引号 (') 键是一个 死键,用于给后续字符添加锐音符号。 键盘映射会包含一条,将 Quote(此键的 code)映射到 "'" (单引号,U+0027)。

2.4. 死键与组合字符

由于键盘映射 API 的目的是为用户键盘上的键提供可读描述,死键或 组合字符需要转换为可以直接使用的独立形式。

下表定义了常见死键和 组合字符如何映射为独立字符。

表 1:死键的独立字符等价表
死键
名称
Unicode
组合
独立
字符
Unicode
独立字符
Grave U+0300 "`" U+0060
Acute U+0301 "'" U+0027
Circumflex U+0302 "^" U+005e
Tilde U+0303 "~" U+007e
Diaeresis U+0308 "¨" U+00a8

2.5. 支持 ASCII 的键盘布局

支持 ASCII 的键盘布局指:

常用书写系统按键 是所有 键盘布局均包含的按键,如 [UIEvents-Code] 规范的 图 13 蓝色部分。

3. 键盘事件

3.1. layoutchange 事件

当用户代理检测到当前键盘布局已更改时,必须触发 layoutchange 事件。布局变化的具体时机取决于底层平台,通常会在用户主动操作(如选择新布局)时触发,或因切换到偏好特定布局的应用间接触发。

请注意以下几点:

如果用户代理在非前台应用时发生键盘布局更改,当其重新获得焦点时 MUST 触发 layoutchange 事件

处理此事件的示例:
navigator.keyboard.addEventListener("layoutchange", function() {
  // 更新用户键盘映射设置
  updateGameControlSettingsPage();
});

3.1.1. 触发 layoutchange 事件

要触发 "layoutchange" 事件,需在用户代理的 keyboard 属性(Navigator 对象)上,派发名为 layoutchange 的事件

4. 集成

4.1. 权限策略

本规范定义了一个控制 getLayoutMap() 方法是否在 Keyboard 接口上暴露的特性。

该特性的名称为 "keyboard-map"。

该特性的默认允许列表为 "self"。

5. 移动设备注意事项

由于该 API 以键盘为核心,且移动设备通常没有物理键盘,因此该 API 一般不会在移动设备上实现或支持。

但若移动设备允许物理键盘连接,也可选择支持该 API。在这种情况下,它们可以返回适合平台的一部分书写系统按键

对于需要用户手动配置物理键盘布局(且没有合理默认项)的移动平台,也可通过返回一个内容为空的布局映射支持此 API。

6. 安全注意事项

本 API 返回静态数据,不会改变系统状态,因此没有特殊的安全风险。

7. 隐私注意事项

如同所有返回设备当前状态信息的 API 一样,提供此 API 后,可能会被用于扩大用户的“指纹”特征,从而增加用户被识别的风险。

通过只返回最高优先级支持 ASCII 的键盘布局的信息(而不是活动布局),可以降低该信息被用于指纹识别的价值,因为用户更容易获得相同映射。

注意以下几种可能利用布局信息识别个体的情形:

如果没有此 API,也可以尝试类似的指纹识别,但难度更高,因为需用户实际输入字符后分析生成的 KeyboardEvent

7.1. 隐私缓解措施

作为用户第一道防线,本规范要求该 API 只能在安全上下文中,且只能由当前活动顶层浏览上下文调用,或通过策略控制特性授予访问权限。

担心键盘映射信息隐私风险的用户代理,还可以考虑如下缓解措施:

7.2. 隐私模式

如果用户代理提供“隐身”或“隐私模式”,该 API 表现应与普通模式一致。这是因为不存在通用的中性返回值能确保用户隐私。

用户代理可以允许用户自行设定隐私模式下返回的值,但应注意须避免让用户产生安全错觉,因为该值在用户离开本地区时需及时调整。

8. 致谢

感谢以下人员的讨论促成了本提案的产生:

Hadley Beeman(W3C TAG), Joe Downing(Google), Masayuki Nakano(Mozilla), Julien Wajsberg(Mozilla)

9. 术语表

扫描码

键盘硬件分配给每个按键以便独立识别的值。 参见 https://en.wikipedia.org/wiki/Scancode 获取更多信息。

一致性

文档约定

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

除非明确标记为非规范性内容、示例和注释,否则本规范的所有文本都是规范性的。[RFC2119]

本规范中的示例以 “例如” 引入,或与规范性内容用 class="example" 区分, 如下所示:

这是一个说明性示例。

说明性注释以 “注” 开头,且与规范性文本用 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/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[Keyboard-Lock]
Keyboard Lock. cg-draft. URL: https://wicg.github.io/keyboard-lock/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. 16 July 2020. WD. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[UIEVENTS]
Gary Kacmarcik; Travis Leithead. UI Events. 1 June 2022. WD. URL: https://www.w3.org/TR/uievents/
[UIEvents-Code]
Gary Kacmarcik; Travis Leithead. UI Events KeyboardEvent code Values. 1 June 2017. CR. URL: https://www.w3.org/TR/uievents-code/
[UIEVENTS-KEY]
Gary Kacmarcik; Travis Leithead. UI Events KeyboardEvent key Values. 1 June 2017. CR. URL: https://www.w3.org/TR/uievents-key/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL 索引

[Exposed=Window]
interface KeyboardLayoutMap {
  readonly maplike<DOMString, DOMString>;
};

partial interface Keyboard {
  Promise<KeyboardLayoutMap> getLayoutMap();

  attribute EventHandler onlayoutchange;
};