[1] 本項では HTML と XML の構文解析器の基礎的な概念を扱います。専ら一方のみに関係する事項は、 HTML構文解析器とXML構文解析器の項をご覧ください。
[83] 次のような状態を持ちます。
[11] 文書から見て動作中の構文解析器のことを(あれば)活性構文解析器といいます。
[73] 文書が構文解析器を持つか否かとその状態は、 try to scroll to the fragment で参照されます。
[12] ある構文解析器のインスタンスが扱う文書は1つだけです。
[13] 文書に対してそれを扱う構文解析器は1つとは限りません。例えばその文書自体を作った構文解析器と
innerHTML
の構文解析器は別物です。また document.open
では新しい構文解析器が作られます。
[81] スクリプトの準備では、script
要素の節点文書と script
要素を作成した構文解析器の文書が等しいか比較されます。
[82] スクリプトの準備では、 script
要素を作成した構文解析器の種別 (HTML or XML)
やスクリプト入れ子水準が参照されます。
作成した構文解析器の文書のスクリプトをブロックしているスタイルシートを持つかどうかや、
スクリプトに関するリストや集合各種も参照されます。
[100] この他、文書には document.write
の動作に関わる内部状態がいくつかあり、
構文解析器から参照・変更されます。
[75]
文書は、
活性構文解析器がabortされたを持ちます。
初期値は偽です。
HTML Standard
文書のabortで真に設定されます。
document.open
, document.write
などに影響します。
[24] 構文解析の対象となる入力は、入力バイトストリーム (バイト列) または入力ストリーム (文字列) として構文解析器に与えられます。
[25] 入力がバイト列の場合は、バイトの解釈に用いる文字符号化やロケール、 呼び出される文脈 (navigate か XHR か) といった情報も与える必要があります。 詳しくは charset sniffing を参照。
[30] HTML構文解析器とXML構文解析器では、構文解析の各段階の動作に様々な違いが生じます。
[27] HTML構文解析器の動作は、スクリプト有効か否かで変化します。
[29] innerHTML
など素片構文解析では、
文脈要素によって構文解析の開始の時点の状態やその後の動作が左右されます。
[28] 構文解析の結果は、開始時に指定された親となる節点の子孫という形で現れます。 また、奇癖モードか否かなど、内部状態 (IDL属性など) を変化させることもあります。
[31] また、構文解析器は、1つ以上の構文解析エラーを発見すると、 これを報告することがあります。仕様上義務付けられてはいませんが、 Webブラウザーによっては Console に構文解析エラーを報告するようです。
[2] 構文解析器は HTML の script
終了タグの処理の一部でイベントループをスピンすることがあります。
また字句解析器・構文解析器がブロックされます。これらにより、特定のスクリプト群の実行が終わるまで構文解析器は処理を停止することになります。
[3] HTML構文解析器に(のみ)は、 parser pause flag があり、このフラグが設定されている間は字句化器の処理が止まります。
これは入れ子の document.write
の呼び出しや
SVG script
要素からの document.write
の呼び出しの構文解析処理をスクリプト実行後まで遅延させるために使われています。
document.write
が使えないので、相当するフラグはありません。[15] XML構文解析器で外部実体を展開する時は、主たる構文解析を一時停止して、外部実体側の構文解析が終了したら戻ってくる形で実装することになるでしょう。
これは document.write
のように入力ストリームの現在位置に外部実体側の入力ストリームからバイト列を挿入していく形でも実装できるでしょうし、
別の構文解析器の結果を受け取って主たる構文解析器の出力に挿入する形でも実装できるでしょう。
[52] HTML字句化器は「各ステップ前」に parser pause flag をチェックして、
真なら字句化しないことになっています。「各ステップ」が何を指しているか不明瞭ですが、
parser pause flag は HTML/SVG script
タグによるスクリプト処理の際と字句の要素の作成でカスタム要素を作成する際にのみ真になることがあるものなので、
スクリプト実行の次の字句の取得の処理や、
次のタスクによる字句化器からの字句の取得の処理を実行しようとした時にチェックすれば十分そうです。
document.write
が呼び出された場合は、 parser pause flag が真でなければ、
その字句化は行われます。[54] 構文解析器によるスクリプト実行のための一時停止以外にも、 構文解析器から呼び出されたスクリプトの処理のためにイベントループの実行の一時停止が発生することがあります。 当然ながらその間も構文解析器は停止したままです。
[55] 例えばスクリプト内部で alert
を実行するとイベントループは
pause します。また showModalDialog
を実行するとイベントループのスピンが派生します。
[56] 明示的に実行が停止されなくても、処理の実行が長時間に渡り実質的に停止状態となることもあります。
[40] 構文解析は、正常終了 (>>6) か中断 (>>4) のいずれかによって終了します。
[6] 構文解析の処理がすべて終了すると、 stop parsing が実行されます。
[4] 構文解析器は abort a parser が呼び出されると入力ストリームや開いている要素のスタックを破棄します。 これによって未処理の入力は捨てられ、それ以降の字句化が行われません。
[5] abort は、 window.close
や document.open
が呼ばれた時や、
navigate されて現在の文書が破棄される場合などに呼ばれます。
[58] スクリプトは実行時間等の制限超過によって停止させられることがあります (スクリプトの項を参照)。構文解析器から呼び出されたスクリプトが停止された場合、 スクリプトが正常終了した場合と同じように構文解析器の処理が再開されるものと思われます。
[59] スクリプトから呼び出した構文解析器の処理に時間がかかるような場合、 構文解析器の動作が途中で停止させられることもあるかもしれません。 構文解析器から呼び出されたスクリプトからの構文解析器の呼び出しに時間がかかる場合どのように処理されるのか謎です。
[7] HTML構文解析器(のみ)は document.write
や document.writeln
により、スクリプトを介して再帰的に呼び出されることがあります。外側と内側でどちらも同じ構文解析器ですから、
入力ストリームや種々の状態はすべて共有されています。外側の構文解析処理は、内側で行われた構文解析器への操作により発生する処理の続きとしてスクリプト終了後に継続されます。
[8] このため HTML構文解析器はスクリプトが実行するまで続きを字句解析・構文解析できません (投機的実行はできますが)。
[14] HTML構文解析器(のみ)は文字符号化宣言を発見し、処理中の文字符号化と実際の文字符号化が一致していないことを検出したとき、 change the encoding 処理により再起動することがあります。この再起動は、現在の文書破棄して同じ入力バイト列に対して navigate しなおすというかなり強引な形で行われます。スクリプトにより著者に、 レンダリングにより利用者に観測可能なこともあります。
[70] HTML構文解析器やXML構文解析器によって呼び出されるアプリケーションキャッシュ選択アルゴリズムも、 navigate 全体をやり直させることがあります。
[63] 字句化と木構築は、2つのほぼ独立した処理段階として規定されていますが、 いくつか例外的に両者の動作が干渉することがあります。いずれもHTML構文解析器のみの動作です。
[67] これは一例として次のように実現できます。
[94] ここで、 >>63 だけでなく、スクリプトが document.write
から入力ストリームに介入する可能性も考慮する必要があります。
具体的には、次のものがあります。
[71] アプリケーションキャッシュ選択アルゴリズムの実行タイミングにも注意が必要です。 >>67 だけでは正しく処理できないケースがあります。 (構文解析器の動作そのものには影響しませんが、 スクリプトと fetch 経由で動作が変化する可能性はあります。)
[68] navigate によって、あるいはスクリプトが DOM API によって構文解析器・字句化器を作成し動作させる場合以外に、 次の場合に外部から字句化器の挙動が操作されることがあります。 これは構文解析器中の文書に含まれるスクリプトの実行によって呼び出されることもあれば、 他の閲覧文脈から呼び出されることもあります。
[84] HTML でも XML でも、文字字句以外のすべての字句に対応するソース上の構造は
<
から始まります。
[87] 特定の属性の値によって分岐することは、字句化器内ではありません。
[88] HTML構文解析器の木構築器では次の分岐があります。
[89] XML構文解析器の木構築器では次の分岐があります。
[91] なお、属性はこれ以外には、 Element
の作成時に内容属性として設定される他は、
既存の html
要素や body
要素に追加されるケース、
活性書式付け要素のリストへの追加時に比較されるケースがあります (いずれもHTML構文解析器のみ)。
[21] 構文解析器によるDOM木の操作は、基本的には DOM API によって行われる操作と同じものですが、一部は特別な動作が定義されていることもあります。
[22] 構文解析器による DOM 操作の動作については、次の各項を参照してください。
[23] 節点の作成や節点の内部状態 (IDL属性の値) の設定
(createElementNS
や compatMode
など) に加えて、次のようなメソッドに相当する操作が行われます。
[49] 構文解析器による DOM の操作は、変異観察器に報告されます。
[51] 雛形内容は別の文書に属するため、またスクリプトによって節点がまったく別の文書に移動される可能性があるため、 構文解析器が扱う節点が同じ文書に属する保証はありません。
[60] 構文解析器は純粋なDOM木の構築操作の他に、次のような動作をします。
[48] 構文解析器の動作中にスクリプトが実行されることがあります。 スクリプトと構文解析器の処理は複雑に関わっています。
[93] スクリプトの前後には、マイクロタスクが実行されます。 構文解析器による挿入に伴う変異観察器への報告 (>>49) は、 このタイミングで実行されます。
[18] HTML構文解析器の動作は、長らく明確に規定されていませんでしたが、 2007年頃、 Web Applications 1.0 (後の HTML5) によって初めて詳細な動作が記述されました。
[19] XML構文解析器の動作は、 XML 本体仕様によってごく限定的に制約が規定されていました。 より詳細な動作は XML5 (後の XML-ER) によって HTML構文解析器に倣って定義されましたが、 XML に対する関心の低さのため、仕様書は未完成のまま保留状態にあります。 HTML Standard にも限定的ながらXML構文解析器の動作の規定があります。
[20] 構文解析器を呼び出したり、操作したりする API は、 HTML Standard と DOM Parsing の両仕様書によって定義されています。 これらも長らく明確な定義が存在していませんでしたが、 WHATWG において2007年頃から数年かけて整備されました。
[101] Ignore document.open/write after the active parser has been aborted (foolip著, ) https://github.com/whatwg/html/commit/ead4aa8ec576d7d330a04f7ec8508e336b895fdb
[102] [css-selectors-4] Include whitespace in non-optional `<combinator>` · Issue #7027 · w3c/csswg-drafts · GitHub, https://github.com/w3c/csswg-drafts/issues/7027
[103] Hixie が形式的言語記述を避けて英語による厳密な構文・構文解析器の記述にこだわったのはこういう混乱を避けるためだったというのがわかる一件。