2013年やったこと - Mozc 編

2013年やったこと - Mozc 編,いつもどおりコードが公開されている範囲で.

Mozc IPC の Windows 64-bit 対応

Windows 環境で動く Mozc は名前付きパイプ使ってプロセス間通信を行うのですが,いわゆる "Squatting Attack" 対策として,GetNamedPipeServerProcessId API で接続先のプロセス ID を取得し,さらにそのプロセス ID から GetModuleFileNameExW API で実行ファイル名を取得して検証しています.が,この仕組みが今は動いていても将来的にうまく動かないことがあるかもという問題.
このころ mozc_server や mozc_renderer を 64-bit で動かす実験を行っていて気付いたわけですが, 32-bit プロセスから 64-bit プロセスに対して GetModuleFileNameExW API を呼び出すと必ず失敗するようです.仕方ないので GetProcessImageFileNameW API を使うように変更して対処.これまで mozc_server と mozc_renderer は常に 32-bit プロセスだったので問題が表面化していなかったというわけでした.

TSF-Mozc 使用時に ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0) が動作しない

IMM32 の頃には,アプリケーション側から進行中のコンポジションをキャンセルするという API が存在しました.TSF になってそういう API はなくなったのですが,CUAS はなにかしらの方法でこの IMM32 API を エミュレートする必要があります.実際これは「コンポジションの範囲を空にする」という形で行われています.ということで TSF-Mozc でも対応する必要があるというわけでした.
Excel が,インライン補完のためにこの機能に依存しているので割と重要だったりします.

TSF-Mozc で候補ウィンドウの候補をマウスで選択・決定できない問題の修正

経験上ほとんどの人はマウスで候補ウィンドウを選択しないのを知っていたので,TSF-Mozc ではマウス関係の処理を後回しにしていました.とはいえ,タッチイベントでも動作しないのはそろそろよろしくないというのもあって修正という感じです.

TSF-Mozc での InputScope のサポート

InputScope も登場後 10 年以上経っている仕組みで,いまさらという感もありますが,Windows 8 以降はだいぶ InputScopeへの移行が進んでいます.最近はブラウザの HTML Forms の種類が InputScope で通知されたり,Windows 8 以降でImmSetConversionStatus API が (デフォルト設定では) 実質廃止になったりで,IME 側も InputScope 対応が割と重要になってきています.というわけで TSF-Mozc でも対応してみました.
アプリケーション開発者様におかれましても,Windows 8 以降,ImmSetConversionStatus API は大多数のユーザー環境で期待通り動作していない可能性が高いため,この機会に InputScope に移行してしまうのが良いのではないかと思います.詳細については下記資料等を参照してください.

さて,デスクトップ IME の文脈で気を付けるべき点として,アプリケーション側からのモード変更リクエストを永続的なものとして扱うか,一時的な変更リクエストとして扱うかという問題があります.たとえば次のような順序でテキストフィールドを移動していったとしましょう.

  1. 通常のテキストフィールド
  2. パスワードフィールド
  3. 通常のテキストフィールド

多くの場合,ステップ 1 で IME がオンの状態であれば,ステップ 3 でIME がオンの状態に復帰します.つまり,パスワードフィールドで IME が自動的にオフになるのは,永続的なモード変更ではなく一時的なモード変更というわけです.
では次の場合はどうなるべきでしょうか?

  1. 通常のテキストフィールド
  2. 全角カタカナが期待されるテキストフィールド
  3. 通常のテキストフィールド

ステップ 2 で IME の入力モードが「全角カタカナ」になるのはまあ良いとします.では,ステップ 3 に移動したときにも「全角カタカナ」のままであるべきでしょうか? それともステップ 1 のときのモードに復帰すべきでしょうか.
TSF-Mozc に関しては,InputScope によるモード変更リクエストはその場限りの一時的なものであるという解釈を行いました.したがって,上記ステップ 3 ではステップ 1 のときのモードに復帰します.簡単に Design Doc を書いていますので詳細についてはそちらを参照してください.

Windows 版 Mozc での Mode Indicator のサポート

これ.
f:id:NyaRuRu:20140102115121p:plain
位置形状とフォントサイズの決定以外のほぼ全てを担当しました.個人的にはカーソル直下よりもカーソルに対して左揃えの方が良いんではないかとか,ちょっと大きすぎるんではないかとか色々思うところはあるのですが,まあデザイナーさんがそう言っているわけで案件.個人的にはもうちょっと作りこみたかったのですが,別の仕事が忙しくてあんまり専念できなかったのが心残りです.
InputScope で自動モード変更を行う以上,この種の視覚的フィードバックは必須ですね.というわけで InputScope 対応とセットで実装されました.

周辺文字列を利用した変換の (部分的) サポート

周辺文字列を利用した変換も十数年以上の歴史があるいまさら技術ではあります.前世紀の IMM32 時代から IMR_DOCUMENTFEED メッセージをサポートしたアプリケーションでは周辺文字列を利用した変換は可能でした.
さて,周辺文字列を利用した変換というのは実際のところあんまりコストパフォーマンスの良くないアプローチだと思っています.細切れ変換対策という意味では,変換エンジン側で前回コミットされた文字列を覚えておけばすむ話です.というわけで,今回注目したのは,IME をオフにして入力しがちな文字種,つまり半角英数です.
たとえば「明日1日」という文章を入力するのに,

  1. 「明日」を入力
  2. IME をオフにする
  3. 「1」を入力
  4. IME をオンにする
  5. 「日」を入力

という使い方をする人は一定いることが分かっています.以前の Mozc では,ステップ 4 実行時に内部ステートは「文頭」状態でした.というのも,IME がオフの状態で入力された内容はモニタリングしていないので,ステップ 5 で入力する内容がどういう文章に続くものなのか把握できません.今回救いたかったのはまさにこういうケースで,カーソル前にある文字種がアルファベットか数字かどうかだけを判定します.このあたりの詳細についても Design Doc を書いておきましたので詳細が気になる人はどうぞ.

imm32-mozc と tsf-mozc,ibus-mozc および mozc_server の実装を担当しました.
IMM32 版では IMR_DOCUMENTFEED を単純に使うだけですが,TSF では Transitory Extensions という拡張的な仕組みも使っていて,メモ帳や Internet Explorer にも対応しています.軽く調べてみた限り,ATOK 2013 も MS-IME 2013 も Transitory Extensions までは使っていないようなので,ちょっとしたアピールポイントではないかと思っております.
このとき書いたコードは別のオープンソースプロジェクトで使われたりもしているようです.

C++11 対応

最近社内コード規約的に C++11 が解禁されつつあるので Mozc でもいくつかコードのクリーンアップを行っています.具体的には,

  • 自前 scoped_ptr から std::unique_ptr への置き換え
  • COMPILE_ASSERT マクロの static_assert への置き換え
  • auto での書き換え
  • NULL から nullptr への置き換え

あたりをちまちまやってます.
最近は Visual C++ の C++11 適応度合が上がってきていてだいぶ楽なのですが,意外に足を引っ張るのが Mac OS X だったり.というのも,libc++ の実行時ライブラリが標準で入るようになったのが Mac OS X 10.7 以降.未だに Snow Leopard をサポートしていたりすると C++11 の大部分の機能が使えないのでした.

UCS2 しかサポートできない内部関数の削除

Mozc コードベースに残っていた UCS2 専用関数をすべて削除しました.

TSF-Mozc でタッチに最適化されたオンスクリーンキーボードを表示するように

ITfFnGetPreferredTouchKeyboardLayout を実装するだけ,というわけでもなくて Windows のオンスクリーンキーボードは U+F003 と U+F004 に特別な意味を持たせたりしているのでその対応とか.詳しくは下記資料参照のこと.

可能なら LogicalToPhysicalPointForPerMonitorDPI API を使う

Mozc の候補ウィンドウや Mode Indicator は,入力中のプロセスとは別のプロセスで動いているので,DPI 仮想化にともなう座標変換が必要になることがあります.Windows 8.1 Preview で LogicalToPhysicalPoint API が動作しなくなっていたので,新しく追加された LogicalToPhysicalPointForPerMonitorDPI API が利用可能ならそちらを使う,といった修正を行っていました.Windows の DPI 周り,ほんと落ち着かないですね.
次の資料とかが詳しいです.
http://go.microsoft.com/fwlink/?LinkID=307061

アプリケーションマニフェストに Windows 8.1 対応宣言を追加

恒例行事.

ITfLangBarItemButton::GetIcon で返すモノクロフォントを実行時に描画

Accessibility (a11y) 案件.
Windows 版の Mozc は,控えめに言っても Accessibility 関係の機能が全然足りていないのですが,珍しく対応している機能のひとつに言語バーのテキストアイコン色があります.Windows では,デスクトップテーマをハイコントラストモード(背景色黒・文字色緑) に変更すると,言語バーのテキストアイコンも緑色に切り替わります.
f:id:NyaRuRu:20140102115125p:plain
f:id:NyaRuRu:20140102115128p:plain
ではこれどうやって実装するかですが,大まかに方法は 2 つ.
A. 実行時にアイコンを描画する.この場合,テーマカラーを実行時に取得できるので文字色は問題ない.テーマが変更されるたびにアイコンを再生成する必要がある.
B. モノクロアイコンを事前に生成しておき,言語バーAPI に TF_LBI_STYLE_TEXTCOLORICON フラグを渡す.この場合,言語バー側で色変更を行ってくれる.
Mozc はながらく B の方法を使っていました.が,最近になってビットマップハンドルのリークが発生しているという報告を受けて調べてみると驚きの事実が! ITfLangBarItemButton::GetIcon にモノクロアイコンを渡すと,言語バー内部でビットマップハンドルのリークが発生するようです.というわけで方向 A で書き直しました.
この問題,原因の発見までがとにかく大変だったのが印象的でした.ハンドルリークが全然再現できず困っていたのですが,ユーザーの方からアップロードしていただいたスクリーンショットがクラシックテーマだったことが気になってテーマを変更してみたところやっと再現できた,というケースです.どういうやりとりが行われたか興味がある人は以下のスレッドをどうぞ.

TF_LBI_STYLE_TEXTCOLORICON を使用している IME 作者様はご注意ください.

Adobe Sandbox をサポート

@corvussolis さんから Adobe Flash および Adobe Acrobat Reader で使われている Sandbox の動作について教えてもらって Mozc でも対応してみたという話.これでやっと Firefox でも「保護モード」オンのままニコニコ動画にコメントを書けるようになりました.
ご協力いただいた皆様に感謝.

Windows 8 以降で MS-IME からのユーザー辞書インポートが動かなくなっていたのを修正

詳細については diff を参考にしてください.

Owner Rights SID を設定

Windows では,オブジェクト所有者に暗黙に WRITE_DAC および READ_CONTROL 権限が許可されるという仕様がありましたが,Vista 以降この挙動を拒否することが可能になりました.

というわけで Mozc でも不要な権限を削除してみたという話.追加すると権限が減る系の SID ですね.

Windows 版 Mozc のビルド時に Cygwin が不要になった

Mozc のビルドは,もともと Cygwin に依存しないように作られていました.しかし,Mozcのビルドシステムを SCons から gyp に変更したときに,gyp に引っ張られる形で Cygwin 依存が生まれてしまいました.
外部コマンドの呼び出しに時に色々気を付ければ Cygwin は不要というのは分かっていたのですが,とりあえず重要度は低いので後回しのままとなってはや数年,いい加減直すかということでやっと依存関係を解消したという話です.Windows 版 OSS Mozc のチェックアウトサイズもだいぶ減ったはず.
ちなみに Chromium も同様の問題があって,以下のようにビルド時の Cygwin 依存を取り除く努力が行われています (こっちも数年がかり).

以下 OSS Mozc 固有っぽいもの

Issue 42: get rid of absolute path in unix/ibus/gen_mozc_xml.py

https://code.google.com/p/mozc/issues/detail?id=41
ibus-mozc のバイナリとアイコンファイルのインストール先はカスタマイズできるべきだという案件.3 年ぐらい放置されていのですが (すみません) リファクタリングの一環として対応.

Issue 180: Mozc and IBus does not respect xinerama at all

https://code.google.com/p/mozc/issues/detail?id=180
マルチディスプレイ環境で mozc_renderer が画面境界の判定に失敗するというバグ.gdk_screen_get_monitor_at_point を使うことで解決.

Issue 182: mozc property titles are empty in gnome-shell

https://code.google.com/p/mozc/issues/detail?id=182
Red Hat で ibus-mozc をメンテナンスされている Tagoh さんから転送されてきたバグの対応.Gnome-Shell はいくつかの IBus メタデータに依存しているのですが,ibus-mozc がそれらを設定していないという問題.具体的には,

  • IBus 1.5 以降にのみ存在する symbol というプロパティをセットするようにする (これを行わないと現在の入力モードがアイコンとして表示されない)
  • Gnome Shell は "InputMode" という名前のプロパティに現在のモード (全角カタカナとか直接入力とか) が設定されていることを期待しているので,プロパティ名を変更

という感じです.ちなみにこのあたりの Gnome-Shell の仕様はどこにもドキュメント化されていません.自分でソースを読んで調べる必要があります.結果的に以下のような見た目になりました.
f:id:NyaRuRu:20140102115130p:plain
このパッチがあたっていないバージョンの Mozc (具体的には 1.6.1187.102 以前) では,入力中のモードは分かりませんし,入力モードを Gnome Shell から選択することもできません.

Issue 188: Add Session command for ConvertPrevPage and ConvertNextPage

https://code.google.com/p/mozc/issues/detail?id=188
Fcitx の開発者から来た機能追加リクエスト.Fcitx の候補ウィンドウには次頁・前頁に相当するボタンが存在するのですが,その動作に対応するメッセージが Mozc のプロトコルに存在しなかったという問題.たまたま Windows 8 のソフトウェアキーボード対応で似たような需要があったので合わせて対応となりました.

Issue 189: use_libprotobuf=1 does not work in Mozc 1.10.1390.102

https://code.google.com/p/mozc/issues/detail?id=189
Red Hat で ibus-mozc をメンテされている Tagoh さんから,システムにインストールされている libprotobuf を使用する場合のビルドが壊れているとの報告があったので粛々と修正.壊したのも私なのでマッチポンプ案件だったりします.

Issue 195: Candidate Popup is hidden by Cinnamon menu

https://code.google.com/p/mozc/issues/detail?id=195
Linux で Cinnamon を使っていると,mozc_renderer がメニューの後ろ側に行ってしまうという問題.修正は 2 行で済んだんですが,環境のセットアップやら調査やらで結局 1 日ぐらいかかっているという.ちなみにこのケースでは日曜日が消えました.

Issue 198: 'build_mozc.py gyp' fails when gyp r1667 or higher is used

https://code.google.com/p/mozc/issues/detail?id=198
Mozc 開発ではビルドに使用する gyp のリビジョンを固定しているのですが,OSS Mozc をビルドする人は推奨リビジョン以外の gyp と組み合わせている人もいるらしく,gyp 側の変更でビルドできなくなることがあります.
今回は,Mozc r171 以前と gyp の r1667 以降を組み合わせるとビルドに失敗するという問題でした.

Issue 199: ibus-mozc must be locked down on the Gnome Shell's locked screen

https://code.google.com/p/mozc/issues/detail?id=199
CVE-2013-4509 の ibus-mozc 側の対処を一通りやりました.
私も完全に背景を把握できているか怪しいのですが,概ね以下のような感じだと理解しております.

  1. 2013年1月4日,Gnome 3 環境で IME をオンにしたままスクリーンをロックし, Gnome-Shell のスクリーンロック解除画面からパスワードを入力してロック解除しようとすると,パスワードフィールドで IME がオンのままになっているというバグが Red Hat の Jens Petersen さんから報告される.Red Hat Bug 891835
  2. 2013年1月9日,Red Had の Ueno さんから,GTK の "input-purpose" と "input-hints" を,パスワードフィールドにセットすることで問題が回避したらよいのではという提案がなされる.Gnome Bug 691392
  3. 2013年1月10日,Red Hat の Ueno さん,ibus-gtk2 にパッチをコミット (927e9f58).この変更は IBus 1.5.3 としてリリースされる.変更は ibus-gtk2 に対するもので,IM Engine 側では何もする必要がない.具体的には,"input-purpose" が password であれば,使用している IM Engine に関わらず IM Engine が無効化される.
  4. 2013年8月14日,Red Hat の Ueno さん,ibus-gtk にパッチをコミット (3b3a7cec).この変更は IBus 1.5.4 としてリリースされる.この変更によりまたしても挙動が変わり,"input-purpose" が password のときの振る舞いは各 IM Engineの責任になる.具体的には,IM Engine が IBus 1.5.4 以降の環境で動作する場合のみ,このコミットで追加された ibus_engine_get_content_type 等の新しい API を使って IM Engine の動作を変更する必要がある.
  5. 2013年9月21日,Red Hat の Fujiwara さんから IBus 1.5.4 のリリースがアナウンスされる.この中で "Handle GTK+ input purose for gnome-shell password dialog and each engine need to implement it" として各 IM Engine 開発者向けにアナウンス.
  6. 2013年9月21日,俺のターン! IBus 1.5.4 のリリースノートを受けて OSS Mozc Issue 199 をファイル.この時点では状況を完全に把握できていませんでした.
  7. 2013年9月30日,俺のターン! やっと状況を把握.急いでOSS Mozc Issue 199 へのパッチを公開.帰宅後色々弄っていて事態の深刻さに気付き急いで書き上げた感じの奴です.翌日ほんと眠かったです.
  8. 2013年10月1日,Red Hat の Mike FABIAN さんから,IBus 1.5.4 側の問題で "input-purpose" が正しく通知されないことがある問題が報告される.Red Hat Bug 1013948 この問題はすぐに修正されるものの,対応する IBus の安定板リリースは行われておらず,各ディストリビューションでは独自にパッチを取り込んで IBus 1.5.4 として配布している.
  9. 2013年10月25日,openSUSE のコミュニティメンバーの Takeyama さんから,Novel Bug 847718 がファイルされる.
  10. 2013年11月4日,Red Hat の Kurt Seifried さんにより今回の問題に CVE-2013-4509 が付与される.

この後 IBus を採用する各種ディストリビューションで色々対応が進んだようです.

Issue 201: making initial mode customizable for ibus-mozc

https://code.google.com/p/mozc/issues/detail?id=201
1.5.2 のころまでの IBus は,各 IM Engine は「直接入力 (いわゆる IME オフ)」というステートを持たないことを前提に設計されていました.従って,ibus-mozc は起動直後にひらがなモードである必要があります.
が,IBus 1.5.3 頃から,日本語 IME に関しては各 IM Engine 内部でオンオフ状態を切り替える方向に IBus 上流の人たちは心変わりしたようです.Red Hat の Fujiwara さんが ibus-anthy の挙動変更について下で説明されています.
https://code.google.com/p/ibus/issues/detail?id=747#c31
この変更にともない,以前の使い勝手を再現するためには,ibus-mozc が起動直後に「直接入力 (いわゆる IME オフ)」になって欲しいという要望が来たのが OSS Mozc Issue 201 です.
とはいえ Mozc は IBus 1.4 もまだ対応していますから,IBus のバージョンによらずデフォルトモードを変えてしまうと今度は IBus 1.4 ユーザーが困ることになります.また,デフォルトモードを選択する UI を追加しようにも,Mozc の設定画面は imm32-mozc, tsf-mozc, ibus-mozc, uim-mozc, fcitx-mozc, emacs-mozc, Mozc for IMKit で共有されているため,IBus 1.5.3 以降専用の項目を追加するのは躊躇するところです.
とりあえず初期状態をオフにするパッチだけは公開しましたが,以後のリリースでどうするかは未定です.
https://code.google.com/p/mozc/issues/detail?id=201#c8
ちなみに,初期値を変更するぐらい簡単だろうと思われる方もいらっしゃるかもしれません.が,実際は結構面倒です.事実,上のパッチを適用すると,mozc_server が予想外のクラッシュをした時のプレイバックの仕組みが壊れることが分かっています.初期値が変わっちゃったわけですね.

その他 OSS Mozc 関係でやったこと

2013年4月ぐらいに fcitx-mozc のマニュアルテストを行って 6 件ぐらいバグを見つけたところ,作者様にものすごい勢いで修正していただきました.

あと,fcitx-mozc の再変換には,ibus-mozc の再変換用に私が書いたコードが使われていたりするようです.

今年の話とか

入社後なんだかんだでずっと Mozc に関わってきましたが,2014 年 1 月から社内の別プロジェクトに移ることにしました.オープンな場で何をやったか話すのはいつも通り書いたコードが公開されてからにしようかと思います.
Windows 版 Mozc は 20% の枠内で今後も協力していきたいところですが,ibus-mozc のメンテナンスまでやる余裕はなさそうです.ibus-mozc の今後の開発・メンテナンスを引き継いでもいいという方がいらっしゃいましたら社内外問わずお知らせください.