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

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

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

2014年8月6日

Bug-org 1037328 [TSF] Enable TSF mode in default settings in Nightly build 初回投稿日時: 2014年08月06日20時55分11秒
カテゴリ: Firefox Mozilla Core Mozilla34 SeaMonkey Thunderbird TSF Windows バグ修正
固定リンク: id=2014080600
リンク元: 0件
SNS: (list)

Nightlyビルドでのみ、TSFモードを有効化しようというバグです。

TSFモードに関する簡単な解説は先日、modestに投稿していますので、そちらを参照してください。

TSFに関する技術的な解説は、Nyaruruさんの記事が詳しいです

この修正により、Google日本語入力のユーザさんのみ、まだ、ハックを入れてもいない上に、Google日本語入力側でも対応されていないので、若干、候補ウインドウの位置がばたつくことがあり、不快かもしれませんが、そのうちにハックを入れる予定ですので、気になる方は、Bug-org 970860を追跡しておいてください。

ある程度、未実装の機能(主にマウスのハンドリングに利用される機能)を実装し、分かっているパフォーマンスの問題が片付いたら、Auroraでも有効にする予定です。

なお、すでに、Windows XPは使うべきOSではありませんが、Windows XPでのTSFサポートは今後も進まないと思います。TSFモードがデフォルトで有効になるのは、Windows Vista以降で利用している場合のみで、intl.tsf.force_enabletrueにすることで、一応、Windows XPとWindows Server 2003でもTSFモードを有効にすることができます。ただし、私が知る限り、Windows XPで動作するサードパーティー製のTSF対応IME (TIP)は存在していませんので、事実上、使い物にならないと思います(MS-IMEとの組み合わせでは常用に耐えないことを確認済み)。

2014年8月12日

Bug-org 1046101 Redesign nsEventStructType 初回投稿日時: 2014年08月12日00時47分54秒
最終更新日時: 2014年08月12日00時48分54秒
カテゴリ: Events Mozilla Core Mozilla34 バグ修正
固定リンク: id=2014081200
リンク元: 0件
SNS: (list)

nsEventStructTypeのリネーム等々を行って整理しようというバグです。

DOMイベントの実装クラスのメンバに、WidgetEventや、その派生クラスをイベントの種類ごとに固有のものを持っていますが、この内部処理用のクラスは、基底クラスのポインタから、インスタンスのクラスを特定するために、eventStructTypeメンバを持っており、これは、イベントのクラスごとにひとつ、nsEventStructTypeで列挙されている固有の値が、インスタンス生成時に設定されています。

今回の再デザインの主目的は、この、nsEventStructTypeを、EventClassNameList.hをインクルードして、自動的に生成することを可能にする、というものでした。そのため、NS_FOO_EVENTという名前だった各値を、eFooEventClassという名前に変更しています。

また、C言語ではstructというと、構造体をイメージしてしまうので、nsEventStructTypeは、mozilla::EventClassIDに改名し、WidgetEvent::eventStructTypeは、WidgetEvent::mClassに改名しています。

これらの修正により、

if (aEvent->eventStructType == NS_MOUSE_EVENT) {

というコードが、

if (aEvent->mClass == eMouseEventClass) {

という感じになりました。

Bug-org 1049768 [TSF] Glitches in Chinese IME with intl.tsf.enable=true 初回投稿日時: 2014年08月12日01時02分03秒
最終更新日時: 2014年08月12日01時03分48秒
カテゴリ: Mozilla Core Mozilla34 TSF Windows バグ修正
固定リンク: id=2014081201
リンク元: 0件
SNS: (list)

台湾のサードパーティー製のIME、Free ChangJie IMEと、Easy Changjeiでは、一文字確定するためにスペースキーを押す度に、スペースと、確定された文字が入力される、というバグです。

nsTextStoreのログを調べてみると、これらのTIPからは、ITfContextOwnerCompositionSink::OnUpdateComposition()ITextStore::SetSelection()や、ITextStore::SetText()等の後に呼び出されるにも関わらず、新しい未確定文字列のレンジを引数で渡してきていないため、compositionupdateイベントと、そのためのtextイベントを発火させるための準備情報を完全に埋めないまま、これらのイベントが発火されてしました。これが、おかしな挙動の原因です。

今回の修正で、ITfContextOwnerCompositionSink::OnUpdateComposition()が更新済みのレンジを引数で受け取らなかった場合、compositionupdateの発火を未完であることを明示した上で予約しておき、ドキュメントのロックが解除される時に、予約されたその情報が未完のままであれば、その場で最新の情報を取得した後でイベントを発火するように修正しました。

2014年8月14日

Bug-org 1050703 [TSF] Cursor doesn't move when input an Enter with IME in Contenteditable = true field 初回投稿日時: 2014年08月14日11時56分13秒
最終更新日時: 2014年08月14日11時56分59秒
カテゴリ: Mozilla Core Mozilla34 TSF Windows バグ修正
固定リンク: id=2014081400
リンク元: 0件
SNS: (list)

TSFモードで、HTMLエディタの行頭に文字を入力しようとすると、前の行末に入力される、というバグです。

contenteditableな要素等、HTMLエディタ上でEnterキーを押すと、<p>要素が閉じられ、新たに、<p>要素が生成されます。つまり、

<p>一行目</p>
<p></p>

という感じになります。ただし、空の要素に通常のフォントサイズの行高を持たせるため、実際には<br>要素が挿入されています。

<p>一行目</p>
<p><br></p>

TIPにテキストやキャレット位置を返す場合、このHTMLの内容を、プレーンテキストとして扱って、その内容やオフセットを利用しているのですが、この、変換時に、全ての要素の末尾に<br>要素が存在しているのを期待していたのが原因でした。

例えば、

<p>一行目</p>
<p>二行目</p>

という場合、生成されるプレーンテキストは、"一行目\r\n二行目"ではなく、"一行目二行目"となっていました。このため、HTMLエディタ上での一行目末尾と、二行目行頭は同一のオフセットとなり、selection setイベントを利用して、キャレット位置を指定すると、一行目の末尾が優先されていました。

根本的なバグの修正方法は、<p>要素の末尾に改行文字を挿入して扱うということになりますが、ちょっとパッチを書いただけではうまくいきませんでした。また、成功しても副作用の恐れがあり、現在対応中のバグの数から、もう新しいバグを処理する余裕がありませんでしたので、nsTextStore側で回避することにしました。

nsTextStoreは常に、compositionstartイベントを送信する前に、selection setイベントを利用して、キャレット位置をTIPの指示通りの位置に移動させています。しかし、設定すべき位置が元々の位置と同じであれば、そもそもこれを行う必要がありませんので、そのように変更しました。これにより、行頭にキャレットがある場合、キャレット位置が行末に再設定されることを抑制しています。

Bug-org 1051689 [TSF] Should call RecordCompositionUpdateAction() when adding a pending action and the last compositionupdate is incomplete 初回投稿日時: 2014年08月14日12時07分23秒
カテゴリ: Mozilla Core Mozilla34 TSF バグ修正
固定リンク: id=2014081401
リンク元: 0件
SNS: (list)

Bug-org 1049768の修正のフォローアップです。ドキュメントロック中にTIPから適切に未確定文字列の範囲を示すレンジ付きでITfContextOwnerCompositionSink::OnUpdateComposition()が呼び出されないままに、compositionstartcompositionendをキュー登録しなければいけなくなった場合、不完全なcompositionupdateがキューに残ることになります。

この際に、未確定文字列の情報を強制的に取得しに行き、不完全な状態を解消するように修正しています。ただし、実際にこれで問題が発生しているケースを見つけていませんので、そのような状況下で情報の整合性がとれるのかは不透明ではあります(ATOK 2014ではログを見ると時々発生してる様に見えますが、それで実害は出ていなかった)。

ちなみに、これは以前にTIP作者の方から指摘されていた問題でした。

Bug-org 1050124 [TSF] Support TSATTRID_Text_VerticalWriting 初回投稿日時: 2014年08月14日12時17分37秒
カテゴリ: Mozilla Core Mozilla34 TSF Windows バグ修正
固定リンク: id=2014081402
リンク元: 0件
SNS: (list)

TSFでは、エディタ全体や、キャレット位置の書式をかなり詳細にTIPが取得できるようになっています。このうち、日本語TIP開発者の方々から、Geckoでは縦書きかどうかを問い合わせると、エラーになり、これは他のTSF対応アプリでは発生していないので、トラブルの元になりそう、と警告を受けていたので今回、対応することにしました。

この対応により、現在は常に、水平方向へ流れるテキストであると、明示的にTIPに返すようになっています。

他の取得可能な属性を見ると、TSATTRID_Text_RightToLeftあたりは重要なのかな、と思いますが、今のところ、query contentイベントがこれを取得するようになっていませんので、実害が報告されるか、暇になったら対応しようかと考えています。

Bug-org 1052230 IMEContentObserver shouldn't flush pending notifications again during flushing notifications 初回投稿日時: 2014年08月14日12時26分13秒
カテゴリ: Mozilla Core Mozilla34 TSF Windows バグ修正
固定リンク: id=2014081403
リンク元: 0件
SNS: (list)

Bug-org 496360の修正により、IMEContentObserverは、一回の編集処理中に発生した通知は全て、編集が終了してから一括で送るように修正しましたが、通知により、コンテンツ内容の問い合わせが発生した場合に、保留されているレイアウトの変更がフラッシュされ、レイアウト変更通知が発生することで、通知がネストすることがあるのを、他のバグの調査中に、スタックトレースから偶然発見しました。

通知中は再度、呼び出されても無視するようにし、全ての通知が終わった後で、再び、通知が必要になっていないか確認し、必要になっていたら、非同期でもう一度通知を行うように修正しています。

2014年8月26日

Bug-org 1053048 Accessing selectionStart or selectionEnd from nsISelectionListener::NotifySelectionChanged() may cause cancelling the edit action 初回投稿日時: 2014年08月26日11時45分49秒
最終更新日時: 2014年08月26日11時48分36秒
カテゴリ: Firefox OS Mozilla Core Mozilla34 TSF バグ修正
固定リンク: id=2014082600
リンク元: 0件
SNS: (list)

<input>要素や、<textarea>要素に対して、アドオン等がnsISelectionListener利用して、キャレット位置の変化を監視し、NotifySelectionChanged()が呼び出された時に、selectionStartや、selectionEndの値を取得しようとしている場合、これらの要素のkeydownイベントで、要素のレイアウトを変更すると、編集した内容がキャンセルされる、というバグです。

日本で必要としている、Firefox OSのキーボードAPIの修正が投入されないので、該当のバグを読んでみると、このバグが原因で、一部のテストがオレンジになっていることが分かりました。そこで、急遽、調査して修正することにしました。ちなみに、TSFには、キャンセル前のテキストの変更通知が行われた後、キャンセルされていたので、TIPを混乱させる原因になっていそうです。実害は未確認ですが。

nsEditorは、編集作業を行っている間、表示がばたついてパフォーマンスが落ちないように、選択範囲・キャレット位置の変更通知や、画面の再描画を抑制し、編集が終わってから一気に行う様になっています。それが終わると、締めに、nsIEditorObserver::EditAction()または、nsIEditorObserver::CancelEditAction()を呼び出しています。

一方、<input>要素や、<textarea>要素は初めてフォーカスを持った時にnsPlaintextEditorを生成し、編集を管理させ、レイアウトの変更で、フレームが再構築される場合には一旦、エディタを破棄し、フレームが再生成された後にエディタを再度作成し、そのvalue値を編集対象としています。

この、value値の保存タイミングと、値に変化があったかどうかを保持しているフラグの更新タイミングが問題でした。前者は、エディタを破棄する直前にvalue値を内部で保持している変数にエディタの最新情報を設定していたのですが、後者のフラグは、nsIEditorObserver::EditAction()が呼び出された時に、初めて更新されていました。このため、エディタを破棄する際には、新しいvalue値をすでに取得しているものの、変更されたことはまだ記録していないので、デフォルトのvalue値を取得しにいき、編集前のvalue値でエディタが再生成され、編集がキャンセルされてしまっていました。

今回の修正で、要素を管理している側では、エディタを破棄する前に、まだ、nsIEditorObserver::EditAction()が呼び出されていなかった場合には、先に呼び出すように変更し、何があっても、期待通りの順序で処理が行われるように修正しています。

Bug-org 1054108 [TSF] Implement hack for FreeCJ and Easy Changjei 初回投稿日時: 2014年08月26日14時08分59秒
カテゴリ: Mozilla Core Mozilla34 TSF Windows バグ修正
固定リンク: id=2014082601
リンク元: 0件
SNS: (list)

TSFモードでは、台湾のFree ChangJie 2010と、Easy Changjeiの候補ウインドウが表示されない、という、TIP側のバグがあります。Free ChangeJie 2010は、Bug-org 1050041に、Easy Changjeiは、Bug-org 1050050にTech Evangelismバグとして登録していますが、どちらのベンダにコンタクトをとっても、全く反応がない上に、台湾のNightlyユーザには常用に問題があるレベルのバグですので、取り急ぎ、ハックを入れることにしました。

この修正で、これらのTIPがアクティブな場合、nsTextStore::GetTextExt()は、acpEndが未確定文字列の開始位置以降なら、未確定文字列の最初の文字の前にあるキャレットの矩形を返すようにし、問題のあるTS_E_NOLAYOUTを返さないようにしています。

これらのハックは、intl.tsf.hack.free_chang_jie.do_not_return_no_layout_errorintl.tsf.hack.easy_changjei.do_not_return_no_layout_errorで無効化することができます。

Bug-org 1052286 [TSF] nsTextStore::SetInputContext() should not ignore "password" state 初回投稿日時: 2014年08月26日16時24分01秒
カテゴリ: Mozilla Core Mozilla34 TSF Windows バグ修正
固定リンク: id=2014082602
リンク元: 0件
SNS: (list)

nsTextStoreの一部のコードが、編集可能なコンテンツがフォーカスを持っているか否かを判定すべきところで、IMEが有効であるかどうかをチェックしている、というバグです。

mozilla::widget::IMEState::IsEditable()というユーティリティメソッドを用意し、全プラットフォームでこれを利用できるようにしつつ、該当箇所を修正しています。

2014年8月28日

Bug-org 1056545 Cleanup event handlers of nsEditorEventListener and nsHTMLEditorEventListener 初回投稿日時: 2014年08月28日11時50分05秒
カテゴリ: Mozilla Core Mozilla34 バグ修正
固定リンク: id=2014082800
リンク元: 0件
SNS: (list)

Netscape時代からの名残の残っている、nsEditorEventListenernsHTMLEditorEventListenerのコードを現在のコーディングルールにあわせて、一部、書き直そうというバグです。

この修正により、余分なvirtual callが削減され、イベントの妥当性検査が徹底され、Event.typeの文字列値ではなく、WidgetEvent::messageの整数をswitch文で比較するようになったので、イベントハンドリング時の処理が若干軽くなっています(パフォーマンスよりも、CPUの使用率が下がってるのではないかと)。

このため、chrome権限のあるJavascriptで、不正なイベントインターフェースで生成されたイベント(例えば、KeyboardEventで作られたmousedownイベント)は確実に無視されるようになっています。

ただし、nsFocusManagerの動作と整合性持たせるため、また、古いGeckoとの互換性のため、focusイベントとblurイベントは不適切なインターフェースで生成されていても、ハンドリングされるままにしてあります。ですので、new FocusEvent("focus", {});ではなく、new Event("focus", {});と記述しても、例外的にエディタでもフォーカスの移動が発生したものとして処理されます。ただし、この場合は従来通り、Event.typeの文字列比較が発生しますので、新規にコードを書き下ろす場合は、正しく、あるべきFocusEventインターフェースを持ったインスタンスを生成してください。

Bug-org 1055342 [TSF] Rename CurrentContent of nsTextStore to LockedContent 初回投稿日時: 2014年08月28日12時00分59秒
カテゴリ: Mozilla Core Mozilla34 TSF バグ修正
固定リンク: id=2014082801
リンク元: 0件
SNS: (list)

TSFの実装であるnsTextStoreのプチリファクタリングです。

これまでは、コンテンツを取得する必要があるのは、常に、ドキュメントがロックされている間だけでした。しかし、マウスイベントの通知に対応するためには、ドキュメントがロックされていない間に発生するITfMouseSinkのインストール時にもコンテンツを取得することが必要になります。

しかし、常にコンテンツを取得し、キャッシュし続けるのは、メモリ消費量の観点からすると非常にコストパフォーマンスが悪すぎます。たとえば、wiki等の巨大なコンテンツを編集していると、エディタ内のテキストが1MB以上ある可能性も普通にあります。

このため、従来から存在するnsTextStore::mContentは引き続き、ドキュメントロック中のコンテンツのみキャッシュすることにし、変わらず、ドキュメントのアンロック時に内容を破棄するようにすることにしました。このため、nsTextStore::mLockedContentにリネームし、ロック中のコンテンツのキャッシュであることが自明なように修正しました。これにあわせて、mLcokedContentを初期化するメソッドも、nsTextStore::CurrentContent()から、nsTextStore::LockedContent()に改名しています。

さらに、コンテンツの取得を二重化しないように、nsTextStore::GetCurrentText()というメソッドを新設し、nsTextStore::LockedContent()もこれを呼び出すようにしています。

2014年8月30日

Bug-org 1057192 [TSF] Microsoft IME puts twice the first character of new composition when you start new composition when there is old composition 初回投稿日時: 2014年08月30日16時47分12秒
カテゴリ: Mozilla Core Mozilla34 TSF Windows バグ修正
固定リンク: id=2014083000
リンク元: 0件
SNS: (list)

MS-IMEを使って、変換文字列がすでにある状態で、新しい文字を入力すると、変換済みの文字列が確定され、新しい未確定文字列として文字が入力されるのですが、TSFモードでは、この最初に入力した文字ごと確定され、未確定文字列にも含まれるため、文字がダブってしまう、というバグです。

このバグの原因は、Bug-org 1050703の修正によるregressionでした。Bug-org 1050703では、キャレット位置が変化しない場合、compositionstartイベントを送信する前に、選択範囲の調整を行わないように修正しましたが、この際に、誤って、未確定文字列の一部のみが確定される、今回のようなケースの際には、常に、選択範囲の調整を行っていないのが原因でした。

ひとまず、regressionは、即、修正しましたが、そのままでは、「あい」と入力し、変換後、「う」と入力すると、「あいう」と確定され、「う」が選択された状態で、未確定文字列の編集が始まり、未確定の「う」で上書きする、という動作になっていました。

これでは、「う」を確定後にUndoすると、「あいう」から「あいう」になり、次のUndoで全てがキャンセルされます。これは明らかに、ユーザの感覚とは異なるものですので、未確定文字列の一部が確定される際には、一度、未確定のままキープされる文節の文字列を削除した上で確定し、その後、未確定文字列を復元するように修正しています。これにより、「あいう」→「あい」→「」というトランザクションが記録されるようになっています。

Bug-org 826657 [TSF] Implement ITfMouseTrackerACP 初回投稿日時: 2014年08月30日17時09分58秒
カテゴリ: Mozilla Core Mozilla34 TSF Windows バグ修正
固定リンク: id=2014083001
リンク元: 0件
SNS: (list)

TSFでは、TIPは、マウスのイベントを受け取りたい場合、ITextStoreに、マウスイベントのリスナを登録する必要があります。このバグはそれに、対応しようというバグです。

まず、イベントリスナのインターフェースである、ITfMouseSinkを受け入れるための、ITfMouseTrackerACPインターフェースを単純に、nsTextStoreに実装しました。

ただし、実際の登録・削除処理は若干、面倒なことに、任意の個数のITfMouseSinkを同時にインストール可能です。これは、MSDNでは名言されていませんが、ITfMouseTrackerACP::AdviseSink()で登録時にDWORD値のCookieを返し、ITfMouseTrackerACP::UnadviseSink()を呼び出す時には、そのCookie値のみで削除するITfMouseSinkを指定しようとしている形になっていることから、DWORDで表現できる個数程度は登録可能である必要があるということが伺えます。現に、他のオープンソースソフトウェアのソースコードを調査しましたが、やはり、複数のITfMouseSinkを同時にインストールできるようになっていました。

nsTextStore::MouseTackerクラスを新設し、これをnsTArrayで配列として管理し、各インスタンスには、Cookie値、ITfMouseSinkの実装へのポインタ、マウスイベントを受け取りたい、文字列の範囲を保存しています。現在、Cookie値は配列のインデックスと同値ですが、将来的に、フットプリントが問題になるようなら、配列から削除しても処理が破綻しないように、二重管理する形にしています。

次に、マウスのボタンが押されたときと離された時に、その座標に文字があるかどうか調査し、無ければ無視、あれば、その文字のどの位置でイベントが発生したのかをITfMouseSink::OnMouseEvent()に通知しなくてはいけません。

これまでの実装(IMM等)では、widgetから、NS_QUERY_CONTENT_CHARACTER_AT_POINTイベントを送信し、カーソル位置の情報を取得していましたが、このままでは、現在開発が進んでいる、e10s(Firefoxのマルチプロセス化)には対応できません。なぜなら、ネイティブイベントは、chromeを管理する、親プロセスが受け取りますが、contentは子プロセスにあります。子プロセスの情報を取得するには、同期通信で情報を取得する必要がありますが、子プロセスが処理落ちやハングアップしている時に、chromeプロセスが巻き添えを食わないように、これは禁止されています。

そこで、全く逆のアプローチをとることにしました。content側には現在、フォーカスを持ったエディタの状態を監視し、nsIWidget::NotifyIME()で様々なことを通知している、IMEContentObserverが居ます。エディタ上の文字でマウスのボタンが押されたとき、もしくは、話された時に、ここから、新しい通知を同期通信で出せば、widget側でIMEにマウスイベントを通知できますし、IMEContentObserverは、そのイベントがIMEによって消費されたかどうかを確認することもできます。

どのような通知が行われているのかは、nsIWidget::IMENotification::mMouseButtonEventDataを参照してください。

このアプローチにより、各プラットフォームのIMEイベントハンドラごとに、似たような処理を書かなくて済むようになった上に、e10sにも対応可能なので、かなり、成功した修正になったと思っています。