Neil's Place

July 13, 2015

Comparing Flexible Box Layouts

Filed under: Uncategorized — enndeakin @ 8:53 am

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.

XUL flexbox spends 16 milliseconds of time in reflow during Firefox startup. CSS flex spends 217 milliseconds.

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.

XUL box scales relatively well, with With 5000 children, in a horizontally laid out box, around 300 milliseconds is spent during reflow, with XUL box being about 8 percent higher. For a vertically laid out box, this is around 150 milliseconds, again with XUL box being about 8 percent slower. At a smaller number of children, the difference in all types is negligible.

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.

January 14, 2013

So What’s Up With That Strange Focus Model on Mac?

Filed under: Mozilla — enndeakin @ 8:45 pm

This is a question that comes up a lot from other people when you are somebody that is expected to investigate and fix focus related Firefox bugs. People are confused as to why Mac has such an unusual way of handling of focus. The questions and confusion can usually be summarized as follows:

This Macintosh doesn’t behave exactly the same as the Windows (or Linux) machine I’ve been using for years does! It must be wrong!

Now I don’t know the actual reasoning as to why Apple might have chosen the particular focus behaviour they did, not for that matter why Microsoft, or any other platform vendor might have chosen the behaviour they did, but I can make some guesses.

On Windows, it is expected that a user uses both the mouse and the keyboard to interact with the user interface. The user is expected to only be interacting with one control at a time. If the user starts to interact with a control with the mouse, it is expected that the user, when then using the keyboard, will want to interact with that same control. There are exceptions however, for example, buttons on certain toolbars. Assuming this, Windows will move the focus to a control when you click on it.

The Macintosh however assumes that most people who are using a mouse aren’t going to interact with controls with the keyboard if they don’t need to. For instance, while you could toggle a checkbox by tabbing to it and pressing the space bar, most people don’t do this. Thus, the focus is only placed on those controls where one must use the keyboard to interact with it, or those where keyboard interaction is quite common. This means, text entry fields and list boxes. For text fields you need to be able to type in them and perhaps people commonly change items with the cursor keys in lists (or perhaps because you cannot select multiple non-contiguous items in a list without holding a key down) Thus, on Mac, clicking on any other control that isn’t a text field or list keeps the keyboard focus where it is rather than changing it.

For most typical users, it is quite handy to only have textboxes (and lists) in the tab navigation order. For example, when entering a login and password or other personal information, you can cycle between only those fields where you enter text, rather than having to skip over the various other buttons and links that might appear nearby. In addition, you can click the ‘Remember this Password’ checkbox and continue entering the password. On Windows, you would need to click the password field again. You can always set the ‘Full Keyboard Access’ setting to make all controls part of the tab navigation sequence.

Modern versions of Windows don’t show focus indicators (the dotted rings) around controls until the keyboard is used to navigate within a window, for example by pressing Tab or an access key. There’s a system setting that can always enable focus indicators. In addition, older versions of Windows that didn’t have this setting will always show focus indicators. Actually, there’s one, almost secret, way to show focus indicators by default and that is to open a dialog box by using the keyboard instead of the mouse. What heuristics Windows uses to determines this is unclear, but you can see the effect for yourself by opening the Properties dialog for a file. When using the mouse alone the focus indicator doesn’t appear on a control, whereas if you navigated the menu and selected Properties using the cursor keys and Enter, the focus indicator does appear on a control in the resulting dialog. This little known feature works in Firefox too! (Or it would if the main menu wasn’t so broken.)


Here’s one! The focus ring has appeared around the button.

Because Macintosh doesn’t focus most controls when clicking them, you rarely see the blue glowing focus rings around them. However, if one was to focus, for example, a button manually using code, the focus ring will appear around it. You can see this if you look around the Mac OS X UI a bit, even without using the keyboard at all.

Here is a rough guide to how the focus is expected to behave within Firefox across the three main platforms.

September 21, 2012

Status Update of Happenstance

Filed under: Mozilla, Status Updates — enndeakin @ 9:25 pm

I decided to work on two bugs today. The first is bug 792296 to reduce the number of reflows that happen when calling various popup methods. The other is bug 621944 which causes wrapping around to the start of the message when finding text in Thunderbird to fail to function properly. Both bugs are completely unrelated and involved changing very different parts of code.

The first bug was simple. Just change a couple places where a flush is done so as to not flush and get the frames for the popup directly. Test and then submit to the tryserver.

While that is happening, investigate the review comments from the second bug which clued me in as to what was wrong. The simple fix here is that the wrong window was being used to start the text search; instead of using the focused child frame of the window to find in, the focused child frame of the active window was being used. Write a patch and local testing shows that it fixes the problem in Thunderbird, and also fixes a simple testcase.

Some tryserver results from the first bug are now available, showing that there are failures in some private browsing and network tests. Odd, those tests aren’t using popups and look unrelated. It fails on every machine so doesn’t look like an intermittent failure. No one else’s builds show the error, so this is strange.

Run the failing tests locally. They work fine. Scratch head. Run a larger batch of tests. They still work fine. Consider making Ehsan fix the problem. Nope, he’s eating lunch.

After a while I discover an amazing thing. I don’t get the test failures locally because they are fixed by the patch to the second bug! By sheer coincidence, the patches to two unrelated bugs come together to fix both bugs! And that’s not all! It looks like it’s intermittent orange bug 707599! Fixed! Fixed! Fixed!

Also today: bug 793157 which adds a moveToAnchor method to popups and panels to allow one to move an open popup to a particular anchor location.

August 29, 2012

How to Install Xcode 4 in 79 Easy Steps.

Filed under: Uncategorized — enndeakin @ 1:14 pm

I decided to install Xcode 4. When I tried once before there was a cost so I didn’t bother. But for a while now it has been free to download. Here’s how to get it:

  1. Go to https://developer.apple.com/xcode/
  2. Click on the View in Mac App Store button and open it.
  3. Spend a few moments trying to determine how to install it from the window that opens. Decide to click the button with only the label ‘Free’. The button mysteriously transforms into a button labelled ‘Install’. Click that.
  4. Click the button in the dialog that tells you that Xcode can only be installed on Mac OS X 10.7 and later.
  5. File a support request so someone will come and provide you will an updated Mac OS X 10.8 installation.
  6. Copy the Mac OS X 10.8 installer onto your disk.
  7. Open the installer and click OK in the dialog that tells you that the installer only runs on Mac OS X 10.6.8 and later.
  8. Go the Apple menu and select Software Update and perform a system update.
  9. Wait for a bit and restart your system.
  10. Open the installer and click through a couple of dialogs.
  11. Click OK in the dialog that tells you something about some virtual machine that could not be shut down.
  12. Click OK in the dialog that tells you that the Mac OS X 10.8 installer crashed.
  13. Open the installer again and click through the same couple of dialogs again. This time it works fine.
  14. Wait 15 minutes for the progress bar to tick down from ’34 minutes remaining’ to ’30 minutes remaining’ and then go to bed.
  15. Later, wake up and click though a dialog asking for a network password.
  16. Click Cancel in the dialog that tells you that system updates are now available.
  17. Notice some new icons on the dock and click them just to see what they are.
  18. Click the double arrows on the upper right corner of the window that weren’t in previous OS versions of one of the new applications (some notepad type thing). The application fills the screen.
  19. Spend a few moments trying to figure out how to leave the full screen mode. Unable to, find that moving the mouse to the top of the screen sometimes reveals the menubar. Select Quit from the menu after a few failed attempts.
  20. Go back to https://developer.apple.com/xcode/
  21. Click on the View in Mac App Store button again.
  22. Click the ‘Free’ button again and then its transformed form ‘Install’.
  23. Enter your Apple ID and password. Fail to remember it from so long ago and click the forgot password button.
  24. Enter your email address and press the button to send a notification.
  25. Check your email and click the link in the message you receive.
  26. Enter a new password.
  27. Shake fist at your password must contain a capital letter message.
  28. Add a capital letter to your password.
  29. Shake fist again at your password must contain a number message.
  30. Add a number to your password.
  31. Successfully change your password.
  32. Go back to the app store application and try to figure out out to get it to use the new password and continue installing.
  33. Click the button that has now transformed yet again into one labelled ‘Installing’. Nothing happens.
  34. Quit and again go back to the Xcode page in the app store.
  35. Click the ‘Free’ button, click the ‘Install’ button.
  36. Enter your Apple ID and new password in the dialog that opens.
  37. The password dialog reappears. Think that maybe your Apple ID was wrong so enter a different one. Doesn’t work.
  38. Try the original name and password again. The dialog reappears. Read the dialog more carefully. Ah ha! Realize that this second dialog on first glance looks the same but is subtely different with a button that says ‘Billing Info’ instead of the OK button.
  39. Click the ‘Billing Info’ button and get presented with a new window asking you to enter your credit card info because the existing card had expired.
  40. Go back to the App Store just to see whether the ‘Free’ button had secretly transformed into a ‘Now only $100’ button. Nope.
  41. Quit the app store and start it again.
  42. Look through the menus and click the View My Account menuitem on the Store menu.
  43. Wondering whether you will get charged, decide to update the credit card information from there.
  44. Go back to the Xcode page for the fourth time, click the Free button and then the Install button.
  45. Enter your Apple ID and password. This time, no credit card dialog appears. Instead a window appears asking you to set up your security questions.
  46. Look through the provided questions and realize that you can’t answer any of them.
  47. Enter your email address in the backup email address field in the lower area of the window and decide to just click OK without setting up security questions.
  48. Shake fist that it doesn’t let you continue.
  49. Make up some answers to the questions and click OK.
  50. After another warning, change the backup email address to a different one so it doesn’t complain about it being the same as your ordinary one.
  51. Click OK and the window disappears. The only thing visible is the app store Xcode page. The transforming button says “Installing” but there is no progress indicator or anything similar.
  52. Wait a bit and wonder if anything is happening.
  53. Click buttons in the app store interface. Find that the Purchases button along the top shows a download and install progress bar for Xcode.
  54. Wait until it installs.
  55. Close the app store application.
  56. Double-click the Xcode icon on the dock. A dialog appears asking you to install a “Java SE 6 runtime”. Click the Not Now button. Xcode doesn’t open.
  57. After some thought, wonder if the new version was really installed. Check the app store again. Yep, the magic button now reads “Installed”.
  58. Wonder if the new version of Xcode was installed in a different place. Open a Finder window to check.
  59. Try to find the /Developer directory where the previous version of Xcode is installed. After a few moments of searching around, realize that the Finder window contains no ‘Macintosh HD’ item on the sidebar any more, so you can’t get to the /Developer directory.
  60. Use the Terminal instead and find that the old version of Xcode is there in the /Developers directory and there is a new version in the /Applications directory.
  61. Open the Applications flyout list from the dock.
  62. Try to get to the Xcode icon, but the flyout list has no scrollbar nor any resize handles. Wonder if you’ll only be able to start applications that start with the letter P and earlier from now on.
  63. Think for a moment and try to scroll using the trackpad. Success! Click the Xcode icon.
  64. This time a setup dialog appears, so assume that this is the new version.
  65. Click though a couple of dialogs and realize that all of your settings from the previous version have been reset.
  66. Go back to the terminal and try to open development tools from the command line as you did with the previous version. They don’t work.
  67. Assume that the path is set incorrectly.
  68. Try to open .bashrc in an editor. It doesn’t work as the path is set incorrectly.
  69. Open TextEdit in an editor, remembering to use the trackpad to scroll the flyout list of applications. The open dialog doesn’t show hidden files.
  70. Open the terminal and open .bashrc from the command line and change the path to point to the new version.
  71. Try to use the developer commands from the command line. Nothing.
  72. Click the Xcode icon on the dock to use the Xcode editor. It asks you to install a “Java SE 6 runtime”. Click Not Now.
  73. Delete that Xcode icon and replace it with the icon for the new version of Xcode.
  74. Open the new version of Xcode. Look around the menus for a bit.
  75. Open the Xcode preferences dialog and look around at the settings. Click the Downloads tab. Select the Install button next to Command Line Tools.
  76. Wait for the command line tools to be installed.
  77. Try the command line tools from the command line. They seem to work.
  78. Check if the Xcode bug you’ve been annoyed with for the last year is fixed. It’s been fixed and replaced with a different annoying bug.
  79. Go back to the Xcode page in the app store. The Free button now reads ‘Sucker’.

And that’s how to install Xcode 4. And remember, it’s free!

March 30, 2012

XBL Forward

Filed under: Mozilla — enndeakin @ 4:50 pm

I’ve been working on investigating what it would take to make some improvements to XBL, either by implementing XBL 2 or something else. My motivation here is to improve the XUL widgets so that are easier to use, stabler and faster. I gave a talk at Fosdem outlining the various forms of XBL at present and possible routes forward in the future. There are three possible ways forward.

The first option is to implement the XBL 2 specification as is. There are two versions of this specification, the original one, created in 2007, and a newer version, created in 2010, that removes a number of features.

The advantages of this approach:

  • Already has a complete specification that underwent some design already.
  • To some degree, it builds on top of the existing XBL, so we may be able to build some features incrementally.

Disadvantages:

  • As no other browser has implemented or attempted to implement it, there isn’t any reason to implement it as is.
  • For internal use, a specification can restrict certain extended features that will be needed. These include connecting to native components as well as accessibility.

Approach two involves a set of new features being designed by Google, under the umbrella term Web Components.

Advantages:

  • The design here is broken into several smaller features, rather than having one large feature. This may make doing the implementation in stages easier.
  • Since Google is already working on this, it is likely an easier route to multiple compatible implementations.

Disadvantages:

  • The features described aren’t necessarily better design, just different, and likely don’t encompass all of the design issues we’ve experienced.

A third approach is to take the existing XBL implementation, and simply improve the features that we don’t think are working as well, and enhance or replace those parts with implementations that are more robust, offer better performance and are easier to use. In addition, new features would be added to address shortcomings, generally from the XBL 2 specification or otherwise where they are useful.

Advantages:

  • Likely easier to implement, as it builds on top of an existing implementation.
  • It has no constraints on others to design or agree on a specification, so features can be designed to fit our needs

Disadvantages:

  • The main disadvantage is that is use would be limited to Mozilla’s own use internally.

I’ve spent much time over the last few months trying to determine which approach is best.

XBL belongs to a larger class of application that is attempting to allow for component creation, abstract representation and general templating mechanism. There are many tools and libraries available both running on a client and a server that attempt to provide these types of features. XBL attempts to solve specific use cases, but doesn’t attempt to solve others. One way to design XBL would be to allow it to solve the more complete set of component and template generation features.

For example, the documents for Web Components describe some examples of templating, which XBL doesn’t address directly. Although it doesn’t specify a syntax, it gives an example containing a tag written as: <li class=”${importance}”>. This implies that some form of value substitution is desired. XBL only provides attribute substitution in a limited form. Does this mean that XBL should be extended to allow a more sophisticated template mechanism to allow the markup structure to be changed in more powerful ways?

Naturally, the more features added, the more complicated the implementation will become. More importantly, it becomes much more difficult for multiple implementations or their users to agree on a common syntax. Indeed, the presence of Web Components and multiple XBL specifications implies that such agreement is difficult. Another example of this is persistent storage in a browser. Several variants have been proposed and/or implemented, including a key/value storage mechanism (localStorage), an SQL database syntax, and IndexedDB. There are probably others, and some use cases are not covered by all of these. Do we simply choose one or some of these and ignore the remaining use cases?

Mozilla hasn’t shown any interest in implementing XBL 2, and neither have any other browsers. So it seems to me that XBL is one of those areas where having a specification hasn’t served much purpose.

If I were implementing the future of XBL today I would favour approach three that will get useful functionality implemented faster. This is for several reasons, one, is that I don’t feel that consensus on anything beyond a basic set of features will happen any time soon, or at all. Two, spending extensive amounts of energy working on trying to create and/or expand on some form of multi-vendor specification isn’t interesting to me, as I don’t care about web development. Third, since I’m talking about what I would do if I were implementing it, it’s the simplest way to improve the XUL UI widgets we have.

I wrote the start of a feature page.

November 3, 2011

Window Reflows on Startup

Filed under: Mozilla — enndeakin @ 2:02 pm

I took a look at what happens on Firefox startup on Mac with a profile I’ve used for testing for a while. Specifically, how often and why the root element (<window>) in the browser window is asked to be laid out. Each line below is an attempt to lay out (reflow) the root element. A reflow can happen as a result of a number of different things such as new elements being inserted, images being loaded and so forth. What happens is that the document layout objects are marked as dirty as necessary, then at a future point, a reflow occurs. This allows a script to make a number of changes without having each change cause a full refresh. I’ve broken down each attempt to mark the root element (or some other reflow root) as dirty, separated by commas in each line. In the table below, a reflow occurs 15 times.

Action

Time to finish
Hidden Window

0.55ms

Main Window is parsed and laid out

14.95ms

Attribute changed, and loaded page (about:blank) is laid out

0.78ms

Search icon is set

0.35ms

Scrollbar attributes changed, toolbarspring is inserted, window’s load event fires and listeners called and finished

2.21.ms

Scrollbar attributes changed, tabview.png is set

0.34ms

folderDropArrow.png is set

0.27ms

browser.js delayedStartup() is called, splitter is inserted into urlbar

0.24ms

menupopup is appended to back button, delayedStartup() finishes, bookmarks toolbar handling begins

5.77ms

chrome://browser/skin/places/query.png is set

0.56ms

chrome://browser/skin/page-livemarks.png is set

0.39ms

chrome://global/skin/tree/folder.png is set

0.46ms

Call to flush frames

0.33ms

Bookmark icon is set

0.48ms

Bookmark icon is set

0.37ms

The last few images are all the assignment of the icons on the bookmarks toolbar. These don’t occur if the toolbar is hidden. In total, approximately 400 changes to an element’s attribute are made and 41 nodes are appended to the document after it is parsed.

Now let’s take a look at the mobile version, running on an Android tablet. Here, 24 reflows occur over the 2200ms startup time.

Action

Time to finish
Insert html element into main window?, insert html element into hidden window?, parse and lay out main window

7.43ms (also 183.7ms reading xbl)

Four dirty marks caused by scrollbar attribute changes

1.07ms

Remove text node (a <br> is appended to a <div> immediately following), append <documenttab>

2.55ms

Append <notificationbox>

0.78ms

Some attributes on <label> and <image> elements are changed, some <label> elements are removed

1.20ms

scrollbar.pageincrement is changed

0.28ms

SizeToContent is called

0.22ms

Unknown reason, but probably caused by bug 230959

0.94ms

slider.pageincrement is changed, attributes on <tablet> are changed

5.329ms

Three dirty marks caused by scrollbar attribute changes

1.65ms

Attribute changed on a <scrollbox>

1.19ms

Attribute changed on a <scrollbox>

0.57ms

Attributes left, width and height changed on a <vbox>

1.40ms

Two scrollbar related dirty marks, mode attribute changed on various things (no dirty mark), loading attribute on an <image>

1.88ms

mode attribute changed on various things (no dirty mark), loading attribute on an <image>

0.76ms

Some <image> elements are inserted and removed

0.72ms

Unknown reason, probably a flush caused by retrieving some layout information

0.53ms

Unknown reason, probably a flush caused by retrieving some layout information

0.68ms

Loaded page starts appearing, <toolbarbutton> label changed

2.27ms

Text node appended to page

0.51ms

Text node removed from page, height changed on an <hbox> related to a nearby <canvas>.

1.54ms

mode attribute changed on various things (no dirty mark), loading attribute on an <image>

0.74ms

<box> is marked not hidden (think this is the autocomplete widget), insert frames into box, some attributes on <richlistitem> and <menuitem> are changed

33.78ms (also 265.18ms reading xbl)

Eight dirty marks caused by scrollbar attribute changes

2.70ms

Observations:

  • There are lots of changes to scrollbars here, despite no scrollbars on mobile Firefox being visible. The set listed in the last row is likely caused by the resize caused by the on-screen keyboard appearing. 120ms of time occurs from when this begins to when this ends, although it is likely that only a small amount of this is directly scrollbar related.
  • I didn’t measure XBL parsing time on desktop Firefox in this test, but it typically takes about 10% of the startup time. Of course, XBL caching should reduce the time to load XBL here. It should be noted though that parsing and creating the main document takes 25% of the rest of the time.
  • The time between reflow 2 and reflow 18 is 550ms. I’m not familiar enough with the mobile code, but this appears to be the time to process and manipulate things after the main document is parsed, equivalent to desktop Firefox’s handling in browser.js.
  • The last two rows are for the autocomplete and on-screen keyboard appearing. It takes up 675ms of time, but the main window has already appeared, so the user’s perception of the time to start up may not need to include this time.

October 4, 2011

XBL Performance Tips

Filed under: Mozilla — enndeakin @ 5:13 pm

As I’ve been investigating XBL caching, I’ve been looking into some additional performance characteristics. Here are some tips on improving your use of XBL in Mozilla, relying on specific implementation details to gain performance benefits. You probably won’t notice much difference, but lots of minor changes can add up.

Properties and methods, that is the code in <property> and method <body> elements are compiled once per session. The code is compiled once when the first element that uses the binding is added into a document. For elements included in a XUL document, this means that they will be compiled while the window is being created and opened. Constructors and destructors are treated similarly.

Fields and handlers (<field> and <handler> elements) however are not compiled when the element is added. Instead, they are compiled on demand when they are used. This means that both fields and handlers do not directly affect the time to open a window, but can instead, in the case of handlers, affect the time to respond to an event.

Fields

The field element defines a value on a element. The code for it is compiled when the field is first accessed, not when it is defined. For example, assume a field exists such as that below:

 <field name="someValue">10 * 50</field>

The code ’10 * 50′ will be compiled and evaluated when code that checks ‘element.someValue’ is encountered. Naturally, if this happens during the binding constructor, this will affect creation time. Although I haven’t investigated this, if this is the case, it might be better to just initialize the field directly in the constructor to save the overhead of having to evaluate the field separately.

However, for fields that are rarely accessed, initialization within the field itself is generally more optimal.

A very important note however, is that the field value is compiled and evaluated not once per session or even once per window. The code in a field body is compiled and evaluated once for every element that uses it. Sometimes, people write large blocks of initialization code directly in the field element. If this field isn’t accessed often, or the binding is only used once or twice in a window, this won’t matter too much. But if the binding is used frequently, this can have notable impact on performance. Instead, call a method from the field initializer such that at least the compilation is only performed once.

It also goes without saying that enumerating an object with a binding will invoke all of the fields and evaluate all of their code.

Handlers

Event handlers defined in XBL are compiled the first time the event occurs. So, for example, a handler for a keydown event will get compiled when the element is focused and a key is pressed down. This saves time when creating the element for rarely used event handlers.

Unlike fields, handlers are only compiled once per window, even when multiple elements exist within the same window. This means that putting all of your code in a method and having handlers that simply wrap the method requires more time to create as methods are compiled when created. If improved creation or startup time is more important to you, then place the code in the handler instead.

It helps to use the event filtering attributes on the <handler> element when possible. These will filter out events that aren’t what the handler code is interested in, and avoids the need to compile and call the script code at all, as these filters are implemented entirely in native code. Attributes for filtering available include: modifiers, button, clickcount, keycode, charcode, and phase.

The description above mainly applies to compile time of the various XBL parts. In all cases, for fields, properties, methods and handlers, the compiled code must still be attached to the content each time the binding is used.

In summary, the choice of how to structure the specific parts of the XBL depends on where better performance is needed.

The display and extends attributes

This isn’t a entirely a performance tip but sometimes the display attribute (or the extends attribute) on a binding is used when it doesn’t need to be, for example:

 <binding extends="xul:box"> 

This particular example doesn’t generally do anything for XUL elements as they are already boxes. The example above might do something if you had changed the display type in a some other manner, but that is a very rare occurrence. The colon-form syntax used here does not mean that it extends some other binding or element. In fact, where it does have a use doesn’t relate to XBL at all. Instead, it specifies which internal layout objects get created for the element.

Although in the past any element could be used for the display/extends colon-syntax, today there are only sixteen tags that are allowed, only half of which are actually useful. (The list of tags to allow was generated by searching the Mozilla source for usage rather than fixing up the cases where it was used and wasn’t necessary).

One such tag that might be worth investigating for performance is using display=”xul:spacer” on a binding. It creates a layout object that will never have children. If you know your binding will have no children, either regular or anonymous, it saves a very tiny amount of extra time and storage (60 bytes or so per element).

Many specific additional tips could be made but it is hard to measure the exact benefit in many cases. I’ve investigated some cases with xbl inheritance, but haven’t found any specific tips there yet. I’m continuing to look into other areas.

July 17, 2011

Status Report for July 17

Filed under: Mozilla, Status Updates — enndeakin @ 7:48 pm

This week I mainly did two things:

  • Made a few minor changes to the patch to handle using popups as drag feedback, to simplify the code.
  • Posted patches to store and read XBL bindings to the startup cache. This is a work in progress, but I spent this week getting the patches to the point where they don’t cause any test failures. There is still some work to be done: various warnings occur when reading the bindings, error handling needs to be improved, there may be a minor memory leak to investigate and, for whatever reason, the cache is only read the second time rather than the first time it is available. The latter is some remnant of the switch to using a startup cache, which stores the cached data in a zip file, rather than the specialized format fastload file.

July 8, 2011

Status Update for July 8

Filed under: Mozilla, Status Updates — enndeakin @ 8:14 pm

This week I mostly finished off a patch to handle panels as drag feedback images. Currently, when dragging, a feedback image is created from the element being dragged, or a script may change the feedback image with the setDragImage function. However, the image is always a static image. With this patch, if you pass a <panel> element to the setDragImage function, the panel will be used directly as the drag feedback, allowing the feedback image to be changed while dragging.

This will allow you to not only change the content and appearance of the drag feedback image while the drag is occurring, but also the position and size. This should allow for some interesting drag effects.

The only other step besides using the setDragImage function, is to ensure that you set type=”drag” on the <panel> element. This ensures that the popup behaves as a drag feedback image. Otherwise, drag events will get fired on the panel itself, and you will likely want them to be ignored and instead fired on what you are dragging over underneath the panel.

See bug 533460 for more details. Some builds are also available with these feature.

Some other things I have done recently:

  • Put up a some updated patches for clipboard event handling.
  • Fixed some elements such as the datepicker and tree such that they cancel mouse scroll events and key events properly
  • An issue where dropping on plugins was not allowed.
  • Some cleanup of xul headers.
  • Changed xml-datasource template generation so that the ids from the source document are used. This allows persistence to work properly.

April 4, 2011

Loading XBL Performance

Filed under: Mozilla — enndeakin @ 3:56 pm

A while ago I spent a bit of time looking at the performance of opening a new window recently. My first few experiments were actually a bit off track as I realized that I had my XUL cache disabled. This isn’t representative of most users, so isn’t completely valid for testing real performance, but it did get me thinking a bit.

In case you’re wondering, the XUL cache does two things. When a XUL file is loaded, it is parsed into an in-memory form and the scripts compiled into a bytecode-like form. When that file is needed again, the already parsed and compiled form is used instead of reading it again. The second step is that the parsed form is saved out to disk in a file. When the browser is restarted, this file is read instead. This way, the source form is not reparsed each time. This process happens for both XUL content and for Javascript. Instead of compiling the script into bytecode each time, the bytecode is serialized to disk and reused the next time. This process improves performance significantly at the cost of a couple of megabytes of disk space.

The file the parsed data is stored in, generally called the fastload file, is currently located in the same place as the network cache and can be found with the filename XUL.mfasl. You can safely delete it and it will get recreated when you start the browser again if you want to see how it affects startup time. (although that test will of course be affected by the time it takes to write it out again).

With the cache disabled, none of the above happens and the files must be reread from the source every time. However, when I had the cache disabled, I noticed that a significant amount of time was taken up by compiling the script associated with XBL bindings. Much less time was used with the cache enabled again.

Now, one thing of note here, is that, unlike XUL documents, only one part of the cache mechanism is used for XBL. XBL is only cached in memory and it isn’t saved out to the fastload file on disk. This means that this compilation time occurs upon each startup. I decided to investigate what would happen if XBL was also saved into the fastload file.

But first, let’s look at the performance with the current behaviour. Reading XBL generally has three steps, the first is to load and parse the source XML document and convert it into an internal representation. The second step is to attach the binding into the document and create the anonymous content. The third and final step is to compile the property and method scripts associated with the binding. Testing shows that the second step takes only 15 percent of the total time, so I’m going to focus on the first and last steps here. (Here I’m only considering the time spent executing within the area of code used to implement XBL.)

This chart shows the time to read a few selected bindings used by the Firefox UI. The first three are some of the pieces that make up the tabbrowser, the fourth is the tree element, the fifth is the dropdown autocomplete popup for the URL address field, and the last is the URL address field itself. This chart only shows a selection of the more complex bindings; in reality about 60 bindings or so are read at startup to create a window.

The chart breaks down the time to load and parse the binding (the blue bar) and the time to compile the properties and method scripts associated with the bindings (the green bar).

The first binding ‘tabbrowser-tabs’ takes a lot longer to parse than the following two tabbrowser related bindings. This should be expected since all three bindings are stored in the same XML file. As the document is only loaded and the XML parsed once per file, we expect that most of the parsing time will be for whatever binding is asked for first. The parse time for ‘tabbrowser’ and ‘tabbrowser-tabbox’ is mostly just overhead from having to locate the cached bindings previously read. (Remember that in-memory caching of XBL is performed currently.)

The compile time for the bindings, especially ‘tabbrowser’ correlates to the amount of script used by that binding. As evidenced, the ‘tabbrowser’ binding has a lot of methods, so significant time is spent on this.

The tree binding shows that a more complex binding that requires both parsing and compiling does indeed require notable time for both parsing and for script compiling.

As with the tabbrowser, the two urlbar bindings are contained within the same file, so the parsing time of the first takes the brunt of the total time. But notice that the ‘urlbar’ also requires significant time to parse as well. Again, there is an explanation. This last binding inherits from the autocomplete binding, so the time here also includes the time to load and parse the base autocomplete binding as well.

As evidenced, script compilation is a significant part of reading a binding. It is this part that we hope to reduce by fastloading.

I implemented a simple XBL fastloading mechanism to see what would happen. The hope is that we can see faster loading if the parsing and compiling steps are replaced by a single mechanism to read data from the fastload file that is already in a format that is close to the in-memory representation used. We can’t eliminate the time entirely of course, as we still need to read the compiled form, but if the original testing is correct, we should be able to eliminate the time needed to compile the scripts at least. The following chart shows the results.

This chart includes a bar showing the time taken when fastloading the same set of bindings (the orange bar) with the original data for comparison.

We can clearly see that the compilation time is entirely gone. The ‘tabbrowser’ binding shows this most obviously as it eliminates over 17 milliseconds off of the original time. But all of the other bindings have also saved this compilation time as well.

In all, it appears that the parse time is reduced by around 20 to 25 percent. In the two cases where parsing is not done, ‘tabbrowser’ and ‘tabbrowser-tabbox’, there is no difference in parse time. Note that in the implementation I did, all of the loading for all bindings in a file happens when the first binding for that file is loaded, so the parse time for all three actually occurs during the first tabbrowser-tabs binding. The small amount that exists is from the overhead of retrieving a binding from a file in the cache that is already been read. That might be worth investigating as well, since this overhead occurs over 500 times just when starting up Firefox.

This last chart shows the total time taken up by XBL parsing and compilation to load all of the bindings using two tests. The first is the time taken during startup. The second is a test which starts the browser, opens the bookmarks window, the sidebar, a couple of panes in the preferences window, a new blank window and then closes them again. As not all elements and bindings are used in the basic Firefox window, this latter test ensures we read a good selection of the additional bindings.

This chart shows that overall, using XBL fastloading removes the time needed for compilation, but has only a marginal effect on the total parsing time.

Note that this testing is only based on a few basic observations, but other tests I’ve done show that similar results occur at least on Windows and Mac, both with optimized and debug builds. Testing suggests about a 3-5% improvement in startup time once the fastload data is cached.

Older Posts »

Create a free website or blog at WordPress.com.