In this article, I will add very basic painting code. This
code takes the tree of boxes from the layout module and turns them into an
array of pixels. This process is also known as “rasterization.”

Browsers usually implement rasterization with the help of graphics APIs and
libraries like Skia, Cairo, Direct2D, and so on. These APIs provide functions
for painting polygons, lines, curves, gradients, and text. For now, I’m going
to write my own rasterizer that can only paint one thing: rectangles.

Eventually I want to implement text rendering. At that point, I may
throw away this toy painting code and switch to a “real” 2D graphics library.
But for now, rectangles are sufficient to turn the output of my block layout
algorithm into pictures.

Catching Up

Since my last post, I’ve made some small changes to the code from previous
articles. These includes some minor refactoring, and some updates to keep the
code compatible with the latest Rust nightly builds. None of these changes
are vital to understanding the code, but if you’re curious, check the commit
history.

Building the Display List

Before painting, we will walk through the layout tree and build a display
list. This is a list of graphics operations like “draw a circle” or
“draw a string of text.” Or in our case, just “draw a rectangle.”

Why put commands into a display list, rather than execute them immediately?
The display list is useful for a several reasons. You can search it for items
that will be completely covered up by later operations, and remove them to
eliminate wasted painting. You can modify and re-use the display list in
cases where you know only certain items have changed. And you can use the
same display list to generate different types of output: for example, pixels
for displaying on a screen, or vector graphics for sending to a printer.

Robinson’s display list is a vector of DisplayCommands. For now there is only
one type of DisplayCommand, a solid-color rectangle:

To build the display list, we walk through the layout tree and generate a
series of commands for each box. First we draw the box’s background, then we
draw its borders and content on top of the background.

By default, HTML elements are stacked in the order they appear: If two
elements overlap, the later one is drawn on top of the earlier one. This is
reflected in our display list, which will draw the elements in the same order
they appear in the DOM tree. If this code supported the z-index
property, then individual elements would be able to override this stacking
order, and we’d need to sort the display list accordingly.

The background is easy. It’s just a solid rectangle. If no background color
is specified, then the background is transparent and we don’t need to generate
a display command.

fnrender_background(list:&mutDisplayList,layout_box:&LayoutBox){get_color(layout_box,"background").map(|color|list.push(DisplayCommand::SolidColor(color,layout_box.dimensions.border_box())));}// Return the specified color for CSS property `name`, or None if no color was specified.fnget_color(layout_box:&LayoutBox,name:&str)->Option<Color>{matchlayout_box.box_type{BlockNode(style)|InlineNode(style)=>matchstyle.value(name){Some(Value::ColorValue(color))=>Some(color),_=>None},AnonymousBlock=>None}}

The borders are similar, but instead of a single rectangle we draw
four—one for each edge of the box.

fnrender_borders(list:&mutDisplayList,layout_box:&LayoutBox){letcolor=matchget_color(layout_box,"border-color"){Some(color)=>color,_=>return// bail out if no border-color is specified};letd=&layout_box.dimensions;letborder_box=d.border_box();// Left borderlist.push(DisplayCommand::SolidColor(color,Rect{x:border_box.x,y:border_box.y,width:d.border.left,height:border_box.height,}));// Right borderlist.push(DisplayCommand::SolidColor(color,Rect{x:border_box.x+border_box.width-d.border.right,y:border_box.y,width:d.border.right,height:border_box.height,}));// Top borderlist.push(DisplayCommand::SolidColor(color,Rect{x:border_box.x,y:border_box.y,width:border_box.width,height:d.border.top,}));// Bottom borderlist.push(DisplayCommand::SolidColor(color,Rect{x:border_box.x,y:border_box.y+border_box.height-d.border.bottom,width:border_box.width,height:d.border.bottom,}));}

Next the rendering function will draw each of the box’s children, until the
entire layout tree has been translated into display commands.

Rasterization

Now that we’ve built the display list, we need to turn it into pixels by
executing each DisplayCommand. We’ll store the pixels in a Canvas:

Note that this code only works with opaque colors. If we added transparency
(by reading the opacity property, or adding support for rgba() values
in the CSS parser) then it would need to blend each new pixel with
whatever it’s drawn on top of.

Now we can put everything together in the paint function, which builds a
display list and then rasterizes it to a canvas:

// Paint a tree of LayoutBoxes to an array of pixels.fnpaint(layout_root:&LayoutBox,bounds:Rect)->Canvas{letdisplay_list=build_display_list(layout_root);letmutcanvas=Canvas::new(bounds.widthasusize,bounds.heightasusize);foritemindisplay_list{canvas.paint_item(&item);}returncanvas;}

Write a function to optimize the display list by culling items that are
completely outside of the canvas bounds.

If you’re familiar with OpenGL, write a hardware-accelerated painting
function that uses GL shaders to draw the rectangles.

To Be Continued…

Now that we’ve got basic functionality for each stage in our rendering
pipeline, it’s time to go back and fill in some of the missing
features—in particular, inline layout and text rendering. Future
articles may also add additional stages, like networking and scripting.

I’m going to give a short “Let’s build a browser engine!” talk at this
month’s Bay Area Rust Meetup. The meetup is at 7pm tomorrow
(Thursday, November 6) at Mozilla’s San Francisco office, and it will also
feature talks on Servo by my fellow Servo developers. Video of the talks will
be streamed live on Air Mozilla, and recordings will be published
there later.

In systems that rely heavily on “most popular” lists, like Reddit or Hacker
News, the rich get richer while the poor stay poor.
Since most people only look at the top of the charts, anything that’s not
already listed has a much harder time being seen. You need visibility to
get ratings, and you need ratings to get visibility.

Aggregators try to address this problem by promoting new items as well as
popular ones. But this is hard to do effectively. For example, the “new”
page at Hacker News gets only a fraction
of the front page’s traffic. Most users want to see the best content, not
wade through an unfiltered stream of links. Thus, very little input is available
to decide which links get promoted to the front page.

As an experiment, I wrote a userscript that uses the Hacker News
API to search for new or low-ranked links
and randomly insert just one or two of them into the front page. It’s also
available as a bookmarklet for those who can’t or don’t want to install the
user script.

This gives readers a chance to see and vote on links that they otherwise
wouldn’t, without altering their habits or wading through a ton of unfiltered
content. Each user will see just one or two links per visit, but thanks to
randomness a much larger number of links will be seen by the overall user
population. My belief, though I can’t prove it, is that widespread use of
this feature would improve the quality of the selection process.

The script isn’t perfect (search for FIXME in the source code for
some known issues), but it works well enough to try out the idea.
Unfortunately, the HN API doesn’t give access to all the data I’d like, and
sometimes the script won’t find any suitable links to insert. (You can look
at your browser’s console to see which which items were randomly inserted.)
Ideally, this feature would be built in to Hacker News—and any other
service that recommends “popular” items.

This article will continue the layout module that we started in Part 5. This
time, we’ll add the ability to lay out block boxes. These are boxes that are
stack vertically, such as headings and paragraphs.

To keep things simple, this code implements only normal flow:
no floats, no absolute positioning, and no fixed positioning.

Traversing the Layout Tree

The entry point to this code is the layout function, which takes a takes a
LayoutBox and calculates its dimensions. We’ll break this function into three
cases, and implement only one of them for now:

implLayoutBox{// Lay out a box and its descendants.fnlayout(&mutself,containing_block:Dimensions){matchself.box_type{BlockNode(_)=>self.layout_block(containing_block),InlineNode(_)=>{}// TODOAnonymousBlock=>{}// TODO}}// ...}

A block’s layout depends on the dimensions of its containing block. For
block boxes in normal flow, this is just the box’s parent. For the root
element, it’s the size of the browser window (or “viewport”).

You may remember from the previous article that a block’s width depends on its
parent, while its height depends on its children. This means that our code
needs to traverse the tree top-down while calculating widths, so it can lay
out the children after their parent’s width is known, and traverse bottom-up
to calculate heights, so that a parent’s height is calculated after its
children’s.

fnlayout_block(&mutself,containing_block:Dimensions){// Child width can depend on parent width, so we need to calculate// this box's width before laying out its children.self.calculate_block_width(containing_block);// Determine where the box is located within its container.self.calculate_block_position(containing_block);// Recursively lay out the children of this box.self.layout_block_children();// Parent height can depend on child height, so `calculate_height`// must be called *after* the children are laid out.self.calculate_block_height();}

This function performs a single traversal of the layout tree, doing width
calculations on the way down and height calculations on the way back up. A
real layout engine might perform several tree traversals, some top-down and
some bottom-up.

Calculating the Width

The width calculation is the first step in the block layout function, and also
the most complicated. I’ll walk through it step by step. To start, we need
the values of the CSS width property and all the left and right edge sizes:

This uses a helper function called lookup, which just tries a
series of values in sequence. If the first property isn’t set, it tries the
second one. If that’s not set either, it returns the given default value.
This provides an incomplete (but simple) implementation of shorthand
properties and initial values.

Note: This is similar to the following code in, say, JavaScript or Ruby:

margin_left=style["margin-left"]||style["margin"]||zero;

Since a child can’t change its parent’s width, it needs to make sure its own
width fits the parent’s. The CSS spec expresses this as a set of
constraints and an algorithm for solving them. The
following code implements that algorithm.

First we add up the margin, padding, border, and content widths. The
to_px helper method converts lengths to their numerical values. If
a property is set to 'auto', it returns 0 so it doesn’t affect the sum.

This is the minimum horizontal space needed for the box. If this isn’t equal
to the container width, we’ll need to adjust something to make it equal.

If the width or margins are set to 'auto', they can expand or contract to
fit the available space. Following the spec, we first check if the box is too
big. If so, we set any expandable margins to zero.

// If width is not auto and the total is wider than the container, treat auto margins as 0.ifwidth!=auto&&total>containing_block.content.width{ifmargin_left==auto{margin_left=Length(0.0,Px);}ifmargin_right==auto{margin_right=Length(0.0,Px);}}

If the box is too large for its container, it overflows the container. If
it’s too small, it will underflow, leaving extra space. We’ll calculate the
underflow—the amount of extra space left in the container. (If this
number is negative, it is actually an overflow.)

letunderflow=containing_block.content.width-total;

We now follow the spec’s algorithm for eliminating any
overflow or underflow by adjusting the expandable dimensions. If there are no
'auto' dimensions, we adjust the right margin. (Yes, this means the
margin may be negative in the case of an overflow!)

match(width==auto,margin_left==auto,margin_right==auto){// If the values are overconstrained, calculate margin_right.(false,false,false)=>{margin_right=Length(margin_right.to_px()+underflow,Px);}// If exactly one size is auto, its used value follows from the equality.(false,false,true)=>{margin_right=Length(underflow,Px);}(false,true,false)=>{margin_left=Length(underflow,Px);}// If width is set to auto, any other auto values become 0.(true,_,_)=>{ifmargin_left==auto{margin_left=Length(0.0,Px);}ifmargin_right==auto{margin_right=Length(0.0,Px);}ifunderflow>=0.0{// Expand width to fill the underflow.width=Length(underflow,Px);}else{// Width can't be negative. Adjust the right margin instead.width=Length(0.0,Px);margin_right=Length(margin_right.to_px()+underflow,Px);}}// If margin-left and margin-right are both auto, their used values are equal.(false,true,true)=>{margin_left=Length(underflow/2.0,Px);margin_right=Length(underflow/2.0,Px);}}

At this point, the constraints are met and any 'auto' values have been
converted to lengths. The results are the the used values for
the horizontal box dimensions, which we will store in the layout tree. You
can see the final code in layout.rs.

Positioning

The next step is simpler. This function looks up the remanining
margin/padding/border styles, and uses these along with the containing block
dimensions to determine this block’s position on the page.

fncalculate_block_position(&mutself,containing_block:Dimensions){letstyle=self.get_style_node();letd=&mutself.dimensions;// margin, border, and padding have initial value 0.letzero=Length(0.0,Px);// If margin-top or margin-bottom is `auto`, the used value is zero.d.margin.top=style.lookup("margin-top","margin",&zero).to_px();d.margin.bottom=style.lookup("margin-bottom","margin",&zero).to_px();d.border.top=style.lookup("border-top-width","border-width",&zero).to_px();d.border.bottom=style.lookup("border-bottom-width","border-width",&zero).to_px();d.padding.top=style.lookup("padding-top","padding",&zero).to_px();d.padding.bottom=style.lookup("padding-bottom","padding",&zero).to_px();d.content.x=containing_block.content.x+d.margin.left+d.border.left+d.padding.left;// Position the box below all the previous boxes in the container.d.content.y=containing_block.content.height+containing_block.content.y+d.margin.top+d.border.top+d.padding.top;}

Take a close look at that last statement, which sets the y position. This
is what gives block layout its distinctive vertical stacking behavior. For
this to work, we’ll need to make sure the parent’s content.height is updated
after laying out each child.

Children

Here’s the code that recursively lays out the box’s contents. As it loops
through the child boxes, it keeps track of the total content height. This is
used by the positioning code (above) to find the vertical position of the next
child.

fnlayout_block_children(&mutself){letd=&mutself.dimensions;forchildin&mutself.children{child.layout(*d);// Track the height so each child is laid out below the previous content.d.content.height=d.content.height+child.dimensions.margin_box().height;}}

The total vertical space taken up by each child is the height of its margin
box, which we calculate like so:

implDimensions{// The area covered by the content area plus its padding.fnpadding_box(self)->Rect{self.content.expanded_by(self.padding)}// The area covered by the content area plus padding and borders.fnborder_box(self)->Rect{self.padding_box().expanded_by(self.border)}// The area covered by the content area plus padding, borders, and margin.fnmargin_box(self)->Rect{self.border_box().expanded_by(self.margin)}}implRect{fnexpanded_by(self,edge:EdgeSizes)->Rect{Rect{x:self.x-edge.left,y:self.y-edge.top,width:self.width+edge.left+edge.right,height:self.height+edge.top+edge.bottom,}}}

For simplicity, this does not implement margin collapsing.
A real layout engine would allow the bottom margin of one box to overlap the
top margin of the next box, rather than placing each margin box completely
below the previous one.

The ‘height’ Property

By default, the box’s height is equal to the height of its contents. But if
the 'height' property is set to an explicit length, we’ll use that instead:

fncalculate_block_height(&mutself){// If the height is set to an explicit length, use that exact length.// Otherwise, just keep the value set by `layout_block_children`.ifletSome(Length(h,Px))=self.get_style_node().value("height"){self.dimensions.content.height=h;}}

And that concludes the block layout algorithm. You can now call layout() on
a styled HTML document, and it will spit out a bunch of rectangles with
widths, heights, margins, etc. Cool, right?

Exercises

Parallelize the layout process, and measure the effect on performance.

If you try the parallelization project, you may want to separate the width
calculation and the height calculation into two distinct passes. The top-down
traversal for width is easy to parallelize just by spawning a separate task
for each child. The height calculation is a little trickier, since you need
to go back and adjust the y position of each child after its siblings are
laid out.

To Be Continued…

Thank you to everyone who’s followed along this far!

These articles are taking longer and longer to write, as I journey further
into unfamiliar areas of layout and rendering. There will be a longer hiatus
before the next part as I experiment with font and graphics code, but I’ll
resume the series as soon as I can.

This article will begin the layout module, which takes the
style tree and translates it into a bunch of rectangles in a two-dimensional
space. This is a big module, so I’m going to split it into several articles.
Also, some of the code I share in this article may need to change as I write
the code for the later parts.

The layout module’s input is the style tree from Part 4, and its
output is yet another tree, the layout tree. This takes us one step further
in our mini rendering pipeline:

I’ll start by talking about the basic HTML/CSS layout model. If you’ve ever
learned to develop web pages you might be familiar with this already—but
it may look a bit different from the implementer’s point of view.

The Box Model

Layout is all about boxes. A box is a rectangular section of a web page.
It has a width, a height, and a position on the page. This rectangle is
called the content area because it’s where the box’s content is drawn. The
content may be text, image, video, or other boxes.

A box may also have padding, borders, and margins surrounding its
content area. The CSS spec has a diagram showing how all these
layers fit together.

Robinson stores a box’s content area and surrounding areas in the following
structure. [Rust note:f32 is a 32-bit floating point type.]

// CSS box model. All sizes are in px.structDimensions{// Position of the content area relative to the document origin:content:Rect,// Surrounding edges:padding:EdgeSizes,border:EdgeSizes,margin:EdgeSizes,}structRect{x:f32,y:f32,width:f32,height:f32,}structEdgeSizes{left:f32,right:f32,top:f32,bottom:f32,}

Block and Inline Layout

Note: This section contains diagrams that won't
make sense if you are reading them without the associated visual styles.
If you are reading this in a feed reader, try opening the
original page in a regular browser
tab. I also included text descriptions for those of you using screen readers
or other assistive technologies.

The CSS display property determines which type of box an
element generates. CSS defines several box types, each with its own layout
rules. I’m only going to talk about two of them: block and inline.

I’ll use this bit of pseudo-HTML to illustrate the difference:

<container><a></a><b></b><c></c><d></d></container>

Block boxes are placed vertically within their container, from top to bottom.

a,b,c,d{display:block;}

Description: The diagram below shows
four rectangles in a vertical stack.

a

b

c

d

Inline boxes are placed horizontally within their container, from left to
right. If they reach the right edge of the container, they will wrap around
and continue on a new line below.

a,b,c,d{display:inline;}

Description: The diagram below shows
boxes `a`, `b`, and `c` in a horizontal line from left to right, and box `d`
in the next line.

a

b

c

d

Each box must contain only block children, or only inline children. When
an DOM element contains a mix of block and inline children, the layout engine
inserts anonymous boxes to separate the two types. (These boxes
are “anonymous” because they aren’t associated with nodes in the DOM tree.)

In this example, the inline boxes b and c are surrounded by an anonymous
block box, shown in pink:

a{display:block;}b,c{display:inline;}d{display:block;}

Description: The diagram below shows
three boxes in a vertical stack. The first is labeled `a`; the second
contains two boxes in a horizonal row labeled `b` and `c`; the third box in
the stack is labeled `d`.

a

b

c

d

Note that content grows vertically by default. That is, adding children to
a container generally makes it taller, not wider. Another way to say this is
that, by default, the width of a block or line depends on its container’s
width, while the height of a container depends on its children’s heights.

This gets more complicated if you override the default values for properties
like width and height, and way more complicated if you want to support
features like vertical writing.

The Layout Tree

The layout tree is a collection of boxes. A box has dimensions, and it may
contain child boxes.

A box can be a block node, an inline node, or an anonymous block box. (This
will need to change when I implement text layout, because line wrapping can
cause a single inline node to split into multiple boxes. But it will do for
now.)

To build the layout tree, we need to look at the display property for each
DOM node. I added some code to the style module to get the display value
for a node. If there’s no specified value it returns the initial value,
'inline'.

enumDisplay{Inline,Block,None,}implStyledNode{// Return the specified value of a property if it exists, otherwise `None`.fnvalue(&self,name:&str)->Option<Value>{self.specified_values.get(name).map(|v|v.clone())}// The value of the `display` property (defaults to inline).fndisplay(&self)->Display{matchself.value("display"){Some(Keyword(s))=>match&*s{"block"=>Display::Block,"none"=>Display::None,_=>Display::Inline},_=>Display::Inline}}}

Now we can walk through the style tree, build a LayoutBox for each node, and
then insert boxes for the node’s children. If a node’s display property is
set to 'none' then it is not included in the layout tree.

If a block node contains an inline child, create an anonymous block box to
contain it. If there are several inline children in a row, put them all in
the same anonymous container.

// Where a new inline child should go.fnget_inline_container(&mutself)->&mutLayoutBox{matchself.box_type{InlineNode(_)|AnonymousBlock=>self,BlockNode(_)=>{// If we've just generated an anonymous block box, keep using it.// Otherwise, create a new one.matchself.children.last(){Some(&LayoutBox{box_type:AnonymousBlock,..})=>{}_=>self.children.push(LayoutBox::new(AnonymousBlock))}self.children.last_mut().unwrap()}}}

This is intentionally simplified in a number of ways from the standard CSS
box generation algorithm. For example, it doesn’t handle the
case where an inline box contains a block-level child. Also, it generates
an unnecessary anonymous box if a block-level node has only inline children.

To Be Continued…

Whew, that took longer than I expected. I think I’ll stop here for now, but
don’t worry: Part 6 is coming soon, and will cover block-level layout.

Once block layout is finished, we could jump ahead to the next stage of the
pipeline: painting! I think I might do that, because then we can finally see
the rendering engine’s output as pretty pictures instead of just numbers.

However, the pictures will just be a bunch of colored rectangles, unless we
finish the layout module by implementing inline layout and text layout. If I
don’t implement those before moving on to painting, I hope to come back to
them afterward.

This article will cover what the CSS standard calls assigning property
values, or what I call the style module.
This module takes DOM nodes and CSS rules as input, and matches them up to
determine the value of each CSS property for any given node.

This part doesn’t contain a lot of code, since I didn’t implement the really
complicated parts. However, I think what’s left is still quite interesting,
and I’ll also explain how some of the missing pieces can be implemented.

The Style Tree

The output of robinson’s style module is something I call the style tree.
Each node in this tree includes a pointer to a DOM node, plus its CSS property
values:

// Map from CSS property names to values.typePropertyMap=HashMap<String,Value>;// A node with associated style data.structStyledNode<'a>{node:&'aNode,// pointer to a DOM nodespecified_values:PropertyMap,children:Vec<StyledNode<'a>>,}

What’s with all the 'a stuff? Those are lifetimes,
part of how Rust guarantees that pointers are memory-safe without requiring
garbage collection. If you’re not working in Rust you can ignore them; they
aren’t critical to the code’s meaning.

We could add new fields to the dom::Node struct instead of creating a new
tree, but I wanted to keep style code out of the earlier “lessons.” This also
gives me an opportunity to talk about the parallel trees that inhabit most
rendering engines.

A browser engine module often takes one tree as input, and produces a
different but related tree as output. For example, Gecko’s layout
code takes a DOM tree and produces a frame tree, which is
then used to build a view tree. Blink and WebKit transform the DOM tree
into a render tree. Later stages in all these engines produce
still more trees, including layer trees and widget trees.

The pipeline for our toy browser engine will look something like this, after we
complete a few more stages:

In my implementation, each node in the DOM tree has exactly one node in the
style tree. But in a more complicated pipeline stage, several input nodes
could collapse into a single output node. Or an input node might expand into
several output nodes, or be skipped completely. For example, the style tree
could exclude elements whose display property is set to
'none'. (Instead I’ll remove these in the layout stage, because my code
turned out a bit simpler that way.)

Selector Matching

The first step in building the style tree is selector matching.
This will be very easy, since my CSS parser supports only simple
selectors. You can tell whether a simple selector matches an element just by
looking at the element itself. Matching compound selectors would require
traversing the DOM tree to look at the element’s siblings, parents, etc.

Rust note: This function uses the any method, which
returns true if an iterator contains an element that passes the provided
test. This is the same as the any function in Python (or
Haskell), or the some method in JavaScript.

Building the Style Tree

Next we need to traverse the DOM tree. For each element in the tree, we will
search the stylesheet for matching rules.

When comparing two rules that match the same element, we need to use the
highest-specificity selector from each match. Because our CSS parser stores
the selectors from most- to least-specific, we can stop as soon as we find a
matching one, and return its specificity along with a pointer to the rule.

To find all the rules that match an element we call filter_map, which
does a linear scan through the style sheet, checking every rule and throwing
out ones that don’t match. A real browser engine would speed this up by
storing the rules in multiple hash tables based on tag name, id, class, etc.

// Find all CSS rules that match the given element.fnmatching_rules<'a>(elem:&ElementData,stylesheet:&'aStylesheet)->Vec<MatchedRule<'a>>{stylesheet.rules.iter().filter_map(|rule|match_rule(elem,rule)).collect()}

Once we have the matching rules, we can find the specified values for the
element. We insert each rule’s property values into a HashMap. We sort the
matches by specificity, so the more-specific rules are processed after the
less-specific ones, and can overwrite their values in the HashMap.

// Apply styles to a single element, returning the specified values.fnspecified_values(elem:&ElementData,stylesheet:&Stylesheet)->PropertyMap{letmutvalues=HashMap::new();letmutrules=matching_rules(elem,stylesheet);// Go through the rules from lowest to highest specificity.rules.sort_by(|&(a,_),&(b,_)|a.cmp(&b));for(_,rule)inrules{fordeclarationin&rule.declarations{values.insert(declaration.name.clone(),declaration.value.clone());}}returnvalues;}

Now we have everything we need to walk through the DOM tree and build the
style tree. Note that selector matching works only on elements, so the
specified values for a text node are just an empty map.

// Apply a stylesheet to an entire DOM tree, returning a StyledNode tree.pubfnstyle_tree<'a>(root:&'aNode,stylesheet:&'aStylesheet)->StyledNode<'a>{StyledNode{node:root,specified_values:matchroot.node_type{Element(refelem)=>specified_values(elem,stylesheet),Text(_)=>HashMap::new()},children:root.children.iter().map(|child|style_tree(child,stylesheet)).collect(),}}

That’s all of robinson’s code for building the style tree. Next I’ll talk
about some glaring omissions.

The Cascade

Style sheets provided by the author of a web page are called author style
sheets. In addition to these, browsers also provide default
styles via user agent style sheets. And they may allow users
to add custom styles through user style sheets (like Gecko’s
userContent.css).

The cascade defines which of these three “origins” takes
precedence over another. There are six levels to the cascade: one for each
origin’s “normal” declarations, plus one for each origin’s !important
declarations.

Robinson’s style code does not implement the cascade; it takes only a single
style sheet. The lack of a default style sheet means that HTML elements will
not have any of the default styles you might expect. For example, the
<head> element’s contents will not be hidden unless you explicitly add this
rule to your style sheet:

head{display:none;}

Implementing the cascade should by fairly easy: Just track the origin of each
rule, and sort declarations by origin and importance in addition to
specificity. A simplified, two-level cascade should be enough to support the
most common cases: normal user agent styles and normal author styles.

Computed Values

Initial values are defaults for properties that aren’t specified in the
cascade. Computed values are based on specified values, but may have some
property-specific normalization rules applied.

Implementing these correctly requires separate code for each property, based
on its definition in the CSS specs. This work is necessary for a real-world
browser engine, but I’m hoping to avoid it in this toy project. In later
stages, code that uses these values will (sort of) simulate initial values by
using a default when the specified value is missing.

Used values and actual values are calculated during and after layout, which
I’ll cover in future articles.

Inheritance

If text nodes can’t match selectors, how do they get colors and fonts and
other styles? The answer is inheritance.

When a property is inherited, any node without a cascaded value will receive
its parent’s value for that property. Some properties, like 'color', are
inherited by default; others only if the cascade specifies the special
value 'inherit'.

My code does not support inheritance. To implement it, you could pass the
parent’s style data into the specified_values function, and use a hard-coded
lookup table to decide which properties should be inherited.

Style Attributes

Any HTML element can include a style attribute containing a list of CSS
declarations. There are no selectors, because these declarations
automatically apply only to the element itself.

<spanstyle="color: red; background: yellow;">

If you want to support the style attribute, make the specified_values function
check for the attribute. If the attribute is present, pass it to
parse_declarations from the CSS parser. Apply the resulting
declarations after the normal author declarations, since the attribute is
more specific than any CSS selector.

Exercises

In addition to writing your own selector matching and value assignment code,
for further exercise you can implement one or more of the missing pieces
discussed above, in your own project or a fork of robinson:

Cascading

Initial and/or computed values

Inheritance

The style attribute

Also, if you extended the CSS parser from Part 3 to include compound
selectors, you can now implement matching for those compound selectors.

To Be Continued…

Part 5 will introduce the layout module. I haven’t
finished the code for this yet, so there will be another delay before I can
start writing the article. I plan to split layout into at least two articles
(one for block layout and one for inline layout, probably).

In the meantime, I’d love to see anything you’ve created based on these
articles or exercises. If your code is online somewhere, feel free to add a
link to the comments below! So far I have seen Martin Tomasi’s Java
implementation and Pohl Longsine’s Swift version.

This article introduces code for reading Cascading Style Sheets (CSS).
As usual, I won’t try to cover everything in the spec. Instead, I tried to
implement just enough to illustrate some concepts and produce input for later
stages in the rendering pipeline.

Anatomy of a Stylesheet

Next I’ll walk through the css module from my toy browser engine,
robinson. The code is written in Rust, though the
concepts should translate pretty easily into other programming languages.
Reading the previous articles first might help you understand some the code
below.

A CSS stylesheet is a series of rules. (In the example stylesheet above,
each line contains one rule.)

structStylesheet{rules:Vec<Rule>,}

A rule includes one or more selectors separated by commas, followed by a
series of declarations enclosed in braces.

structRule{selectors:Vec<Selector>,declarations:Vec<Declaration>,}

A selector can be a simple selector, or it can be a chain of
selectors joined by combinators. Robinson supports only simple selectors
for now.

Note: Confusingly, the newer Selectors Level 3 standard uses
the same terms to mean slightly different things. In this article I’ll
mostly refer to CSS2.1. Although outdated, it’s a useful starting point
because it’s smaller and more self-contained (compared to CSS3, which is
split into myriad specs that depend on each other and CSS2.1).

In robinson, a simple selector can include a tag name, an ID prefixed by
'#', any number of class names prefixed by '.', or some combination of the
above. If the tag name is empty or '*' then it is a “universal selector” that
can match any tag.

There are many other types of selector (especially in CSS3), but this will do
for now.

All other CSS syntax is unsupported, including @-rules, comments, and any
selectors/values/units not mentioned above.

Parsing

CSS has a regular grammar, making it easier to parse correctly
than its quirky cousin HTML. When a standards-compliant CSS parser encounters
a parse error, it discards the unrecognized part of the stylesheet
but still processes the remaining portions. This is useful because it allows
stylesheets to include new syntax but still produce well-defined output in
older browsers.

Robinson uses a very simplistic (and totally not standards-compliant)
parser, built the same way as the HTML parser from Part 2.
Rather than go through the whole thing line-by-line again, I’ll just paste in
a few snippets. For example, here is the code for parsing a single selector:

Note the lack of error checking. Some malformed input like ### or *foo*
will parse successfully and produce weird results. A real CSS parser would
discard these invalid selectors.

Specificity

Specificity is one of the ways a rendering engine decides
which style overrides the other in a conflict. If a stylesheet contains two
rules that match an element, the rule with the matching selector of higher
specificity can override values from the one with lower specificity.

The specificity of a selector is based on its components. An ID selector is
more specific than a class selector, which is more specific than a tag
selector. Within each of these “levels,” more selectors beats fewer.

(If we supported chained selectors, we could calculate the specificity of a
chain just by adding up the specificities of its parts.)

The selectors for each rule are stored in a sorted vector, most-specific
first. This will be important in matching, which I’ll cover in the next
article.

// Parse a rule set: `<selectors> { <declarations> }`.fnparse_rule(&mutself)->Rule{Rule{selectors:self.parse_selectors(),declarations:self.parse_declarations()}}// Parse a comma-separated list of selectors.fnparse_selectors(&mutself)->Vec<Selector>{letmutselectors=Vec::new();loop{selectors.push(Selector::Simple(self.parse_simple_selector()));self.consume_whitespace();matchself.next_char(){','=>{self.consume_char();self.consume_whitespace();}'{'=>break,// start of declarationsc=>panic!("Unexpected character {} in selector list",c)}}// Return selectors with highest specificity first, for use in matching.selectors.sort_by(|a,b|b.specificity().cmp(&a.specificity()));returnselectors;}

The rest of the CSS parser is fairly straightforward. You can read the whole
thing on GitHub. And if you didn’t already do it for Part 2,
this would be a great time to try out a parser generator. My hand-rolled
parser gets the job done for simple example files, but it has a lot of hacky
bits and will fail badly if you violate its assumptions. Someday I might
replace it with one built on rust-peg or similar.

Exercises

As before, you should decide which of these exercises you want to do, and
skip the rest:

Implement your own simplified CSS parser and specificity calculation.

Extend robinson’s CSS parser to support more values, or one or more
selector combinators.

Extend the CSS parser to discard any declaration that contains a parse
error, and follow the error handling rules to resume parsing
after the end of the declaration.

Make the HTML parser pass the contents of any <style> nodes to the CSS
parser, and return a Document object that includes a list of Stylesheets in
addition to the DOM tree.

Shortcuts

Just like in Part 2, you can skip parsing by hard-coding CSS data structures
directly into your program, or by writing them in an alternate format like
JSON that you already have a parser for.

To Be Continued…

The next article will introduce the style module. This is where
everything starts to come together, with selector matching to apply CSS styles
to DOM nodes.

The pace of this series might slow down soon, since I’ll be busy later this
month and I haven’t even written the code for some of the upcoming articles.
I’ll keep them coming as fast as I can!

This article is about parsing HTML source code to produce a tree of
DOM nodes. Parsing is a fascinating topic, but I don’t have the time or
expertise to give it the introduction it deserves. You can get a detailed
introduction to parsing from any good course or book on compilers.
Or get a hands-on start by going through the documentation for a parser
generator that works with your chosen programming language.

HTML has its own unique parsing algorithm. Unlike parsers for most
programming languages and file formats, the HTML parsing algorithm does not
reject invalid input. Instead it includes specific error-handling
instructions, so web browsers can agree on how to display every web page, even
ones that don’t conform to the syntax rules. Web browsers have to do this to
be usable: Since non-conforming HTML has been supported since the early days
of the web, it is now used in a huge portion of existing web pages.

A Simple HTML Dialect

I didn’t even try to implement the standard HTML parsing algorithm. Instead
I wrote a basic parser for a tiny subset of HTML syntax. My parser can handle
simple pages like this:

At each stage of this project I’m writing more or less the minimum code
needed to support the later stages. But if you want to learn more about
parsing theory and tools, you can be much more ambitious in your own project!

Example Code

Next, let’s walk through my toy HTML parser, keeping in mind that this is just
one way to do it (and probably not the best way). Its structure is based
loosely on the tokenizer module from Servo’s cssparser library. It
has no real error handling; in most cases, it just aborts when faced with
unexpected syntax. The code is in Rust, but I hope it’s fairly
readable to anyone who’s used similar-looking languages like Java, C++, or C#.
It makes use of the DOM data structures from part 1.

The parser stores its input string and a current position within the string.
The position is the index of the next character we haven’t processed yet.

structParser{pos:usize,// "usize" is an unsigned integer, similar to "size_t" in Cinput:String,}

We can use this to implement some simple methods for peeking at the next
characters in the input:

implParser{// Read the next character without consuming it.fnnext_char(&self)->char{self.input.char_at(self.pos)}// Do the next characters start with the given string?fnstarts_with(&self,s:&str)->bool{self.input[self.pos..].starts_with(s)}// Return true if all input is consumed.fneof(&self)->bool{self.pos>=self.input.len()}// ...}

Rust strings are stored as UTF-8 byte arrays. To go to the next
character, we can’t just advance by one byte. Instead we use char_range_at
which correctly handles multi-byte characters. (If our string used fixed-width
characters, we could just increment pos by 1.)

// Return the current character, and advance to the next character.fnconsume_char(&mutself)->char{letrange=self.input.char_range_at(self.pos);self.pos=range.next;returnrange.ch;}

Often we will want to consume a string of consecutive characters. The
consume_while method consumes characters that meet a given condition, and
returns them as a string. This method’s argument is a function that takes a
char and returns a bool.

We can use this to ignore a sequence of space characters, or to consume a
string of alphanumeric characters:

// Consume and discard zero or more whitespace characters.fnconsume_whitespace(&mutself){self.consume_while(CharExt::is_whitespace);}// Parse a tag or attribute name.fnparse_tag_name(&mutself)->String{self.consume_while(|c|matchc{'a'...'z'|'A'...'Z'|'0'...'9'=>true,_=>false})}

Now we’re ready to start parsing HTML. To parse a single node, we look at its
first character to see if it is an element or a text node. In our simplified
version of HTML, a text node can contain any character except <.

// Parse a single node.fnparse_node(&mutself)->dom::Node{matchself.next_char(){'<'=>self.parse_element(),_=>self.parse_text()}}// Parse a text node.fnparse_text(&mutself)->dom::Node{dom::text(self.consume_while(|c|c!='<'))}

An element is more complicated. It includes opening and closing tags, and
between them any number of child nodes:

// Parse a single element, including its open tag, contents, and closing tag.fnparse_element(&mutself)->dom::Node{// Opening tag.assert!(self.consume_char()=='<');lettag_name=self.parse_tag_name();letattrs=self.parse_attributes();assert!(self.consume_char()=='>');// Contents.letchildren=self.parse_nodes();// Closing tag.assert!(self.consume_char()=='<');assert!(self.consume_char()=='/');assert!(self.parse_tag_name()==tag_name);assert!(self.consume_char()=='>');returndom::elem(tag_name,attrs,children);}

Parsing attributes is pretty easy in our simplified syntax. Until we reach
the end of the opening tag (>) we repeatedly look for a name followed by =
and then a string enclosed in quotes.

// Parse a single name="value" pair.fnparse_attr(&mutself)->(String,String){letname=self.parse_tag_name();assert!(self.consume_char()=='=');letvalue=self.parse_attr_value();return(name,value);}// Parse a quoted value.fnparse_attr_value(&mutself)->String{letopen_quote=self.consume_char();assert!(open_quote=='"'||open_quote=='\'');letvalue=self.consume_while(|c|c!=open_quote);assert!(self.consume_char()==open_quote);returnvalue;}// Parse a list of name="value" pairs, separated by whitespace.fnparse_attributes(&mutself)->dom::AttrMap{letmutattributes=HashMap::new();loop{self.consume_whitespace();ifself.next_char()=='>'{break;}let(name,value)=self.parse_attr();attributes.insert(name,value);}returnattributes;}

To parse the child nodes, we recursively call parse_node in a loop until we
reach the closing tag. This function returns a Vec, which is Rust’s name
for a growable array.

Finally, we can put this all together to parse an entire HTML document into a
DOM tree. This function will create a root node for the document if it
doesn’t include one explicitly; this is similar to what a real HTML parser
does.

// Parse an HTML document and return the root element.pubfnparse(source:String)->dom::Node{letmutnodes=Parser{pos:0,input:source}.parse_nodes();// If the document contains a root element, just return it. Otherwise, create one.ifnodes.len()==1{nodes.swap_remove(0)}else{dom::elem("html".to_string(),HashMap::new(),nodes)}}

That’s it! The entire code for the robinson HTML parser. The whole
thing weighs in at just over 100 lines of code (not counting blank lines and
comments). If you use a good library or parser generator, you can probably
build a similar toy parser in even less space.

Exercises

Here are a few alternate ways to try this out yourself. As before, you can
choose one or more of them and ignore the others.

Build a parser (either “by hand” or with a library or parser generator)
that takes a subset of HTML as input and produces a tree of DOM nodes.

Modify robinson’s HTML parser to add some missing features, like comments.
Or replace it with a better parser, perhaps built with a library or
generator.

Create an invalid HTML file that causes your parser (or mine) to fail.
Modify the parser to recover from the error and produce a DOM tree for your
test file.

Shortcuts

If you want to skip parsing completely, you can build a DOM tree
programmatically instead, by adding some code like this to your program (in
pseudo-code; adjust it to match the DOM code you wrote in Part 1):

The full series will describe the code I’ve written, and show how you can make
your own. But first, let me explain why.

You’re building a what?

Let’s talk terminology. A browser engine is the portion of a web browser
that works “under the hood” to fetch a web page from the internet, and
translate its contents into forms you can read, watch, hear, etc. Blink,
Gecko, WebKit, and Trident are browser engines. In contrast, the the
browser’s own UI—tabs, toolbar, menu and such—is called the
chrome. Firefox and SeaMonkey are two browsers with different chrome but
the same Gecko engine.

A browser engine includes many sub-components: an HTTP client, an HTML parser,
a CSS parser, a JavaScript engine (itself composed of parsers, interpreters,
and compilers), and much more. Those components involved in parsing web
formats like HTML and CSS and translating them into what you see on-screen are
sometimes called the layout engine or rendering engine.

Why a “toy” rendering engine?

A full-featured browser engine is hugely complex. Blink, Gecko,
WebKit—these are millions of lines of code each. Even younger,
simpler rendering engines like Servo and WeasyPrint are each
tens of thousands of lines. Not the easiest thing for a newcomer to
comprehend!

Speaking of hugely complex software: If you take a class on compilers or
operating systems, at some point you will probably create or modify a “toy”
compiler or kernel. This is a simple model designed for learning; it may
never be run by anyone besides the person who wrote it. But making a toy
system is a useful tool for learning how the real thing works. Even if you
never build a real-world compiler or kernel, understanding how they work can
help you make better use of them when writing your own programs.

So, if you want to become a browser developer, or just to understand what
happens inside a browser engine, why not build a toy one? Like a toy compiler
that implements a subset of a “real” programming language, a toy rendering
engine could implement a small subset of HTML and CSS. It won’t replace the
engine in your everyday browser, but should nonetheless illustrate the basic
steps needed for rendering a simple HTML document.

Try this at home.

I hope I’ve convinced you to give it a try. This series will be easiest to
follow if you already have some solid programming experience and know some
high-level HTML and CSS concepts. However, if you’re just getting started
with this stuff, or run into things you don’t understand, feel free to ask
questions and I’ll try to make it clearer.

Before you start, a few remarks on some choices you can make:

On Programming Languages

You can build a toy layout engine in any programming language. Really! Go
ahead and use a language you know and love. Or use this as an excuse to learn
a new language if that sounds like fun.

If you want to start contributing to major browser engines like Gecko or
WebKit, you might want to work in C++ because it’s the main language used in
those engines, and using it will make it easier to compare your code to
theirs.

My own toy project, robinson, is written in Rust. I’m part
of the Servo team at Mozilla, so I’ve become very fond of Rust programming.
Plus, one of my goals with this project is to understand more of Servo’s
implementation. Robinson sometimes uses simplified versions of Servo’s data
structures and code.

On Libraries and Shortcuts

In a learning exercise like this, you have to decide whether it’s “cheating”
to use someone else’s code instead of writing your own from scratch. My
advice is to write your own code for the parts that you really want to
understand, but don’t be shy about using libraries for everything else.
Learning how to use a particular library can be a worthwhile exercise in
itself.

I’m writing robinson not just for myself, but also to serve as example code for
these articles and exercises. For this and other reasons, I want it to be as
tiny and self-contained as possible. So far I’ve used no external code except
for the Rust standard library. (This also side-steps the minor hassle of
getting multiple dependencies to build with the same version of Rust while the
language is still in development.) This rule isn’t set in stone, though.
For example, I may decide later to use a graphics library rather than write my
own low-level drawing code.

Another way to avoid writing code is to just leave things out. For example,
robinson has no networking code yet; it can only read local files. In a toy
program, it’s fine to just skip things if you feel like it. I’ll point out
potential shortcuts like this as I go along, so you can bypass steps that
don’t interest you and jump straight to the good stuff. You can always fill
in the gaps later if you change your mind.

First Step: The DOM

Are you ready to write some code? We’ll start with something small: data
structures for the DOM. Let’s look at robinson’s dom module.

The DOM is a tree of nodes. A node has zero or more children. (It also has
various other attributes and methods, but we can ignore most of those for now.)

structNode{// data common to all nodes:children:Vec<Node>,// data specific to each node type:node_type:NodeType,}

There are several node types, but for now we will ignore most of them
and say that a node is either an Element or a Text node. In a language with
inheritance these would be subtypes of Node. In Rust they can be an enum
(Rust’s keyword for a “tagged union” or “sum type”):

enumNodeType{Text(String),Element(ElementData),}

An element includes a tag name and any number of attributes, which can be
stored as a map from names to values. Robinson doesn’t support namespaces, so
it just stores tag and attribute names as simple strings.

And that’s it! A full-blown DOM implementation would include a lot more data
and dozens of methods, but this is all we need to get started.

Exercises

These are just a few suggested ways to follow along at home. Do the
exercises that interest you and skip any that don’t.

Start a new program in the language of your choice, and write code to
represent a tree of DOM text nodes and elements.

Install the latest version of Rust, then download and build
robinson. Open up dom.rs and extend NodeType to include
additional types like comment nodes.

Write code to pretty-print a tree of DOM nodes.

In the next article, we’ll add a parser that turns HTML source
code into a tree of these DOM nodes.

References

For much more detailed information about browser engine internals, see Tali
Garsiel’s wonderful How Browsers Work and its links to further
resources.

For example code, here’s a short list of “small” open source web rendering
engines. Most of them are many times bigger than robinson, but still way
smaller than Gecko or WebKit. WebWhirr, at 2000 lines of code, is the only
other one I would call a “toy” engine.

"While Portal has been critically praised in part as being subversively feminist, the same can’t really be said about Mirror’s Edge– and it has more to do with the presentation than anything else." (But see the comments for some strong critiques, and some retractions by the author.)

"The Ultra Gleeper solves or avoids the problems that give recommendation engines a bad reputation. It won't give you a lot of links you've already seen, because it knows about your subscriptions and what they've posted. It won't just recap the most popular links of the day, because its indie rock algorithm distrusts excessive popularity."

"The Theoretical Minimum is a series of Stanford Continuing Studies courses taught by world renowned physicist Leonard Susskind. These courses collectively teach everything required to gain a basic understanding of each area of modern physics including all of the fundamental mathematics."

It was a busy year. Nora finished kindergarten and started first grade. She's become an avid reader, finishing over 200 books (mostly chapter books she read herself!) during the library's summer reading program, and winning our branch's drawing to go to the "Breakfast of Champions" in the Space Needle along with the winners and librarians from all the Seattle Public Library branches.

During summer vacation we also took a family trip to Europe, and got to meet our new Norwegian cousins and learn new languages.

I started my fourth year at Mozilla and second year on the "Window 8 Style" Firefox project. I've taken steps toward managing/leading an engineering team, like handling the weekly one-on-one meetings for several of my minions teammates, and taking a larger role in product decisions.

Mozilla has declared a long company-wide end-of-year break this month, so I've had a relaxing Christmas at home, mostly reading books and visiting family. Happy New Year, everyone.

Last spring I spent some of my spare time improving the automated script
that detects regressions in Talos and other Firefox performance data.
I’m finally writing up some of that work in case it’s useful or interesting to
anyone else.

Talos is a system for running performance benchmarks; we use it to run a suite
of benchmarks every time a change is pushed to the Firefox source repository.
The Talos test harness reports these results to the graph server which
stores them and can plot the recorded data to show how it changes over time.

Like most performance measurements, Talos benchmarks can be noisy. We need to
use statistics to separate signal from noise. To determine whether a change
to the source code caused a change in the benchmark results, an automated
script takes multiple datapoints from before and after each push. It computes
the average and standard deviation of the “before” datapoints and the “after”
datapoints, and uses a Student’s t-test to estimate the likelihood that
the datasets are significantly different. If the t-test exceeds a certain
threshold, the script sends email to the author(s) of the responsible patches
and to the dev-tree-management mailing list.

By nature, these statistical estimates can never be 100% certain. However, we
need to make sure that they are correct as often as possible. False negatives
mean that real regressions go undetected. But false positives will generate
extra work, and may cause developers to ignore future regression alerts. I
started inspecting graph server data and regression alerts by hand, recording
and investigating any false negatives or false positives I found, and filed
bugs to fix the causes of those errors.

Some of these were straightforward implementation bugs, like one where an
infinite t-test score (near certain likelihood of regression) was treated
as a zero score (no regression at all). Others involved tuning the
number of datapoints and the threshold for sending alerts. I also added some
unit tests and regression tests, including some past datasets with known
regressions. Now we can ensure that future revisions of the script still
identify the correct regression points in these datasets.

Some fixes required more involved changes to the analysis. For example,
if one code change actually caused a regression, the pushes right before or
after that change will also appear somewhat likely to be responsible for the
regression (because they will also have large differences in average score
between their “before” and “after” windows). If multiple pushes in a row had
t-test scores over the threshold, the script used to send an alert for the
first of those pushes, even if it was not the most likely culprit. Now the
script blames the push with the highest t-test score, which is almost
always the guilty party. This change had the biggest impact in reducing
incorrect regression alerts.

After those changes, there was still one common cause of false alarms that I
found. The regression analyzer compares the 12 datapoints before each push to
the 12 datapoints after it. But these 12-point moving averages could change
suddenly not just at the point where a regression happened, but also at an
unrelated point that happens to be 12 pushes earlier or later. This caused
spooky action at a distance where a regression in one push would cause a
false alarm in a completely different push. To fix this, we now compute
weighted averages with “triangular” weighting functions that give more weight
to the point being analyzed, and fall off gradually with increasing distance
from that point. This smooths out changes at the opposite ends of the moving
windows.

There are still occasional errors in regression detection, but as far as I can
tell most of them are caused by genuinely misleading random noise or
bimodal data. If you see any problems with regression emails, please
file a bug (and CC :mbrubeck) and we’ll take a look at it. And if you’d
like to help reduce useless alert emails, maybe you’d like to help fix
bug 914756…

“Firefox for Metro” is our project to build a new Firefox user interface
designed for touch-screen devices running Windows 8. (“Metro” was Microsoft’s
code name for the new touch-friendly user interface mode in Windows 8.) I’m
part of the small team working on this project.

For the past year we’ve been fairly quiet, partly because the browser has been
under heavy construction and not really suitable for regular use. It started
as a fork of the old Fennec (mobile Firefox) UI, plus a new port of
Gecko’s widget layer to Microsoft’s WinRT API. We spent part of that
time ripping out and rebuilding old Fennec features to make them work on
Windows 8, and finding and fixing bugs in the new widget code. More recently
we’ve been focused on reworking the touch input layer. With a ton of help
from the graphics team, we replaced Fennec’s old multi-process JavaScript
touch support with a new off-main-thread compositing backend for the
Windows Direct3D API, and added WinRT support to the async pan/zoom
module that implements touch scrolling and zooming on Firefox OS.

All this work is still underway, but in the past week we finally reached a
tipping point where I’m able to use Firefox for Metro for most of my everyday
browsing. There are still bugs, and we are still actively working on
performance and completing the UI work, but I’m now finding very few cases
where I need to switch to another browser because of a problem with Firefox
for Metro. If you are using Window 8 (especially on a touch-screen PC) and
are the type of brave person who uses Firefox nightly builds, this would be a
great time to try Metro-style Firefox and let us know what you think!

Looking to the future, here are some of our remaining development priorities
for the first release of Firefox for Metro:

Improve the installation and first-run experience, to help users figure out
how to use the new UI and switch between “Metro” and desktop modes. (Our UX
designer has user testing planned to help identify issues here and
throughout the product.)

Fix any performance and rendering issues with scrolling and zooming,
and add support for double-tap to zoom in on a specific page element.

Make the Metro and desktop interfaces share a profile, so
they can seamlessly use the same bookmarks and other data without connecting
to a Firefox Sync account.

And here are some things that I hope we can spend more time on once that work
has shipped:

Improve the experience on pages with plugins, which currently require
the user to switch to the desktop Firefox interface (bug 936907).

Implement a “Reader Mode,” like Firefox for Android. (A pair of students have
started working on this project, and their work should also be useful
for adding Reader Mode to Firefox for desktop.)