Bug-org 496360 [TSF] Severe performance problems pasting into contenteditable editor or designMode editor when TSF is enabled
初回投稿日時: 2014年07月31日23時29分47秒
カテゴリ: Android Fennec Mozilla Core Mozilla34 Thunderbird TSF Windows バグ修正
SNS:
Tweet (list)
TSFを有効にしていると、膨大な行数の貼り付けのパフォーマンスが異様に悪くなる、というバグです。バグIDを見れば分かるように、かなり長い間修正できなかったバグですが、ここ最近のIMEContentObserver
周りや、様々な他の修正により、この修正がようやく可能になりましたが、それでも強引な最適化を行って解決しています。
TSFは、フォーカスのあるエディタの内容が変更される度に、その旨をTSFに通知しないといけないのですが、その際に変更された範囲を示す、最初のオフセット、編集前の選択されていた文字列の最後のオフセット、編集後のオフセットを渡さなければいけませんが、これの計算が非常にコスト高だったことがこのバグの原因です。
もともとは、<textarea>
要素でも再現していたのですが、プレーンテキストエディタは途中から、内部処理で、テキストノードと<br>
要素ではなく、white-space: pre;
なテキストノードで表現されるようになったため、このバグは、contenteditable
や、designMode
によって生成されたHTMLエディタでのみ発生するようになっていました。ですので、Thunderbirdや、Gmail等はこのバグの影響が大きかったはずです。
テキストが追加された場合、IMEContentObserver
のmutation observerに、先頭のノードの追加から順次、通知されます。この際に、毎回、テキストの挿入位置、追加されたテキストの長さを計算し、widgetへ通知していました。
ちなみに、テストでは見つけにくいのですが、削除の場合にも同様の問題があり、IMEContentObserver
のmutation observerに、先頭のノードの削除から順次、通知されます。この際に、毎回、削除されるテキストの先頭のオフセットと、削除されたテキストの長さを計算し、widgetへ通知していました。
これらを解決するために、まずは、複数の変更をひとつのテキスト変更と見なして通知できるように、widgetへ通知するオフセットをマージ可能にし、さらに、エディタ上での編集が終了するまで通知を行わないようにしました。これにより、一回の編集につき、一回のテキスト変更、選択範囲変更、レイアウト変更通知がwidgetへ送信されないようになっています。
しかし、この修正では残念ながら、手元の環境ではパフォーマンスは上がりませんでしたが、IME (TIP)の実装・仕様によっては、この修正だけでも劇的にパフォーマンスが上がっていると思います。
次に、連続したテキストの追加時と、連続したテキストの削除時に、何度も似たような、もしくは同じオフセットを計算している部分を改善することにしました。追加時は、追加されたノードの位置と、その終端のオフセットを記憶しておき、次の追加時に同じ範囲を計算する場合にはキャッシュしたオフセット値を使うようにしました。削除時は、削除されるノードの位置とその先頭までのオフセット値を記憶しておき、同じ位置が次に削除される場合にはキャッシュしたオフセットを使うようにしました。
この、ノード位置もキャッシュしておくことで、MutationObserver
をJavascriptで利用して複雑な編集作業をエディタがDOMツリーに変更してしまい、キャッシュした値が破損した可能性がある場合には破棄して再計算するようにして、安全策を講じています。
これらの修正により、ひとまず、見つかっている編集時の極端なパフォーマンス低下は解決しました。手元での検証結果では、TSFモードと、IMMモードで同じようなスコアが出ました。理論上はTSFモードの方が、どれだけ最適化していても若干遅いはずではありますが。
また、コードを調べたところ、テキストの変更は、Windows版のTSFモードの他に、Android版でも監視対象となっていました。このため、Androidでもこのバグは発生していたようですが、同様に修正されています。