Geckoでのアニメーション処理の流れ

Animation

Gecko の内部には主に3つのアニメーションがあります。

CSS animations、 CSS transitions、 web animations です。 正確に言えば、もうひとつ SVG animation というものもありますが、ここでは取り上げません。 なぜなら、私が理解していないからです。 SVG animation 以外のアニメーションは細かい所は別として共通の処理で動作しています。 これからその処理について説明していきます。

Refresh driver

アニメーションの処理は基本的に繰り返しの処理です。  style を計算して、その結果 layout の変更が必要な場合は layout の処理を行い、そして、その結果を paint します。 この繰り返しの処理を担っているのが、nsRefreshDriver というクラスです。 nsRefreshDriver は各ドキュメント毎に、正確には nsPresContext 毎にですが、存在しています。 Firefox の chrome ドキュメント、タブ内の HTML ドキュメント、また、HTML ドキュメント内の iframe ドキュメントなどです。 ただし、iframe に対応している nsRefreshDriver はその祖先の HTML ドキュメントの nsRefreshDriver と共有されています。 この nsRefreshDriver が ディスプレイハードウェアの垂直同期に従って動作します。 ※ 2016年1月8日時点で Linux platform ではハードウェアの垂直同期ではなく、ソフトウェアでエミュレートしています。 例えば、垂直同期が60Hzの場合、1/60秒つまり、16.6ms 毎に nsRefreshDriver の Tick という関数が呼び出されます。 この Tick の中から、style/layout/paint のそれぞれ処理が呼び出されることになります。 ただし、paint の処理だけは、他と違い root の nsRefreshDriver のみで実行されます。 root の nsRefreshDriver というのは、マルチプロセス動作の場合は、chrome ドキュメントに対応した nsRefreshDriverと タブ内で読み込まれている HTML ドキュメントの nsRefreshDriver になります。 マルチプロセス動作でない場合には、chrome ドキュメントに対応した nsRefreshDriver のみが root の nsRefreshDriver になっています。 つまり、マルチプロセス動作でない場合の paint 処理は、タブ内の HTML ファイルに対する paint 処理であっても、 chrome ドキュメントの nsRefreshDriver が駆動していることになります。

nsRefreshDriver::Tick

続いて、nsRefreshDriver::Tick 内の処理を見て行きましょう。 nsRefreshDriver::Tick 内でのアニメーションに関する処理は以下の順序で実行されます。

Animation::Tick

dispatch animation events (animationend etc.)

requestAnimationFrame

style

layout

paint

先ほど説明した style/layout/paint の前に3つの処理が行われています。 後に処理されるものから順に見ていきましょう。

requestAnimationFrame

requestAnimationFrame は皆さん良くご存知だと思います。 ブラウザの描画処理が実行される直前に呼び出される callback を登録しておき、 callback 内で style を変更するなどしてスクリプトでアニメーションを制御するのに利用できる機能です。 この callback を呼び出す処理が Gecko の描画処理、style/layout/paint の処理の直前に実行されています。 非常に理にかなった実装ですね。 もちろん、requestAnimationFrame が呼び出されていない時にはこの処理は実行されません。 続いてanimationイベントの処理を見ていきましょう。

dispatch animation events

requestAnimationFrame の処理の直前には、CSS animation/CSS transition のイベント発火処理が実行されています。 animationstart/animationend/animationiteration そして transitionend の4つのイベントです。 これらのイベントはこの後に説明する Animation::Tick から呼び出されている処理でイベントを発行する必要があるかどうかのチェックが 行われ、発行が必要なイベントはキューに保存されています。 そのキュー内に保存されているイベントをここではそれぞれ発火する処理を実行しています。

Animation::Tick

nsRefreshDriver::Tick では主に描画に関連する処理が実行されていました。 一方、Animation::Tick ではアニメーションの状態をチェックし、その状態に応じてその後の描画処理等を行うように要請する処理を実行しています。 もし、アニメーションがその経過時間 duration を過ぎて終了しているのであれば、Web Animations の spec に定義されている finished promise を resolve する 処理を micro task のキューに入れます。また、onfinish イベントもキューに入れています。 またここでは、アニメーションが compositor スレッドで動作可能かを判断し、動作可能であれば、style/layout/paint の処理をスキップするようにしています。