POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

FeedlyRSSTwitterFacebook

本記事は、原著者の許諾のもとに翻訳・掲載しております。

Project Quantumのことをお聞きになったことがあるでしょう。これはFirefoxを高速化するために、Firefoxの中身を大幅に書き換えたものです。実験的なブラウザ、Servoから部分的に交換を実施し、エンジンの他の部分の著しい改善を図っています。

このプロジェクトは、飛行中のジェット機でのジェットエンジンの取り替えに例えられます。現場でコンポーネントごとに変更を実施するので、各コンポーネントの準備が整い次第、Firefoxで効果を確認することができます。

また、Servoから採用した最初の重要なコンポーネントは、Quantum CSSと呼ばれる新しいCSSエンジン(以前の別名はStylo)で、現在はNightly版でテストすることが可能です。(編注:Firefox 57からはデフォルトで有効化されています) about:config に行き、 layout.css.servo.enabled をTrueに設定すれば、動作しているのを確かめることができます。

新しい素晴らしいCSSエンジンを生み出すために、4つの異なるブラウザから最新技術をこの新しいエンジンに結集しています。

4 browser engines feeding in to Quantum CSS
注釈:
並行処理(Servo)
ルールツリー(Firefox)
スタイルの共有キャッシュ(ChromeとSafari)
巧みな調整と最適化(Styloチーム)

最新のハードウエアを利用して、マシンの全てのコアで作業を並列化します。つまり、2倍、あるいは4倍、もしくは18倍も速く実行できるということです。

それに加えて、他のブラウザから現存する最新式の最適化を組み合わせます。そのため、並列に実行しなかったとしても、非常に高速なCSSエンジンになるでしょう。

Racing jets

しかし、CSSエンジンは何を行うのでしょうか。まずCSSエンジンに着目し、どうやってブラウザの残りの部分に適合させているのかを見てみましょう。その後、Quantum CSSがエンジン全体をどのように高速化しているのかを見ていきます。

CSSエンジンは何をするのか?

CSSエンジンは、ブラウザのレンダリングエンジンの一部です。レンダリングエンジンは、WebサイトのHTMLやCSSファイルを取得して、スクリーン上のピクセルに変換します。

Files to pixels

ブラウザは、それぞれレンダリングエンジンを備えています。ChromeではBlink、EdgeではEdgeHTML、SafariではWebKitと呼ばれています。またFirefoxではGeckoという名前です。

ファイルからピクセルを取得するには、基本的に、これら全てのレンダリングエンジンは同じことを行います。

1. ファイルをブラウザが理解できるようなオブジェクトにパースします。このオブジェクトにはDOMも含まれます。この時点で、DOMはページ構造を知り、要素間の親子関係が分ります。しかし、その要素がどのようなものかということは分りません。

Parsing the HTML into a DOM tree

2. 要素がどのようなものであるべきかを把握します。各DOMノードに対して、CSSエンジンはどのCSSルールを適用するかを割り出します。それから、そのDOMノードに対してそれぞれのCSSプロパティの値を算出します。

Styling each DOM node in the tree by attaching computed styles

3. 各ノードのディメンションと、スクリーンのどこに配置するかを算出します。画面上に表示する事項ごとに、ボックスが作成されます。このボックスは単にDOMのノードを表すだけではありません。DOMノード内部の事項、例えばテキスト行も表しています。

Measuring all of the boxes to create a frame tree

4. 様々なボックスを描画します。この描画は、複数のレイヤで生じる可能性があります。これは、タマネギの皮のように紙を層状に重ねる旧式の手書きアニメーションのようなものだと考えています。他のレイヤで再描画することなく、1つのレイヤだけを書き換えることが可能です。

Painting layers

5. これらの別々に描画されたレイヤを集め、transformのようなコンポジタのみのプロパティを適用し、1つの画像に変換します。ようするに、まとめて重ねたレイヤの写真を撮るようなことです。この画像はスクリーン上でレンダリングされます。

Assembling the layers together and taking a picture

つまり、CSSエンジンは、スタイルの計算を始めるに当たり、次の2つを持っているということです。

  • DOMツリー
  • スタイルルールのリスト

CSSエンジンはDOMノードを1つずつ処理し、各DOMノード用のスタイルを割り出します。この処理の一部分として、スタイルシートがプロパティの値を宣言していなくても、CSSエンジンはDOMノードに全てのCSSプロパティの値を与えます。

これは、フォームの項目を全て記入しようとするようなものだと思います。それぞれのDOMノードにつき1枚のフォームを記入しなければならず、更にフォームの各項目について、回答を記入する必要があります。

Blank form with CSS properties

そのためには、CSSエンジンは2つの処理をしなければなりません。

  • 対象ノードに適用するルールを割り出します。別名、 セレクタマッチング
  • 親の値、または初期値を用いて欠落している値を埋めます。別名、 カスケード

セレクタマッチング

このステップのために、DOMノードにマッチするあらゆるルールをリストに追加します。というのも、複数のルールがマッチする可能性があり、同じプロパティに対して複数の宣言があるかもしれないからです。

Person putting check marks next to matching CSS rules
注釈:次は、divの内部にメッセージクラスと一緒にpタグがある…。
だから、これはマッチする…
これは…

更に、ブラウザ自体が初期値のCSS(ユーザエージェントスタイルシートと呼ばれています)をいくつか追加します。CSSエンジンは、選択すべき値を、どのように見つけるのでしょうか。

ここで出番となるのが、詳細度ルールです。CSSエンジンは基本的に、スプレッドシートを生成します。生成後、異なる列に基づいて宣言を並べ替えます。

Declarations in a spreadsheet
注釈:作者のスタイルシートはユーザエージェントスタイルシートに優先される
より詳細度の高いセレクタが優先される

最も高い詳細度を持つルールが優先されます。このスプレッドシートに基づいて、CSSエンジンは記入可能な値を埋めます。

Form with some CSS properties filled in

残りの項目の値については、カスケードを使用します。

カスケード

カスケードによって、CSSによる書き込みと維持管理が一層容易になります。カスケードのおかげで、bodyに color プロパティを設定できます。そして、 p span そして li 要素内の全てのテキストが、(詳細度がより高いオーバーライドをしない限り)そのcolorを使用すると分かります。

そのためには、まずCSSエンジンが、フォームの空欄をチェックします。初期設定でプロパティが継承される場合、CSSエンジンはツリーをさかのぼり、先祖が値を持っているかどうか確認します。値を持っている先祖が1つもないか、プロパティが継承されていなければ、初期値を取得します。

Form will all CSS properties filled in

これで、このDOMノードについては、全てのスタイルが計算されました。

注釈:スタイル構造の共有

ここまでご紹介してきたフォームは、少々、説明にふさわしいとは言いえないものでした。CSSには何百ものプロパティがあります。CSSエンジンが、各DOMノードの各プロパティに対する値をずっと保持していれば、じきにメモリ不足になってしまいます。

値を保持する代わりに、CSSエンジンは通常、スタイル構造の共有と呼ばれる処理を行います。通常、スタイル構造を呼び出す異なるオブジェクトの中に(フォントのプロパティのように)一緒に使われるデータを格納しておきます。その時、同じオブジェクト内に全てのプロパティを持つのではなく、計算されたスタイルオブジェクトは、ポインタを持っているだけです。対象となるDOMノード用の適正な値を持ったスタイル構造へのポインタが、それぞれのカテゴリについて存在します。

Chunks of the form pulled out to separate objects

これは、メモリと時間の節約につながります。(兄弟ノードのような)似たプロパティを持つノードは、ノードが共有しているプロパティ用の同一構造をポイントするだけでいいのです。そして、多数のプロパティが継承されているので、先祖ノードは、詳細度の高いオーバーライドを指定していないノードならば、どんな子孫ノードとも構造を共有できます。

それでは、どのように高速化するのか?

最適化していない状態でのスタイルの計算は、以下の図のようになります。

Steps in CSS style computation: selector matching, sorting by specificity, and computing property values

注釈:セレクタのマッチ->詳細度によって宣言をソート->プロパティの値の計算

ここでは、多くの処理が行われています。それは、最初のページをロードする時だけ必要なわけではありません。複数の要素間を行き来したり、DOMに変更を加えたり、スタイルの再計算を引き起こしたり、ユーザがページに干渉するたびに何度も処理が発生します。

Initial styling plus restyling for hover, DOM nodes added, etc
注釈:内部レンダリング->スタイルの再計算->スタイルの再計算

つまり、CSSのスタイルの計算は、最適化の有力な候補であるということです。過去20年間、各種ブラウザは、様々な最適化の方策を試みてきました。新たな超高速エンジンを作り出すにあたり、Quantum CSSは、各種エンジンのいい所を寄せ集めて組み合わせているのです。

それでは、それらが一体となってどのように動くのか、詳細を見ていきましょう。
後編は コチラ です。

監修者
監修者_古川陽介
古川陽介
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
複合機メーカー、ゲーム会社を経て、2016年に株式会社リクルートテクノロジーズ(現リクルート)入社。 現在はAPソリューショングループのマネジャーとしてアプリ基盤の改善や運用、各種開発支援ツールの開発、またテックリードとしてエンジニアチームの支援や育成までを担う。 2019年より株式会社ニジボックスを兼務し、室長としてエンジニア育成基盤の設計、技術指南も遂行。 Node.js 日本ユーザーグループの代表を務め、Node学園祭などを主宰。