Cloud Printer – Print from Firefox Mobile

Recently, Google released the beginnings of a cloud-based printing system called – Google Cloud Print. The project is still in Beta, but you can install a recent version of Google Chrome on Windows and attach your local printers into the Cloud Print system (more). Google has added support for Cloud Print to few of their mobile web apps, but has not released a client application API yet. However, they did release a simple webapp demo – and where there is a demo, there are people reverse engineering it.

Cloud Printer is a restartless Firefox Mobile add-on that integrates into the Google Cloud Print system. Firefox Mobile already has code to save web pages as PDF. I took that code and send the PDF to the Google Cloud Print system.

Using the demo code and some other examples, it was fairly simple to get this to work. The current API is fairly simple and nice. I expect a few changes when the APIs become official, which should happen soon.

Note: You need to be signed into your Google Account for this to work. If you are not signed in, you’ll see a prompt and we open the Google Account login page for you.

Let me know how this works for you!

Restartless Fun

I like experimenting with restartless add-ons. This time I used a variation of the include technique to assign an image to the toolbar button and get access to a CSS file and IPC frame script. The CSS file is loaded and unloaded using nsIStyleSheetService. The IPC frame script is loaded using the global message manager. The frame script is needed because the PDF file must be generated in the content process, not the main process. You do remember that Firefox Mobile is multi-process, right?

One gotcha. Once loaded, you can’t currently unload a IPC frame script. So if you enabled/disable a restarless add-on that loads a frame script, you’ll end up with multiple instances running. Which means, when you send an IPC message, multiple listeners will respond – this is bad. I hacked around that by sending a “disable” message to my frame script when uninstalling the add-on. The “disable” message just removes the message listeners in the frame script so they won’t respond to any messages in the future. See the source code.

These restartless techniques are documented in various places, but we should start some code snippet docs on MDC too.

Firefox Mobile Add-on – Cloud Viewer

I just put together a simple restartless add-on, Cloud Viewer, that integrates the Google Docs Viewer into Firefox Mobile. I saw a few similar add-ons on AMO for desktop Firefox, but none for Firefox Mobile. Opera and Dolphin also have add-ons integrating support for online document viewers.

The concept is pretty nice, especially for mobile devices: Open online documents in an online viewer. No need to waste time downloading the file and waste space on your device saving the file. Google’s viewer supports Powertpoint (PPT), Word (DOC, DOCX), Excel (XLS, XLSX), PDF and some non-web image formats. The viewer even uses a mobile UI when running on devices.

Restartless Fun – The Details

I wanted the add-on to be restartless because restartless add-ons rock. I also wanted the add-on to work at a deeper level than a context menu. I could have put a menu item on the context menu to open links in Google’s viewer, but that wouldn’t work on links that weren’t directly pointing to the file – think Google search results. The link Google gives you in a search result redirects you to the actual file, so a context menu item wouldn’t be the best solution. The redirection issue occurs more than you think on the web. Instead, I override an XPCOM component the controls how Firefox treats files the browser doesn’t natively handle. Normally, this component (nsIHelperAppLauncherDialog) starts a file download and will open the file using a local application.

Cloud Viewer overrides the component. I check the extension of the direct URI to the file and if supported, I ask the user if we should open the file in an online viewer. Since the add-on is restartless, I can’t use the normal method of registering JS XPCOM components. I have to manually register and unregister the component myself. This worked fine and was pretty easy. See the source code.

I really think we should be writing more restartless add-ons, especially the simple ones. Great UX and a really small download. Only 2.5KB XPI file for Cloud Viewer. The internal support for restartless add-ons will only keep getting better.

Zippity & Test Harness Updates

A few weeks ago I posted about Zippity, a way for anyone to help collect performance data for Firefox Mobile. Data has been coming in and so has some feedback. I added some features to the Test Harness add-on to make it easier to use and collect some new types of data. Here’s a list of changes:

  • Added a proper in-content page for starting tests, and checking out previous result.
  • Added support for measuring startup time, as well as, SunSpider and V8 JS benchmarks.
  • Added basic support for desktop Firefox (thanks to Dietrich Ayala.)
  • Added a page timeout for pageload tests. Sometimes pages just get stuck – either a bug in Fennec or a mystery of the ether.
  • Added a simple goodput measure for pageload tests. We time how long it takes to download a 250KB test file from Zippity. It’s a simple metric we can use to categorize the pageload results.

All tests are preconfigured, you just need to tap the test you want to run and the add-on does the rest. Here’s a basic rundown of how each test works, so you have an idea of what to expect:

  • Pageload: Opens a new tab and starts loading a predefined list of pages. We load the list of pages 3 times and take the average.
  • Startup: Fennec will restart itself five (5) times, remembering the startup time for each restart and then taking the average. Startup time is defined as the time from launching the process to the end of loading the initial page.
  • SunSpider and V8: Each subtest of the benchmark is a web page, loaded and executed. The page reports the time needed to complete the subtest. The final result is the total of all subtests.


I also updated the Zippity server to support the new data types and added some new features:

  • Added graphs for viewing startup time, SunSpider and V8 results.
  • New mobile pages built using jQuery Mobile. Now you can install the add-on and view the graphs from your mobile device. Still needs some tweaks, but it’s a start.
  • Added the goodput measure to the graph tooltips. Will only appear for newly collected data that has the metric.

The SunSpider and V8 tests are taken from the Mozilla Talos test suite. The Test Harness add-on can load and time the tests just like the Talos system can, so I didn’t need to change a thing about the tests. Yes, that means I can add other Talos based page tests too, such as the DHTML and SVG tests.

I addition to some new Talos tests, I was thinking of adding some monitoring to Test Harness. Dietrich has an add-on to collect some UI responsiveness measures. I think we could add something like that to Zippity. Taras Glek has ideas for monitoring memory usage and posting the results once a day. That also sounds like a good use of Zippity.

If you use Zippity / Test Harness and have ideas, comments or found bugs – let me know. Comment here or join Mozilla IRC and look for “mfinkle” in #mobile (or several other channels).

Updated: I fixed a problem with the Pageload test manifest preference. It was pointing a 404 so the pageload tests wouldn’t work. The add-on is fixed now. Thanks AaronMT.

Zippity – Using the Crowd to Collect Performance Data

If you’re serious about improving your application’s performance, you need to collect data. At Mozilla, we use Talos. The Mobile team has been using Talos to track performance on Maemo (using N810 and N900) devices for a few years and recently started tracking Android (using Tegras) as well. Here are a few of the metrics we track:

  • Startup (Ts)
  • Pageload (Tp)
  • HTML Rendering (Tdhtml)
  • Sunspider (Tss)

We build the application every time code is checked into the source repository. After each build, we run these performance tests and watch for changes. Regressions happen and need to be fixed as soon as possible.

One limitation of the current Talos system is the hardware variety. For Firefox Mobile, we run tests on two (2) types of hardware: N900 (Maemo) and NVidia Tegra (Android). One problem is that this doesn’t map to the real world very well. Don’t get me wrong – the current system minimizes measurement noise and we can see regressions fairly easily. However, without testing on a variety of devices, we just don’t get a good picture of how Firefox Mobile runs on your device.

Hello Zippity!

One idea that has talked about is to get the community involved in testing performance. If we could leverage many different people, using many different phones, we might get a better picture of how Firefox Mobile performs in the real world.

I created a simple add-on (Test Harness) that can install in any Firefox 4 for Mobile release and can run a series of tests. Then it posts the result to a public data server (Zippity) and anyone can view the results. When a test result is posted to Zippity, we capture a few bits of information:

  • Test Type: Pageload, startup – whatever the test might be tracking.
  • Test Manifest: What was the actual sequence performed? What series of pages did you load? This is important if we want to try to minimize noise in the results. If you want to only compare test runs that used the same input data, it can help reduce noise and make trends easier to see.
  • User Key: In case you want to only see results submitted by a particular user. The user key is completely optional. It can be used to help filter extraneous results.
  • Device Metadata: Information like device type, OS, OS version. Again, helpful for filtering.

I have been running Firefox Mobile nightly builds on several Android devices using a static set of webpages, hosted from a local webserver. Very controlled, to minimize noise and try to see small changes over time. You can see the results here.

Anyone can run the same set of live, real world pages using the add-on. Because of differences in devices, networking latency and changes to the actual website content – the results won’t be the same. That’s OK! The real world results are just as meaningful and provide just as much valuable insight.

Currently, the Test Harness add-on only sends pageload (Tp) data to Zippity, but plans are underway to extend the test types. If you want to play along, just install the Test Harness add-on. It is already configured to run the pre-defined pageset and submit the results to Zippity. Just push the button:

Zippity is still evolving and doesn’t have a lot of features. The source code should be in my Mercurial user repo soon. I’ll post more about Zippity and the Test Harness add-on as things improve.

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.