Writing a Panel to the Browser

During our most recent client meeting we realized that we should not actually be utilizing the add-on SDK’s Panel object to render a panel. Instead, we were informed that we should be writing XUL directly to the main panel in the browser window. This involves two parts: locating the exact parent element to which we are writing and actually writing XUL from a file to our panel element. We had no idea how to do either of these things.

In our group session on Friday we spent a lot of time as a group tackling grabbing the correct parent panel element. Gijs pointed us toward the Window Mediator, which is a Services module. To load this, we run the following snippet:

let {Cc, Ci, Cu, Cr, Cm, components} = require("chrome");
Cu.import("resource://gre/modules/Services.jsm");

Then, we get a list of all of the browser’s “windows” using the window mediator’s getEnumerator() method. It can enumerate all windows of a certain interface if you pass it arguments, but for now we’re getting all of them by passing it null.

var allWindows = Services.wm.getEnumerator(null); // Use the window mediator object to get all windows in the browser

Once we’ve got all of the windows, we need to find the main browser window. To do so, we iterate over all of the windows using the window mediator’s hasMoreElements() and getNext() methods. When we did this the first time, we manually inspected each window’s innerHTML property to see if we recognized anything. Once we found the main window, we figured out that it has a location.href of ‘chrome://browser/content/browser.xul’. Now we have a condition to check for to indicate whether it’s the main window. The whole loop looks like this:

var allWindows = Services.wm.getEnumerator(null); // Use the window mediator object to get all windows in the browser
var browserWindow, // Firefox's top-level "window"
    thisWindow; // Iterated window
while (allWindows.hasMoreElements()) {
    thisWindow = allWindows.getNext();
    if (typeof(thisWindow.location.href) !== 'undefined' && thisWindow.location.href === 'chrome://browser/content/browser.xul') {
        browserWindow = thisWindow;
        break;
    }
}

So we’ve got the main window. Now we need to write our XUL to it. I spent several hours on Saturday trying to figure out how to use Firefox’s File I/O capabilities to do this, but it turned out to be really simple. We can actually do this with the Add-on SDK’s data loading capabilities. We can access the add-on’s data directory by getting its data property.

let data = require("sdk/self").data;

This property has a very nice method, load(), which reads the contents of a file in our data directory, such as test.xul, and returns that data as a string. If we have an HTML element, we can do something like this to write our file’s contents as HTML to the DOM:

someElement.innerHTML = data.load("test.xul");

Awesome! So easy! How did I waste 5 hours before remembering this was a thing?!

So now that we’ve got all of the pieces, we put all of it together to make a panel and write the contents of our test.xul file to be its inner HTML.

/* ********
 * Requires
 * ********/
// Chrome
let {Cc, Ci, Cu, Cr, Cm, components} = require("chrome");
Cu.import("resource://gre/modules/Services.jsm");

// SDK
let data = require("sdk/self").data;

/* ***********
 * Panel Setup
 * ***********/
// Get the window
var allWindows = Services.wm.getEnumerator(null); // Use the window mediator object to get all windows in the browser
var browserWindow, // Firefox's top-level "window"
    thisWindow; // Iterated window
while (allWindows.hasMoreElements()) {
    thisWindow = allWindows.getNext();
    if (typeof(thisWindow.location.href) !== 'undefined' && thisWindow.location.href === 'chrome://browser/content/browser.xul') {
        browserWindow = thisWindow;
        break;
    }
}

// Put our extension's XUL in the main panel
if (typeof(browserWindow) !== 'undefined') {
    // Create a panel view
    let panelview = browserWindow.document.createElement("panelview");
        panelview.id = "testId";
        panelview.className = "testClass";
        panelview.innerHTML = data.load("test.xul");

    // Inject our panel view into the multiView panel
    let multiview = browserWindow.document.getElementById("PanelUI-multiView");
        multiview.appendChild(panelview);
}
Advertisements

~ by slenkeri on September 24, 2013.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: