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

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

もずはっく日記(2014年10月)

2014年10月30日

Bug-org 975383 Dispatch compositionupdate from TextComposition rather than widget 初回投稿日時: 2014年10月30日16時47分26秒
最終更新日時: 2014年10月30日17時20分18秒
カテゴリ: e10s Events Mozilla Core Mozilla35 バグ修正
固定リンク: id=2014103000
リンク元: 0件
SNS: (list)

現在、compositionupdateの内部イベントである、NS_COMPOSITION_UPDATEイベントは、各OS用のwidgetから発火していますが、同じロジックで発火している上、XPレベルでは、TextCompositionクラスで管理している未確定文字列の情報をwidget側と二重管理するのは勿体ないということで、NS_COMPOSITION_UPDATEを、TextCompositionで発火するようにしよう、というバグです。

この修正により、widgetが発火する内部イベントは、NS_COMPOSITION_STARTNS_COMPOSITION_ENDNS_TEXT_TEXTイベントの三種類のみになりました。TextCompositionイベントは、NS_TEXT_TEXTイベントが発火されると、PresShellから、IMEStateManager::DispatchCompositionEvent()経由で、DOMツリーにイベントを発火前に受け取り、この際に、未確定文字列に変化がある場合にのみ、NS_COMPOSITION_UPDATEイベントを発火するようになりました。

これにより、一部のwidgetでは未確定文字列を保存しておかなくてよくなりました。メモリ容量は未確定文字列の長さからして、問題にはなりませんが、メモリのフラグメンテーションについては多少、懸念が無くなりました。また、NS_COMPOSITION_UPDATEの発火後の各種オブジェクトの生存確認をXPレベルで確実に行えるので、安全性が高まっています。そして、XP化により、全てのプラットフォームで確実に同じ条件で発火するようになりました。今までは、Android版のみ、未確定文字列に変化がなくても、compositionupdateイベントが発火されていたように思います(実際には未確認)。

最後に、e10sモードでは、compositionupdateを発火するにあたり、widgetから発火して、子プロセスがフォーカスを持つ場合には、プロセス間通信が必要でしたが、これが不要になったため、未確定文字列操作時のパフォーマンスがかなり改善しています。

nsIDOMWindowUtils.sendCompositionEvent()に、compositionupdateを渡しても、このメソッドは何もしないように変更されています(互換性のため、例外も発生しません)。nsICompositionStringSynthesizer.dispatchEvent()を呼び出すと、未確定文字列の変更が発生した場合にのみ、自動的にcompositionupdateイベントも発火されます。

Bug-org 960871 Get rid of WidgetTextEvent and WidgetCompositionEvent should take over the job 初回投稿日時: 2014年10月30日17時15分38秒
カテゴリ: Events Mozilla Core Mozilla35 バグ修正
固定リンク: id=2014103001
リンク元: 0件
SNS: (list)

compositionupdateは、未確定文字列が変化する前に発火するイベントということに、一応、なっていますので、これは、エディタの未確定文字列を書き換えるためには現在使っていません(というか、内部処理では使っていません)。内部処理では、WidgetTextEventを、NS_TEXT_TEXTイベントとして発火し、textという独自DOMイベントとして発火したものを利用しています。

しかし、この、独自のイベント型であるWidgetTextEventが存在し続けると、各種引数は、共通のスーパークラスである、WidgetGUIEventを使わなくてはいけなかったり、WidgetEvent::mClassメンバの値を何度もチェックする必要があり、コードがスッキリとしない原因になっていました。

このバグの修正により、NS_TEXT_TEXTイベントは、NS_COMPOSITION_CHANGEイベントに変更され、WidgetTextEventクラスは単純に廃止、代わりに、WidgetCompositionEventに、WidgetTextEventのみが持っていた未確定文字列の文節情報を持たせています。独自DOMイベントもUIEventから、CompositionEventに変化していますので、若干の非互換が発生していますが、これが問題になることは実際のケースでは存在しないと思います(CompositionEvent.dataで未確定文字列にはアクセス可能に変更になっています)。

これにより、未確定文字列を取り扱うメソッドで、引数にWidgetGUIEventをとっていたメソッドは、(見つけられた限り)全て、WidgetCompositionEventに修正し、メソッドの利用方法をより明確にしています。

自動テストを記述する際には、EventUtils.synthesizeText()が、EventUtils.synthesizeCompositionChange()に名前が変更されているので注意してください。

Bug-org 1081992 Crash in mozilla::TextComposition::OnCompositionEventDiscarded(mozilla::WidgetCompositionEvent const*) 初回投稿日時: 2014年10月30日17時28分39秒
カテゴリ: Mozilla Core Mozilla35 Mozilla36 バグ修正
固定リンク: id=2014103002
リンク元: 0件
SNS: (list)

クラッシュのスタックからは何が起きているのかはっきりとしませんでしたが、クラスへのポインタがnullptrの場合に、そのメソッドを呼び出して、そのままクラッシュせずに妙なことになっていました。Bug-org 1065835の修正によるregressionです。

単純にnullチェックを挟むことで対応しています。元々はその必要が無いはずの場所でしたが、特殊な条件下ではnullになることが判明した訳ですので、単なる見落としとも言えますね。

Bug-org 1081993 [TSF] Candidate window position is always the top-left of browser window or just disappeared if IMM IME is active in TSF mode (~ATOK 2010, Japanist) 初回投稿日時: 2014年10月30日17時42分06秒
カテゴリ: Mozilla Core Mozilla35 Mozilla36 TSF Windows バグ修正
固定リンク: id=2014103003
リンク元: 0件
SNS: (list)

TSFモードで、IMMのIME (ATOK 2010以前やJapanist、Windows 7かVistaでGoogle日本語入力)を利用していると、候補ウインドウがキャレット位置に正しく表示されず、ウインドウの左上に表示されたり、全く表示されなかったりする、というバグです。Bug-org 975383の修正によるregressionです。

IMMのIMEは、TSFモードであっても、ITextStoreACPを利用せず、IMMのメッセージとAPIを利用して、従来通りにハンドリングしなくてはいけません。ですので、nsIWidget::NotifyIME(NOTIFY_IME_OF_COMPOSITION_UPDATE)が呼び出された場合、ImmSetCandidateWindow() APIでウインドウ位置をTSFモードであっても指定しておく必要があります。しかし、これまではTSFモードでは無い場合、NOTIFY_IME_OF_COMPOSITION_UPDATEを無視していました。

では、なぜ、今までは問題なかったのか、ということになるのですが、Bug-org 975383の修正で、NS_COMPOSITION_UPDATEの発火コードをnsIMM32Handlerから削除した際、誤ってImmSetCandidateWindow()を呼び出していたコードを正しく削除してしまったのが、このバグが表面化した理由でした。つまり、元々バグっていたのですが、バグにより、過剰な呼び出しがあったので、それに救われていたものの、その過剰な呼び出しが削除されたということです。

今回、IMMとTSFのどちらに処理を任せるか判断しているIMEHandlerクラスの処理を見直して、TSFモードでIMM IMEが有効かどうかをきちんと確認するようにしました。これにより、TSFモードでIMM IMEを利用している場合にのみ、未確定文字列があってもKeyboardEventが発生するバグがありましたが、このバグも修正されています。

Bug-org 1083629 Composition in a designMode editor is canceled when you click the editor on Linux 初回投稿日時: 2014年10月30日22時23分48秒
カテゴリ: GTK Mozilla Core Mozilla35 Mozilla36 バグ修正
固定リンク: id=2014103004
リンク元: 0件
SNS: (list)

designModeのエディタで、未確定文字列を入力中に、<body>要素の外側、つまり、<html>要素の部分をクリックすると、GTK版でIMEがiBusの場合、未確定文字列が確定されず、キャンセルされてしまう、というバグです。Bug-org 1065835の修正によるregressionです。

Bug-org 1065835の修正では、未確定文字列の確定のリクエストがあった場合、IMEの挙動に関わらず、直前の未確定文字列をそのまま確定するように修正しました。これに伴い、もし、XPレベルで確定のリクエストを出さなかった場合、各widgetがIMEに確定のリクエストを出した場合は、そのIMEの挙動に依存するようになっています。最近のiBusに関しては、gtk_im_context_reset()を呼び出した場合、非同期で未確定文字列をキャンセルするという挙動になっていますので、このようなバグが発生しました。

具体的に、どこでキャンセル処理が発生していたのかは確認していませんが、<html>要素をクリックした場合、<body>要素内でクリックしたのとは違って、XPレベルでの確定リクエストが発生していないことに問題があります。そこで、nsHTMLEditorEventListener::MouseDown()を調査してみると、contenteditableで生成されたHTMLエディタのために、ハンドリングすべきmousedownイベントかどうかを、イベントのターゲットが、エディタのルート要素かその子孫であるかどうかを確認していました。ここで問題なのが、エディタのルート要素の定義です。nsHTMLEditorでは、ルート要素は、contenteditable属性で生成されている場合は、その、editing hostになりますが、designMode"on"に設定して生成された場合は、<body>要素になります。このため、<html>要素をクリックした場合は、エディタ外でのmousedownイベントであると判定される結果になってしまっていました。

今回の修正では、Auroraでの修正も必要だったので、ルート要素の定義は変更せず、単純に、mousedownイベントを処理すべきかどうかの判断を、nsEditor::IsAcceptableInputEvent()で確認するようにしました。このメソッドは、もともと、この用途での利用を想定したものなので、期待通りに判断できますし、実績もあります。

これにより、designModeで生成されたHTMLエディタでは、どこでmousedownイベントが発生しても、強制確定のリクエストがXPレベルで発生するようになりました。

Bug-org 1083098 Composition string doesn't appear after forcibly commit composition by moving focus on Linux 初回投稿日時: 2014年10月30日22時48分48秒
カテゴリ: GTK Mozilla Core Mozilla35 Mozilla36 バグ修正
固定リンク: id=2014103005
リンク元: 0件
SNS: (list)

未確定文字列があるエディタ以外をクリックして、フォーカス移動により、未確定文字列を強制確定させた場合、Linux GTK版で、iBusを利用していると、次にどのエディタでもIMEの未確定文字列が入力されず、それを一度確定なりキャンセルすると、通常の状態に戻る、というバグです。Bug-org 1065835の修正によるregressionです。

例によってiBusの非同期での未確定文字列のキャンセル処理がバグの原因になっています。

Bug-org 1065835の修正で、非同期で確定、もしくはキャンセルが行われるIMEを利用していた場合、TextCompositionは自動的に、確定、もしくはキャンセルをDOMイベントを生成して、エミュレーションするようになりました。この際に、TextCompositionのインスタンスは、まだ、破棄されないように修正されました。待機して、その後に非同期で発生するはずのIMEから来るWidgetCompositionEventを無視し、DOMツリーでは発火しないようにしなくてはいけないからです。

フォーカス移動で強制確定が行われた場合かつ、IMEが有効なエディタが続けてフォーカスを得ていない場合、同期でnsIWidget::SetInputContext()が呼び出され、nsGtkIMModule::GetContext()の戻り値が、パスワードフィールド用のGtkIMContextか、IMEを利用しないための、ダミーコンテキストに変わってしまっていることがあります。その後、IMEが非同期で確定、もしくはキャンセルのためのシグナルをnsGtkIMModuleに送信しても、その時点でのGetContext()の戻り値とは異なるコンテキストであるという理由で無視してしまっていました。これにより、TextCompositionが期待する、NS_COMPOSITION_ENDイベントが発火されず、これを待ち続ける状態が続き、新しい未確定文字列が表示されなくなっていました。そして、それの確定、もしくはキャンセル時に発生するNS_COMPOSITION_ENDイベントを受け取ることで、ようやく破棄されて、新しい未確定文字列を処理可能な状態に戻っていた、という訳です。

この修正では、シグナルを受信した場合に渡されたGtkIMContextが、Gecko自身が生成して管理しているもののうちのどれかと同じであるかを確認する形に変更しました。これにより、フォーカス移動でnsGtkIMContext::GetContext()が異なる値を返してきてもシグナルを処理するようにしています。また、シグナルを処理するメソッドの一部は、GtkIMContextを引数で受け取るようにし、GetContext()を利用しないようにしています。

Auroraでも修正しないといけないので、最低限の変更にしてありますが、追々、ほとんどのシグナル処理のメソッドが、GtkIMContextを引数で受け取るように修正する予定です。

Bug-org 1084302 nsGtkIMModule::DeleteText() calls DispatchCompositionChangeEvent() with wrong argument 初回投稿日時: 2014年10月30日22時59分48秒
カテゴリ: GTK Mozilla Core Mozilla36 バグ修正
固定リンク: id=2014103006
リンク元: 0件
SNS: (list)

GTKのIMEでは、南アジアの言語でキャレット周囲の文字を含めて、未確定文字列を生成する場合や、日本語の再変換の実装のために、キャレット前後の任意の文字を削除するシグナルが存在しています。これを処理する場合、アプリ側は未確定文字列を含まない、元の文字列でその削除される範囲を決めなくてはいけません。また、nsEditorは、未確定文字列がある状態での、未確定文字列外の文字の編集をサポートしていません。このため、文字列を削除するシグナルを、未確定文字列が存在する際に受け取った場合、まずは、確定してから、文字を削除し、再度、未確定文字列を復元する必要があります。しかし、その実装であるnsGtkIMModule::DeleteText()では、未確定文字列の文節状態を含んだまま、NS_COMPOSITION_ENDを発火し、復元時には逆に、文節状態を含まないまま、NS_COMPOSITION_CHANGEイベントを発火していました。これは、NS_COMPOSITION_CHANGEイベントを発火するメソッドが、boolの引数で文節情報を含めるかどうかを決めるという設計だったため、呼び出し側のDeleteText()が単純に、truefalseを指定しなくてはいけないところ、それぞれ、逆を指定してしまっていました。

ちなみに、これは、nsGtkIMModuleのリファクタリングの作業中にコード上で発見したバグで、実際にこれが問題になっている環境は無いのかもしれません。かなり前から存在しているバグで、発生したら目立つにも関わらず、バグ報告が今まで無いので。