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

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

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

2015年9月29日

Bug-org 1203381 IMEContentObserver should not post new notification after it posts some notifications but some of them are not performed yet
初回投稿日時: 2015年09月29日00時46分03秒
最終更新日時: 2015年09月29日00時48分02秒
カテゴリ: e10s IME Mozilla Core Mozilla43 バグ修正
SNS: (list)

IMEContentObserverのログを記録できるようにしてみたところ、異常な回数、NOTIFY_IME_OF_POSITION_CHANGEを送信しているのを発見しました。もし無駄な呼び出しがあった場合、e10sモードではかなり無駄にCPUを走らせていることになります。

調べて見ると、nsRunnableの派生クラスを利用して、script blockerが取り除かれた瞬間に何らかの通知を送信すると、特にe10sモードではContentCacheが必ず、WidgetQueryContentEventを発火してきます。これを処理するためにContentEventHandlerがペンディングとなっているレイアウトをフラッシュすることで、reflowが発生し、これがNOTIFY_IME_OF_POSITION_CHANGEを送信の原因になっていることが多い事が分かりました。しかし、よく考えてみれば、何らかの通知を送信中に発生したreflowに対して、NOTIFY_IME_OF_POSITION_CHANGEを送信する必要はないのです。なぜなら、そのreflowの結果から計算された情報をWidgetQueryContentEventで返すため、IMEからすると既に最新のレイアウト情報を持っているのに余分なNOTIFY_IME_OF_POSITION_CHANGEを受け取ることになります。

これを解決するには通知を送信中に追加で発生した変更情報はマージし続けたり、不要なものは無視する必要があります。しかし、今までのIMEContentObserverは各通知を個別のnsRunnableの派生クラスに保存し、新しい通知が来た場合にはまだ前回の通知が処理されていなくても、再びnsRunnableの派生クラスを生成していました。そのため、この複雑な状態を管理するためには多くのフラグを必要とします。しかし、そのようなコードでは限界が見えていますので大きく設計を変更することにしました。

まず、通知内容は全て実際に送信されるまでIMEContentObserver自身が保持し続けるようにしました。これにより、nsRunnable派生クラスを生成した後、実行までに新しい通知が発生しても、変更内容をマージするだけで済みます。

次に、nsRunnableクラスを各通知ごとに生成するのではなく、通知を送信するクラスを一つにまとめ、これが実行された時に所定の順番で通知が行われるようになりました。これにより、通知の順番を常に保証することが可能になりました(テキストの変更範囲通知、選択位置の変更通知、レイアウトの変更通知の順でなくてはIMEから見た場合に矛盾が発生するため)。

最後に、順番の保証にも絡みますが、再帰呼び出しが発生していないか確認するようにし、再帰呼び出しになってしまう場合には実行予約だけをメインスレッドに投げるようにしました。これにより、スタックオーバーフローが発生する可能性も修正しています。

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

bug-org 1203381を含むエントリ