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

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

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

2014年9月25日

Bug-org 1052343 [TSF] crash in msctf.dll@0x2efce 初回投稿日時: 2014年09月25日00時02分04秒
カテゴリ: Mozilla Core Mozilla35 TSF Windows バグ修正
固定リンク: id=2014092500
SNS: (list)

Bug-org 1053048のテストケース<textarea>要素でEnterキーをTSFモードで押すと、長時間起動していたPCでは、ランダムにクラッシュするというバグを見つけました。

クラッシュ自体は、OnLockGranted()を呼び出している間に、TSF内部でクラッシュしていたので、ある意味、TSFのバグと言えそうです。

問題のテストケースでは、Enterキーを押した時に、<textarea>要素がリサイズされるため、一旦、フォーカスを失い、また、フォーカスを再度得る、という動作になるのですが、OnLockGranted()を呼び出し中にフォーカスを失うことで、nsTextStore::Destroy()が呼び出され、nsTextStoreが生成・保持していたTSFのオブジェクトの参照をやめた際に、実際に、それらのオブジェクト自体が解放されているようでした。

つまり、TSF内部で直接参照しているこれらのうちのいずれか、もしくは全てが、TSF内部でstrong referenceではなく、weak referenceで管理されていることを意味します。つまり、OnLockGranged()を呼び出している間は、ITextStoreACPインターフェースの実装側、つまり、アプリは、自信が生成したオブジェクトを手放してはいけない、ということになります。

これを解決するために、このバグでは、根本的な挙動に修正を入れました。

これまでは、nsTextStoreは、プロセス起動時にひとつだけ生成され、エディタがフォーカスを持っている間のみ、必要なTSFのオブジェクトを生成して保持し、エディタがフォーカスを失った際に、それらを解放していました。ですが、この疑似シングルトンのような挙動では複雑なケース、例えばドキュメントがロックされている最中にフォーカスが複数回移動するようなケースがあると、破綻しそうです。

そこで、エディタがフォーカスを得る度に、nsTextStoreを生成し、sEnabledTextStoreで掴んでおくようにし、フォーカスを失った際には、sEnabledTextStoreがそれまでのnsTextStoreのインスタンスを手離し、参照カウントがそのままゼロになったら完全に破棄されるようにしました。これで、メンバの複雑な管理には悩まされずに済みます。

続いて、OnLockGranted()を呼び出す前に、nsTextStore自身のインスタンスをスタック上のnsRefPtrで掴んでおき、sEnabledTextStoreが手放しても、参照カウントが絶対にゼロにならないようにし、OnLockGranted()の呼び出し中は、各種インスタンスが解放されないようにしました。

そして、sEnabledTextStoreがクリアされる直前には今まで通り、nsTextStore::Destroy()を呼び出しますが、Destroy()は、ドキュメントをロック中には呼び出された場合には、これを表すフラグを立てるだけにし、実際にメンバの解放は行わないようになりました。その後、RequestLock()の処理が全て終わってから(ドキュメントのロックが解除されてから)、このフラグが立っている場合には、再度Destroy()を呼び出し、クリーンナップを行うようにしました。

まとめると、nsTextStoreは、エディタがフォーカスを得た際に生成され、エディタがフォーカスを失う際に解放されますが、RequestLock()が呼び出されている最中にフォーカスを失った場合のみ、直ちに解放されず、処理が完了後に自動的に解放されます。

Bug-org 1061810 [TSF] MS-IME 2012 (Win 8.1) sometimes fails to move candidate window to proper position 初回投稿日時: 2014年09月25日00時18分25秒
カテゴリ: Mozilla Core Mozilla35 TSF Windows バグ修正
固定リンク: id=2014092501
SNS: (list)

Firefoxの検索ボックスで、MS-IMEを利用していると、時々発生するバグです。

Windows 8.1上で、MS-IMEで候補ウインドウを開いた状態で、スペースキーで順に、変換候補を切り替えていくと、希に、候補ウインドウがディスプレイの左上に表示されてしまうことがあるというバグがあることに気付きました。

早速ログをとってみると、NS_QUERY_CARET_RECTがランダムに失敗している様に見えました。その原因について、色々と考えてみましたが、全く、納得できる答えが発見できず、久々にデバッガに頼りました。

デバッガで失敗した箇所を探してみると、nsRangeでセキュリティチェックに引っかかり、コンテンツへのアクセスに拒否されているケースがあることが分かりました。何故、そのような事が発生するのか、詳しいことは分かりませんが、スタック上には、Javascriptで実装された、autocompleteのタイマーからの呼び出しと思われるメソッドの呼び出しがあり、それによって、TSFに通知が飛び、TSFからnsTextStoreへ文字位置の問い合わせが来ていました。つまり、TSFからのリクエストが、content権限で処理されていたことが原因な様です。

ひとまず、解決策として、nsRangeに新設されていた、内部処理用のメソッドをContentEventHandlerからは利用することで、セキュリティチェックを回避するようにしました。ContentEventHandler自身はJavascriptから呼び出して利用するには、chrome権限が必要ですので、セキュリティ的なチェックがそもそも必要ないためです。

Bug-org 1059643 [IMM32] Reimplement mouse button event handling with NOTIFY_IME_OF_MOUSE_BUTTON_EVENT 初回投稿日時: 2014年09月25日00時24分48秒
カテゴリ: Mozilla Core Mozilla35 Windows バグ修正
固定リンク: id=2014092502
SNS: (list)

Bug-org 826657の修正により、フォーカスを持ったエディタ内の文字上でマウスボタンが押されるか、離された時、content側から、widgetにその通知を出せるようになりました。

このバグでは、IMMモードのマウスイベントのハンドリングを、この通知を利用して再実装しました。これにより、実装がより単純になり、e10sへの対応も可能になりました。

Bug-org 1062053 [TSF] nsTextStore should use StaticRefPtr 初回投稿日時: 2014年09月25日00時30分18秒
カテゴリ: Mozilla Core Mozilla35 TSF Windows バグ修正
固定リンク: id=2014092503
SNS: (list)

nsTextStoreには、プロセス起動中にTSFのオブジェクトをずっと参照し続ける、staticなメンバが多数あります。今までは、これらの参照カウントを、マクロを活用して操作していましたが、これを分かりやすいコードに書き換えられるStaticRefPtrが新たに作成されているので、全てこれで置き換えよう、というバグです。

ついでに、sTsfFooBarという冗長な名前だったメンバは、sFooBarとリネームされました。

Bug-org 1059680 [e10s][TSF][IMM32] IME should be able to handle mouse events fired in content process 初回投稿日時: 2014年09月25日00時37分35秒
カテゴリ: e10s Mozilla Core Mozilla35 TSF Windows バグ修正
固定リンク: id=2014092504
SNS: (list)

Bug-org 826657の修正により、フォーカスを持ったエディタ内の文字がクリックされた際に、widgetはその通知を受け取ることができるようになり、IMEがそのイベントを消費した場合には、content側ではそのイベントを無視するようになりました。

この通知をe10sに対応させよう、というのがこのバグです。

単純に、プロセス間で、同期通信で通知を、子から親に通知し、その結果(消費されたか否か)を返すようにしています。

Bug-org 1066594 [TSF]Microsoft IME 2010 remove typed text by converting character 初回投稿日時: 2014年09月25日01時11分44秒
カテゴリ: Mozilla Core Mozilla35 TSF Windows バグ修正
固定リンク: id=2014092505
SNS: (list)

MS-IMEで、変換中に新たに文字を追加入力すると、変換している部分が確定され、追加の文字が未確定文字列として挿入されます。しかし、MS-IMEは非常にトリッキーにこれを実現しています。まず最初に、未確定文字列に新しい文字を追加し、変換済みの文節のみを部分的に確定します。そして、新しい文字だけ未確定のまま、その状態が継続します。この複雑な動作がGeckoではうまく処理できていなかったのをBug-org 1057192で修正したのですが、その修正が中途半端で、未確定文字列の後ろに確定済みの文字が既にある場合は、追加入力した一文字が、キャレット直後の一文字を上書きしてしまう、というバグが残ってしまっていました。

部分確定を行う際に、まず、最新のコンテンツと選択範囲を保存しておき、未確定部分を一旦、コンテンツから取り除き、通常通りに確定。その後でコンテンツと選択範囲を復元し、compositionstartを生成する、という前回の修正の仕方にこのバグの原因がありました。

nsTextStoreは、TSFのドキュメントロックを実現するために、コンテンツや選択範囲変更のリクエストを即座にエディタに反映させるのではなく、キャッシュしておいたコンテンツと選択範囲を、TSFからのリクエストに応じて変更しつつ、それに必要な後でエディタに反映される際に必要なDOMイベントをpending actionとして、配列に保存し、ロック解除時にDOMイベントを連続して発火するという方法をとっています。

このため、上記の処理の流れでは、pending actionからcompositionstartイベントを生成する際には、復元したはずの、新しい未確定文字列をエディタ側に送っていないのに、未確定文字列が存在しているはずの、その部分を選択するようにエディタにイベントを発火し、その部分を上書きする形で未確定文字列を生成しようとしていました。これが、新しい未確定文字列と同数の確定済みの文字が上書きされてしまう原因です。

新しい未確定文字列はcompositionstartイベントの後に続くtextイベントで自動的に送信・挿入されるので、compositionstartイベントの発火時には、選択範囲を変更して上書きするのではなく、部分確定後のキャレット位置にそのまま、未確定文字列が挿入されるように修正しました。

今回の一件で、キャッシュをトリッキーにいじっても、それがpending actionの解消時に結果として反映されるわけではないという、当たり前の教訓が得られました。

Bug-org 1069726 bustage: mfbt/tests/TestJSONWriter.cpp(300) : error C2143: syntax error : missing ';' before ':' 初回投稿日時: 2014年09月25日01時17分33秒
カテゴリ: Mozilla Core Mozilla35 Windows バグ修正
固定リンク: id=2014092506
SNS: (list)

TestJSONWriterという、コンパイルコードテストが追加されていたのですが、日本語版のWindowsでこれをビルドしようとすると、コンパイルエラーが出るというバグです。

エラーの発生した行を見てみると、非ASCII文字が含まれていて、cppファイルは、BMP無しのUTF-8形式で保存されていました。

調べて見ると、VisualStudioは、BOM無しのファイルはUTF-8として読み込まない仕様のようなので、日本語環境ではShift-JISとして読み込み、たまたま、"がエスケープされてしまっている様でした。

仕方ないので、非ASCII文字を全てエスケープシーケンスで書き直し、VisualStudioでも、OSのロケールを選ばず、ビルドできるようにしました。

2014年9月30日

Bug-org 1072137 Assertion failure: editingHost == mEditableNode (found editing host should be mEditableNode) 初回投稿日時: 2014年09月30日17時59分46秒
カテゴリ: Javascript Mozilla Core Mozilla35 バグ修正
固定リンク: id=2014093000
SNS: (list)

documentのルートノードを、Javascriptを使って、<textarea>要素にし、さらに、その子要素に、<input type="text">要素を追加し、これにフォーカスをあわせると、デバッグビルドでは異常を検知して、IMEContentObserver内でクラッシュする、というバグです。

検知した異常というのは、フォーカスノードから算出した、編集可能なルートノードと、そのエディタが一致しない、というものでした。

IMEContentObserver::Init()内でIMEStateManager::GetRootEditableNode()を呼び出し、フォーカスノードの親が編集可能なら、さらに祖先が編集可能か調査し、編集可能な最後の祖先を探します。これにより、IMEContentObserver::Init()はこのケースでは<input type="text">要素が欲しかったのですが、<textarea>要素が返され、さらに、このような異常な状況下では、<textarea>要素は自身のエディタを生成していませんでした。

これだけなら、非常にマイナーかつ、実際には起こりえないようなバグですが、このバグの原因からすると、<body contenteditable><input type="text"></body>の様な場合にも、<input type="text">要素がフォーカスを持っていても、算出される編集可能なルート要素は、<body>要素、ということになります。

そのため、IMEContentObserverはこのようなケースでは、誤った要素を監視していることになり、これはデリケートなTSFモードでは色々と嫌なシナリオが思い浮かびます。

ひとまず、このバグでは、要素を祖先方向に辿って、編集可能か調べていく際に、そのノードが独立したセレクションを管理しているかどうかを調査し、該当した場合にはそれをルート要素として返すように、IMEStateManager::GetRootEditableNode()を修正しました。

独立したセレクションは現在、<input><textarea><select>要素のみが持ち得ていて、<select>要素は常に編集不能と判定されるので、論理的にはまずい対応ですが、とりあえずは動く形になっています。

Bug-org 1065835 TextComposition should have API to commit or cancel composition and if widget doesn't perform it synchronously, it should synthesize composition end event 初回投稿日時: 2014年09月30日18時12分53秒
カテゴリ: e10s GTK Mozilla Core Mozilla35 バグ修正
固定リンク: id=2014093001
SNS: (list)

TextCompositionに、IMEの未確定文字列を強制確定、もしくは強制キャンセルさせるAPIを追加し、TextComposition自身が同期的にこれを実行することを保証すべき、というバグです。

現在、これらのリクエストは、ネイティブのAPIを利用して、IMEにリクエストし、それがどのように実行されるかは各OSやIMEの挙動に依存する、という形になっています。このため、iBusのように、非同期で未確定文字列を操作されてしまうと、Gecko内部でAPIを呼び出した側は、まだ、未確定文字列が残っているかどうかを意識して処理を行わなければいけません。

これはもちろん、Gecko本体のみならず、Webアプリにも、OSやIME依存のバグが産まれやすいことを意味していて、非常によくありません。また、e10sでは確実に非同期での確定になってしまいますので、e10s対応のためにもどうにかしなければいけない話です。

今回の修正では、nsIWidget::NotifyIME()を呼び出し、その呼び出し直後に確定やキャンセルが実行されていない場合にはDOMイベントを発火し、これをエミュレートし、その後にやってくる非同期で実行されたネイティブイベントは無視するようにしました。

また、Linuxのように、明示的に確定や破棄といったネイティブAPIが存在しない環境のために、それぞれのリクエストで期待した確定文字列が来ていない場合には、確定文字列を期待される文字列で上書きする機能も入れています(ただし、全角スペースをプレースホルダーとして未確定文字列に挿入する中国語のIMEでは、非同期確定の場合には期待通りに動作しませんが……)。

この修正により、nsGtkIMModuleは、同期確定・キャンセルをエミュレートするコードが削除されたので、処理が単純化しています。強制確定に絡んだバグがもしあったら、それも同時に修正されているかもしれません。