Today I think I’ll describe WebKit’s most central task: to render the text it’s parsed from a webpage.
There’s a fair bit to this, most but not all of which I’d consider necessary in order to reach an international audience. I’ll start from the DocumentLoader which manages the webpage’s download as described in a previous toot thread, and try to skip over other aspects this code touches.
DocumentLoader streams it’s responses into via DocumentWriter to a DocumentParser. This parser is created by the DOMImplementation per the request of DocumentWriter, the latter of which also handles clearing the page amongst other things upon starting the stream.
Specifically it constructs a HTMLDocumentParser, which hands the text off to a tokenizer to split the document by HTML tags with attributes attached. This tokenizer is implemented as a state machine using C macros and a switch branch.
WebKit then sends those “tags” and raw text to the “HTMLTreeBuilder”. This structures the tree differently based on it’s current state and the tag being inserted into the document tree.
To do the actual work it hands the tag on to the HTMLConstructionSite, which in turn wraps HTMLElementFactory (a Perl-compiled C++ hashmap) and the HTMLElement instances it creates.
NOTE: I’m not particularly fond of the parts of the HTML standard which calls for these components, they’re overly complex.
That gives WebKit the DOM tree it exposes to JavaScript, but it lacks all the details on how to render the text so it applies CSS. So the StyleResolver associated with the document or containing “shadow DOM” resolves CSS via it’s ElementRuleCollector, before iterating over all cascaded properties to construct a fixing styles up both as it does so and after.
I’ll cover the details of CSS next time.
The Document will then use a RenderTreeUpdater to translate this into “RenderObjects”.
To translate from a “style tree” update (which it got from the DOM) into a “render tree”, RenderTreeUpdater first finds all the updated subtrees and for each constructs/updates RenderObjects appropriately. Text nodes translate directly to RenderText objects, and otherwise it’s determined by the element.
Most of the time this uses the default implementation of having RenderElement choose a class to instantiate based on it’s display: property.
As it does so it fixes up the tree and dirties it.
Now that we’ve got a Render Tree we need to lay it out. So the containing block will (amongst other things) decide whether all it’s children are inline or block.This is normalized during render tree construction.
If they’re all blocks, layout is simple. As for inline it first constructs an iterator over all leaf (text) nodes for each one lays it out on a line. Filling each line with text after asking the containing block about any floats and before applying text justification.
That iterator is preinitialized with an array of all leaf nodes, and sizes/splits the text itself.
The splitting is done via a wrapper around strings representing slices thereof. And the sizing is done by calling out to one of three algorithms for trivial, simple, and complex cases. The latter of which are handed to platform specific logic around the FreeText font parsing library.
Similar platform-specific logic is also used in the seperate rendering pass.