Subscribe: Advogato blog for mbrubeck
Added By: Feedage Forager Feedage Grade B rated
Language: English
block  box  browser engine  browser  code  css  display  engine  input  layout  list  part  selector  simple  style  tree 
Rate this Feed
Rate this feedRate this feedRate this feedRate this feedRate this feed
Rate this feed 1 starRate this feed 2 starRate this feed 3 starRate this feed 4 starRate this feed 5 star

Comments (0)

Feed Details and Statistics Feed Statistics
Preview: Advogato blog for mbrubeck

Advogato blog for mbrubeck

Advogato blog for mbrubeck

Published: Wed, 28 Jun 2017 00:43:50 GMT


Let's build a browser engine! Part 7: Painting 101

Wed, 5 Nov 2014 18:12:46 GMT

I’m returning at last to my series on building a simple HTML rendering engine: Part 1: Getting started Part 2: HTML Part 3: CSS Part 4: Style Part 5: Boxes Part 6: Block layout Part 7: Painting 101 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 add support for text rendering. At that point, I may throw away this 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: type DisplayList = Vec; enum DisplayCommand { SolidColor(Color, Rect), // insert more commands here } 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. fn build_display_list(layout_root: &LayoutBox) -> DisplayList { let mut list = Vec::new(); render_layout_box(&mut list, layout_root); return list; } fn render_layout_box(list: &mut DisplayList, layout_box: &LayoutBox) { render_background(list, layout_box); render_borders(list, layout_box); // TODO: render text for child in layout_box.children.iter() { render_layout_box(list, child); } } 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. fn render_background(list: &mut DisplayList, layout_box: &LayoutBox) { get_color(layout_box, "background").map(|color| list.push(SolidColor(color, layou[...]

A little randomness for Hacker News

Wed, 22 Oct 2014 22:11:48 GMT

This is a little thing I made to try out an idea for improving the ranking of items on Hacker News, Reddit, and similar sites. In systems that rely heavily on “Most Popular” charts, the rich get richer while the poor tend to stay poor. That is, most people will only look at and rate the items that are featured in the charts. Anything that’s not already in the list will have a much harder time getting votes or ratings. You need visibility to get ratings, and you need ratings to get visibility. Aggregators try to address this problem by promoting the newest items as well as the most popular ones. But this is hard to do well. From what I can tell, 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. So the selection of links that make it to the front page is based on very little input. So 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. When the script runs, it will log the items it inserts to the JavaScript console. Install user script (may require a browser extension) It’s also available as a bookmarklet for those who can’t or don’t want to install the user script: Randomize HN (right-click to bookmark) This gives HN users the chance to see and upvote links that they otherwise wouldn’t, without altering their habits or wading through a ton of unfiltered dreck. The randomness means that each user of the script will see a different set of links. My belief, though I can’t prove it, is that widespread use of this feature would provide additional input that 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. What I really want to see is a bit of built-in randomness in every system that recommends “popular” items.[...]

Let's build a browser engine! Part 6: Block layout

Thu, 18 Sep 2014 05:15:50 GMT

Welcome back to my series on building a toy HTML rendering engine: Part 1: Getting started Part 2: HTML Part 3: CSS Part 4: Style Part 5: Boxes Part 6: Block layout This article will continue the layout module that we started previously. 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: impl LayoutBox { /// Lay out a box and its descendants. fn layout(&mut self, containing_block: Dimensions) { match self.box_type { BlockNode(_) => self.layout_block(containing_block), InlineNode(_) => {} // TODO AnonymousBlock => {} // 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. fn layout_block(&mut self, 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 to know the values of the CSS width property and all the left and right edge size properties: fn calculate_block_width(&mut self, containing_block: Dimensions) { let style = self.get_style_node(); // `width` has initial value `auto`. let auto = Keyword("auto".to_string()); let mut width = style.value("width").unwrap_or(auto.clone()); // margin, border, and padding have initial value 0. let zero = Length(0.0, Px); let mut margin_left = style.lookup("margin-left", "margin", &zero); let mut margin_right = style.lookup("margin-right", "margin", &zero); let border_left = style.lookup("border-left-width", "border-width", &zero); let border_right = style.lookup("border-right-width", "border-width", &zero); let padding_left = style.lookup("padding-left", "padding", &zero); let padding_right = style.lookup("padding-right", "padding", &zero); // ... } This uses a helper function called lookup, which just tries a series of values in sequence. If the first propert[...]

Let's build a browser engine! Part 5: Boxes

Tue, 9 Sep 2014 00:10:45 GMT

This is the latest in a series of articles about writing a simple HTML rendering engine: Part 1: Getting started Part 2: HTML Part 3: CSS Part 4: Style Part 5: Boxes 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. struct Dimensions { // Top left corner of the content area, relative to the document origin: x: f32, y: f32, // Content area size: width: f32, height: f32, // Surrounding edges: padding: EdgeSizes, border: EdgeSizes, margin: EdgeSizes, } struct EdgeSizes { 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: 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 bo[...]

Let's build a browser engine! Part 4: Style

Sat, 23 Aug 2014 23:12:56 GMT

Welcome back to my series on building your own toy browser engine. If you’re just tuning in, you can find the previous episodes here: Part 1: Getting started Part 2: HTML Part 3: CSS Part 4: Style 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’ve left out all 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 the 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. type PropertyMap = HashMap; /// A node with associated style data. struct StyledNode<'a> { node: &'a Node, // pointer to a DOM node specified_values: PropertyMap, children: Vec>, } What’s with all the 'a stuff? These are lifetime annotations, part of how Rust guarantees that pointers are memory-safe without requiring garbage collection. If you are not working in Rust you can safely ignore them; they aren’t critical to the meaning of this code. We could add style information directly to the dom::Node struct from Part 1 instead, but I wanted to keep this code out of the earlier “lessons.” This is also a good opportunity to talk about the parallel trees that inhabit most layout 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 engines will look something like this after we complete a few more stages: In my implementation, each node in the DOM tree produces 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 one 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 this will happen 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. fn matches(elem: &ElementData, selector: &Selector) -> bool { match *selector { Simple(ref simple_selector) => matches_simple_selector(elem, simple_selector) } } To help, we’ll add some convenient ID and class accessors to our DOM element type. The class attribute can contain multiple class names separated by spaces, which we return in a hash table. [Note: The Rust types below look a bit hairy because we are passing around pointers rather than copying values. This code should be a lot more concise in languages that are not so concerned with this distinction.] impl ElementData { fn get_attribute<'a>(&'a self, key: &str) -> Option<&'a String> { self.attributes.find_equiv(&[...]

Let's build a browser engine! Part 3: CSS

Wed, 13 Aug 2014 20:10:36 GMT

This is the third in a series of articles on building a toy browser rendering engine. Want to build your own? Start at the beginning to learn more: Part 1: Getting started Part 2: Parsing HTML Part 3: CSS 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 Here’s an example of CSS source code: h1, h2, h3 { margin: auto; color: #cc0000; } div.note { margin-bottom: 20px; padding: 10px; } #answer { display: none; } Now I’ll walk through some the CSS code from my toy browser engine, robinson. A CSS stylesheet is a series of rules. (In the example stylesheet above, each line contains one rule.) struct Stylesheet { rules: Vec, } A rule includes one or more selectors separated by commas, followed by a list of declarations enclosed in braces. struct Rule { selectors: Vec, declarations: Vec, } 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 than CSS3 (which is split into myriad specs that reference both 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. enum Selector { Simple(SimpleSelector), } struct SimpleSelector { tag_name: Option, id: Option, class: Vec, } A declaration is just a name/value pair, separated by a colon and ending with a semicolon. For example, "margin: auto;" is a declaration. struct Declaration { name: String, value: Value, } My toy engine supports only a handful of CSS’s many value types. enum Value { Keyword(String), Color(u8, u8, u8, u8), // RGBA Length(f32, Unit), // insert more values here } enum Unit { Px, /* insert more units here */ } All other CSS syntax is unsupported, including at-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: /// Parse one simple selector, e.g.: `type#id.class1.class2.class3` fn parse_simple_selector(&mut self) -> SimpleSelector { let mut result = SimpleSelector { tag_name: None, id: None, class: Vec::new() }; while !self.eof() { match self.next_char() { '#' => { self.consume_char(); = Some(self.par[...]

Let's build a browser engine!

Mon, 11 Aug 2014 15:10:57 GMT

This is the second in a series of articles on building a toy browser rendering engine: Part 1: Getting started Part 2: Parsing HTML 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:


Hello world!

The following syntax is allowed: Balanced tags:


Attributes with quoted values: id="main" Text nodes: world Everything else is unsupported, including: Namespaces: Self-closing tags:
with no closing tag Character encoding detection. Escaped characters (like &) and CDATA blocks. Comments, processing instructions, and doctype declarations. Error handling (e.g. unbalanced or improperly nested tags). 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. struct Parser { pos: uint, input: String, } We can use this to implement some simple methods for peeking at the next characters in the input: impl Parser { /// Read the next character without consuming it. fn next_char(&self) -> char { self.input.as_slice().char_at(self.pos) } /// Do the next characters start with the given string? fn starts_with(&self, s: &str) -> bool { self.input.as_slice().slice_from(self.pos).starts_with(s) } /// Return true if all input is consumed. fn eof(&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.) [...]

Let's build a browser engine!

Fri, 8 Aug 2014 17:10:39 GMT

I’m building a toy HTML rendering engine, and I think you should too. This is the first in a series of articles describing my project and 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. The many 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. (I’ve written a lot of browser chrome code, and a few small patches for Gecko, but before joining the Servo project I knew nothing about many areas of the browser engine.) Robinson sometimes uses simplified versions of Servo’s data structures and code. If you too want to start contributing to Servo, try some of the exercises in Rust! On Libraries and Shortcuts In a learning exercise like this, you have to decide whether it’s “cheating” to us[...]

Better automated detection of Firefox performance regressions

Sun, 10 Nov 2013 20:09:00 GMT

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. 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.[...]

A good time to try Firefox for Metro

Sun, 10 Nov 2013 18:13:09 GMT

“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.) Add more features, and more ways to customize and tweak the Metro UI. If you want to contribute to any of this work, please check out our developer documentation and come chat with us in #windev on or on our project mailing list![...]