この日記はMozillaのプロダクトへの貢献者としての私の成果を中心に、気になったバグやWeb界隈の話題について書いていますが、 断り書きがある場合を除き、いかなる団体のオフィシャルな見解ではありません。あくまでも個人的なものです。 Mozilla Foundation、Mozilla Corporation、及び関連企業の公式情報ではないことに注意してください。

現在、XHTML 1.0 (もどき)から、HTML5なコンテンツに修正中です。古い日記は修正が完了していませんので表示が崩れます。 順次、修正していく予定ですのでしばらくお待ちください。

もずはっく日記(2015年7月)

2015年7月7日

Bug-org 1053053 [e10s][TSF] Typed text appears in unfocused textarea when using IME
初回投稿日時: 2015年07月07日01時05分38秒
カテゴリ: e10s Mozilla Core Mozilla42 TSF バグ修正
SNS: (list)

e10sを有効にしていると、contentプロセス内のエディタにフォーカスを移し、その後、chromeプロセス内のエディタにフォーカスを移動させてもcontentプロセス内のエディタに未確定文字列が表示されるというバグです。

このバグは目には見えないだけで、Windows固有のもので、TSFモードだけではなく、IMMモードでも問題がありました。

問題の根本的な原因は、IMEStateManagerがマルチプロセスに全く対応させられておらず、TabParentで疑似的にWidgetCompositionEvent等、IMEのハンドリングに必要なイベントをアクティブなタブへ送信していたことにあります。そこで、このバグではIMEStateManagerのマルチプロセス対応を行いました。

実際に何が起きてWindowsでのみ、この問題が出ているのかという理由ですが、それは、Windowsでのみ全てのウインドウがアクティブではなくなっても、未確定文字列を強制確定せず、次にウインドウがアクティブになった場合に未確定文字列の編集を続行できるようにしているためです。

何故、Windowsだけこのような挙動になっているかというと、Windowsではプロセスがビジー状態に陥った場合、Windows自身がウインドウを白っぽく描画し、タイトルバーに「応答無し」と表示を行います。この時、アプリはウインドウが一時的にディアクティブになったかのようにイベントをOSから受け取ります。ですので、この時に強制確定してしまうと、システム全体でCPUやディスクアクセスがビジー状態の場合に、まともにIMEが利用できなくなってしまいます。これを避けるため、意図的にこのような動作になっているのです。

では、IMEStateManagerがこの仕様が原因できちんと動かないのかというと、フォーカスが子プロセスから親プロセスに移動した時、子プロセス側のIMEStateManagerはウインドウがディアクティブになった場合と同じ通知を受けとっているためです。このため、実際にはフォーカスが親プロセスの別のエディタに移動した場合にも、子プロセスのIMEContentObserverが破棄されないため、IMEにフォーカスを失ったという通知が送信されません。しかし一方では、親プロセスのIMEStateManagerは、IMEContentObserverを生成し、IMEにフォーカスを得たという通知を出しますので、IMEからすると、複数のエディタが同時にフォーカスを持っているという異常な事態が発生していました。

また、それ以外にも、プロセスをまたいだフォーカス移動では様々な問題が起きていて、例えば、子プロセスのエディタがフォーカスを持っているときに、親プロセスのメニューを開いてもIMEが有効なままというバグもありました。

今回の修正ではまず、IMEStateManagerがアクティブなTabParentを自身で管理するようにしました。nsIContentTabParentを持っているのかどうなのかは、TabParent::GetFrom()で取得できますので、これまでのコードに少し変更を入れるだけで簡単に変更できました。

次に、TabParentのプロセスがフォーカスを失う時(親プロセスがフォーカスを得るときと、他の子プロセスがフォーカスを得る時)に、フォーカスを持っていた子プロセスに、フォーカスを失ったという通知を出すようにしました。これにより、ウインドウ全体がディアクティブになったと思い込んでいる子プロセス側のIMEStateManagerIMEContentObserverを適切に破棄することができます。また、フォーカスを持っていたnsPresContextnsIContentを忘れることで、今までの処理と整合性がとれます。

最後に、メニューが開かれたときと閉じられたときに発生する疑似フォーカス移動を、フォーカスを実際に持っている子プロセスに通知するようにしました。これで、メニューを開き、一時的に全てのキーイベントを処理する親プロセス側のIMEStateManagerと、IMEを一時的に無効化する必要がある子プロセス側のIMEStateManagerで状態を同期できるため、従来のコードそのままでメニューの開閉にも対応することができました。

これまでのIMEStateManagerのコードを読んだことがある人には特に違和感が無いような修正になっていますので、我ながらなかなかうまく修正できたなと思ってます。

ちなみにこの一連のデバッグとパッチ作成は、カナダからの帰国時に太平洋上で行ったものです。いや、どうでもいいことですが。

関連するかもしれないエントリ

bug-org 1053053を含むエントリ