キーボードロック

コミュニティ グループ報告書草案,

このバージョン:
https://wicg.github.io/keyboard-lock/
課題追跡:
GitHub
編集者:
(Google)
(Google)

概要

この仕様は、通常は基盤となるホスト オペレーティングシステムによって予約されているキーを、Web サイトが 捕捉できるようにする API を定義する。これは、フルスクリーンの没入型体験 (ゲームやリモートアクセスアプリなど)を提供する Web アプリケーションで使用されることを意図している。

この文書のステータス

この文書は、First Public Working Draft として提案された編集者草案である。

1. 導入

豊かにインタラクティブな Web サイト、ゲーム、およびリモート デスクトップ/アプリケーションのストリーミング体験は、 没入型のフルスクリーン体験を提供したい。そのためには、サイトは フルスクリーンモード中に、ナビゲーション、メニュー、またはゲーム機能に 使用できるよう、特別なキーやキーボードショートカットにアクセスする 必要がある。必要となる可能性があるキーの例としては、Escape、Alt+Tab、Cmd+`、および Ctrl+N がある。

既定では、これらのキーはブラウザーまたは基盤となるオペレーティング システムによって捕捉されるため、Web アプリケーションでは利用できない。 Keyboard Lock API は、OS によって許可される利用可能なすべてのキーを Web サイトが捕捉して使用できるようにする。

2. Keyboard Lock API

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute Keyboard keyboard;
};

2.2. Keyboard インターフェイス

[SecureContext, Exposed=Window]
interface Keyboard : EventTarget {
  Promise<undefined> lock(optional sequence<DOMString> keyCodes = []);
  undefined unlock();
};

keyboard オブジェクトは キーボードロックを有効にするを持つ。これは、 Keyboard Lock が有効なとき true に設定される boolean である。 既定では、これは false に設定される。

keyboard オブジェクトは 予約キーコードを持つ。これは、 DOMString の集合であり、それぞれは キーコード属性値として有効な値であり、[UIEvents-Code] で定義される。 既定では、この集合は空である(つまり、キーボードロックを有効にするが有効であれば、 すべてのキーを捕捉する)。

keyboard オブジェクトは キーボードロックタスクキューを持ち、これは 新しい並列キューを開始することの結果に初期化される。

注記: Keyboard インターフェイスは EventTarget から継承する。これは、 layoutchange のような キーボード関連イベントの一部を処理できる必要があるためである。

2.2.1. lock()

lock() が呼び出されたとき、ユーザーエージェントは 次の手順を実行しなければならない:

  1. p を新しい Promise とする。

  2. 現在アクティブな トップレベルブラウジングコンテキスト内で 現在実行されていない場合、次を実行する:

    1. p を "InvalidStateError" DOMException で却下する。

  3. 次の手順をキューに入れるキーボードロックタスクキューへ:

    1. 予約キーコードを空集合に リセットする。

    2. 任意の keyCodes 引数が存在する場合、次の サブ手順を実行する:

      1. keyCodes 内の 各文字列 key について:

        1. key が有効な キーコード属性 値でない場合、次を実行する:

          1. キーボードロックを 有効にするを false に設定する。

          2. p を "InvalidAccessError" DOMException で却下する。

        2. key予約キー コード追加する

    3. キーボードロックを有効にするが 現在 false である場合、次の サブ手順を実行する:

      1. 任意で、システムキー 押下ハンドラーを登録する

      2. キーボードロックを有効にするを true に設定する。

    4. lock() タスクが キーボードロックタスクキュー内で 保留中である場合、次を実行する:

      1. キーボードロックを有効にするを false に設定する。

      2. p を "AbortError" DOMException で却下する。

    5. p を解決する。

  4. p を返す。

注記: lock() が、間に unlock() 呼び出しを挟まずに複数回呼び出された場合、 最後の要求呼び出しで指定された keyCodes のみが有効になる。 2 回目の lock() 呼び出しが、最初の呼び出しが完了する前に行われた場合、 最初の呼び出しは "AbortError" で却下される。

すべてのキーを捕捉するには、単に 引数なしで lock() を呼び出す:
navigator.keyboard.lock();
"W"、"A"、"S"、および "D" キーを捕捉するには、 これらの各キーの キーコード属性値を含むリストを指定して lock() を呼び出す:
navigator.keyboard.lock(["KeyW", "KeyA", "KeyS", "KeyD"]);

これにより、キー押下でどの修飾キーが使用されているかにかかわらず、 これらのキーが捕捉される。標準の US QWERTY 配列を想定すると、 KeyW を登録すると、 "W"、Shift+"W"、Control+"W"、Control+Shift+"W"、および "W" を伴うその他すべての キー修飾組み合わせがアプリへ送信されることが保証される。同様に、KeyAKeyS、および KeyD についても同様である。

キーを要求することは、修飾されたすべてのバージョンが アプリで利用可能になることを保証するものではないことに注意。例として、 Delete を考える:
navigator.keyboard.lock(["Delete"]);

これにより、ほとんどの Delete キー押下(例: Shift+Delete、 Control+Delete、Shift+Control+Delete)は利用可能になるが、Windows では “secure attention sequence” である Control+Alt+Delete は利用可能にならない。

2.2.2. unlock()

unlock() が呼び出されたとき、ユーザーエージェントは 次の手順を実行しなければならない:

  1. 次の手順をキューに入れるキーボードロックタスクキューへ:

    1. キーボードロックを有効にするが true である場合、 次のサブ手順を実行する:

      1. システムキー 押下ハンドラーの登録を解除する

      2. キーボードロックを有効にするを false に設定する。

      3. 予約キーコードを 空シーケンスにリセットする。

注記: 文書が閉じられたとき、ユーザーエージェントは [system key press handler=](もしあれば)が登録解除されるように、暗黙的に unlock() を呼び出さなければならない。

3. キーボードキー押下の処理

3.1. システムキー押下ハンドラー

システムキー押下 ハンドラーは、プラットフォームレベルでキーをフィルターするために使用できる、 プラットフォーム固有のハンドラーである。Keyboard Lock 機能は、通常は ブラウザーで利用可能にされないキー押下(たとえば Cmd/Alt-Tab)へのアクセスを 提供することを意図しているため、ほとんどのプラットフォームでは特別なハンドラーを 設定する必要がある。

システムキー 押下ハンドラーは、次のプロパティを持たなければならない:

3.1.1. 登録

システムキー押下ハンドラーを登録するには、ユーザーエージェントは、 プラットフォームが新しいキー押下の処理を開始するたびに呼び出される 低レベルフックを追加するための、プラットフォーム固有の手順に従う必要がある。

システムキー押下ハンドラーを追加する正確なプロセスは、 プラットフォームごとに異なる。一般的なプラットフォーム上でキー押下を処理する低レベルフックを 登録する方法の例については、Windows については [LowLevelKeyboardProc]、Mac OS X については [QuartzEventServices]、X Windows については [GrabKeyboard] を参照。

注記: ユーザーエージェントが別の目的でキー押下ハンドラーを すでに登録している場合、上記の要件を満たすなら、そのハンドラーを任意で拡張して Keyboard Lock 機能をサポートしてもよい。

3.1.2. 登録解除

システムキー押下ハンドラーの登録を解除するには、 ユーザーエージェントは、新しいキー押下を処理するための(以前に追加された) 低レベルフックを削除するための、プラットフォーム固有の手順に従う必要がある。

システムキー押下ハンドラーの登録と同様に、システムキー押下ハンドラーの登録解除プロセスも プラットフォーム固有である。詳細および例については、§ 3.1.1 登録に 挙げられている参考文献を参照。

3.2. キーボードイベントの処理

ユーザーがキーを押したことに応答して、システムキー押下ハンドラー登録されている場合、 それは次の手順を実行するべきである:

  1. トップレベルブラウジングコンテキストの 現在フォーカスされている領域フルスクリーン要素が非 null である場合、 isFullscreen を true に設定する ([Fullscreen] を参照)。

    注記: たとえば、フォーカスを持つシステムダイアログが 表示されている場合、フルスクリーン要素はフォーカスを持たない。

  2. isFullscreen および キーボードロックを有効にするが すべて true に設定されている場合、次のサブ手順を実行する:

    1. keyEvent を新しいキー押下のキーイベントとする。

    2. code を、keyEventcode 属性の値とする。

    3. 予約キーコードが空である場合、または code予約キーコードに列挙されている場合、 次のサブ手順を実行する:

      1. code が "Escape" と等しい場合、次の サブ手順を実行する:

        1. 任意で、ユーザーが Escape キーを長押しして フルスクリーンから抜けられることを伝えるメッセージを 画面上に重ねて表示する。

        2. キーが 2 秒間押し続けられた場合、キーボードハンドラーから抜け、 通常処理のためにそのキーをユーザーエージェントへ渡す (これによりフルスクリーン(およびアクティブであれば pointer lock)を終了する)。

      2. 通常のユーザーエージェント処理を迂回して、keyEvent を フルスクリーン文書または要素へ直接ディスパッチする。

    4. そうでない場合、そのキーイベントを、通常処理されるのと同じように処理する。 すなわち、キーイベントをディスパッチするか、通常のキーボードショートカット動作を実行する。

注記: この API は「ベストエフォート」方式で動作する。 適合する実装が、可能なすべてのキー組み合わせについて OS の既定の動作を 上書きできることは要求されない。特に、ほとんどのプラットフォームには、 アプリケーションが上書きできない “secure attention sequence”(たとえば Windows 上の Ctrl-Alt-Del)があり、この仕様はそれに優先しない。

注記: この API を実装するとき、ユーザーエージェントは、 キーボードイベントがページへディスパッチされる順序を変更しないよう注意するべきである。 予約キーコードの集合に含まれるキーは、 その集合に含まれていなかった場合に送信されていたのと同じ相対順序でディスパッチされなければならない。

4. 他の Web プラットフォーム API との統合

[Fullscreen] および [PointerLock] は、ページがユーザー体験の一部 (それぞれ画面およびマウスカーソル)を一時的に制御できるようにする API である。 これらの機能の濫用に対する懸念から、これらはユーザーがその機能を終了するために 依拠できる "escape" または "unlock" ジェスチャーを提供する。既定では、 このジェスチャーは Escape キーを押すことであり、これはこの API によって捕捉できる キーの 1 つである。

4.1. Escape キーに関する特別な考慮事項

Escape キーに関連付けられた特別な動作があるため、lock() 要求に Escape キーが含まれる場合、ユーザーエージェントは変更された挙動を考慮して、 UX に追加の変更を加える必要があるかもしれない。

たとえば、JavaScript により開始されたフルスクリーンが有効化されたときに、 ユーザーエージェントが "Press ESC to exit fullscreen" というユーザーメッセージを表示する場合、 キーボードロックが有効なときには、そのメッセージを "Press and hold ESC to exit fullscreen" と読めるよう更新する必要がある。

フルスクリーンがすでに有効になった後でキーボードロックが有効化された場合、ユーザーは フルスクリーンを終了する方法に関する複数のメッセージを見る可能性がある。 このため、開発者はフルスクリーンに入る前に lock() を呼び出すことを推奨する:

navigator.keyboard.lock();
document.documentElement.requestFullscreen();

キーボードロックとフルスクリーンを終了するときにも、複数のユーザーメッセージに関する同様の懸念があるため、 逆の順序で呼び出すことが推奨される:

document.exitFullscreen();
navigator.keyboard.unlock();

一般に、開発者は、そのキーを実際に必要とする場合にのみ、ロックされるキーの集合に Escape キーを含めるべきである。また、Escape キーがロックされる場合でも、 ユーザーが現在の状態から抜けられるようにするという主たる意味を維持することが推奨される。

4.2. フルスクリーンに関する考慮事項

現代的なユーザーエージェントでは、2 つの異なる種類のフルスクリーンを利用できる: JavaScript により開始されるフルスクリーン([Fullscreen] API 経由)と、 ユーザーにより開始されるフルスクリーン(ユーザーがキーボードショートカットを使用して フルスクリーンに入る場合)である。ユーザーにより開始されるフルスクリーンは、 フルスクリーンモードへの出入りに使用される一般的なキーショートカットであるため、 しばしば "F11" フルスクリーンと呼ばれる。

F11 フルスクリーンと JavaScript(JS)フルスクリーンは同じようには動作しない。 ユーザーが F11 フルスクリーンに入った場合、それに入るために使用した同じ キーボードショートカットによってのみ終了できる。この場合、exitFullscreen() 関数は機能しない。さらに、JS フルスクリーンで通常発火するフルスクリーンイベントは、 F11 フルスクリーンでは送信されない。

これらの違いにより(また、F11 フルスクリーンの標準ショートカットがないため)、 Keyboard Lock API は、JavaScript により開始されたフルスクリーンがアクティブな場合にのみ有効である。 F11 フルスクリーン中は、キーボードイベントに対する Keyboard Lock 処理は行われない。

5. Pointer Lock に関する考慮事項

先に述べた UX 変更以外に、Pointer Lock の動作に変更はない。

Pointer Lock がフルスクリーンの外で有効になっている場合、Keyboard Lock は 有効にできない。

Pointer Lock、Keyboard Lock、および Fullscreen がすべて有効である場合、 Keyboard Lock に Escape キーが含まれていない限り、挙動は変更されない。その場合、 唯一の変更は UX に対するものである(上記のとおり)。

6. モバイルデバイスに関する考慮事項

これはキーボードに焦点を当てた API であり、モバイルデバイスは一般に 物理キーボードを持たないため、この API は通常、モバイルデバイス上に存在せず、 サポートされない。

ただし、物理キーボードが接続されている場合に意味があるなら、モバイルデバイスは この API をサポートすることを選択してもよい。

7. セキュリティに関する考慮事項

この API に関する 1 つの懸念は、すべてのキーを奪い取り、 ([Fullscreen] および [PointerLock] API と組み合わせて) ユーザーが Web ページから抜けることを妨げるために使用され得ることである。

これを防ぐため、ユーザーエージェントは、すべてのキーが API によって要求されている場合でも、 ユーザーがキーボードロックから抜けるための方法を提供しなければならない。

この仕様は、長い(2 秒を超える)Escape キー押下により Keyboard Lock からの終了を トリガーできるようにするサポートを要求する。さらに、ユーザーエージェントは Keyboard Lock を終了する代替手段を提供することを選択してもよい。

8. プライバシーに関する考慮事項

該当なし。この API は、現在のユーザーに関する個人情報を使用したり 明らかにしたりしない。

9. 謝辞

この提案の作成につながった議論を行った次の人々に感謝する:

Jon Dahlke (Google), Joe Downing (Google)

適合性

文書の慣例

適合性要件は、記述的な表明と 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/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[Fullscreen]
Philip Jägenstedt. Fullscreen API Standard. Living Standard. URL: https://fullscreen.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. RFC において要求レベルを 示すために用いるキーワード. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[UIEVENTS]
Gary Kacmarcik; Travis Leithead; Doug Schepers. UI Events. 2019年5月30日. WD. URL: https://www.w3.org/TR/uievents/
[UIEvents-Code]
Gary Kacmarcik; Travis Leithead. UI Events KeyboardEvent code Values. 2017年6月1日. CR. URL: https://www.w3.org/TR/uievents-code/
[WebIDL]
Boris Zbarsky. Web IDL. 2016年12月15日. ED. URL: https://heycam.github.io/webidl/

参考情報文献

[GrabKeyboard]
X11 GrabKeyboard API. URL: https://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html#requests:GrabKeyboard
[LowLevelKeyboardProc]
MSDN 上の LowLevelKeyboardProc 文書. URL: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644985(v=vs.85).aspx
[PointerLock]
Vincent Scheib. Pointer Lock. 2016年10月27日. REC. URL: https://www.w3.org/TR/pointerlock/
[QuartzEventServices]
Quartz Event Services. URL: https://developer.apple.com/reference/coregraphics/1658572-quartz_event_services

IDL 索引

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute Keyboard keyboard;
};

[SecureContext, Exposed=Window]
interface Keyboard : EventTarget {
  Promise<undefined> lock(optional sequence<DOMString> keyCodes = []);
  undefined unlock();
};