I decided to take a look at what would happen if I replaced some code in Firefox such that instead of XUL box layout, CSS flex layout was used. The latter is generally a standardized version of XUL box layout using only CSS properties. It supports the same properties and values using different names, and has a few additional features. One possible way to implement this is to just set ‘display: flex’ on elements by default, and assign some rules to map the various attributes to the CSS flex properties. This isn’t enough though and just makes the UI look all confused. I then tried extending the CSS flex implementation to check for the XUL attributes as well as the existing XUL flex properties, but only for XUL elements. This worked slightly better.
It turns out that CSS flex doesn’t care for things that aren’t blocks or flexible elements as children, so I needed to tweak some checks to allow for certain types of XUL layout types that have no standard equivalent (such as the deck element needed for displaying a tabbed browser). I also disabled an assertion, but since this is just an experiment, I didn’t feel that was important. Finally, I was able to get a working Firefox browser.
It looks pretty good. There are a few glitches here and there that could be fixed up, for example, the titlebar overlaps the tab bar, and the back button and location field box overlap a bit, but it mostly looks and works fairly well. The preferences panel and other dialogs also work reasonably well. I had to disable menus, tooltips and panels though and didn’t investigate why they didn’t work.
Let’s take a look at how well this special version of Firefox performs.
Not very well actually. The CSS flex version spends over 13 times more time within the layout/reflow step than the XUL box version. When I then tried this in a debug build, there was noticeable delay while interacting with the browser when significant layout steps were needed, such as opening the sidebar.
There are several possible explanations. One is that CSS flex is just slower in general. Another is that it has trouble mixing flexible boxes with specific XUL layout types that are used within the Firefox UI (stacks, decks, etc) that I didn’t change. Or perhaps there are some specific arrangements of elements that cause an issue. In a debug build, some layout and flexbox related warnings are printed out; these could also account for performance issues. For example, some code may be getting confused, printing a warning, then trying to lay out some elements again; I’m not familiar enough with the implementation to know for sure. Let’s dig deeper.
I first tried a number of simpler XUL testcases, and found that performance was still better with XUL boxes, although not by an order of magnitude. I decided then to switch to an entriely HTML testcase, where any other aspects of XUL wouldn’t hopefully be involved. First, I tried a simple HTML testcase using the CSS flex type compared to the -moz-box (XUL box) type on the same element. I used a fresh optimized build of Mozilla code with only minor changes to disable scrollbars for this and the following tests. I loaded the test and measured the time spent.
The first test involves a single parent element with either of the two layout types with a variable number of children, up to 5000, as shown in the chart.
The first thing to notice is that a horizontally laid out element is slower than a vertical one for both XUL and CSS flex layout. I’m sure some layout developer could explain why that might be the case. Second, the XUL layout scales slightly worse than the CSS layout, although there isn’t a noticable difference at small values.
The second test involves a structure of a few elements mixed with some text. To check the scalability, I copied this structure of elements multiple times and nested them inside each other to create a very deeply nested structure many elements deep. I tried this test first with a mixture of horizontal and vertical layout, then with all elements set to use horizontal layout.
For this test, CSS flex is slightly worse at scaling to very deeply nested structures than XUL box layout, although again, only by a small amount. I then tried the same test where every element was assigned a horizontal layout. The XUL flex case is slightly worse, although not significantly. The CSS flex case however becomes significantly worse quite quickly, taking almost twice as long.
After I made this chart, I tried a case with only vertical laid out elements. Unfortunately, the XUL test case with only vertical elements got into some kind of infinite loop situation so I wasn’t able to make a direct comparison. However, the CSS flex case did appear to perform much better in this case, and scaled much better.
The obvious conclusion is that CSS flex layout is slow at horizontal layout. But that still might not be the case. One theory I have is that the overflow and scrolling mechanism for the document causes a delay only in the horizontal direction. I may investigate that further later. But even if CSS flex is slower horizontally, that doesn’t fully explain the extra 200 milliseconds of time taken to start Firefox. The tests done here tests scalability, but noticeable delays don’t really start until much higher values of children or nested structures, generally higher complexity than I would expect the Firefox UI to actually have.
For fun, I decided to add a rule that made almost every element a XUL box. The rule below should handle most cases, except for other style rules where the important marker is also used and a number of specific XUL elements that are mapped by tag name.
* { display: -moz-box !important; }
The result is that 40 milliseconds was taken up doing reflow at startup to load the initial page. A bit slower than the 16 milliseconds from before but still faster than CSS flex. I also tried replacing the rule above with one that used CSS flex by setting the display property to ‘flex’ but still maintaining the important marking. The result was that 49500 milliseconds of reflow time was spent during start up. You read that right: 49 thousand. That’s almost 50 seconds. I’ve no idea what could be causing such a drastic slow down when CSS flex is used, but that clearly isn’t good.
With the investigation so far, I can only conclude that it looks as if CSS flex box is not something that can be dropped into the Firefox UI with minimal changes, as I thought it might be. While it offers some feature advantages and fixes issues that XUL box layout has with inline text, the startup performance degradation with a simple fix is quite high. More detailed work and investigation is needed to determine the causes of the performance problems to determine whether there are specific element structures that cause problems that could be avoided, whether certain XUL layout types mixed with flexible boxes cause errors that can be fixed, or whether significant additional performance optimizations need to be done.