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.

Advertisements

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 17, 2010

Panel Improvements

Filed under: Uncategorized — enndeakin @ 9:26 pm

Currently the <panel> element is used to create temporary floating popup windows. The normal type of panel should be used for popups that the user interacts with temporarily, then dismisses by clicking away from it, pressing a close button or presses the Escape key. In a sense, they act like modal dialogs but aren’t actually modal. The bookmarks ‘star’ panel is an example.

The second type of panel, created by setting the noautohide attribute to true, is of a more persistent nature. However, it currently isn’t as useful as it should be. I’d like to make it more useful by allowing it to be used for floating tool palette windows.

In some cases, a separate window could be used instead. However, this can be inconvenient when there are only a few buttons or fields due to having to create a separate file. Also, windows tend to be separate, more permanent entities that appear on the operating system window list, whereas panels are smaller and are generally associated with another window.

See Panel Improvements for some ideas on what kinds of features would be useful. Let me know if there are other features I’ve not included on the list, or if you’d like to help.

March 4, 2010

Clipboard Data

Filed under: Uncategorized — enndeakin @ 1:24 am

The drag and drop API available in Firefox 3.5 and later has simplified much code both in Firefox itself as well as elsewhere. The next step is to create a similar improvement for clipboard usage, which has a number of things in common with drag and drop. Once this is done, many redundant underlying pieces can be removed and we can rely only on the new APIs. Some parts of Mozilla code (such as the editing components) are still relying on the older interfaces; these will need to be changed. Fortunately, this will mostly involve simplifying code.

For instance, the current code needed to paste from the clipboard, or to cut or copy anything more complicated than simple text is generally 15 or so lines of code.

The general idea is to to allow the use of the DataTransfer object for clipboard operations, in a similar manner as can be done with drag and drop.

There are three possible approaches that could be used to improve clipboard access.

Method One

The first method is to fire three events, cut, copy and paste, which provide a data transfer which can be used to set or retrieve data. For example, for a copy event, a listener might set some text onto the data transfer. Once the event has finished firing, any data added would be put on the clipboard. This technique was first implemented by IE, but is also supported in Safari and Chrome. Mozilla implements the events, but does not supply the data transfer object needed to retrieve or modify the clipboard. Thus, in Mozilla, one can know when the events occur, but cannot affect their behaviour except to prevent it by cancelling the event.

A second piece implemented by IE, Safari and Chrome is three additional events, beforecut, beforecopy, and beforepaste. Mozilla does not implement these events. These are odd events, made more odd by poor and incorrect documentation. Their intent is to indicate whether the cut, copy or paste events are allowed or not. They are fired when, for example, the Edit menu is opened, to update the enabled state of the clipboard related menu items.

There are some disadvantages of this approach. The before events are an unusual design, as firing several events every time the Edit menu opens seems tedious. Compounding the problem for Mozilla is that Firefox has toolbar buttons for the cut, copy and paste operations which need to be updated every time the focus is changes, or when a selection is made. This means that the events need to fire quite often. Other browsers ‘work around’ this problem in different ways. IE has toolbar buttons, but never updates the enabled state of them, instead only updating the state on the menu when it is opened. Safari doesn’t have toolbar buttons so gets off easy. And, oddly, Chrome doesn’t update the enabled state of anything, even for its own UI. Likely, it’s just not implemented yet.

The other disadvantage is that the browsers only implement the events for text selections and textboxes, so you can’t really use it for clipboard operations on other kinds of elements.

Method Two

The HTML specification offers a different model that treats clipboard operations as a special form of drag and drop, in fact, treating the clipboard as just a special virtual drag source and drop target. It is expected that the normal drag and drop events would fire when using the clipboard commands on the menu, although the specification is vague about how this is supposed to work.

There are a number of disadvantages to this approach. Most notably, it isn’t compatible with other browsers. In addition, writing a simple handler for a clipboard action requires handling several events instead of one, separating the code to put items on the clipboard from the code to delete it in the case of a cut, which in some cases can be unweidly. Finally, this model doesn’t provide a means to distinguish a clipboard operation from a real drag and drop operation, nor to indicate whether a cut or copy only is allowed.

If you haven’t guessed already, I don’t like this approach at all. The only advantage of note is that keyboard only usage is provided (presumably) in typical browser usage automatically without having to write additonal code, although it is assumed that keyboard usage will use the clipboard instead, which may not be a given.

Method Three

An issue for Mozilla applications with either of the above approaches is that one can only manipulate the clipboard when the event is fired. Thus, one cannot, for example, empty the clipboard or retrieve data from it outside of an event. This isn’t an issue for web pages though; as security concerns would want us to prevent web pages from accessing the clipboard unless there has been a real request from the user to do so.

In a sense, IE (unintentionally) handles this by providing the data transfer as a global property of the window called clipboardData. An application can check this property to retrieve the data on the clipboard at any time, and can modify it at any time as well.

In Mozilla, this approach could be used as well, although changes from web pages would be restricted to accessing and modifying this object only during the clipboard events. Extensions and other privileged code could access this at any time. The disadvantage here is when you’re putting multiple types of data on the clipboard, since it would need to update the whole set on the real clipboard every time one type of data is changed.

In case you’re wondering what security issues exist, consider if a web page could retrieve the clipboard data at any time without user intervention. It’s possible that the clipboard could contain passwords or other sensitive information. Likewise, modifying the clipboard is also undesirable, as a web page could put ads onto the clipboard. Naturally, of course, this could pose a problem anyway, but at least we can restrict this to those cases only when a user requests a clipboard operation.

Conclusion

I’m favouring method one, with an appropriately secured method three, possibly chrome only. I’m not sure about the before events though, but there is a need to be able to indicate whether a cut, copy or paste event is allowed at a given time, and to be able to determine this state quickly.

One possibility I’d thought of is to make use of the global clipboardData’s effectAllowed property. Essentially, an application would update it when necessary, and the corresponding UI will check this state as needed. We can solve the issue of the events only firing on selections and text inputs only by instead firing the events on the focused content instead, perhaps only in chrome contexts. For example, when a mail message is selected, the effectAllowed property might be changed to indicate that a cut or copy was allowed, but not a paste. The property would be reset automatically when the focus changes.

An example:

<listbox onfocus="clipboardData.effectAllowed = 'copy';"
         oncopy="event.dataTransfer.setData('text/plain', this.selectedItem.label)">
   <listitem label="Item Value"/>
<listbox>

For XUL, of course, the controller and command mechanism would still be used as well, and would likely be a cleaner implementation in the case above.

December 10, 2009

Template Logging

Filed under: Uncategorized — enndeakin @ 5:03 pm

I’ve been going over some of the patches I’ve had sitting around for a while. One of the oldest was adding some error checking and logging to XUL templates.

See bug 321169 to try this out.

October 24, 2009

Command Updating Performance

Filed under: Uncategorized — enndeakin @ 2:13 pm

This week, I’ve been investigating how to improve command handling in XUL, both in terms of manner they’re used and particularly, performance. Command handling is what deals with performing actions in response to menu or keypresses. There are several types of commands. The first are those like cut and paste that typically appear on menus, where the bulk of the handling deals with ensuring that the menus, toolbar buttons and actions are enabled and disabled at the right time. The second are input field cursor and selection commands (such as move to next word, page down, etc). Mozilla cheats a bit and doesn’t check if they should be enabled as they don’t normally have UI that shows this. The third are complex edit commands such as bold, font changes and so forth. These are similar to the first set really. As an aside, it would be quite nice to be handle all of these in a similar fashion.

For whatever reason, most of the handling is done in C++ with a small amount of code done in script. In many cases, though the script is unnecessary. For example, a typical cut command looks like the following:

<command id=”cmd_cut” oncommand=”goDoCommand(‘cmd_cut’)”/>

This is quite pointless as the act of ‘do’ing the command should just be the default behaviour.

Updating command state is another issue and one which can be quite slow. In fact, a workaround was added just to improve performance due to this. Updating command state refers to the process of updating the UI when the focus or selection changes to reflect which commands are now enabled. For example, a cut operation is only valid when there is text selected.

Here’s an overview of how the update process breaks down in time used:

Fire commandupdate event at updaters: 17%
Retrieve the topmost window: 14%
Retrieve the command dispatcher: 8%
Retrieve the right controller: 33%
Checking if the command is enabled: 15%
Set disabled attribute on command elements: 13%

I was expecting the latter two steps to be the bulk of the time used, but this wasn’t the case. This was a good sign since the other steps are easier to optimize away. One particular issue is that, except for the first step, the remaining steps occur again and again for every command that needs to be updated, which includes additional time due to calling into Javascript and back several times.

Instead, by optimizing to perform the repeated steps only once, as well as not calling into Javascript unnecessarily we can reduce the amount of time taken up. Most of the time, the script just calls back to update each command in the same way every time much like goDoCommand is called in the earlier example, so this is also a bit silly.

I created a simple performance test which updates the typical eight edit-related commands one thousand times. With current code, the test took over 625 milliseconds to complete (which means about half a millisecond each). This isn’t much individually, but might be noticeable if lots of commands were being updated.

I created a simple implementation of the changes described above, still a work in progress, and have reduced this to about 60 milliseconds to update one thousand times. This is a speedup of over 10 times.

I have also been experimenting with caching the list of commands and their disabled state, rather than setting a disabled attribute on the command elements. This can improve the time to 40 milliseconds. One advantage to this is that it could mean that the command element isn’t needed for many commands — if we cache the disabled state internally, in many cases we don’t really need the command element any more.

In many cases, the UI for commands such as undo isn’t actually visible until the Edit menu is opened. Using some smart caching, we can make the work of updating these commands only occur when the menu is opened. This already happens to some degree, but much additional and extraneous work is done upfront rather than later.

Next, I’m going to investigate some of the other types of commands and further see how a smart command caching mechanism might work.

October 5, 2009

Visible Focus Rings

Filed under: Uncategorized — enndeakin @ 3:59 pm

Platforms differ in the way that they show which element is focused. So does Firefox, which differs in a number of ways from platform conventions.

The typical way is to draw either a glow or dotted outline around a button or input field that accepts keyboard entry. However, this behaviour can sometimes cause unwanted visible display or bad interactions when the keyboard is not being used. For instance, when clicking a link on a page, the behaviour is to draw a visible dotted outline around the link, then load the next page. This is useful to know that the link has been clicked, but causes an unnecessary outline that can disrupt the appearance, especially if the link is an image or part of an image map.

One significant advantage of showing the link however, is that when going back to a page, one can immediately see where the keyboard focus is, and for non-keyboard use, indicates clearly which link was clicked, a useful indicator when visiting a series of links in succession.

Another behaviour is that, especially on Mac, various elements don’t switch the focus when clicking on them. For instance, clicking a checkbox in a dialog box or selecting from a dropdown list doesn’t change which element is focused. In this image, the first dropdown has a visible focus indicator (a blue glow around it), but clicking the later dropdown has not changed this.

On Windows, what used to be the norm is to show dotted outlines around focused elements, and show underlines under letters of the labels of controls or menus to indicate a key which can be used to focus or activate it. In today’s versions of Windows, though, that behaviour isn’t used, although it can be enabled through a system setting. Instead, these indicators are only visible once the user presses a key. For instance, pressing Alt shows the menu label indicators, as well as shows a visible focus ring around the element that accepts keyboard input. Tabbing to an element does similar.

The goal of bug 418521 is to improve the situations described above in Mozilla to be more compatible with how the system behaves. Followup bugs will address additional issues with specific controls. More specifically, this means:

On Windows, don’t show indicators at all until the user uses the keyboard to access an element. But, respect the system setting if the user wants them visible at all times.

On Mac, don’t focus elements when clicking on them, except for lists and text input fields.

On Linux, always show indicators. This is the current behaviour so Linux users won’t notice changes from this bug.

One change of note on all platforms is that links will no longer show the dotted focus outline when they are clicked, whether they are image links or otherwise. However, the dotted outline will be made visible when navigating back to the page, or when switching tabs back to that page. This solves the issue of not adding visible noise to the page in normal usage, but doesn’t prevent the user from seeing which link was clicked when going back to that page again.

For this change to be effective, a new css pseudoclass is proposed, to accompany the existing :focus pseudoclass.

The :focus pseudoclass will continue to work as it does now. If will match any element which is focused. However, the new pseudoclass :-moz-focusring will only match when the element is focused and the focus indicator should be shown. So if you are creating a theme for instance, the :-moz-focusring pseudoclass should be used to show focus indicators as this deals with all of the platform specific details. For example:


button:-moz-focusring {
  outline: 1px dotted black;
}

For details of the changes see comment 57 in the bug.

September 8, 2009

How to Fix Focus Related Problems in Tests

Filed under: Uncategorized — enndeakin @ 10:27 pm

One common problem with the existing tests is that they make a number of assumptions about a window being focused. For instance, tests tend to assume that once the load event fires, everything is ready to go. This is incorrect. In fact, the window may not even be visible yet. Because of this, some tests tend to fail randomly, especially on Linux.

The focusing of a window can occur before or after the load event, depending on circumstances and platform specific variations. For instance, an incrementally loaded page can be focused long before the page has finished loading, whereas a new XUL window can be focused afterwards, since the entire XUL file needs to be parsed and laid out before the window size is known.

Even the performance tests aren’t immune to this. If they operate based on the load event, they test only the time it takes to load, not the time to focus a window or page.

A number of things rely on a window being focused in order to work. For instance, tests that synthesize key presses or open popups. These are kinds of things that will fail when the window isn’t focused.

One common, yet not actually correct solution is to call window.focus(). This function is typically used to raise (bring a window to the front) and focus the window. However, it should not be assumed that the window.focus() method will do so before it returns. (Technically, on Linux the window manager isn’t required to honour this request at all). For this reason, it is best to wait for a focus event on the window before proceeding.

This can be a bit of hassle, so a few days ago I added a new function to do this for you, called waitForFocus. It’s very easy to use, and does lots of magical incantations for you. Just add something like the following at the end of the test’s script:

SimpleTest.waitForFocus(callback, window)

Set callback to a callback function to be called when the specified window has been loaded and focused, typically this will be the function that executes the actual tests. (The window argument is optional, if it happens to be ‘window’) If the window is not focused, an attempt is made to focus it. Don’t worry if the window has already loaded or is already focused, the function will take care of checking that for you. All you need to do is add the line above and everything will work out. No need to listen to any load or focus events. At least that’s the intent. The tests that now use this function haven’t failed due to focus issues yet.

Currently, waitForFocus also logs some debugging output. I’d expect this to be removed at some point — for now it’s being used to look for problems that might occur in the near future.

This method should be used for tests that:

  • synthesize key presses
  • tests that open popups
  • tests that open a new window
  • tests that close a window
  • tests that rely on focus rings to be drawn
  • tests that rely on focus or blur events to occur

Know any tests that could have problems? Why not fix them?

Blog at WordPress.com.