.. Translation of "How Browsers Work" documentation master file, created by sphinx-quickstart on Fri Aug 19 00:54:08 2011. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. How Browsers Work - Behind the Scenes of Modern Web Browsers ============================================================== この翻訳について -------------------------------------------------------------- この文章は `HTML5 Rocks `_ で公開されている `How Browsers Work: Behind the Scenes of Modern Web Browsers `_ を非公式に和訳したものです. 内容の正確性は保証しません. ライセンスは原文と同じく, 文章は `Creative Commons Attribution 3.0 License `_ , サンプルコードは `Apache 2.0 License `_ です. フィードバックは `Issue への登録 `_ , あるいは `Kosei Moriyama `_ (`@cou929 `_ または cou929 at gmail.com) へ直接お願いします. GitHub に `この和訳のリポジトリ `_ があります. 以下の Preface でも言及されていますが, この文章は `Tali Garsiel `_ さんが自身のサイトで公開していたものを `Paul Irish `_ さんが "より多くの人の目に届けるため" に HTML5 Rocks で再公開したものです. すばらしいドキュメントを書いてくれた Tali さん, 及びその文章を知るきっかけを提供してくれた Paul さんに感謝します. Author, Editor -------------------------------------------------------------- * Tali Garsiel (author) - Developer, Incapsula, * Paul Irish (editor) - Developer Relations, Google Updated at (original document) -------------------------------------------------------------- August 16, 2011 Preface -------------------------------------------------------------- この WebKit と Gecko の内部動作についての包括的な入門書は, イスラエルの開発者 Tali Garsiel の多大な調査によるものです. 彼女は数年にわたりブラウザの内部に関する多くの文章を読み (詳しくは Resources を参照), またブラウザのコードを読むことにも多くの時間を費やしました. 彼女はこう書いています: IE が 90% のシェアを占めていたの時代, 多くのことができませんでしたがブラウザは "ブラックボックス" と考えられていました. しかし現在はオープンソースのブラウザが `過半数のシェアを占めているので `_ ブラウザの中身を覗くには良いタイミングです. ただし中身は何百万行の C++ コードでした… 彼女のこの調査は `彼女のサイト `_ で公開されています. しかし私達はこの文章はより多くの人に読まれるべきと考え, `ここ `_ に再公開します. Web 開発者とって **ブラウザの内部動作を知ることは, よりよい判断をすること・開発のベストプラクティスの背後にある理由を知る手助けになります**. この文章は長文になるので, より深掘りして読むことをお勧めします. そうすることで最後によかったと思えることを私たちは保証しましょう. *Paul Irish, Chrome Developer Relations* Introducion -------------------------------------------------------------- ブラウザは最も広く使われているソフトウエアです. この入門書ではブラウザが裏でどのように動いているかを説明します. 私たちがアドレスバーに ``google.com`` とタイプしてから Google のページが表示されるまでに何が起こっているかを見ていきましょう. The browsers we will talk about ************************************************************** 今日では IE, Firefox, Safari, Chrome, Opera という5つのメジャーブラウザがあります. ここではオープンソースのブラウザ - Firefox, Chrome, Safari (一部はオープンソースです) - を取り上げます. `StatCounter browser statistics `_ によると, 現在 (2011年8月) では Firefox, Safari, Chrome のシェアを合計すると 60% に達します. 今ではオープンソースのブラウザがブラウザビジネスの大きな部分を占めています. The browser's main functionality ************************************************************** ブラウザの主要機能はユーザが選択した Web のリソースを表示させることです. サーバにリクエストを出し, それをブラウザウィンドウに表示させます. リソースのフォーマットは通常は HTML ですが pdf や画像にも対応しています. リソースの場所はユーザによって URI (Uniform resource Identifier) で指定されます. ブラウザがどのように HTML を解釈し表示するかは HTML と CSS ファイルで指定されています. その仕様は **W3C** (World Wide Web Consortium) によってメンテナンスされています. W3C は Web の標準化団体です. 数年にわたり各ブラウザは標準の全てに準拠せず, 独自の拡張を進めてきました. その結果, Web サイト制作者にとっては深刻なブラウザ互換の問題が引き起こされました. 今日では多くのブラウザがより仕様に準拠するようになっています. 各ブラウザのユーザインタフェースは多くの部分で共通しています. 代表的なものは以下です: * URI を入力するアドレスバー * 戻る・進むボタン * ブックマーク機能 * 再読み込み・読み込み停止ボタン * ホームボタン 奇妙なことにこれらのユーザインタフェースは仕様で定められているわけではありません. これらは長年のブラウザの歴史の中で互いに洗練されてきたグッドプラクティスです. HTML5 ではブラウザが持たなければいけない UI 要素は定義していませんが, アドレスバー・ステータスバー・ツールバーなど, 一般的な要素をリストアップしています. もちろん, Firefox のダウンロードマネージャのようにブラウザ特有の要素もあります. The browser's high level structure ************************************************************** ブラウザの主要コンポーネントは以下です [#ref1.1]_ 1. **ユーザインタフェース** - アドレスバー, 戻る・進むボタン, ブックマークメニューなど, メインウィンドウに表示されるページ以外の部分. 2. **ブラウザエンジン** - UI とレンダリングエンジン間のアクションを制御するもの 3. **レンダリングエンジン** - リクエストしたコンテンツを表示させるもの. 例えばコンテンツが HTML の場合, HTML と CSS をパースし, パースしたコンテンツをスクリーンに表示させることに責任を持つ 4. **ネットワーク** - HTTP リクエストのようなネットワーク通信を行う. プラットフォーム非依存で, その差異を吸収している. 5. **UI バックグラウンド** - コンボボックスやウィンドウなど基本的な UI 要素. プラットフォーム非依存のインタフェースを提供する. 背後では OS の UI メソッドを使用している. 6. **JavaScript インタプリタ** - JavaScript をパースし実行する. 7. **データストレージ** - 永続的なレイヤー. クッキーなど, ある種のデータをハードディスクに保存する. HTML5 では *Web Database* など完全な (かつ軽量な) DB がブラウザで提供される. .. figure:: _static/layers.png :alt: Browser main components Figure 1: Browser main components Chrome は他のブラウザと異なり, タブごとに別のレンダリングエンジンのインスタンスを持ちます. つまりタブごとに別プロセスを立ち上げています. The rendering engine -------------------------------------------------------------- レンダリングエンジンの役割は…レンダリングです. つまりリクエストしたコンテンツをブラウザのスクリーンに表示させることです. デフォルトではレンダリングエンジンは HTML, XML, 画像に対応しています. プラグイン (またはブラウザエクステンション) を入れることで PDF など別のフォーマットのデータも表示できます. しかしこの文章では主要なユースケースである CSS によってフォーマットされた HTML と画像のレンダリングについて扱います. Rendering engines ************************************************************** 今回私たちが参考にする Firefox, Chrome, Safari は2つのレンダリングエンジンの上に構築されています. Firefox は Mozilla 製の **Gecko** を, Safari と Chrome は **WebKit** を使用しています. WebKit はオープンソースのレンダリングエンジンです. もとは Linux で動作するように始まったものを Apple が Mac と Windows に対応するよう改良しました. 詳細は `webkit.org `_ を参照してください. The main flow ************************************************************** レンダリングエンジンはネットワークレイヤーからリクエストしたコンテンツを受け取ります. 通常 8K のチャンクです. その後, 通常は次のようなフローを経ます. .. figure:: _static/flow.png :alt: Rendering engine basic flow Figure 2: Rendering engine basic flow レンダリングエンジンは HTML をパースしタグを **コンテントツリー** の DOM ノードに変換します. その後外部 CSS ファイルやスタイル要素の CSS をパースします. スタイルの情報が取り込まれ, **レンダーツリー** という別の木が作成されます. レンダーツリーは色や大きさといった属性値をもつ矩形を持っています. これらの矩形はスクリーンに表示される正しい並びで配置されています. レンダーツリーができたあとはレイアウトプロセスに移行します. このプロセスでは各要素がスクリーン上のどの位置に表示されるかが決まります. 次は描画 (Painting) です. レンダーツリーが走査され, 各ノードが UI バックエンドレイヤーによって描画されます. 重要なのはこれらのプロセスが漸進的に実行されるということです. よりよいユーザ体験のためには, よりはやくコンテンツを表示させることが重要です. そのためすべての HTML がパースされるのを待たずにレンダーツリーを作り始めます. ネットワークから残りのコンテンツを受信している間に, 受信済みのコンテンツをパース・表示していきます. gradual ************************************************************** .. figure:: _static/webkitflow.png :alt: Webkit main flow Figure 3: Webkit main flow .. figure:: _static/image008.jpg :alt: Figure : Mozilla's Gecko rendering engine main flow Figure 4: Mozilla's Gecko rendering engine main flow [#ref3.6]_ 図3, 4 より WebKit と Gecko は違う用語をつかっていますが, 主な流れは大体同じということがわかります. Gecko ではビジュアル要素のツリーを *Flame tree* と呼んでいます. 各ノードはフレームです. WebKit では *Render tree* で, 各ノードは *Render Object* です. WebKit ではその要素の配置を行うことは *Layout* と呼ばれますが, Gecko では *Reflow* です. DOM ノードとビジュアル要素をつなげレンダーツリーを作る過程は, WebKit では *Attachment* と呼ばれます. また Gecko は HTML と DOM ツリーの間に *Content sink* という, DOM 要素を作る別のレイヤーを持っています. 次章以降ではこれらのフローをそれぞれ見ていきます. Parsing and DOM tree construction -------------------------------------------------------------- Parsing - general ************************************************************** パース (構文解析) はレンダリングエンジンのなかでも特に重要な部分なので, より深く見ていきましょう. まずはパースについての簡単なイントロダクションから始めます. 文章をパースするとは, 文章を意味のある構造に変換するということを意味します. "意味のある" とはコードから理解・利用できるという意味です. パースされた結果は通常文章の構造を表す木になります. この木構造はパースツリー・シンタックスツリー (構文木) と呼ばれます. 例として ``2 + 3 - 1`` という式をパースすると次のような木が得られます. .. figure:: _static/image009.png :alt: mathematical expression tree node Figure 5: mathematical expression tree node Grammars ############################################################## パースはその対象の文章が従う構文ルールに基づいて行われます. パースできるフォーマットは必ず **語彙** (*vocabulary*) と **構文ルール** (*syntax rule*) から成る文法で記述できる必要があります. これは **文脈自由文法** (*Context free grammer*) と呼ばれるものです. 例えば自然言語はこのように文法を記述できないので, ここで述べるテクニックではパースできません. Parser - Lexer combination ############################################################## パースは字句解析と構文解析という2つのサブプロセスに分割できます. 字句解析は入力をトークンに分割するプロセスです. トークンとはその言語の語彙, つまりその言語の valid なかたまりのことです. 自然言語で例えるとその言語の辞書に載っている単語ということになります. 構文解析はその言語の構文ルールを適用するプロセスです. パーサーは通常2つのコンポーネントに分割できます. 字句解析を行う **lexer** (あるいは tokenizer) と, 構文ルールに基づきパースツリーを作成する **parser** です. lexer は空白や改行などの関係のない文字を除くことも行います. .. figure:: _static/image011.png :alt: from source document to parse trees Figure 6: from source document to parse trees パースのプロセスは反復的です. parser は lexer から新たなトークンを受け取り, それを構文ルールにマッチさせます. もしいずれかのルールにマッチしたら, そのトークンに紐付くノードはパースツリーに追加され, parser は次のトークンを lexer から受け取ります. もしルールにマッチしなかった場合, parser はそのトークンを内部的に保持し, 保持しているすべてのトークンがマッチするようなルールが見つかるまで繰り返します. もし最後までどのルールにもマッチしなかった場合例外が投げられます. つまりドキュメントが valid でなく, シンタックスエラーが起こったということです. Translation ############################################################### 多くの場合パースツリーが最終目的ではありません. パースはトランスレーション, つまり入力されたドキュメントを別のフォーマットに変換するために使われます. コンパイルはその例です. コンパイラはソースコードをパースし, それを機械後に変換します. .. figure:: _static/image013.png :alt: compilation flow Figure 7: compilation flow Parsing example ############################################################### 図5 では数式からパースツリーを作成しました. ここではシンプルな数学言語を定義してパースのプロセスを見てみましょう. まず語彙として, 私たちの言語は整数とプラス・マイナス記号を扱います. 言語の構文は: 1. 言語の構文は expressions, terms, operations から成ります 2. expression はいくつでも使えます 3. expression は "term opeation term" という形式です 4. operations は プラストークンかマイナストークンのいずれかです 5. term は整数トークン, または expression です ここで ``2 + 3 - 1`` という式を分析してみましょう. 最初にルールにマッチするのは ``2`` という文字です. ルール 5 によりこれは term となります. 次にマッチするのは ``2 + 3`` です. これは3番目のルールにマッチします. 次にマッチするのはインプットの最後で ``2 + 3 - 1`` です. ``2 + 3`` が term であることはすでにわかっているので, これも "term operation term" のフォーマットになっていることがわかります. そして, 例えば ``2 + +`` というインプットはどのルールにもマッチしないので invalid な入力ということになります. Formal definitions for vocabulary and syntax ############################################################### 語彙は通常 `正規表現 `_ で表されます. 例えば上記の私たちの言語の語彙は次のようになります.:: INTEGER :0|[1-9][0-9]* PLUS : + MINUS: - このように INTEGER は正規表現で表されています. 構文は通常 `BNF `_ と呼ばれる記法で定義されます. 私たちの言語の場合は以下です.:: expression := term operation term operation := PLUS | MINUS term := INTEGER | expression 文脈自由文法に従う文章は通常のパーサーでパースできると述べました. 文脈自由文法の直感的な説明は, BNF で完全に記述できる文法ということです. より形式的な定義は `Wikipedia の Context-free grammer の項 `_ を参照してください. Types of parsers ############################################################### パーサーには一般的にトップダウンパーサ・ボトムアップパーサの二種類があります. 直感的に説明すると, トップダウンパーサは文法のハイレベルな構造から見ていき, そのうちのどれかにマッチさせていきます. ボトムアップパーサは入力データからスタートし徐々に文法をローレベルからハイレベルへ当てはめていきます. 上記の例を二種類のパーサに当てはめてみましょう. トップダウンパーサは高いレベルのルールからスタートします. まず ``2 + 3`` が expression としてマッチし, 次に ``2 + 3 - 1`` がマッチします. (実際に expression がマッチするのは別のルールですが, スタート地点は最高レベルのルールからです) ボトムアップパーサはルールにマッチするまで入力を読み取り, マッチした入力をルールで置き換えます. これを入力の最後まで続けます. マッチ途中の expression はパーサースタックに格納されます. ===================== ============== Stack Input ===================== ============== [empty] ``2 + 3 - 1`` --------------------- -------------- term ``+ 3 - 1`` --------------------- -------------- term operation ``3 - 1`` --------------------- -------------- expression ``- 1`` --------------------- -------------- expression operation ``1`` --------------------- -------------- expression ===================== ============== このタイプのボトムアップパーサは *Shift-reduce parser* とも呼ばれます. 入力が右にシフトし (最初ポインタが入力の先頭を指していて, 右に動いていくとイメージしてください) 構文ルールによって徐々に減っていくことからです. Generating parsers automatically ############################################################### パーサジェネレータというパーサを生成してくれるツールがあります. 自分の言語の文法 (語彙と構文) を渡すとパーサを作成してくれます. パーサをいちから作るには構文解析に対する深い理解が必要で, パーサーの最適化も簡単ではありません. よってパーサジェネレータは非常に有用なツールです. WebKit は有名なパーサジェネレータである `Flex `_ (lexer) と `Bison `_ (パーサ生成) を使用しています. (Lex and Yacc という名前で聞いたことがある人もいるかも知れません). Flex の入力はトークンを定義する正規表現, Bison の入力は BNF です. HTML Parser **************************************************************** HTML パーサの仕事は HTML マークアップをパースしパースツリーを作ることです. The HTML grammar definition ############################################################### HTML の語彙と構文は W3C によって定義されています. 現在のバージョンは HTML4 で HTML5 の策定が進行中です. Not a context free grammar ############################################################### 前節のパーサーのイントロダクションではグラマーは BNF など形式的なフォーマットで表現できると述べました. しかしこれは HTML には当てはまりません (しかしただ単に趣味でパーサーについて説明したわけではありません. CSS と JavaScript は前述の方法でパースされています). HTML は文脈自由文法では簡単に表現できないからです. DTD (Document Type Definition) と呼ばれる HTML の形式的な定義があります. しかしこれは文脈自由文法ではありません. HTML は XML と似ているので, これがおかしなことに思えるかもしれません. XML のパーサはたくさんあります. それにXML のバリエーションの1つである XHTML もあるくらいです. では XML と HTML の大きな違いとは何なのでしょうか. 違いは HTML が "寛容" であることです. 開きタグ, 閉じタグが抜けていても暗黙に補完してくれたりします. XML の固く厳しい構文に比べ, HTML は Soft です. この小さな違いは 2 つの大きく違う世界を生み出しています. 片面はこのことによって HTML がここまで広く使用されることになったということです. HTML はミスに寛容で Web サイト制作者はとても簡単に Web サイトを作成できます. 一方で文法を形式的に記述することが非常に難しくなっています. HTML をパースするのは簡単ではなく, 通常のパーサや XML パーサをそのまま適用することはできません. HTML DTD ############################################################### HTML は DTD 形式で定義されています. これは `SGML `_ ファミリーを定義するのに使われるフォーマットです. このフォーマットではすべての許可される要素とその属性, 及びヒエラルキーが定義されています. 前述のように DTD は文脈自由文法を形成しません. DTD にはいくつかのバージョンがあります. strict モードは単独で標準に準拠しますが, 他に過去のブラウザで使われていたマークアップを含むものもあります. これは古いコンテンツの後方互換性のためです. 現在の strict な DTD はこちらです `www.w3.org/TR/html4/strict.dtd `_ . DOM ############################################################### HTML をパースすると **パースツリー** という木構造のデータが出力されます. このツリーのノードは DOM 要素とその属性です. DOM とは Document Object Model の略です. DOM は HTML ドキュメントのオブジェクト表現で JavaScript などの外部から HTML 要素を操作するためのインタフェースです. DOM ツリーのルートは `Document `_ オブジェクトです. DOM はマークアップとほぼ 1 対 1 の関係になります. 例えば以下のマークアップは .. code-block:: html

Hello World

このような DOM ツリーになります. .. figure:: _static/image015.png :alt: DOM tree of the example markup Figure 8: DOM tree of the example markup HTMLと同様に DOM も W3C で仕様が策定されています. ``_ を参照してください. これは HTML に限らず一般的な文章を操作するための仕様です. HTML 特有の仕様は ``_ です. この文章で私が DOM ノードを含むツリーのことを言う時は, その要素が DOM インタフェースのいずれかの実装である木のことを意味します. ブラウザはブラウザ内部で使う属性を含んだ実装をしています. The parsing algorithm ############################################################### 前述のように HTML はトップダウンパーサ・ボトムアップパーサではパースができません. その理由は, 1. 言語の寛容な特性のため 2. invalid な HTML をサポートするためにエラーに対して寛大にならないといけないため 3. 再入可能なパースプロセスのため. 通常パースが完了するまで入力は変化しませんが, 例えば script 要素に ``document.write`` がある場合など, パース中にも入力が変化します. このように通常のテクニックが使えないので, ブラウザは HTML をパースするのにカスタムパーサを用いています. `パースのアルゴリズムは HTLM5 の仕様で詳しく説明されています `_ . アルゴリズムは *tokenization*, *tree construction* の2つにわかれます. tokenization では字句解析を行い入力をトークンに分割します. HTML のトークンは開始タグ, 終了タグ, 属性名, 属性値などです. tokenizer は新たなトークンを認識し次第それを tree constructer に渡し次の入力を読み込んでいきます. これを入力が終わるまで続けます. .. figure:: _static/image017.png :alt: HTML parsing flow (taken from HTML5 spec) Figure 9: HTML parsing flow (taken from HTML5 spec) The tokenization algorithm ############################################################### アルゴリズムの出力は HTML トークンです. アルゴリズムはステートマシンとして表現できます. 各状態は入力の 1 つ以上の文字を消費し, その文字に応じて次の状態へ遷移します. 遷移のルールはその時のトークンの状態と tree constructer の状態によって決まります. つまり同じトークンが来ても状態に応じて違う結果になりうるということです. アルゴリズム全体を扱うのは非常に複雑なので, 原理を理解しやすいようシンプルな例を見てみましょう. 次の HTML を tokenize すると考えてください. .. code-block:: html Hello world 最初の状態は *Date state* です. ``<`` に達したとき状態は *Tag open state* に変わります. ``a-z`` の文字を消費し *Start tag token* を作ります. 状態は *Tag name state* に変わります. ``>`` に達するまではそのままの状態で, その間の文字はトークン名となります. この場合は ``html`` トークンです. ``>`` に到達したらその時のトークンが発行され状態は *Date state* に戻ります. ```` タグも同様に処理されます. Hello の ``H`` 文字から ```` の ``<`` まではキャラクタートークンです. この段階で *Tag open state* に戻っています. ``/`` に達したときに *End tag token* を作成し *Tag name state* に遷移します. そしてまた ``>`` に達するまでこの状態にとどまります. そしてトークンを発行し *Data state* に戻ります. ```` 要素も同様に処理されます. .. figure:: _static/image019.png :alt: Tokenizing the example input Figure 10: Tokenizing the example input Tree construction algorithm ############################################################### パース中でも Document Object は作成されます. tree construction の処理の間もDOM ツリーには変更が加わります. tokenizer によって発行されたノードは tree constructer によって処理されます. 各トークンに対してどの DOM 要素が関係しているのかが仕様で定義されています. DOM ツリーに追加できないものはスタックに追加されます. これは open 要素のスタックと呼ばれるものです. このスタックはタグのネストのミスマッチやタグの閉じ忘れに対応する為のものです. このアルゴリズムもステートマシンで説明ができます. その状態は *insertion modes* と呼ばれます. この例の tree construction プロセスを見ていきましょう .. code-block:: html Hello world tree construction 処理での入力は tokenizer からのトークン列です. 初めは *initial mode* から始まります. html トークンを受け取ると *before html* モードに入ります. このモードでは HTMLHtmlElement が作られ, ルートである Document オブジェクトに追加されます. 状態は次に *before head* に移ります. 次に tokenizer から body トークンを受け取ります. ここで HTML に head 要素がありませんが, 暗黙的に HTMLHeadElement が作られツリーに追加されます. 次に *in head* モード, *after head* モードへと移り, 再び body トークンが処理され, HTMLBodyElement が作成・ツリーに追加され *in body* 状態に遷移します. 次に `Hello world` という文字列を受け取ります. 始めの一文字で Text ノードをツリーに追加し, あとの文字はそのノードに加えられていきます. body の閉じタグを受け取ると *after body* モードに遷移します. html の閉じタグを受け取ると *after after body* モードになり, EOF トークンを受け取りパースを終了します. .. figure:: _static/image022.gif :alt: tree construction of example html Figure 11: tree construction of example html Actions when the parsing is finished ############################################################### この段階でドキュメントは "インタラクティブ" という状態です. ここで *deferred* なスクリプト (パースが完了してから実行されるスクリプト) の解釈と実行を開始します. それが完了するとドキュメントの状態は *completed* になり, *load* イベントが発火します. `tokenization と tree construction の詳細は HTML5 の仕様を参照してください `_ . Browsers error tolerance ############################################################### ブラウザで構文エラーが起こることはありません. ブラウザは invalid なコンテンツを修正しそのまま続行します. この HTML を例に取ります. .. code-block:: html

Really lousy HTML

この例はたくさんのルール違反 (``mytag`` は標準でない, ``p`` と ``div`` のネストがおかしい, etc...) を犯していますが, ブラウザは文句を言わず正しくこれを表示させます. パーサーは Web サイト制作者の間違いをたくさん修正しているのです. こうしたエラーハンドリングは各ブラウザで一貫性がとれていますが, 驚くことに現在の HTML で挙動が定義されているわけではありません. ブックマークや戻る・進むボタンと同じように今までのブラウザの歴史の中で洗練されて来たものです. 多くのサイトでよく発生する invalid HTML があり, ブラウザはそれをうまく修正しようとしてきました. HTML5 の仕様ではこうした挙動を仕様に定義しています. WebKit の HTML パーサクラスのコメントによいサマリーがあります:: パーサはトークナイズされた入力をパースしドキュメントツリーを生成します. もしドキュメントが well-formed であればパースはストレートに通ります. しかしながら多くの well-formed でない HTML をパースする必要があります. そのためパーサにはエラートレランスが必要です. 私たちは最低限これらのエラーを扱えなければいけません. 1. 中にその要素を含むことが禁止されているタグの中への要素の追加. この場合禁止されている要素までのタグをすべて閉じ, その後に要素を追加します. 2. 要素を直接追加することを許していません. 制作者が間に書くべきタグを 書き忘れるかもしれないからです (あるいは間のタグがオプショナルな場合). これは次のタグの場合に起こりやすいです: HTML HEAD BODY TBODY TR TD LI (他に何か忘れてない?) 3. ブロック要素をインラインブロックの中に入れたい場合, すべてのインライン要素をブロック要素に到達するまで閉じる. 4. もしこれらでも役に立たない場合, そのタグを追加可能になるまでタグを閉じる. では WebKit でのこうしたエラーへの対応例を見ていきましょう **
instead of
** ``
`` のかわりに ``
`` が使われることがあります. IE や Firefox との互換性のために WebKit はこれを ``
`` として扱います. .. code-block:: cpp if (t->isCloseTag(brTag) && m_document->inCompatMode()) { reportError(MalformedBRError); t->beginTag = true; } 注: エラーハンドリングは内部的に行われているので, エラーがユーザーに出ることはありません. **A stray table** *stray table* とはテーブルタグの中で定義されているテーブルタグで, かつテーブルセルの中にないものです. 例: .. code-block:: html
inner table
outer table WebKit はこのテーブルのヒエラルキーを兄弟になるよう変更します. .. code-block:: html
outer table
inner table
コードは .. code-block:: cpp if (m_inStrayTableContent && localName == tableTag) popBlock(tableTag); WebKit はスタックで現在の要素を扱っています. 内側のテーブルが外側のテーブルのスタックからポップされ, 外側のテーブルと兄弟になっています. **Nested form elements** あるフォームの内側にフォームがあり, ユーザが内側のフォームを使用した際は, もうひとつのフォームは無視されます. .. code-block:: cpp if (!m_currentFormElement) { m_currentFormElement = new HTMLFormElement(formTag, m_document); } **A too deep tag hierarchy** コメントがよい説明になっています:: www.liceo.edu.mx は 1500 のタグがネストしている例です (すべて ```` タグ). 同じタグの 20 以上のネストは認めないので, すべてを無視します. .. code-block:: cpp bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName) { unsigned i = 0; for (HTMLStackElem* curr = m_blockStack; i < cMaxRedundantTagDepth && curr && curr->tagName == tagName; curr = curr->next, i++) { } return i != cMaxRedundantTagDepth; } **Misplaced html or body end tags** こちらもコメントを見てみましょう.:: 本当に壊れている HTML をサポートします. 私たちは body タグを閉じません. なぜなら stupid なサイトは本当のドキュメントの終端ではないのに body タグを閉じているからです. end() は最後までリレーされます. .. code-block:: cpp if (t->tagName == htmlTag || t->tagName == bodyTag ) return; ここではいろいろな WebKit のエラー対処を取り上げてきましたが, だからといって壊れた HTML ではなく well-formed な HTML を必ず書くようにしてください. CSS parsing ************************************************************** パースのイントロダクションの章を覚えていますか? HTML とは異なり CSS は文脈自由文法なのでそこで説明したパーサーでパースすることができます. 実際に `CSS の仕様ではその字句・構文の文法が定義されています `_ . では例を見てみましょう. 語彙は正規表現で定義されています:: comment \/\*[^*]*\*+([^/*][^*]*\*+)*\/ num [0-9]+|[0-9]*"."[0-9]+ nonascii [\200-\377] nmstart [_a-z]|{nonascii}|{escape} nmchar [_a-z0-9-]|{nonascii}|{escape} name {nmchar}+ ident {nmstart}{nmchar}* *ident* は *identifer* の短縮で, クラス名などに対応します. *name* は要素の id です. 構文ルールは BNF です:: ruleset : selector [ ',' S* selector ]* '{' S* declaration [ ';' S* declaration ]* '}' S* ; selector : simple_selector [ combinator selector | S+ [ combinator selector ] ] ; simple_selector : element_name [ HASH | class | attrib | pseudo ]* | [ HASH | class | attrib | pseudo ]+ ; class : '.' IDENT ; element_name : IDENT | '*' ; attrib : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S* [ IDENT | STRING ] S* ] ']' ; pseudo : ':' [ IDENT | FUNCTION S* [IDENT S*] ')' ] ; この BNF の ``ruleset`` はこのような構造です. .. code-block:: css div.error , a.error { color:red; font-weight:bold; } ``div.error``, ``a.error`` はセレクタです. 括弧の中はこの ruleset で適用されるルールです. この構造は次のように形式的に定義されます:: ruleset : selector [ ',' S* selector ]* '{' S* declaration [ ';' S* declaration ]* '}' S* ; この定義より, *selector* には1つ, またはコンマ区切りの複数のセレクタが指定できます (``S`` は空白です). 括弧の中にはセミコロン区切りの *declaration* があります. *selector*, *declaration* は BNF の他の部分で定義されています. Webkit CSS parser ############################################################### WebKit は Flex と Bison で CSS の文法ファイルを読み込み CSS のパーサを生成しています. 前述のパーサーイントロダクションの繰り返しになりますが, Bison はボトムアップ・Shift-reduce パーサを生成します. Firefox は自前のトップダウンパーサを使用しています. どちらの場合も CSS ファイルは StyleSheet オブジェクトにパースされます. それぞれのオブジェクトは CSS Rule オブジェクトを持っています. CSS Rule オブジェクトは selector と declaration オブジェクト, 及び各文法に対応するオブジェクトを持っています. .. figure:: _static/image023.png :alt: parsing CSS Figure 12: parsing CSS The order of processing scripts and style sheets ************************************************************** Scripts ############################################################### Web は同期モデルです. Web サイト制作者はパーサが ``