K2NR.ME

このエントリーをはてなブックマークに追加 Tweet

Chrome: keyIdentifierとOSとキーレイアウト


みんな違ってみんないい
- みすず

わけないだろバカ。
今日はChromeのkeyIdentifier関連のバグはたちが悪いというお話。

Vichromeではあらゆるキーのkeydownを誰よりも早く受け取り、「何が入力されたのか」を正確に認識しなければなりません。

一般にクライアントサイドのJavascript(というかDOM)ではキー押下を検知するのにonkeydownイベントを拾ってkeyIdentifierプロパティを参照します。

これが仕様

このkeyIdentifierという奴はなかなか優れもので、こいつを使うと「どのキーが押されたか」ではなく「何が入力されたか」が一意に認識できます。つまり、’)‘というキーはJPNキーボードレイアウトだとShift+9キーだけど、USレイアウトだとShift+0キーなのに、どちらのレイアウトで’)'が入力されても同じ値"U+0029"が通知されます。そんなの当たり前じゃねーかと思うでしょう。僕もそう思ってましたが、それが当たり前じゃないのがDOMクオリティ。
実際のコードはこんな感じ。

vichrome.key.keyIdentifier = {
    "U+0031"    : "1",
    "U+0032"    : "2",
    "U+0033"    : "3",
    "U+0034"    : "4",
    "U+0035"    : "5",
    "U+0036"    : "6",
    "U+0037"    : "7",
    "U+0038"    : "8",
    "U+0039"    : "9",
    "U+0030"    : "0",
    "U+0021"    : "!",
    "U+0022"    : '"',
    "U+0023"    : "#",
    "U+0024"    : "$",
    "U+0025"    : "%",
    "U+0026"    : "&",
    "U+0027"    : "'",
    "U+0028"    : "(",
    "U+0029"    : ")",
    "U+002D"    : "-",
   "U+003D"    : "=",
    "U+005E"    : "^",
    "U+007E"    : "~",
    "U+00A5"    : "\\",
    "U+005C"    : "\\",
    "U+007C"    : "|",
    "U+0041"    : "a",
    "U+0042"    : "b",
    "U+0043"    : "c",
    "U+0044"    : "d",
    "U+0045"    : "e",
    "U+0046"    : "f",
    "U+0047"    : "g",
    "U+0048"    : "h",
    "U+0049"    : "i",
    "U+004A"    : "j",
    "U+004B"    : "k",
    "U+004C"    : "l",
    "U+004D"    : "m",
    "U+004E"    : "n",
    "U+004F"    : "o",
    "U+0050"    : "p",
    "U+0051"    : "q",
    "U+0052"    : "r",
    "U+0053"    : "s",
    "U+0054"    : "t",
    "U+0055"    : "u",
    "U+0056"    : "v",
    "U+0057"    : "w",
    "U+0058"    : "x",
    "U+0059"    : "y",
    "U+005A"    : "z",
    "U+0040"    : "@",
    "U+0060"    : "`",
    "U+005B"    : "[",
    "U+007B"    : "{",
    "U+003B"    : ";",
    "U+002B"    : "+",
    "U+003A"    : ":",
    "U+002A"    : "*",
    "U+005D"    : "]",
    "U+007D"    : "}",
    "U+002C"    : ",",
    "U+003C"    : "<",
    "U+002E"    : ".",
    "U+003E"    : ">",
    "U+002F"    : "/",
    "U+003F"    : "?",
    "U+005F"    : "_",
    "U+0020"    : "SPACE",
    "Left"      : "LEFT",
    "Down"      : "DOWN",
    "Up"        : "UP",
    "Right"     : "RIGHT",
    "Enter"     : "CR",
    "U+0008"    : "BS",
    "U+007F"    : "DEL",
    "U+0009"    : "TAB",
    "F1"        : "F1",
    "F2"        : "F2",
    "F3"        : "F3",
    "F4"        : "F4",
    "F5"        : "F5",
    "F6"        : "F6",
    "F7"        : "F7",
    "F8"        : "F8",
    "F9"        : "F9",
    "F10"       : "F10",
    "F11"       : "F11",
    "F12"       : "F12",
    "U+001B"    : "ESC",
    "Home"      : "HOME",
    "End"       : "END",
    "Control"   : "CTRL",
    "Shift"     : "SHIFT",
    "Alt"       : "ALT",
    "Meta"      : "META",
    "PageDown"  : "PAGEDOWN",
    "PageUp"    : "PAGEUP",
    "CapsLock"  : "CAPSLOCK"
};

さて、本来全ての入力が一意に識別できるはずのkeyIdentifierですが、Chrome(というか多分Webkit)ではそう上手くいきません。WindowsとLinux版のChromeにはバグがあって、一部の入力はkeyIdentifierが重複しています。実際にコードを見たほうが早いでしょう。

vichrome.key.winKeyIdentifier_ja = {
    "U+00BC":",",
    "U+00BE":".",
    "U+00BF":"/",
    "U+00E2":"\\",
    "U+00BB":";",
    "U+00BA":":",
    "U+00DD":"]",
    "U+00C0":"@",
    "U+00DB":"[",
    "U+00BD":"-",
    "U+00DE":"^",
    "U+00DC":"\\"
};

vichrome.key.shiftWinKeyIdentifier_ja = {
    "U+00BC":"<",
    "U+00BE":">",
    "U+00BF":"?",
    "U+00E2":"_",
    "U+00BB":"+",
    "U+00BA":"*",
    "U+00DD":"}",
    "U+00C0":"`",
    "U+00DB":"{",
    "U+00BD":"=",
    "U+00DE":"~",
    "U+00DC":"|",
    "U+0031":"!",
    "U+0032":'"',
    "U+0033":"#",
    "U+0034":"$",
    "U+0035":"%",
    "U+0036":"&",
    "U+0037":"'",
    "U+0038":"(",
    "U+0039":")"
};

例えば’]'と’}'は同じ値“U+00DD"になっています。Win/Linuxの場合は上記のような感じで、さらにshiftキーで条件分岐させなければ正しい値は取得できません。


ここまでは、いいんです。100歩譲って頑張ればキーを識別できるから。


上のコードをよく見てもらえば分かる通り、通知されるIdentifierはおもいっきりキー配列に依存してます。同一キーの文字は同じIDです。つまり、キー配列が変わると通知されるIDも変わるのでこの解決策では動きません。そこで、USレイアウトの場合は上記の***_jaではなくて、

vichrome.key.winKeyIdentifier_us = {
    "U+00BC":",",
    "U+00BE":".",
    "U+00BF":"/",
    "U+00BB":"=",
    "U+00BA":";",
    "U+00DD":"]",
    "U+00DB":"[",
    "U+00BD":"-",
    "U+00DC":"\\",
    "U+00DE":"'",
    "U+0060":"`"
};

vichrome.key.shiftWinKeyIdentifier_us = {
    "U+00BC":"<",
    "U+00BE":">",
    "U+00BF":"?",
    "U+00BB":"+",
    "U+00BA":":",
    "U+00DD":"}",
    "U+00DB":"{",
    "U+00BD":"_",
    "U+0038":"*",
    "U+00DC":"|",
    "U+007E":"~",
    "U+0036":"^",
    "U+0031":"!",
    "U+00DE":'"',
    "U+0032":"@",
    "U+0033":"#",
    "U+0034":"$",
    "U+0035":"%",
    "U+0037":"&",
    "U+0039":"(",
    "U+0030":")"
};

こんな感じの別のテーブルを準備しなければなりません。世の中にいくつキー配列があるのか知らないですが世界中のあらゆる環境で正しく動くコードを書くことは不可能だということです。実際VichromeでもJPN/US以外のキー配列には対応していません。試せないけど多分バグる。
ちなみにこのキー配列に依存してkeyIdentifierが変わってしまうバグ、OSのキーボード設定で通知される値が変わるわけではありません。ブラウザの言語設定に依存しているようです。例えば次のようなコードで取得できます。

var lang = (navigator.userLanguage||navigator.browserLanguage||navigator.language).substr(0,2);

なので、例えば日本語環境のWindows、日本語設定のChromeでUSキーボードを使ってるようなマニアックな人はVichromeは一部正しく動かないはずです。いったい何がidentifierだというのか。

結論:みんなMacを使いましょう。

comments powered by Disqus