フロントエンドエンジニアをやっています。
頑張るから読んでほしい。

Chromiumのソースコードを読んでみる

この記事はSmartHR Advent Calendar 2020 10日目の記事です。

はじめに

私は、普段フロントエンドエンジニアとしてWebフロントエンドのコードを書いたりしています。 新しいHTML/CSS/JavaScriptの仕様だったり、新しいAPIのドキュメントを読んだりしていると、

この新しい仕様ってブラウザにどんな風に実装されてるんだろ?

Chromiumのコードって公開されてるから見られるんじゃないかな

いや、そもそもChromiumソースコードって私でも見ても分かるのかな…

公開されてるし、ちょっと覗いてみるだけでも何かいいことあるかも

と思いたってChromiumソースコードを読んでみることにしました。

この記事はChromiumのコードを読んだことがない私がChromiumのコードを読むために行った手順と気付きを書いています。

ゴールを決める

Chromiumソースコードを読んでみると決めたものの、Chromiumソースコードは巨大で規模がとても大きいです。 しかも私はC++ 言語を触ったことない、全く読んだことがない、書いたことがないです...

なので、とりあえず div タグ等のHTMLタグを生成してる辺りのコードを見つけて実装を眺めてみるっていうことをゴールにします。

Chromiumとは

Chromium オープンソースのウェブブラウザ。 主に C++ 言語で書かれています。

Chromiumを利用しているプロダクト

実際に読んで見る

ソースコードを読む準備

Chromiumソースコードこちらを参考にgitで落としてくることが可能なようですが、Chromiumのような巨大なプロジェクトのコードを落としてくるのは、

  • かなり時間がかかりそう
  • C++ 言語用にエディタを整えるのが大変そう
  • さくっと読んでみたい

という理由から、Chromium Code Searchを使って読んでみようと思います。

Chromium Code Search とは
Chromiumソースコードの中から自由に検索ができ、クラスの定義や関数の呼び出し元、変数の参照元などをブラウザ上で辿ることができます。

f:id:cidermitaina:20201207193346p:plain

ディレクトリ構成

コードを読む準備ができたので、さあ、読むぞ!という意気込みですが、膨大なソースコードを手当たり次第見ていっても全体像が分からないので、ざっくり全体を把握するためにディレクトリ構成を見ていきます。

トップレベルのディレクトリは以下のようになっています。

src
  - android_webview 
  - apps // Chrome packaged apps
  - base 
  - breakpad
  - build 
  - cc
  - chrome // GoogleChromeのオープンソースのアプリケーション層
  - components
  - content // multi-process sandboxed browserに必要なコアコード
  - device
  - net
  - sandbox
  - skia + third_party/skia
  - sql
  - testing
  - third_party // 画像デコーダー、圧縮ライブラリ、WebエンジンBlinkなどの外部ライブラリ
      - blink // レンダリングエンジン, HTML,CSS, scriptをペイントコマンドや状態変化に変換するWebエンジン
      - ...
  - tools
  -  ui/gfx // 共有グラフィックスクラス。これらは、ChromiumのUIグラフィックのベースを形成
  -  ui/views // UI開発を行うためのシンプルなフレームワーク。レンダリング、レイアウト、イベント処理を提供
  -  url // GoogleのオープンソースURL解析および正規化ライブラリ
  -  v8 // V8Javascriptライブラリ

もっと詳しく知りたい方は以下に詳しく記載されているので読んでみてください。

www.chromium.org

HTMLタグが生成されているっぽいところを探す

ディレクトリ構成をざっと見たので、HTMLタグが生成されているところを探していこうと思います。

私が知っている情報は

なので、とりあえずblinkフォルダを覗いてみようと思います。

blinkフォルダ以下はこんな感じです。

f:id:cidermitaina:20201207195115p:plain

このディレクトリのREADMEには

renderer/: code that runs in the renderer process (most of Blink).

と書いているので、renderer配下にいってみます。

renderer配下を見てみると renderer/core/html/ といういかにもっぽいディレクトリを見つけました。

中身を見てみると、 html_div_element.cc , html_anchor_element.cc, html_li_element.cc などのHTMLタグの名前のファイルがたくさんあります。 html_div_element.ccdiv タグの実装がありそうです。

f:id:cidermitaina:20201207195213p:plain

div の実装みたいなところ見つけた

では、html_div_element.cc の中身を見ていこうと思います。

// html_div_element.cc
...
#include "third_party/blink/renderer/core/html_names.h"

namespace blink {

HTMLDivElement::HTMLDivElement(Document& document)
    : HTMLElement(html_names::kDivTag, document) {}

void HTMLDivElement::CollectStyleForPresentationAttribute(
    const QualifiedName& name,
    const AtomicString& value,
    MutableCSSPropertyValueSet* style) {
  if (name == html_names::kAlignAttr) {
    if (EqualIgnoringASCIICase(value, "middle") ||
        EqualIgnoringASCIICase(value, "center")) {
      AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kTextAlign,
                                              CSSValueID::kWebkitCenter);
    } else if (EqualIgnoringASCIICase(value, "left")) {
      AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kTextAlign,
                                              CSSValueID::kWebkitLeft);
    } else if (EqualIgnoringASCIICase(value, "right")) {
      AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kTextAlign,
                                              CSSValueID::kWebkitRight);
    } else {
      AddPropertyToPresentationAttributeStyle(style, CSSPropertyID::kTextAlign,
                                              value);
    }
  } else {
    HTMLElement::CollectStyleForPresentationAttribute(name, value, style);
  }
}

}  // namespace blink

kAlignAttrという型の名前や middle, centerleft , right の 値で条件分岐をしていることから div タグのalign属性の実装していることが分かります。 HTML5ではalign属性は廃止されていますが、HTML5以前の互換性を保つためにまだ実装の記述がされている?のかな(分からないです。予想です。)

html_div_element.h というファイルがincludeされているので、次はこちらを見ていきます。

// html_div_element.h
 ...
namespace blink {

class CORE_EXPORT HTMLDivElement : public HTMLElement {
  DEFINE_WRAPPERTYPEINFO();

 public:
  explicit HTMLDivElement(Document&);

 private:
  void CollectStyleForPresentationAttribute(
      const QualifiedName&,
      const AtomicString&,
      MutableCSSPropertyValueSet*) override;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_DIV_ELEMENT_H_

class CORE_EXPORT HTMLDivElement : public HTMLElementから HTMLDivElement が定義されていますが、HTMLElement を継承していることが分かります。

では次に HTMLElement を定義している html_element.h を見てみます。

// html_element.h
...
namespace blink {

struct AttributeTriggers;
class Color;
class DocumentFragment;
class ElementInternals;
class ExceptionState;
class FormAssociated;
class HTMLFormElement;
class KeyboardEvent;
class StringOrTrustedScript;
class StringTreatNullAsEmptyStringOrTrustedScript;

enum TranslateAttributeMode {
  kTranslateAttributeYes,
  kTranslateAttributeNo,
  kTranslateAttributeInherit
};

class CORE_EXPORT HTMLElement : public Element {
  DEFINE_WRAPPERTYPEINFO();
...

Color クラス、 KeyboardEvent クラスがあったり、読んでいくとdraggable() , innerText() , offsetHeightForBinding()という名前のメソッドがあることからタグ固有のふるまいではなく共通のHTML要素のふるまいを書いているファイルのような予感です。

また、html_element.h というファイル名、draggable() , innerText()というメソッドがあることからこのファイルは HTMLElement の実装をしていそうです。

HTMLElementElement を継承しているので次は element.h を見てみます。

// element.h
namespace blink {
    
class AccessibleNode;
class Attr;
class Attribute;
class CSSPropertyValueSet;
class CSSStyleDeclaration;
class CustomElementDefinition;
class DOMRect;
class DOMRectList;
class DOMStringMap;
class DOMTokenList;
...

class CORE_EXPORT Element : public ContainerNode, public Animatable {
  DEFINE_WRAPPERTYPEINFO();
...

element.h というファイル名、AccessibleNode, Attribute, などのクラス、ずっと見ていくと ClassNames(), scrollWidth() , innerHTML()というメソッドがあることからこのファイルは Element の実装をしていそうです。

ContainerNode, Animatableを継承しているのですが、このまま継承しているクラスをざっと見ていくと、 ElementContainerNodeNodeEventTarget という流れで継承していました。

ここで HTMLElement - Web API | MDN を見てみると

要素はこれを継承したインターフェイスを通して実装されています。

f:id:cidermitaina:20201208184005p:plain
HTMLElement - Web API | MDN より

と書かれていて、Chromiumの実装と同じことが分かります。

合わせてMDNのHTMLElement, Element のページを見てみると、記載されているプロパティ名と同じ実装を見つけることできます。

また、 divタグは HTMLDivElementHTMLElementElementContainerNode, AnimatableNode … のような実装の流れだったのですが、 a タグなど他のHTMLタグの実装をみても同じ流れでした。

このことからHTMLタグはHTMLElementクラスから派生してできていることが分かりました。

divの実装を探してみてのまとめ

  • third_party/blink/renderer/core/html/ 配下に、HTMLタグごとのファイルがある
  • HTMLタグは HTMLElement クラスを継承してできている
  • HTMLElementElement オブジェクトの実装が気になったら html_element.h, element.h を読んでみると分かりそう
    • HTMLElement.XXXElement.XXXとか使ったりするけど、ちょっと理解が深まった気がする

↑が実際にChromiumソースコードを読んでみての気付きです。

見ていて気になったところ(おまけ)

accessibility

third_party/blink/renderer/core に accessibilityフォルダがありました。 最近働いている会社でaccessibility勉強会をやっていて気になりました。 ここにARIAの実装とかAOMの実装が書いてあるのかな

potal

PortalsというWICGによって提案されているページ間の遷移をシームレスにすることができる仕組みがあるのですが、third_party/blink/renderer/core/html にpotalディレクトリがあったので、ここでpotalタグが実装されてる気がしました。

まとめ

今回は div タグのHTMLタグを生成してる辺りのコードを見つけて実装を眺めてみました。 C++言語がよく分かっていないので、関数名、クラス名で予測しながらでしか見ることができなかったのですが、ディレクトリ構成を眺めて全体像がなんとなく分かったり、いつも何気なく使ってるdiv タグの実装から HTMLタグはHTMLElementクラスから派生してできているというのが分かったり、HTMLElementElement オブジェクトの実装を覗いてみたりすることができました。

使用してるライブラリの新しいバージョンのRelease Noteを読むだけではなく、変更されてるコードを読むことでより理解できることがあります。 ブラウザの新しいAPIができたとき仕様のドキュメントを読むだけではなく、実装を見てより理解を深め、ちゃんと理解してWebフロントエンドのコードが書けるになったらなあと思いました。 (C++言語何も分からなかったので何年先になるんだろう…)

Chromiumのコードって一生読めないものって勝手に思っていましたが、いざ眺めてみるとクラス名、メソッド名で予測しながらほんの少しだけ読めて少し嬉しくなりました。

参考
Chromium のソースコードの歩き方
Getting Around the Chromium Source Code Directory Structure - The Chromium Projects
HTMLElement - Web API | MDN