フルスクリーンでゲームなどをプレイしているときに、うっかりESCを押してしまってフルスクリーンから脱出してしまって興がそがれた、なんて経験はありませんか?
フルスクリーンモードでは、ESC
をキャプチャすることができません。
またCtrl+W
やWindows+D
といったOSによって制御されるショートカットは、そもそもJavaScriptから操作することができません。
ということで、そのあたりのキーボード入力を制御できるようにしようというAPIがWICGに提出されています。
以下は該当のAPI、Keyboard Lockの紹介です。
現在のステータスはDraft Community Group Report
ですが、これどの勧告レベルなのかよくわかりません。
Working Draft
の一種でいいんですかね。
いずれにせよ、まだ実装はされていません。
Keyboard Lock
1 Introduction
リッチでインタラクティブなWebサイト、ゲーム、リモートデスクトップ、ストリーミングなどは没入感を高めるためにフルスクリーンで提供する必要があります。
これを実現するために、各サイトではフルスクリーン時にはナビゲーションやメニューを制御するため、特別なキーボードショートカットを使用できるようにする必要があります。
例としてはEscape
・Alt+Tab
・Cmd+`
・Ctrl+N
などです。
デフォルトでは、これらのキーはOSによってキャプチャされるため、Webアプリケーションから使用することはできません。
Keyboard Lock APIは、全てのキーをWebサイトからキャプチャして使用することを可能にします。
2 Keyboard Lock API
2.1 Navigator Interface
partial interface Navigator {
[SecureContext, SameObject] readonly attribute Keyboard keyboard;
};
2.1.1 keyboard
keyboard属性は、KeyboardのNavigatorオブジェクトを返さなければなりません。
2.2 Keyboard Interface
[SecureContext, Exposed=Window]
interface Keyboard : EventTarget {
Promise<undefined> lock(optional sequence<DOMString> keyCodes = []);
undefined unlock();
};
Keyboardオブジェクトは、キーボードロックが有効であればtrueを返すキーボードロック属性を持ちます。
デフォルトはfalseです。
Keyboardオブジェクトは、キャプチャするキーコードの集合を持ちます。
これは、UIEvents-Codeで定義された有効なキーコード属性のうち、制御可能なキーを示すDOMStringの集合です。
デフォルトは空集合です。
キーボードロックを有効にすると、キャプチャ対象のキーが登録されます。
KeyboardインターフェースはEventTargetを継承しています。
これはlayoutchangeのようなキーボードを制御するイベントを処理する必要があるためです。
2.2.1 lock()
lock()
は、キーボードロックを取得するPromise返します。
lock()
が複数回呼ばれた場合は、最後の要求だけが有効になります。
最初のlock()
が解決しないうちに2回目のlock()
が呼ばれた場合、最初のlock()
はAbortErrorを出して終了します。
例1
全てのキーをキャプチャする場合は、単に引数なしで呼び出します。
navigator.keyboard.lock();
例2
WASDの4キーをキャプチャする場合は、該当のキーコードを引数に渡します。
navigator.keyboard.lock(["KeyW", "KeyA", "KeyS", "KeyD"]);
これは、修飾の有無に関わらずキーをキャプチャします。
すなわち、KeyW
を指定するとW
・Shift+W
・Ctrl+W
・Control+Shift+W
などの組み合わせがキャプチャされるということです。
例3
必ずしもあらゆる組み合わせがキャプチャ可能であるとは限りません。
例としてDelete
を挙げると、Shift+Delete
・Control+Delete
・Shift+Control+Delete
などはキャプチャできますが、Windows環境においてはControl+Alt+Delete
をキャプチャすることはできないかもしれません。
2.2.2 unlock()
unlock()
が呼ばれた場合、キーボードロックを解除し、ハンドラを登録解除しなければならない。
3 Handling Keyboard Key Presses
3.1 System Key Press Handler
System Key Pressハンドラは、プラットフォームごとに固有の、キーをフィルタリングするためのハンドラです。
キーボードロックは、通常はブラウザで使用できないCmd
やAlt
といったキー操作を提供することを目的としているため、ほとんどのプラットフォームでは固有のハンドラを設定することが必要となります。
System Key Pressハンドラは、ユーザエージェントがキーボード入力を処理する前に入力を処理しなければなりません。
3.1.1 Registering
ユーザエージェントは、プラットフォーム固有の手順に従って、プラットフォームがキーを処理する前に割り込んで入力を取得するSystem Key Pressハンドラを登録する必要があります。
登録の正確なプロセスはプラットフォームによって異なりますが、例としてWindowsではLowLevelKeyboardProc、MACではQuartzEventServices、X WindowではGrabKeyboard等となります。
3.1.2 Unregistering
System Key Pressハンドラを解除するには、プラットフォーム固有の手順に従って削除する必要があります。
登録と同様に、解除の方法もプラットフォームによって異なります。
3.2 Handling Keyboard Events
System Key Pressハンドラが登録されている場合、ユーザエージェントはユーザのキーダウンに対して以下の処理を行います。
・現在フォーカスされている領域にfullscreen要素がnullでない場合は、isFullscreenをtrueにする。
たとえばシステムダイアログがフォーカスされている場合は、fullscreen要素はありません。
・isFullscreenがtrueであり、キーボードロックが有効であれば、以下のステップを行う。
・キー入力をキーイベントkeyEventに登録する。
・押されたきーがEscapeである場合、押し続ければフルスクリーンから脱出できるメッセージを表示する。オプショナル。
・Escapeが2秒保持されたら、フルスクリーンを終了し、キーボードハンドラを終了する。
・それ以外のキーボードロックしたキーであれば、keyEventを処理に渡す。プラットフォームには渡さない。
・キーボードロックしたキーでなければ、キーボードショートカットアクションを通常どおり処理する。
本APIは、必ずしも全てのキーの組み合わせについて入力の上書きを保証するものではない。
たとえばWindowsのCtrl+Alt+Del
のように、アプリケーションからは操作することのできない入力も存在する可能性がある。
4 Integration With Other Web Platform APIs
他の類似APIとの統合
FullscreenとPointerLockは、それぞれWebページがユーザエクスペリエンスの一部(スクリーンとマウスカーソル)を一時的に制御することを可能にするAPIです。
これらは悪用が懸念されるため、ユーザの信頼できる脱出機能を提供しています。
デフォルトではEscapeキーが脱出機能に割り当てられており、それは本APIでキャプチャできるキーのひとつです。
4.1 Special Considerations with the Escape Key
Escapeキーの扱い。
lock()
の対象にEscapeキーが含まれる場合、ユーザエージェントは要求に応じてUXの変更を行う必要があるかもしれません。
たとえばこれまでフルスクリーンになったときに『フルスクリーンを終了するにはESCを押す』とメッセージを表示していたとしたら、キーボードロック時にはそのメッセージを『フルスクリーンを終了するにはESCを押し続ける』と変更する必要があります。
フルスクリーンになったあとでキーボードロックを有効にする場合、ユーザはフルスクリーンを終了するメッセージを2回見せられることになります。
従ってフルスクリーンにする前にロックを取得することをお勧めします。
navigator.keyboard.lock();
document.documentElement.requestFullscreen();
フルスクリーンを終了するときは、逆の順番で終了するとよいでしょう。
document.exitFullscreen();
navigator.keyboard.unlock();
一般的に、Escapeキーは必要な場合にのみロックに含めるべきです。
またEscapeキーをロックする場合は、ユーザが現状を終了できるという通常の動作を維持することが推奨されます。
4.2 Fullscreen Considerations
フルスクリーンの考慮事項。
最近のユーザエージェントは、2種類のフルスクリーンモードを持っています。
JavaScriptのFullscreen APIから起動されるものと、ユーザがキーボードショートカットから起動するものです。
ユーザ起動のフルスクリーンは、ショートカットとしてよく使われるキーからF11フルスクリーン
と呼ばれます。
F11フルスクリーンと、JavaScriptフルスクリーンは挙動が異なります。
F11フルスクリーンからは、フルスクリーンを起動したキーと同じキーで脱出することができますが、この場合exitFullscreen()
関数は反応しません。
またJavaScriptフルスクリーンで発生するfullscreenイベントも、F11フルスクリーンでは発生しません。
これら動作の違い、さらにF11フルスクリーンのショートカットキーはF11と決まっているわけではないので、キーボードロックAPIはJavaScriptフルスクリーンでのみ有効になります。
F11フルスクリーンについては、キーボードロックは動作しません。
5 Pointer Lock Considerations
Pointer Lockの考慮事項。
PointerLockの動作には変更がありません。
フルスクリーンでなく、Pointer Lockが有効な場合は、キーボードロックを有効にすることはできません。
フルスクリーン状態では、Pointer Lockとキーボードロックが共に有効で、さらにキーボードロックの対象にEscapeキーが入っていた場合にのみ、UXの文言の変更が行われます。
それ以外の場合は動作は変わりません。
6 Mobile Device Considerations
モバイルデバイスへの考慮事項。
このAPIはキーボードに特化したAPIであり、モバイルデバイスには物理キーが存在しないため、このAPIはサポートされません。
ただし、物理キーボードを接続したときに本APIをサポートする選択を取ることもできます。
7 Security Considerations
セキュリティの考慮事項。
このAPIの懸念事項のひとつは、全てのキーのキーボードロックを取得したうえで、FullscreenとPointerLockを組み合わせることでユーザがWebページから抜け出すことを阻止する可能性です。
これを防ぐため、ユーザエージェントはAPIによって全てのキーがロックされたとしても、ユーザがキーボードロックから抜ける方法を提供しなければならない(MUST)。
この仕様では、Escapeキーを2秒以上押し続けることでキーボードロックから抜けることができます。
ユーザエージェントは、さらに別の方法を提供することもできます。
8 Privacy Considerations
プライバシーに関する考慮事項はありません。
感想
Keyboard LockにESCの操作を奪われている場合、フルスクリーンから抜け出すには『ESCを2秒間押し続ける』必要がある。
こんなもん誰がどう考えても100%絶対確実に、『このPCを乗っ取りました。解除してほしくば〇〇に振り込め』に使われる。
特に、脱出するにはボタンを押し続けるというUIが最悪です。
今でもフルスクリーンにしたときには脱出方法のメッセージが数秒表示されますが、あんなの似たような文字をたくさん出すだけでほぼ視認できなくなります。
それに『ESCを押す』と『ESCを押し続ける』なんて間違い探しレベルの違いだからたとえ意識して読んでいたとしても気付きづらいし、そして意識して読んでいる人なんてほぼいません。
また私なら、一度フルスクリーン化に成功してしまえば、今度はキーを触った瞬間に画面を崩したり叫び声を出したりして、キーボードを押させない・手を離させるように仕向けます。
これで一般人はもう手も足も出せなくなるでしょう。
出せるのは電源ボタンくらいです。
当然懸念を声に出している人もいますが、当事者からの反応は、なんと完全無視。
この後も当RFCにはcommitがあり、これより後のissueには反応しているので、気付かなかったわけではなく意図的なスルーです。
この時点で開発者がどのような姿勢を持っているのか丸わかりですね。
ESCをキャプチャ対象外にするなどの対応を行えば評価は変わるかもしれませんが、現在の仕様としては『最悪』以外の何物でもありません。
そしてもちろん言うまでもなく、このAPIの提唱者はGoogleです。