Firefox for Android: Remote Debugging is Here

Lucas blogged a while ago about some of the initial work on creating a remote debugging system for Firefox on Android. The desktop patches Lucas mentioned have landed, and remote debugging Firefox for Android is now possible.

Remote debugging requires using Desktop Firefox to connect to Mobile Firefox over USB using ADB. Remote debugging is supported in Firefox 15 and newer releases. Here’s a quick guide to getting started:

  1. USB Connection: Connect your Android device running Firefox to a host machine running desktop Firefox using USB. In a terminal, forward the TCP connection using: adb forward tcp:6000 tcp:6000
  2. Firefox Desktop: Use about:config to enable remote debugging. Set the “devtools.debugger.remote-enabled” pref to true. Restart. You should see a “Tools > Web Developer > Remote Debugger” menu.
  3. Firefox for Android: Use about:config to enable remote debugging. Set the “devtools.debugger.remote-enabled” pref to true. Restart.
  4. Firefox Desktop: Choose “Tools > Web Developer > Remote Debugger” and a Remote Connection dialog should open. If you changed none of the other remote debugger preferences, just use the defaults. Press OK.
  5. Firefox Desktop: You should see an empty debugging window appear.
  6. Firefox for Android: You should see a remote debugging connection prompt appear. Press OK to initialize the connection. You have 3 seconds, by default, to accept the connection. You can retry 3 times.
  7. Firefox Desktop: Once the connection is accepted, the empty debugging window will fill with scripts running in the Firefox for Android web page.

Thanks to Lucas, Panos Astithas and the rest of the DevTools team for making this happen. Happy Debugging!

Firefox Android: Add-ons in a Native World

One of the first things I yelled about when we were debating switching to a native Android UI for Firefox was add-on support. Using a XUL-based UI meant add-ons were free. The Mozilla platform has support for add-ons baked right in. Moving to a native UI would surely kill our ability to support add-ons, right? Wrong!

Add-ons are an important part of the Firefox story. Native UI builds of Firefox support add-ons. There are some things an add-on developer needs to be aware:

  • The add-ons system is the same one used in other Mozilla applications. We did not invent a new add-on system.
  • Native UI builds are considered a new application and are not add-on compatible with the XUL versions. The application ID for native UI builds is: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
  • There is no visible XUL in the UI, so using overlays to try to add or change UI is useless.
  • There is a simple NativeWindow object that allows you to manipulate parts of the native Android UI.
  • Services like nsIPromptService and nsIAlertsService are implemented to use native Android UI.
  • Since overlays are useless for UI and JavaScript APIs are available for native UI, you should seriously consider just making a restartless add-on.

NativeWindow

We wanted to give add-on developers some APIs to manipulate the native Android UI, so we create a helper object called NativeWindow. The API is still young, but it gives you access to: Android Menu, Doorhanger Notifications, Context Menus (in web content) and Android popup toast alerts. The object is currently part of the main browser window, but we are considering moving it to a JS module. The basic API is here:


/*
 label: menu label
 icon: file:// or data: URI for an icon
 callback: JS function called when menu is tapped
 returns a menu ID that can be used to remove the menu
*/
menuID = NativeWindow.menu.add(label, icon, callback);
NativeWindow.menu.remove(menuID);

/*
 message: displayed text
 value: string based tag
 buttons: array of JS objects used to create buttons in the notification
 tabID: tab associated with this notification
 options: JS object that has 'persistence' and 'timeout' options
*/
NativeWindow.doorhanger.show(message, value, buttons, tabID, options);
NativeWindow.doorhanger.hide(value, tabID);

/*
 label: menu label
 selector: JS object that has a 'matches(element)' function. Used to show the menu.
 callback: JS function called when menu is tapped
 returns a menu ID that can be used to remove the menu
*/
menuID = NativeWindow.contextmenu.add(label, selector, callback);
NativeWindow.contextmenu.add(menuID);

/*
 message: displayed text
 duration: "short" or "long"; Used for alert timeout
*/
NativeWindow.toast.show(message, duration);

Some examples of what the API can do:

Doorhanger Notification


Menu Item


Context Menu Item


Toast Popup Alert


The NativeWindow API will continue to grow and mature, but I think even now it shows that add-ons can have first-class interactions with the native UI of Firefox. I am looking forward to developers trying it out and helping us push the API forward.

Add-ons: Binary Components and js-ctypes

Lots of discussion going on recently about the affect of 6 week development cycles on binary XPCOM components in add-ons. I don’t want to re-hash those discussions, but Daniel Glazman brought up an interesting point in a comment on Wladimir’s post. Wladimir was suggesting binary component developers start moving to js-ctypes. Daniel pointed out that there are two classes of binary XPCOM components:

  • XPCOM wrappers around 3rd-party binary libraries: We use this model for exposing external binary functionality into JavaScript so add-ons and applications can access the libraries. Using js-ctypes should provide a simple, non-breaking way to expose the libraries. You create a simple JavaScript wrapper in a JavaScript XPCOM component. We need more examples of using js-ctypes to do this, but it works.
  • Pure binary XPCOM components built only using the Mozilla platform: Sometimes the functionality you want to expose is actually locked away in the Mozilla platform itself. Maybe there is no public nsIXxx interface or the existing interface has a [noscript] attribute on a property of method. This model shouldn’t be required anymore, in my opinion. Mozilla is pushing JavaScript based components and we should be exposing as much as possible to chrome JavaScript. I would encourage add-on developers to file bugs and lobby to expose binary-only parts of the Mozilla platform to chrome JavaScript.

JavaScript ctypes has come a long, long way since it was started back in 2007. Let’s start leveraging it more. The ctypes model has been used quite effectively in other languages.

Firefly – Remote JS Shell for Firefox Mobile

A long time ago, in May 2008, I posted about a remote JS shell add-on I was building. I had been tinkering with a few existing projects (JSSH, SD Connect and MozRepl) but wanted to build something small, lightweight and mainly focused on helping add-on / XUL developers interact with JS running in a separate application. I tried to get the protocol closer to that used by Opera DragonFly and Crossfire, but I never had the time to get it exactly right. When I started work on Firefox Mobile, I used the add-on to interact with Firefox running on a mobile device from my desktop machine. Unfortunately, I never felt the UI and the code were good enough for a public release.

Recently, I dusted off the code, converted to restartless add-ons and made a very simple in-content UI. I say add-ons, because there are two: a probe, and a shell. You install the probe into Firefox Mobile and install the shell into Firefox Desktop (or Firefox Mobile running on a desktop – it’s up to you).

Shell

The shell is implemented as an in-content about: page [1]. After installing the shell, navigate to “about:firefly” and you’ll see the simple UI. The shell can act as a listening server or you can connect to a relay server. The listening server is simplest, so start with that.. You start the server on a specific TCP port. Once started, the shell waits for a probe to connect.

Probe

The probe is simple. After installing it, you just point it at an instance of the shell by entering the IP address and port. You can connect and disconnect from the “Remote Debugging” preferences. That’s all you need to do with the probe.

Starting a Session

Once the probe has connected to a shell, you can enter JavaScript commands into the simplistic shell UI, the code is sent to the probe and evaluated in a sandbox running in the application. Because it’s a sandbox, the probe injects some helper properties and methods:

  • window – The active chrome window. With this object, you have full access to the window’s JS and DOM.
  • firefly – Injected API with some special utility methods:
    • getWindow(type) – Returns the chrome window of a given type.
    • getWindows() – Returns an array of all open windows.
    • inspectJS(object) – Lists all properties and functions associated with a given JS object.
    • inspectDOM(selector or element) – Dumps the markup for a given DOM element. You can pass a CSS selector string or a real DOM element to the method.

Examples

Since we are talking about Firefox Mobile, you should be familiar with the internal chrome UI code before starting to poke around. The main browser.js has a Browser object that acts like a manager for the open tabs, so let’s play with it:

URL of active tab:
window.Browser.selectedBrowser.currentURI.spec

Add a new tab:
window.Browser.addTab("http://mozilla.org", true)

Inspect the active tab JS object:
firefly.inspectJS(window.Browser.selectedTab)

If you’re interested in using Firefly, you can install the add-ons from here:
Shell: firefly-shell
Probe: firefly-probe (mobile shortcut: http://bit.ly/irvpjc)

Source code:
Shell
Probe

Next Steps

  • Add access to the web content running in the child process. Firefox Mobile is multi-process, so you can’t directly access the web content from the main process.
  • Add a pretty output for the inspectXXX helpers. Instead of just dumping the simple text output into the HTML, we could make the output more dynamic – think Firebug panels.
  • Add helpers to do more profiling and data collection. Many times I want to know what is happening on the device. Things like CPU and memory usage or why the profile data is exploding.

Bug reports and feature requests welcome.

[1] Yep, an about: page in a restartless add-on. It wasn’t too hard. I am using a resource: alias for the external CSS file and the favicon. I could have just move the CSS into the XHTML file and used a data: URI for the favicon.

Firefox Mobile – Monitor HTTP Headers

I just added support to Mobile Tools for monitoring HTTP headers in Firefox Mobile. I just wanted a way to look at some HTTP traffic to help debug some mobile websites. Screen space is limited on mobile devices, so we don’t get to have nice floating panels like in desktop Firefox’s web console or Firebug. Instead I opted for a slide out panel:

Tap the little tab and the panel slides out and back. Use the Pause and Clear buttons to manage the HTTP traffic. Tap an item in the list to expand the request, response and cookie details:

You can pan the list in any direction, as needed to see the contents. The panel tab takes up very little space while browsing, but you can also hide it completely using the add-on options:

In case you don’t know, Mobile Tools also adds support for viewing Page Info and Page Source too.

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!

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 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.

Fennec – More Theme Goodness

Recently, we released Fennec Beta 3 for Maemo (Alpha 3 for Windows Mobile is very close). The new release has several internal changes that might be of interest to add-on developers – or people curious as to how the darn thing works.

Roy Frostig posted an nice article about the changes to the way we render web pages. The results are dramatic: panning is much smoother. The approach also gives us more ways to increase performance and user experience in the future by creating a great foundation.

We also landed what we hope is the final approach for styling the user interface. I previously posted about the affect of themes on performance and how that drives the decisions we make. We are still using the images-for-buttons approach, but we had a problem. Using images-for-buttons doesn’t work for buttons with text, which can be variable in size.

We decided to extend the image-for-buttons approach for all buttons, using the border-image CSS property. Now all of our buttons have a nice, consistent look. We still have performance concerns with border-image, but we should be able to squeeze more speed out of the code. We also use the border-image approach to improve the appearance of the toggle controls and the radio option controls.

New Toggles and Buttons

New Radios

In fact, add-ons can, and will in some cases, use the new button styles too. We are in the process of updating the Best Practices document with how to make use of the themes and styles.

New Life for Old Projects

So much to do, so little time. My workspace is littered with projects I started but couldn’t find the time to keep going. It’s kinda sad and I do feel guilty.

Sometimes those projects find a new life. Recently, two of my old projects found new life: FizzyPop and js-ctypes are both active again.

Doug Warner has started building a Mozilla Project Wizard on the FizzyPop source code. Doug is requesting feedback for improvements. You can try it out here.

Dan Witte has started looking into moving js-ctypes into the Mozilla tree! There are also plans to add a nice JavaScript wrapper API around the XPCOM too.

I feel less guilty now.