Mobile/Fennec/Architecture

From MozillaWiki
< Mobile‎ | Fennec
Jump to: navigation, search

Note: Fennec's architecture has changed and a lot of componets are moved to Java Native UI. But it's sitll nice to have a look to understand previous Fennec design :)

Background

Fennec is a XUL-based web browser designed for mobile-class devices, especially those with touchscreen support. Fennec shares much of its platform support with Firefox. The two browsers use the exact same HTML rendering engine, as well as Add-on support, Download management, Places bookmarking & history, and JavaScript engine (including JIT support).

Although the underlying platform is the same, the front-end UI is very different. Fennec is designed for smaller screens, lower CPU & memory specs, non-qwerty keyboards and touchscreens. Because of the large UI differences, add-ons that overlay the Firefox UI would need to be ported to work in Fennec. The focus on acceptable performance while running on mobile-class devices coupled with some of the finger-based UI features, has resulted in some unique XUL structure.

Application Structure

Browsing

Fennec and Firefox are very different when it comes to the way HTML is presented. Firefox uses a <tabbrowser> XUL element to create a tabbed browsing environment. The <tabbrowser> is a composite control, made up of <browser> elements contained inside a modified <tabbox> element. Primary interaction is done through the <browser>.

Mobile-typical-structure.png

Fennec takes a different approach. While <browser> elements are still used to render the HTML, they are hidden offscreen. The rendered HTML is drawn into a <canvas> element, which acts as the primary display surface. This is primarily done for the ease in which panning and zooming can be done at acceptable levels of performance. A tabbed browsing environment is created by using an additional strip of thumbnails representing the currently opened <browser> elements.

Mobile-fennec-structure.png

The contents of the <browser> is copied to the <canvas>. The thumbnail used to represent the "tab" is updated either from the display <canvas>, for speed, or from the <browser> if the display <canvas> is stale.

Fennec uses the new MozAfterPaint event to optimize all DHTML updates to the the primary <canvas> display surface. Whenever the contents of the web page changes, for whatever reason, we only update the areas that actually changed. We don’t repaint the entire surface unless it's absolutely required.

Chrome Elements

All of the primary UI elements are children of a <stack>. This allows the UI elements to be absolutely positioned relative to each other, but mostly, relative to the primary <canvas> display surface.

Mobile-fennec-stack.png

Fennec uses a JavaScript helper object, WidgetStack.js, to coordinate panning the individual UI elements. WidgetStack also manages the size to the content viewport. For the most part, the left and right toolstrips are "glued" to the sides of the content display surface. Different web pages are different widths (and heights) and as the width changes, the right toolstrip moves with the growing content. This means wider web pages require more pans to get to the right toolstrip.

Fennec tries to not use any dialogs. There are still some, used by the platform, but we are working to removing them all. Instead of dialogs, Fennec uses other UI to interact with the user. Firefox does similar things with modeless notification boxes and error pages in content.

In those situations where a dialog or secondary window would be used, Fennec typically brings a pseudo-panel into view. Fennec does not use real <panel> elements, as those tend to cause performance issues. Instead, a <vbox> containing the desired UI is brought into view. The boxes are children of the <stack>, can be positioned as needed, and are hidden until needed - also to improve startup performance.

Some examples are Bookmark List, Bookmark Editor and the Tools Panel (Add-ons, Preferences and Downloads). These elements are very similar to the toolstrips, but they are not managed by the WidgetStack.

Panning/Zooming

Panning moves the entire UI, not just the content area. The toolstrips and the content move as a single entity. It should feel as though the entire browser is being moved by your finger.

The <canvas> display surface displays the visible part of the web content. However, a little more than the visible area is actually drawn into the <canvas>. This allows panning to immediately show parts just "off screen" as the area moves. Also note, the entire web content is never drawn into the <canvas>. That would seriously slow down performance.

While panning moves the entire UI, zooming only affects the content area. However, the content are does "grow" when zoomed, so the right toolstrip seems farther to the right then when the content is not zoomed.

Performance Related Coding Guidelines

Panels

Using <panel> elements to float chrome UI over the main window leads to performance problems. In fact, any XUL element that creates a native OS window under the covers will slow things down. In Firefox, the awesomebar autocomplete list uses a <panel>. The autocomplete list was re-worked such that it was a <vbox> in the main <stack> and the results were dramatic. The list displays much faster.

Showing/Hiding Elements

Stacking two or more elements and then toggling the visiblilty of all but one element is a simple way to create a UI that changes based on a state. While useful, this showing/hiding process can be slow. Try to avoid if possible, or find other ways to achieve the same goal.

The URLbar in Firefox is a good example. When displaying the current web page URL, it is a <description>. But when the user is typing a new URL, it is a <textbox>. The process of showing/hiding the elements was taking a noticeable amount of time, affecting the perceived page loading time. In Fennec, the URLbar is always a <textbox>. The readOnly property is toggled to create the "caption" mode.

A similar situation occurred with the favicon indicator. Fennec initially used a <stack> holding two <image> elements - one for the throbber and another for the website’s favicon. Again, we were showing and hiding the images depending on situation. This time it was the <stack> that was the slow down. So it was removed and page loading appeared to complete a little faster. The two image elements were kept because the load time for the swapping the favicon image with the throbber image eclipsed any benefits of removing the show/hide code.

Canvas and Thumbnails

As described above, Fennec copies the contents of a <browser> to the display <canvas>. Updating the contents of a browser to a canvas is not cheap on mobile devices. Each drawWindow call can take ~300-400ms. On the other hand, drawImage is much faster - ~100ms. Fennec uses drawImage whenever possible to update the tab thumbnails.

Fennec also uses the MozAfterPaint event to optimize all DHTML updates to the the main canvas display surface. This minimizes the amount of drawWindow calls. We don't repaint the entire canvas.

Post-Pageload Work

Any work that occurs while the page is loading is potentially bad for performance. This includes any time consuming work that happens in the page "load" event handler. Users like to start interacting with page content as soon as possible. Fennec even delays updating the web page favicon until the page finishes loading.

Fennec also had a time consuming process, converting plain text phone numbers to tel: links, that fired as soon as the page loaded. This process blocked the user from interacting with the page for a noticeable time period. The process was re-worked and became much faster. The blocking behavior was no longer noticeable and the user could interact with the page faster.

File I/O

Various places in the platform had file I/O code that was slowing Fennec down, usually on startup. Keep in mind, file I/O on mobile devices is usually measured in milliseconds. So try to avoid useless I/O whenever possible. Fennec startup improved from around 30 seconds (yes) for a first-time, cold start in Alpha 1 to around 7-10 seconds in Alpha 2.