開発者向けドキュメント

これは kdelibs/khtmlにあるものです。


この文書では、手短かにhtmlライブラリの内部デザインの概観を説明します。私がこれを書いたのは、khtmlライブラリがかなり膨大なものになってしまって最初にソースコードを読む際に途方に暮れてしまうからです。このドキュメントを読んだからといってkhtmlを理解したことにはなりませんが、ソースコードをより楽に読んでもらう助けになったらなと思います。

ライブラリはいくつかの異なるパーツから成り立っています。基本的にライブラリを使用する際にはKHTMLPartのインスタンスを作成し、データを与えればよいです。

(注)実際のコード : http://devel-home.kde.org/~larrosa/tutorial/p4.html

多かれ少なかれkhtmlを他のアプリケーションで用いようと思ったならば、知らなければならない事はこれだけです。
しかし、もしあなたがkhtml自体をハックしたいのであれば、例えばurl引数をkhtmlに与えた時に、内部でどんなオブジェクトが生成されるのかについて概略を今から説明しましょう。

以下では、現在web technolobyの分野で使われている専門用語について貴方がすでに知っているものとします。もし知らないならば、いくつかの参照すべきリストを示します。

  • Document Object model (DOM):
    • DOM Level1 and 2
    • We support DOM Level2 except for the events model at the moment.
  • HTML:
    • HTML4 specs
    • xhtml specs
    • We support almost all of HTML4 and xhtml.
  • Cascading style sheets (CSS):
    • CSS2 specs
    • We support almost all of CSS1, and most parts of CSS2.
  • Javascript:
    • insert some links here...
    • Microsoft javascript bindings
    • Netscape javascript reference
    • Netscapes javascript bindingsはすでに古いので、従うべきではない。IE互換のものに集中する。 KHTMLPartはこれらすべてを表示できるKHTMLView(QScrollView?を継承)のインスタンスを一つ作成します。 同時に、ファイルに書かれたHTMLやXMLからDOMツリーを構築します。 例を用いて説明しましょう。 khtmlは木のような構造を持ったドキュメントを保存するのにdocument object model(DOM)を活用します。
      以下のようなhtmlを想定して下さい。
       <html>
          <head>
              <style>
                  h1: { color: red; }
              </style>
          </head>
          <body>
              <H1>
                  some red text
              </h1>
              more text
              <p>
                  a paragraph with an
                  <img src="foo.png">
                  embedded image.
              </p>
          </body>
       </html>
      
      このインプットから、最終的にスクリーン上で見ることになる視覚的なアウトプットを生成するまでの過程を一歩一歩説明します。 ここではあたかも順番に処理が運んでいるように説明しますが、明快にするためであって、実際にはできる限り早くスクリーンに表示させるために、すべての処理(tokenize処理・rendering treeのレイアウト)は並行に処理が行われます。

Tokenizer and parser

新しいドキュメントをパースし始めるときはまず、Part(in khtml_part.cpp::begin())によってDocumentImpl?* (for XML documents)やHTMLDocumentImpl?*オブジェクトが生成されます。
Tokenizer*オブジェクトはパートによってDocumentImpl?::open()やbegin()が呼ばれるとすぐに作成されます。(これはXMLTokenizerにもHTMLTokenizerでも同じです。)

XMLTokenizerはQtのQXMLクラスを用いてドキュメントをパースし、そのSAX interfaceを用いてkhtmls DOMへとパースします。

HTMLのtokenizerはkhtmltokenizer.cppにあります。
tokenizerはHTMLファイルの中身をインプットとして扱い、トークンの連結リストに解体します。
またHTML-entityやHTML-tagにも配慮します。
タグにはさまれたテキストはほかのタグと区別して処理されます。
区別とは、space、改行、HTML-entityそして他のタグがどのように扱われるかということです。

tokenizerは文字毎に完全に状態が変ります。
tokenizerを通った全てのテキストは直接トークン化されます。
HTMLファイルは文字毎(あまり有効ではありませんが)、もしくは様々なサイズのブロックとしてtokenizerを通ります。

HTMLTokenizerはHTMLParserを生成します。
HTMLParserは、tokenizerによって与えられた一連のトークンを解釈し、Document Object Modelにもとづいた文書を現すNodeの木を作成します。

HTMLとXMLにおける大きな違いの一つは、HTMLのParserとTokernizerはデータが運ばれると供に徐々にパースできるのに対して、XMLTokenizer(QtのXMLParserの制限が原因なのですが)は完全に文書がロードされた後しかその文書をパースできません。


The DOM in khtml

文書をパースすると、以下の様なDOMツリーが作成されます。

HTMLDocumentElement
  |--> HTMLHeadElement
  |       \--> HTMLStyleElement
  |              \--> CSSStyleSheet
  \--> HTMLBodyElement
         |--> HTMLHeadingElement
         |      \--> Text
         |--> Text
         \--> HTMLParagraphElement
                |--> Text
                |--> HTMLImageElement
                \--> Text

実際には上記のクラスはDOMにアクセスするためのインターフェースです。
実際のデータは*Implクラスに保管されています。
なので、実際には次のような木構造になっています。

HTMLDocumentElementImpl*
  |--> HTMLHeadElementImpl*
  |       \--> HTMLStyleElementImpl*
  |              \--> CSSStyleSheetImpl*
  \--> HTMLBodyElementImpl*
         |--> HTMLHeadingElementImpl*
         |      \--> TextImpl*
         |--> TextImpl*
         \--> HTMLParagraphElementImpl*
                |--> TextImpl*
                |--> HTMLImageElementImpl*
                \--> TextImpl*

refcounting schemeを用いて、rootエレメントが削除された時に全てのオブジェクトが削除されることが保証されています。(Implementationを指すポインタを持っているインターフェースクラスが無い場合のみに限ります)

インターフェースクラス(Implという名前でないもの)は、dom/ディレクトリで規定されており、khtml自体では全く使うことがありません。
使われるのは、DOMツリーにアクセスするjavascriptバインディングだけです。
インターフェースクラスとインプレメンテーションクラスの分離による大きな利点は、同じインプレメンテーションを指すインターフェースオブジェクトを複数持てるということです。

ほとんどの場合インターフェースとインプレメンテ‐ションの間には1対1対応があることが分かるでしょう。
インプレメンテ‐ションクラスの中では仲介クラスを追加できます。
この中間クラスは色々な理由から外からは見えないようになっています。(共有とメモリ節約のため)

C++においては、インターフェースクラスを用いてKHTMLの外からDOMツリー全体にアクセス出来るでしょう。
詳細については http://developer.kde.org/ のkhtmlのイントロダクションを見て下さい。*1
エレメント<style>で定義されるスタイルシート(これは一例ですが)と、画像(ロードする必要のある外部リソース)については説明を省略してきました。
続く2つのセクションで説明します。


CSS

エレメント<style>の中身(この場合は h1: { color: red; })はHTMLStyleElementImpl?オブジェクトになります。
このオブジェクトはCSSStyleSheetImpl?オブジェクトを生成し、データをこれに渡します。
CSSパーサーはデータを受取り、パースしてCSS用にDOMツリーを構築します。(HTMLのものに似ています。DOM level 2 specsを参照して下さい。)
これはHTMLエレメントの外観を決定するのに後程使われます。

後で説明するように、実際には"後程"というのは相対的なものです。
DOMツリーを構築する為に部分的に平行に処理されるためです。


''外部オブジェクトのロード"

いくつかのタグ(<img>, <link>, <object>等)は、ロードしなければならない外部オブジェクトの参照を含んでいます。
これはローダーと関連するクラスによって行なわれます。(misc/loader.*)
外部オブジェクトをロードしようとするオブジェクトはCachedObjectClient?を継承したものであり、ローダーに対してwebからダウンロードするように要求することができます。

ローダは準備が完了すると、このCachedObjectClient?に通知し、クライアントはデータを受信します。


表示する ここまででDOMツリーを構築し、それに関連するスタイルシートと外部オブジェクトを準備しました。
それではどうやって実際にスクリーン上に表示するのでしょうか?

CSSに完全対応したレンダリングエンジンを使うのです。
まず最初にやるべきことは、文書に適用するスタイルシートの情報を集めて、エレメントに適応される必要のあるスタイルルールのリストを作ることです。
これはCSSStyleSelector?クラスによって行なわれます。
標準のHTMLスタイルシート(css/html4.cssで定義)、オプションでユーザーが定義したスタイルシート、そして文書中のスタイルシートを繋ぎ合わせ、パースされたスタイルルールのリストにします。(速く調べ挙げられるように最適化されています)
どのようにXMLやHTMLにスタイルシートが適応されるかについての正確なルールはCSS2 specsで見ることができます。

このリストを作ると、CSSStyleSelector?の"styleForElement?(DOM::ElementImpl? *)"関数を呼び出すことによってそれぞれのDOMエレメントからRenderStyle?オブジェクトを得られるようになります。
スタイルオブジェクトはNodeに適応されるべきCSS属性のコンパクトな形です。

そのあと、レンダーツリーが構築されます。
スタイルオブジェクトを使用することによって、DOM Nodeは適切なレンダーオブジェクト(renderingサブディレクトリで定義されています)を作りだし、それをレンダーツリーに加えます。
レンダリングツリーも一般的なDOMツリーのような木の様な構造をしていますが、明白な違いも有ります。 なによりもまず、いわゆるanonymous boxesなのです。(CSS specs参照) anonymous boxesはDOMの要求を満たす為にレンダリングツリーに挿入されるであろうDOM counterpartを持っていません。 次に、スタイルの表示属性はどのタイプのレンダリングオブジェクトが現在のDOMオブジェクトを表示するのに選ばれるかに影響します。

上記の例では次のようなレンダリングツリーが構築されます。

 RenderRoot*
  \--> RenderBody*
         |--> RenderFlow* (<H1>)
         |      \--> RenderText* ("some red text")
         |--> RenderFlow* (anonymous box)
         |      \--> RenderText* ("more text")
         \--> RenderFlow* (<P>)
                |--> RenderText* ("a paragraph with an")
                |--> RenderImage*
                \--> RenderText* ("embedded image.")

RenderRoot?オブジェクトがlayout()を実行することによって、KHTMLViewによって与えられた有効な領域にレンダリングツリーが自らをレイアウトします。
そのあと。KHTMLViewのdrawContents()関数がRenderRoot?->print()関数を適切なパラメータで呼び出すことによって実際に文書が表示されるのです。
これはパースが行なわれる時には100%正しいとは言えませんが、文書をリサイズするときに行なわれていることそのものです。
見れば分かるように、レンダリングツリーへの変換はHTMLコードのhead部分を取り除きます。そして"more text"の辺りに匿名のオブジェクトを挿入するのです。
なぜこのようなことが行なわれるかはCSS specsを見て下さい。


ディレクトリ構造

ディレクトリ構造の簡単な説明をします。

  • css:
    DOM Level2(インプレメンテ‐ションクラスのみ)のCSS部分、CSSパーサ、ノードとスタイルシートからRenderStyle?オブジェクトを作りだすためのものを含みます。
  • dom
    すべてのDOMのための外部DOM API(DOMインターフェースクラス)
  • ecma:
    DOMとkhtmlへのjavascriptバインディング
  • html
    DOM(インプレメンテ‐ションのみ)のhtmlサブパート、HTML tokenizer、HTMLパーサ、HTMLを使うためのDTDを定義したクラス(主にパーサによって使われます)
  • java
    Javaに関連したもの
  • misc
    khtmlに必要な雑多なもの。イメージローダ、雑多な定義、ユニコードに変換するためのデコーダクラス
  • rendering
    CSSともにDOMツリーを画面に表示することに関連したもの全て。レンダリングツリーで使われるものの定義、レイアウトコード、RenderStyle?オブジェクト。
  • xml
    DOMインプレメンテ‐ションのXML部分とxml tokenizer

例外処理

ライブラリサイズを抑えるため、C++の例外は dom/サブディレクトリのみ有効になっています。
なぜなら、DOM APIによって例外を吐くことを義務付けられているからです。
のこりのKHTMLのコードでは、エラーフラグ(通常は"exceptionCode"と呼ばれます)を建てて、dom/*の一部であるクラスがそのフラグを判定して例外を投げます。


最後に

完全で100%正しいとは言えませんが、HTML/XMLファイルを画面に表示するまでの道程を簡単に紹介しました。
まだたくさんの問題が残っていて、リクエストか、もしくはやろうと思ったときに加えようと思います。
ここにkhtmlに欠けているものを挙げてみる。

  • ユニコードに変換するためのデコーダ
  • konqueror/applicationsとの連係
  • javascript
  • dynamic reflowとkhtmlの視覚的アウトプットを処理するためにDOMをどのように用いるか
  • mouse/event 処理
  • パースを行なうときの相互連係
  • java この短いイントロダクションによって、あなたがkhtmlに初めて関わることが少しでも楽になることを期待します。 さて、話を終わる前にkhtmlをハックしようとしている人達にちょっとした警告とアドバイスをしたいと思う。 khtmlは現在非常に巨大なライブラリとなっていて、どのように動いているかを理解するのには少々の時間がかかる。
    すぐに分からないからといって挫折しないこと。
    逆に、現在khtmlはかなり使われているので、多数のバグを抱えていることだろう。(中にはバグだと分かるのすら困難なバグもあるが) khtmlの中にはかなり敏感なコードもあり、(特にレイアウトアルゴリズム)そこに変更を加えること(1つのwebページのためにバグをfixすること)は重大なバグを引き起こすことにもなりかねません。
    khtmlを開発するすべての人間はすでに膨大な時間を、あるページのみで見られるようなバグを探すために割いてきました。そして、1週間後にはもうその変更がバグを引き起こしていることを見付けることも有ります。
    これはかなりイライラする作業ですが、khtmlに精通していない人がそのような重要な部分に加えた変更を、適用する前にkfm-devel等にポストしてくれると嬉しいと思います。 And now have fun hacking khtml. Lars
    Counter: 11267, today: 3, yesterday: 2

以下では、現在web technolobyの分野で使われている専門用語について貴方が


*1 どれのことだろうか?

  最終更新のRSS
Last-modified: 2006-04-06 (木) 23:50:16 (1404d)

このサイトは日本KDEユーザ会が著作権を有します。本著作権を表示する限りにおいて転載を許可します。
Copyright (C) 1999-2008 Japan KDE Users' Group. All Rights Reserved.
このサイトに関するご意見・お問い合わせは webmaster@kde.gr.jp まで