Bug-org 630813によるregressionです。
Bug-org 630813では、Linuxでは現在、Meta一般的ではないので、DOM KeyboardEventからは、Metaキーのイベントを参照できるべきではないとの判断で、押されたキーが、モディファイアキー無しで押された場合に、そのキーがGDK_Meta_(L|R)を生成する場合であっても、DOM_VK_METAを生成しないようにし、さらに、KeyboardEvent.metaKeyがtrueを返さないように修正を行いました。
しかし、実際にBug-org 630813が入ったビルド(Fx13)がリリースされると、一部、特殊なキーボードを利用しているユーザを中心にバグ報告がありました。その内容は、Sunや、MacのキーボードをLinuxで利用した場合、従来まではDOMイベントで、Metaキーとして扱われていたもの(つまり、そのキーと一緒に押したキーのイベントで、metaKeyがtrueとなるモディファイアキー)が、モディファイアキーとして機能しなくなったというもので、この現象自体がこのバグの修正対象です。
Bug-org 630813の修正内容を理解しなければ、このバグの理由、修正方法が理解できないので、再度説明しておきます。
Bug-org 630813で問題としたのは、ネイティブのモディファイアフラグと、DOMのモディファイアとの関係が、そのモディファイアフラグをアクティブにするキーとの関係と一致していない事でした。
Linuxのツールキット、GDKでは、モディファイアフラグが以下のように定義されています。
typedef enum {
GDK_SHIFT_MASK = 1 << 0,
GDK_LOCK_MASK = 1 << 1,
GDK_CONTROL_MASK = 1 << 2,
GDK_MOD1_MASK = 1 << 3,
GDK_MOD2_MASK = 1 << 4,
GDK_MOD3_MASK = 1 << 5,
GDK_MOD4_MASK = 1 << 6,
GDK_MOD5_MASK = 1 << 7,
GDK_BUTTON1_MASK = 1 << 8,
GDK_BUTTON2_MASK = 1 << 9,
GDK_BUTTON3_MASK = 1 << 10,
GDK_BUTTON4_MASK = 1 << 11,
GDK_BUTTON5_MASK = 1 << 12,
/* The next few modifiers are used by XKB, so we skip to the end.
* Bits 15 - 25 are currently unused. Bit 29 is used internally.
*/
GDK_SUPER_MASK = 1 << 26,
GDK_HYPER_MASK = 1 << 27,
GDK_META_MASK = 1 << 28,
GDK_RELEASE_MASK = 1 << 30,
GDK_MODIFIER_MASK = 0x5c001fff
} GdkModifierType;
最新のGDKでは、Super、Hyper、Metaについても定義があるのですが、新しいものですので、現在のGeckoでは無視しています。
それを踏まえてこのenumを見てみると、Shift、CapsLock、Ctrlキー以外のモディファイアに対するモディファイアフラグは、1から5が用意されているだけです。現に、GDK_Alt_(L|R)、GDK_Meta_(L|R)、GDK_Super_(L|R)、GDK_Hyper_(L|R)、GDK_Num_Lock、GDK_Scroll_Lockを生成するキーと、モディファイアフラグとの関係性は一定ではありません。極端な話、UbuntuやFedoraに付属しているキーボードレイアウトの設定から、設定をいじるだけで関係がずれてしまいます。
そこで、Geckoは、DOM Level3 EventsのKeyboardEventに対応するため、XのAPIを利用して、各フラグをアクティブにする物理キーが生成するGDKのkeyval値(上の例だと、GDK_Alt_L等)から各フラグがアクティブにする、DOMのモディファイアとの関係を初期化時に決定することにしました。
そのロジックは、基本的には、モディファイアフラグをアクティブにするキーが生成する可能性のある全てのkeyval値に対応する、DOMのモディファイアを同時にアクティブにするというもので、さらに、GDK_Meta_(L|R)は、DOMのMetaはアクティブにしないという(今、考えてみると)バグも混入されました。
ちなみに、それ以前は、MOD1が、Alt、MOD5が、Metaキーであるという慣例に従って、決め打ちを行っていました。
この事実と、前述の修正内容の結果、MOD5をアクティブにするキーが単独で押された場合に生成されるkeyvalがGDK_Meta_(L|R)、GDK_Super_(L|R)、GDK_Hyper_(L|R)だった場合に、そのキーがショートカットキーや、アクセスキーのためのモディファイアキーとして機能しなくなってしまっていました(Super、HyperはWinキーとして読み替えられ、osモディファイアだと扱われますが、osモディファイア自体が、ショートカットキーや、アクセスキーのモディファイアとして利用ができないため、Metaキー以外の場合にも影響が出ました)。
この問題を修正するために、このバグでは以下のアプローチをとりました。
- DOMのMetaキーを復活
- XULの
<key>要素は簡単に指定すると、全てのモディファイアの状態がマッチしていないと動作しないので、ひとつのモディファイアフラグが、ひとつのDOMモディファイアしかアクティブにしないように修正
MOD5をアクティブにしていたキーが、Super、もしくはHyperの場合にも、ショートカットキーや、アクセスキーのモディファイアキーとして利用できるようにXULの<key>要素のmodifiers属性で、osを指定できるように修正
このような修正ですので、このバグで困っていた人は、ui.key.accelKeyの設定を変えなくては依然として、期待通りに利用できない可能性があります。
ui.key.accelKeyの値は新たに、DOM_VK_WINを意味する、91が指定可能になりました。
ui.key.chromeAccessと、ui.key.contentAccessは、16をOR演算で含めることができるようになりました。
Windows XP(と、Windows Server 2003)では、Ctrlキーや、Altキーを押したときに、常にKeyboardEvent.locationに、KeyboardEvent.DOM_KEY_LOCATION_LEFTがセットされているというバグです。
原因はMapVirtualKeyEx() APIはVista以降で完璧に動くようになったのを失念してのミスでした。
Windowsでは物理キーの情報は、scan codeの値と、extended keyであるか否か、この二つの情報から判断しなくてはいけません。一般的なキーボードの、矢印キー、Insert等の六個のキー、そして右Ctrlと、右Altキーがextended keyです。
Windowsのキーメッセージは、左右の区別のつかない、VK_CONTROLと、VK_MENUで通知されるので、KeyboardEvent.locationの実装のためには、MapVirtualKeyEx() APIを利用して、キーメッセージで同時に通知されている、scan codeと、extended keyか否かの情報から、VK_LCONTROL、VK_RCONTROL、VK_LMENU、VK_RMENUという、左右の区別がつく仮想キーコードに変換しなくてはいけません。しかし、Vistaより古いWindowsでは、extended keyのscan codeであるという情報を、このAPIに指定することができないので、普通に呼び出しても、うまくいかないのです。
厳密に調べる方法は簡単にはいかないので、Windows XPの場合は、シンプルに、extended keyであるか否かの情報から、左右を決め打ちするように修正しました。実際にはもうちょっと、想定しない動作にならないように、また、SendMessage()や、PostMessage()でフェイクのキーメッセージが送られてきた場合にも備えて、いくつかの安全装置を入れてあります。