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

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

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

2014年1月29日

Bug-org 953146 Shouldn't allow other applications to set focus to popup window
初回投稿日時: 2014年01月29日20時29分12秒
最終更新日時: 2014年01月29日20時30分20秒
カテゴリ: Firefox Mozilla Core Mozilla29 Windows バグ修正
SNS: (list)

一部のSynapticsや、Elantechのタッチパッドで、ドロップダウンの中身をスクロールしようとすると、そのドロップダウンが閉じてしまう、というバグです。

Synapticsでは、二本指でスワイプすることでスクロールものが、いくつかこの症状を持っている様です。Elantechのドライバは、HKEY_CURRENT_USER\Software\Elantech\OtherSetting\ScrollSupportAutoFocusが非ゼロになっている場合や、レジストリで設定不能な形で再構成してあると思われる、ASUS SmartGestureで発生していました。

Windowsでは本来、フォーカスを持ったコントロールに対して、ホイールイベントのメッセージが送信されます。

しかし、この仕様は評判があまりよくないようで、各ブラウザはカーソルの下にあるスクロール可能なエリアをスクロールしますし、一部のサードパーティ製のマウスドライバでは、フォーカスの有無を無視して、マウスカーソルの下にあるウインドウにホイールメッセージを送信します。

今回のバグは、そのようなドライバ側でのハックが不適切なものだったのが原因です。

どちらのケースでも、カーソルの下にあるウインドウに強制的にフォーカスを与えてから、ホイールメッセージを送信することで、この挙動を実現しようとしていましたが、Geckoの出すドロップダウンはフォーカスを受け取ることがないという前提で設計されています。そのため、ドロップダウンを開いていたウインドウが非アクティブになってしまい、これをトリガーに、ドロップダウンが閉じられていました。

色々と調べてみたんですが、API経由でドロップダウンが強制的にアクティブにされるのをキャンセルする手段が無いようなので、Geckoのウインドウがフォーカスを失う時に、そのフォーカスの移動先がドロップダウンの場合にだけ、特殊処理を入れることによって、ドロップダウンを閉じないようにすることにしました。

具体的には、まず、ドロップダウンを開いたウインドウが非アクティブ化のWM_NCACTIVATEを受け取り、非クライアント領域を非アクティブ時の色で再描画しようとしますが、これを抑制しておかなければホイールを回す度にウインドウの非クライアント領域がちらつくことになります。このため、このメッセージをGeckoのポップアップウインドウ以外が受けとった場合には、直接、Default WndProcにメッセージをリダイレクトした後、このメッセージを受け入れたと返します。これで、描画のみが抑制され、フォーカスの移動は継続します。

次に、ポップアップではないウインドウは、WA_INACTIVEWM_ACTIVATEを受け取ります。この時に、フォーカスの移動先が分かりますので、ポップアップに移動しようとしている場合はそのまま流し、それ以外の場合は、非クライアント領域をきちんと非アクティブな見た目に描画しないといけないので、そのまま処理するようにフラグをたててから、WM_NCACTIVATESendMessage()で再送します。

最後に、ポップアップウインドウが、WA_ACTIVEWM_ACTIVATEを受け取った場合は、ディアクティブイベントが発生するのを抑制した後に、アプリ定義のメッセージをキューにポストします。そして、そのメッセージを受け取った時に、直前にアクティブだったウインドウをAPIをつかってアクティブ化します。

これにより、フォーカスの移動があたかも発生していないかのように見せつつ、ポップアップにフォーカスが移動させられた場合にはポップアップを閉じずに、フォーカスを直前のウインドウに戻す、ということを行っています。興味のある方は、この変更だけを行っているパッチを参照してみてください。

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

bug-org 953146を含むエントリ