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でもこのバグは発生していたようですが、同様に修正されています。
台湾でメジャーらしい、ChangJieというIMEがあるのですが、<textarea>
要素内で改行を挿入直後に、そのまま入力すると、前の行の行頭に入力されてしまうというバグです。何故かBug-org 496360を修正するパッチのうちのいくつかを適用したら再現するようになりました。
テキストノードの一部が変更された場合、IMEContentObserver
のmutation observerの、CharacterDataChanged()
が呼び出されるのですが、この際に、変更によって削除される範囲と、追加されるテキストの長さを計算する時に、改行文字をCRLF
の二文字として計算していなかったことでした。
これにより、改行を挿入した際に、本来なら二文字追加されていると、TSFに通知しないといけないところを、一文字だけ追加された、と通知していたわけです。しかし、選択位置を調べて見ると、それとはまた違った、一文字分、後ろのオフセットが返されるという矛盾した状態になっていました。
TSFに変更箇所を通知する際に、きちんと、改行文字をWindowsでのみ二文字としてカウントするように修正しています。