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

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

もずはっく日記(2019年12月)

2019年12月23日

Bug-org 1557587 When I open 2 or more windows in different DPI monitors, window size in non-primary monitors are not restored correctly 初回投稿日時: 2019年12月23日23時47分04秒
最終更新日時: 2019年12月23日23時48分25秒
カテゴリ: Mozilla73
固定リンク: id=2019122300
SNS: (list)

自分の環境で毎日発生するため、非常に困っていたバグで、自分の現在の立場でやらなきゃいけないものではないんですが、放置してもたぶん誰もやってくれないバグなので修正しました。

Windows 10ではPer-Monitor DPIという仕組みがあり、PCに接続しているディスプレイそれぞれが個別のDPIを設定することができるようになっています。私の環境だとメインディスプレイが5Kなので200%、コーディング等の作業用ディスプレイが1920×1920で100%となっています。FirefoxのようなPer-Monitor DPIに対応したアプリのウインドウが異なるDPIのディスプレイ間を移動すると、新しいDPIに基づいてウインドウをリサイズし、内容をレンダリングしなおします。

Firefoxの起動時にセッションを復元する設定にしている場合、まず、ユーザの操作に対してできるだけ早く「起動した」ということを分かるようにするために、何もレンダリングされていないウインドウをひとつ開きます。そしてセッションの情報を読み込み終わると、最初に開いたウインドウをひとつめのウインドウとして移動・リサイズし、二つ以上のウインドウが開かれていた場合は、元のディスプレイで直接生成・リサイズされます。

このバグを見つけるきっかけになったのは、ひとつめのウインドウがメインディスプレイとは異なるDPIのディスプレイにある場合、セッション復元中のウインドウの移動時にDPIの切り替わりが発生するものの、それが正しく処理されてないため、例えば上述の私の環境だとサブのディスプレイに常に縦横それぞれ倍のサイズで復元されてしまっていました。200%のメインディスプレイのDPIにあわせてサイズ調整が行われた後、そのデバイスピクセル数でウインドウのリサイズがサブディスプレイで行われているためです。試していませんが、メインとサブのディスプレイが逆のDPIを持っていた場合、セッションを復元するたびに半分の大きさになっていくのだと思います。

さらに同様の問題はJavascriptでウインドウを移動した際にも発生することが分かりました

残念ながら論理的な根本の原因は突き止めきれなかったのですが、発生していたバグは要するに、デバイスピクセルとCSSピクセルの比率を保持しているnsDeviceContextnsIBaseWindowが同時にアップデートされてる訳ではないというのが直接の原因でした。このため、異なるスケールのCSSピクセルを受け取っても、単純に計算していたりということが発生していました。

本来なら同期ができていないことが設計上仕方のないことなのかどうかを調べるべきなのですが、これの修正に時間をかけててもマネージャから怒られるだけなのでとっととやりました(そもそもこの沼にはまりたくない……)。デバッガで異なるスケールをキャッシュしたままやりとりしている場所を上記の症状から探してみると、

  1. e10sの通信時(これは確実に同期されていないのが仕方ないケース)
  2. nsGlobalWindowOuternsIBaseWindowの通信時にnsPresContext経由でnsDeviceContextにキャッシュされている情報を基に計算していた

という二点に落ち着きました。

行った修正自体は単純で、e10sのケースでは送信前のDPIを追加した引数で相手プロセスに渡し、受け取ったプロセス側では自分の持っているスケールではなく、その受け取ったスケールを元にデバイスピクセル数を計算するようにしました。nsGlobalWindowOuterのケースでは連携するnsIBaseWindowがキャッシュしているスケールで計算した結果を、そのメソッドの引数に渡すようにしました。平たく言えば、受け取った側が論理的なピクセル数と、それを計算した際のデバイスピクセルとのスケールが確実に分かるようにしただけです。

Per-Monitor DPIはそもそも対応していないアプリも多く、身近なところではエクスプローラーとか、ATOKとか。対応しようとしてるけど全然できていない感じなのがフォトとか。ノートPCと外付けディスプレイで異なるDPIを使うケースというのは非常によくある今どきな使い方で発生する問題だと思うので、各アプリ、本当に対応を進めていただきたいものです。