You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
347 lines
16 KiB
347 lines
16 KiB
<html>
|
|
<head>
|
|
<title>Internal design of khtml</title>
|
|
<style>
|
|
dt { font-weight: bold; }
|
|
</style>
|
|
<body bgcolor=white>
|
|
<h1>Internal design of khtml</h1>
|
|
|
|
<p>
|
|
This document tries to give a short overview about the internal design of the khtml
|
|
library. I've written this, because the lib has gotten quite big, and it is hard at first to find your
|
|
way in the source code. This doesn't mean that you'll understand khtml after reading this
|
|
document, but it'll hopefully make it easier for you to read the source code.
|
|
</p>
|
|
<p>
|
|
The library is build up out of several different parts. Basically, when you use the lib, you
|
|
create an instance of a KHTMLPart, and feed data to it. That's more or less all you need to
|
|
know if you want to use khtml for another application. If you want to start hacking khtml,
|
|
here's a sketch of the objects that will get constructed, when eg. running testkhtml with
|
|
a url argument.
|
|
</p>
|
|
<p>
|
|
In the following I'll assume that you're familiar with all the buzzwords used in current web
|
|
techology. In case you aren't here's a more or less complete list of references:
|
|
</p>
|
|
<blockquote>
|
|
<p>
|
|
<b>Document Object model (DOM):</b><br>
|
|
<a href="http://www.w3.org/DOM/">DOM Level1 and 2</a><br>
|
|
We support DOM Level2 except for the events model at the moment.
|
|
</p>
|
|
<p>
|
|
<b>HTML:</b><br>
|
|
<a href="http://www.w3.org/TR/html4/">HTML4 specs</a><br>
|
|
<a href="http://www.w3.org/TR/xhtml1/">xhtml specs</a><br>
|
|
We support almost all of HTML4 and xhtml.
|
|
</p>
|
|
<p>
|
|
<b>Cascading style sheets (CSS):</b><br>
|
|
<a href="http://www.w3.org/TR/REC-CSS2/">CSS2 specs</a><br>
|
|
We support almost all of CSS1, and most parts of CSS2.
|
|
</p>
|
|
<p>
|
|
<b>Javascript:</b><br>
|
|
<a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/objects.asp">Microsoft javascript bindings</a><br>
|
|
<a href="http://docs.sun.com/source/816-6408-10/index.html">Netscape javascript reference</a><br>
|
|
Netscapes javascript bindings are outdated. We shouldn't follow them. Let's focus on getting the bindings
|
|
compatible to IE.
|
|
<a href="http://mozilla.org/docs/dom/domref/">Mozilla JS/DOM reference</a>
|
|
</p>
|
|
</blockquote>
|
|
|
|
<p>
|
|
<a href="khtml_part.h">KHTMLPart</a> creates one instance of a
|
|
<a href="khtmlview.h">KHTMLView</a> (derived from QScrollView),
|
|
the widget showing the whole thing. At the same time a DOM tree
|
|
is built up from the HTML or XML found in the specified file.
|
|
<p>
|
|
Let me describe this with an example.
|
|
<p>
|
|
khtml makes use of the document object model (DOM) for storing the document
|
|
in a tree like structure. Imagine some html like
|
|
<pre>
|
|
<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>
|
|
</pre>
|
|
In the following I'll show how this input will be processed step by step to generate the visible output
|
|
you will finally see on your screen. I'm describing the things as if they happen one after the other,
|
|
to make the principle more clear. In reality, to get visible output on the screen as soon as possible,
|
|
all these things (from tokenization to the build up and layouting of the rendering tree) happen
|
|
more or less in parallel.
|
|
|
|
<h2>Tokenizer and parser</h2>
|
|
<p>
|
|
The first thing that happens when you start parsing a new document is that a
|
|
DocumentImpl* (for XML documents) or an HTMLDocumentImpl* object will get
|
|
created by the Part (in khtml_part.cpp::begin()). A Tokenizer*
|
|
object is created as soon as DocumentImpl::open() is called by the part, also
|
|
in begin() (can be either an XMLTokenizer or an HTMLTokenizer).
|
|
<p>
|
|
The XMLTokenizer uses the QXML classes in Qt to parse the document, and it's SAX interface
|
|
to parse the stuff into khtmls DOM.
|
|
<p>
|
|
For HTML, the tokenizer is located in khtmltokenizer.cpp. The tokenizer uses the contents
|
|
of a HTML-file as input and breaks this contents up in a linked list of
|
|
tokens. The tokenizer recognizes HTML-entities and HTML-tags. Text between
|
|
begin- and end-tags is handled distinctly for several tags. The distinctions
|
|
are in the way how spaces, linefeeds, HTML-entities and other tags are
|
|
handled.
|
|
<p>
|
|
The tokenizer is completely state-driven on a character by character basis.
|
|
All text passed over to the tokenizer is directly tokenized. A complete
|
|
HTML-file can be passed to the tokenizer as a whole, character by character
|
|
(not very efficient) or in blocks of any (variable) size.
|
|
<p>
|
|
The HTMLTokenizer creates an HTMLParser which
|
|
interprets the stream of tokens provided by the tokenizer
|
|
and constructs the tree of Nodes representing the document according
|
|
to the Document Object Model.
|
|
<p>
|
|
|
|
<h2>The DOM in khtml</h2>
|
|
<p>
|
|
Parsing the document given above gives the following DOM tree:
|
|
|
|
<pre>
|
|
HTMLDocumentElement
|
|
|--> HTMLHeadElement
|
|
| \--> HTMLStyleElement
|
|
| \--> CSSStyleSheet
|
|
\--> HTMLBodyElement
|
|
|--> HTMLHeadingElement
|
|
| \--> Text
|
|
|--> Text
|
|
\--> HTMLParagraphElement
|
|
|--> Text
|
|
|--> HTMLImageElement
|
|
\--> Text
|
|
</pre>
|
|
<p>
|
|
Actually, the classes mentioned above are the interfaces for accessing the
|
|
DOM. The actual data is stored in *Impl classes, providing the implementation
|
|
for all of the above mentioned elements. So internally we have a tree
|
|
looking like:
|
|
<pre>
|
|
HTMLDocumentElementImpl*
|
|
|--> HTMLHeadElementImpl*
|
|
| \--> HTMLStyleElementImpl*
|
|
| \--> CSSStyleSheetImpl*
|
|
\--> HTMLBodyElementImpl*
|
|
|--> HTMLHeadingElementImpl*
|
|
| \--> TextImpl*
|
|
|--> TextImpl*
|
|
\--> HTMLParagraphElementImpl*
|
|
|--> TextImpl*
|
|
|--> HTMLImageElementImpl*
|
|
\--> TextImpl*
|
|
</pre>
|
|
<p>
|
|
We use a refcounting scheme to assure that all the objects get deleted, in
|
|
case the root element gets deleted (as long as there's no interface class
|
|
holding a pointer to the Implementation).
|
|
<p>
|
|
The interface classes (the ones without the Impl) are defined in the <code>dom/</code>
|
|
subdirectory, and are not used by khtml itself at all. The only place they are used are in the
|
|
javascript bindings, which uses them to access the DOM tree. The big advantage of having this
|
|
separation between interface classes and imlementation classes, is that we can have several
|
|
interface objects pointing to the same implementation. This implements the requirement of
|
|
explicit sharing of the DOM specs.
|
|
<p>
|
|
Another advantage is, that (as the implementation classes are not exported) it gives us a lot
|
|
more freedom to make changes in the implementation without breaking binary compatibility.
|
|
<p>
|
|
You will find almost a one to one correspondence between the interface classes and the implementation
|
|
classes. In the implementation classes we have added a few more intermediate classes, that can
|
|
not be seen from the outside for various reasons (make implementation of shared features easier
|
|
or to reduce memory consumption).
|
|
<p>
|
|
In C++, you can access the whole DOM tree from outside KHTML by using the interface classes.
|
|
For a description see the <a href="http://developer.kde.org/documentation/library/kdeqt/kde3arch/khtml/index.html">introduction to khtml</a> on <a href="http://developer.kde.org/">developer.kde.org</a>.
|
|
|
|
One thing that has been omitted in the discussion above is the style sheet defined inside the
|
|
<code><style></code> element (as an example of a style sheet) and the image element
|
|
(as an example of an external resource that needs to be loaded). This will be done in the following
|
|
two sections.
|
|
|
|
<h2>CSS</h2> The contents of the <code><style></code> element (in this
|
|
case the <code>h1 { color: red; }</code> rule) will get passed to the
|
|
<a href="html/html_headimpl.h">HTMLStyleElementImpl object</a>. This object creates an
|
|
<a href="css/cssstylesheetimpl.h">CSSStyleSheetImpl object</a> and passes the
|
|
data to it. The <a href="css/cssparser.h">CSS parser</a> will take
|
|
the data, and parse it into a DOM structure for CSS (similar to the one for
|
|
HTML, see also the DOM level 2 specs). This will be later on used to define the
|
|
look of the HTML elements in the DOM tree.
|
|
<p>
|
|
Actually "later on" is relative, as we will see later, that this happens partly in parallel to
|
|
the build up of the DOM tree.
|
|
|
|
<h2>Loading external objects</h2>
|
|
<p>
|
|
Some HTML elements (as <code><img>, <link>, <object>, etc.</code>) contain
|
|
references to external objects, that have to be loaded. This is done by the
|
|
Loader and related classes (misc/loader.*). Objects that might need to load external objects
|
|
inherit from <a href="misc/loader_client.h">CachedObjectClient</a>, and can ask
|
|
the <a href="misc/loader.h">loader</a> (that also acts as a memory cache) to
|
|
download the object they need for them from the web.
|
|
<p>
|
|
Once the <a href="misc/loader.h">loader</a> has the requested object ready, it will notify the
|
|
<a href="misc/loader_client.h">CachedObjectClient</a> of this, and the client can
|
|
then process the received data.
|
|
|
|
<h2>Making it visible</h2>
|
|
|
|
Now once we have the DOM tree, and the associated style sheets and external objects, how
|
|
do we get the stuff actually displayed on the screen?
|
|
<p>
|
|
For this we have a rendering engine, that is completely based on CSS. The first
|
|
thing that is done is to collect all style sheets that apply to the document
|
|
and create a nice list of style rules that need to be applied to the
|
|
elements. This is done in the <a href="css/cssstyleselector.h">CSSStyleSelector</a> class.
|
|
It takes the <a href="css/html4.css">default HTML style sheet</a> (defined in css/html4.css),
|
|
an optional user defined style sheet, and all style sheets from the document,
|
|
and combines them to a nice list of parsed style rules (optimised for fast
|
|
lookup). The exact rules of how these style sheets should get applied to HTML
|
|
or XML documents can be found in the CSS2 specs.
|
|
<p>
|
|
Once we have this list, we can get a <a
|
|
href="rendering/render_style.h">RenderStyle object</a>
|
|
for every DOM element from the <a
|
|
href="css/cssstyleselector.h">CSSStyleSelector</a> by calling
|
|
"styleForElement(DOM::ElementImpl *)".
|
|
The style object describes in a compact form all the
|
|
<a href="css/css_properties.in">CSS properties</a>
|
|
that should get applied to the Node.
|
|
<p>
|
|
After that, a rendering tree gets built up. Using the style object, the
|
|
<a href="xml/dom_nodeimpl.h">DOM Node</a> creates an appropriate render object
|
|
(all these are defined in the rendering subdirectory) and adds it to the
|
|
rendering tree. This will give another tree like structure, that resembles in
|
|
it's general structure the DOM tree, but might have some significant
|
|
differences too. First of all, so called
|
|
<a href="http://www.w3.org/TR/REC-CSS2/visuren.html#anonymous-block-level">anonymous boxes</a> - (see
|
|
<a href="http://www.w3.org/TR/REC-CSS2/">CSS specs</a>) that
|
|
have no DOM counterpart might get inserted into the rendering tree to satisfy
|
|
DOM requirements. Second, the display property of the style affects which type
|
|
of rendering object is chosen to represent the current DOM object.
|
|
|
|
<p>
|
|
In the above example we would get the following rendering tree:
|
|
<pre>
|
|
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.")
|
|
</pre>
|
|
|
|
<p>
|
|
A call to of <a href="rendering/render_root.cpp">layout()</a> on the
|
|
<a href="rendering/render_root.h">RenderRoot </a> (the root of the rendering tree)
|
|
object causes the rendering tree to layout itself into the available space
|
|
(width) given by the the KHTMLView. After that, the drawContents() method of
|
|
KHTMLView can call RenderRoot->print() with appropriate parameters to actually
|
|
paint the document. This is not 100% correct, when parsing incrementally, but
|
|
is exactly what happens when you resize the document.
|
|
|
|
|
|
As you can see, the conversion to the rendering tree removed the head part of
|
|
the HTML code, and inserted an anonymous render object around the string "more
|
|
text". For an explanation why this is done, see the CSS specs.
|
|
<p>
|
|
|
|
<h2>Directory structure</h2>
|
|
|
|
A short explanation of the subdirectories in khtml.
|
|
<dl>
|
|
<dt><a href="css/">css:</a>
|
|
<dd>Contains all the stuff relevant to the CSS part of DOM Level2 (implementation classes only),
|
|
the <a href="css/cssparser.h">CSS parser</a>, and the stuff to create
|
|
RenderStyle object out of Nodes and the CSS style sheets.
|
|
<dt><a href="dom/">dom: </a>
|
|
<dd>Contains the external DOM API (the DOM interface classes) for all of the DOM
|
|
<dt><a href="ecma/">ecma:</a>
|
|
<dd>The javascript bindings to the DOM and khtml.
|
|
<dt><a href="html/">html:</a>
|
|
<dd>The html subpart of the DOM (implementation only), the HTML tokenizer and parser and a class
|
|
that defines the DTD to use for HTML (used mainly in the parser).
|
|
<dt><a href="java/">java:</a>
|
|
<dd>Java related stuff.
|
|
<dt><a href="misc/">misc:</a>
|
|
<dd>Some misc stuff needed in khtml. Contains the image loader, some misc definitions and the
|
|
decoder class that converts the incoming stream to unicode.
|
|
<dt><a href="rendering">rendering:</a>
|
|
<dd>Everything thats related to bringing a DOM tree with CSS declarations to the screen. Contains
|
|
the definition of the objects used in the rendering tree, the layouting code, and the RenderStyle objects.
|
|
<dt><a href="xml/">xml:</a>
|
|
<dd>The XML part of the DOM implementation, the xml tokenizer.
|
|
</dl>
|
|
|
|
<h2>Exception handling</h2>
|
|
To save on library size, C++-exceptions are only enabled in the dom/ subdirectory,
|
|
since exceptions are mandated by the DOM API. In the rest of KHTML's code,
|
|
we pass an error flag (usually called "exceptionCode"), and the class that
|
|
is part of dom/* checks for this flag and throws the exception.
|
|
|
|
<h2>Final words...</h2>
|
|
<p>
|
|
All the above is to give you a quick introduction into the way khtml brings an HTML/XML file to the screen.
|
|
It is by no way complete or even 100% correct. I left out many problems, I will perhaps add either on request
|
|
or when I find some time to do so. Let me name some of the missing things:
|
|
<ul>
|
|
<li>The decoder to convert the incoming stream to Unicode
|
|
<li>interaction with konqueror/applications
|
|
<li>javascript
|
|
<li>dynamic reflow and how to use the DOM to manipulate khtmls visual output
|
|
<li>mouse/event handling
|
|
<li>real interactions when parsing incrementally
|
|
<li>java
|
|
</ul>
|
|
|
|
Still I hope that this short introduction will make it easier for you to get a first hold of khtml and the way it works.
|
|
<p>
|
|
Now before I finish let me add a small <b>warning</b> and <b>advice</b> to all of you who plan hacking khtml themselves:
|
|
<p>
|
|
khtml is by now a quite big library and it takes some time to understand how it works. Don't let yourself get frustrated
|
|
if you don't immediately understand how it works. On the other hand, it is by now one of the libraries that
|
|
get used a lot, that probably has the biggest number of remaining bugs (even though it's sometimes hard to
|
|
know if some behavior is really a bug).
|
|
<blockquote>
|
|
Some parts of it's code are however <b>extremely touchy</b> (especially the layouting algorithms),
|
|
and making changes there (that might fix a bug on one web page) might introduce severe bugs.
|
|
All the people developing khtml have already spend huge amounts of time searching for such bugs,
|
|
that only showed up on some web pages, and thus were found only a week after the change that
|
|
introduced the bug was made. This can be very frustrating for us, and we'd appreciate if people
|
|
that are not completely familiar with khtml post changes touching these critical regions to kfm-devel
|
|
for review before applying them.
|
|
</blockquote>
|
|
|
|
<div style="margin-top: 2em; font-size: large;">
|
|
And now have fun hacking khtml.
|
|
<div style="margin-left: 10em; margin-bottom: 1em;">Lars</div>
|
|
</div>
|
|
</body>
|
|
</html>
|