Firefox 4 for Mobile Beta 5 – Performance Improvements

We just released Firefox 4 for Mobile (Beta 5) and it’s packed with some noticeable improvements. The primary objectives of Beta 5 were improvements in general performance, reducing memory usage and improving rendering while loading and panning the web content. Here are some results to show the progress that’s been made.

Startup

We improved application startup speed. We have a system that reports startup time metrics once a day when the application does a check with AMO (addons.mozilla.org). Using this data, we can see the metrics improving from Firefox Mobile Beta 4 to Firefox Mobile Beta 5.

The “First Paint” metric is arguably the most important of the three. That’s when the user should start to see the application appear – not the splash screen, the actual application. Based on the Feb 23rd data, Beta 5 has a nearly 25% improved startup time. The earlier Beta 5 data (Feb 18th) was taken from internal testing and doesn’t represent a large enough population of users and devices. The Feb 23rd data is based almost 2200 data pings and is more comparable to the Beta 4 data, also from Feb 23rd.

Updated: Used actual Beta 5 data and not Beta 5 pre-release data.

Page Loading

We improved page loading performance. We have test suites that measure how long it takes to load a series of static copies of popular websites from a localhost webserver. The test uses static copies, built using wget and some tweaks, to remove the variability of live websites changing over time and network latency issues.

The data shows the exact same test being run on nightly builds of Firefox Mobile, using 3 different Android devices. The SCH-I800 is a Samsung Galaxy Tab. We can see a roughly 21% improvement from Beta 4, beginning of chart, to Beta 5, at the end.

JavaScript

The Mozilla JavaScript team keeps pushing the limits and mobile users get the benefits. We ran some benchmarks on some Android devices using the stock webkit-based browser, Firefox Mobile Beta 3, Firefox Mobile Beta 4 and Firefox Mobile Beta 5.

Go install Firefox 4 Beta 5 for Mobile. Hopefully, you’ll notice the speed improvements too!

Restartless Add-ons – Default Preferences

I saw a neat technique in Bug 564675 for creating default preferences in bootstrapped add-ons. Edward Lee posted a link to some code he uses for a bootstrapped add-on. Essentailly, the default preference branch is writable, but not permanent. This is a good thing for bootstrapped add-ons. The add-on should write it’s default preferences every time it loads and the preferences disappear when the session ends. The code looks like:


const PREF_BRANCH = "extensions.myaddon.";
const PREFS = {
  someIntPref: 1,
  someStringPref: "some text value"
};

function setDefaultPrefs() {
  let branch = Services.prefs.getDefaultBranch(PREF_BRANCH);
  for (let [key, val] in Iterator(PREFS)) {
    switch (typeof val) {
      case "boolean":
        branch.setBoolPref(key, val);
        break;
      case "number":
        branch.setIntPref(key, val);
        break;
      case "string":
        branch.setCharPref(key, val);
        break;
    }
  }
}

function startup(aData, aReason) {
  // Always set the default prefs as they disappear on restart
  setDefaultPrefs();
  ...
}

I haven’t tested to see if there is a clean way to remove these preferences when a bootstrapped add-on is disabled or uninstalled. The default preferences will likely still hang around until a shutdown occurs.

We’re starting to amass a sizable collection of code snippets for bootstrapped add-ons. I think a new section in the MDC Code Snippets area might be needed.

Restartless Add-ons – More Resources

After playing around with bootstrap (restartless) add-ons a bit more, I came to the point where I needed some extra resources in the add-on. For images, I could have stuck with the data: URI approach I mentioned previously, but I also wanted to add an options UI for my add-on. Turns out it was pretty simple. Thanks to Vlad for getting the ball rolling.

The trick I am using now is to programmatically add a resource: alias for my add-on. A resource: alias creates a handy way for you to create URIs pointing to resources, like scripts and images, in your add-on bundle. It’s pretty simple to add a resource: URI pointing to the add-on’s root folder or XPI bundle. Here is the code snippet taken from my bootstrap.js file:


function startup(aData, aReason) {
  let resource = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
  let alias = Services.io.newFileURI(aData.installPath);
  if (!aData.installPath.isDirectory())
    alias = Services.io.newURI("jar:" + alias.spec + "!/", null, null);
  resource.setSubstitution("myaddonpackage", alias);
  ...
}

function shutdown(aData, aReason) {
  if (aReason == APP_SHUTDOWN) return;

  let resource = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
  resource.setSubstitution("myaddonpackage", null);
  ...

The isDirectory check is needed because it is possible for add-ons to be installed in folders or XPI bundles, which require the jar: URI syntax. The code adds a resource alias, after which you can use URIs like this:


resource://myaddonpackage/images/someimage.png
resource://myaddonpackage/content/somescript.js

In these examples, images and content are folders inside your add-on bundle. Now you can add URI-based resources to your install.rdf like this:


  ...
  resource://myaddonpackage/content/icon.png
  resource://myaddonpackage/content/options.xul
  ...

Yes, the em:optionsURL works just fine for providing an options UI in your add-on. I hope this tip gives you more help converting your traditional add-on to a restartless add-on.

Updated: I added the removal of the resource: alias on shutdown based on feedback from Nils in comments. Check out his link to some great bootstrap add-on information too. The part about stale caches after an add-on update presents a painful problem. Waldimir has also previously posted about the limitations of bootstrapped add-ons.

Firefox Mobile – Managing Profiles

Last week we were talking about the need for private browsing, or something like it, in Firefox Mobile. Even though you might not share your phone with other people, you might share a tablet – especially the “family tablet”, sitting there on the coffee table. Private browsing is an obtrusive system, at least as implemented in Mozilla, and it’s doubtful we could add it for Firefox Mobile in time for the upcoming release. Also, private browsing doesn’t really satisfy the sharing use case for tablets.

Firefox has the concept of profiles, which could be useful for sharing. From the Firefox Support site:

Firefox saves your personal information such as bookmarks, passwords, and user preferences in a set of files called your profile, which is stored in a separate location from the Firefox program files. You can have multiple Firefox profiles, each containing a separate set of user information. The Profile Manager allows you to create, remove, rename, and switch profiles.

The built-in Profile Manager, used in desktop Firefox, isn’t really a good fit for mobile, so I made a simple, restartless add-on for managing profiles in Firefox Mobile, called Mobile Profiles. From the Preferences panel, you can add, remove and switch profiles.

I’m thinking about adding support for password protecting profiles and clearing private data when closing Firefox. Let me know what you think.

Development Details

I found the restartless, bootstrapped system I have been playing with worked well for this add-on. The nsIToolkitProfileService handles most of the heavy lifting. The UI is a little more complicated than my original trivial example, but I still only have two (2) files in my XPI. View the source here.

Without XUL overlays, I had to add the UI using DOM operations (createElement and setAttribute). Not a big deal, but it got tedious, so I made a little helper function:


function $x(aDocument, aElement, aAttributes) {
  if (typeof(aElement) == "string")
    aElement = aDocument.createElement(aElement);
  for (attribute in aAttributes)
    aElement.setAttribute(attribute, aAttributes[attribute]);
  return aElement;
}

Using this helper means adding elements via DOM is a little less syntax:


let itemManage = $x(window.document, "setting", {
  "id": "prefs-mobileprofiles-profiles",
  "title": "User Profiles",
  "desc": "Add, remove or switch profiles",
  "class": "setting-group",
  "type": "control"
});

I could probably make the helper function be a little more useful by adding some features you’d find in HTML DOM builders, but it’s good enough for now. I’ll probably add a chrome.manifest so I can support normal localization using properties files.

Bootstrap Jones – Adventures in Restartless Add-ons

We recently landed some code in Firefox Mobile to better handle restartless (bootstrapped) add-ons. Turns out, Dietrich made a non-Jetpack add-on using the SDK and tried unsuccessfully to install it into Firefox Mobile. In the process of getting everything working, I decided to make a simple restartless add-on we could use as a testcase. It’s a copy of Dietrich’s original add-on, without any SDK usage. I’ve not taken the SDK plunge yet. The experience was interesting, educational and slightly enjoyable. The process of testing the install / uninstall and enable / disable mechanism without a restart left me as giddy as a schoolboy.

I’ve decided to approach any future add-on development using the bootstrapped system. If I find a reason why it’s not possible, them I’ll switch back to the traditional XUL overlay approach. Below is the code from the testcase I created. There are only two (2) files! install.rdf and bootstrap.js. That’s it!

The install.rdf has the basic stuff along with the em:bootstrap flag that turns on the magic. My bootstrap.js file has the basic bootstrap methods, but only two, startup and shutdown, needed to be implemented. I added some code to handle adding and removing my add-on UI on demand. All I do is implement the loadIntoWindow and unloadFromWindow methods. Pretty easy, especially for lightweight add-ons.

Notice how I am using a data: URI for a button image, so I don’t need to package the file and add a chrome.manifest.

Just to be clear, I am not using Jetpack or the SDK. Firefox Mobile doesn’t really support Jetpack yet. This is just a simple bootstrapped, restartless add-on. I’m in the process of using this system for a bigger add-on, so I might need to make some adjustments. I’ll let you know how it goes.

install.rdf




    
        bootstrapdemo@starkravingfinkle.org
        2
        Bootstrap Jones
        1.0
        true
        Mark 'Bootstrap' Finkle
        
            
                {a23983c0-fd0e-11dc-95ff-0800200c9a66}
                4.0b4pre
                4.*
            
        
    

bootstrap.js


var Cc = Components.classes;
var Ci = Components.interfaces;

function loadIntoWindow(window) {
  if (!window) return;

  // Get the anchor for "overlaying" but make sure the UI is loaded
  let forward = window.document.getElementById("tool-forward");
  if (!forward) return;

  // Place the new button after the last button in the top set
  let anchor = forward.nextSibling;

  let button = window.document.createElement("toolbarbutton");
  button.setAttribute("id", "tool-homebutton");
  button.setAttribute("label", "Home");
  button.setAttribute("class", "button-control");
  button.style.listStyleImage = "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAo0lEQVR42u3VMQrCMBSA4Q7uOnsNB4%2FR0cM6ZPYyyRRIGhAzly5CNQrfD49Alhe%2BJdMkSZIk6d1ijMdSyryelNJp710hhMPWrpzzeRjAsiyX1tpzPf3%2B%2BgnsrV211hsAAAAAAAAAAAAAAAAAAAAAAACA0QDfHAAAAPweQH%2FUo5%2F3PafvCn4BAAAAAAAAAAAAAAAAAAAAAAAAAADGAkiSJOmPewEpGDS2TaImnAAAAABJRU5ErkJggg%3D%3D)";

  button.addEventListener("click", function() {
    window.Browser.loadURI("about:home");
  }, false);

  anchor.parentNode.insertBefore(button, anchor);
}

function unloadFromWindow(window) {
  if (!window) return;
  let button = window.document.getElementById("tool-homebutton");
  if (button)
    button.parentNode.removeChild(button);
}

/*
 bootstrap.js API
*/

var windowListener = {
  onOpenWindow: function(aWindow) {
    // Wait for the window to finish loading
    let domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
    domWindow.addEventListener("load", function() {
      domWindow.removeEventListener("load", arguments.callee, false);
      loadIntoWindow(domWindow);
    }, false);
  },
  onCloseWindow: function(aWindow) { },
  onWindowTitleChange: function(aWindow, aTitle) { }
};

function startup(aData, aReason) {
  let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);

  // Load into any existing windows
  let enumerator = wm.getEnumerator("navigator:browser");
  while (enumerator.hasMoreElements()) {
    let win = enumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
    loadIntoWindow(win);
  }

  // Load into any new windows
  wm.addListener(windowListener);
}

function shutdown(aData, aReason) {
  // When the application is shutting down we normally don't have to clean up any UI changes
  if (aReason == APP_SHUTDOWN) return;

  let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);

  // Stop watching for new windows
  wm.removeListener(windowListener);

  // Unload from any existing windows
  let enumerator = wm.getEnumerator("navigator:browser");
  while (enumerator.hasMoreElements()) {
    let win = enumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
    unloadFromWindow(win);
  }
}

function install(aData, aReason) { }

function uninstall(aData, aReason) { }

Firefox Mobile – A Few Add-ons

In addition to working on Firefox Mobile, I like to tinker around with building add-ons too. Not surprising I guess, since I seem to go on and on about how to build them.

Anyway, I have a few add-ons that you might find useful – either installed in Firefox Mobile or as examples you can rip apart and use to build your own add-ons.

Mobile Tools

Mobile Tools is a collection of a few utilities that might be useful when debugging mobile webpages. You’ll find commands for viewing page source and basic page info. Both open in new tabs. Page source shows the web page’s raw HTML content. You can zoom and pan through the source just like any other web page. Page info shows some simple bits of information about the web page:

Mobile Tools also supports window.console a little better than the built-in native support, including:

  • String formatting in logging methods (%s, %d, %i, %f)
  • dir and dirxml
  • Simplified trace
  • group and groupEnd
  • time and timeEnd

Start Page Plus

The built-in start page, Firefox Start, is pretty, loads fast and has some nice features. We can always add more! Start Page Plus adds a few sections to the standard Firefox Start page:

  • Favorites: A list of bookmarks you want displayed on the start page. Just tag a bookmark with “start” and it appears on the start page.
  • Local Search: Quickly find various types of places near you with a simple tap. Includes food, hotels, gas, movie theaters and your own location – shown on Google Maps. This feature does use the built-in geolocation feature.
  • Local Twitter: Shows tweets happening near you. You don’t need to be logged into Twitter. The stream is based on your location.

Use the add-on options to show and hide sections as well as move them around the page. You can also set the zone used to pull in tweets from Twitter.

Firefox Mobile and window.console Support

Desktop Firefox added native support for a subset of the window.console API. It’s a subset in that only the following API methods are supported:

  • console.log(arguments)
  • console.info(arguments)
  • console.warn(arguments)
  • console.error(arguments)

Also, the Firebug string formatting features are not supported yet.

Last night we turned on basic support in Firefox Mobile too. It’s basic in the sense that the window.console is merely forwarded to the Error Console.

Mobile Add-on Development

I was part of this year’s Add-on Con event, presenting some information on building add-ons for Firefox Mobile. Learning how to work in multiple processes can be tricky at first, but once you get used to working with messages, it gets easier. The information was also relevant for Firefox Desktop, since multiple processes will show up there in a future release. Felipe Gomes is working very hard on getting that project jump started.

In addition to the presentation, I made some video tutorials on building Firefox Mobile add-ons, including a multi-process add-on. Fellow Mozillian, Sara Yap, encouraged me to add some code to the page to allow subtitling. I am excited to see how that works out.

Mozilla is currently running a contest for mobile add-ons – The Firefox Mobile Add-ons Cup. Get your add-on entered as soon as possible. Join Mozilla IRC (#mobile or #mobileaddons) if you have questions about building your add-on.

Firefox Mobile keeps getting better and better. I recently acquired a Samsung Galaxy Tab and I have to say, Firefox is awesome on the Tab.