2006/07/17

How to preserve scope in Javascript function calls

Developing all of your application logic in Javascript is a bit of a challenge if you grew up in a world of full object orientation and languages like C++, Java and C#. I still have a very strong OO inclination, so I use it in my code as much as possible. I feel this makes things easier to understand and better organized. Leaving dozens or hundreds of loose functions and variables in your code makes things so much harder for everyone. In my opinion it's better to know precisely what object knows X and what object is responsible for doing Y. I'm sure I'm not alone on this. Please stop using globals.

In my own personal experience, and after reading thousands of lines of Mozilla code, I've come to the conclusion that plain classes and objects are pretty rare in XUL code. It's much more common to use the so-called static classes, which are kind of like singletons. The syntax to create such an object goes like this:

var MyObject = {
attribute1 : 5,
attribute2: null,
function1 : function() {alert(this.attribute1); },
function2 : function(param1, param2) { /* ... */ }
}

You get the idea. The MyObject object doesn't really have a class. It's a class and an instance declaration, all in one. You can call its methods or get or set its attributes (obviously not recommended, that's what methods are for) in the usual way:

someResult = MyObject.function1();
MyObject.attribute1 = 7;

You can use the "this" identifier inside the object just like you would on any object, and all attributes preserve their state as long as the object exists.

But there's a problem with this, and maybe you already ran into it. Under certain circumstances you reach for your attributes and end up with uninitialized variables that are not what you expected. This probably happened because you used the method of an object as a callback function for some asynchronous event. Classic examples of this are the "window.setTimeout" function, event handlers and observers. You're probably passing a pointer to your method like this:

window.setTimeout(MyObject.function1, 1000);

This is OK. The problem happens on the other end. Say you execute the following code:

MyObject.attribute1 = 10;
window.setTimeout(MyObject.function1, 1000);

You'll notice the alert box is showing the value "5" as opposed to "10". All variables will be uninitialized, and you'll find yourself as if you had a completely different object. And you do. Since these callbacks occur in a different scope, they don't see the MyObject object we created. They call "function1" as if it were any loose function, so it runs completely out of context. I don't entirely understand what goes on here, but we don't need to. What we need is a solution.

Some valuable feedback made me change the preferred solution in this post. There are some important aspects to consider here, such as elegance and readability of code. My first solution was elegant in that it doesn't use anonymous functions - which I avoid like the plague - but it fails in being very nice to look at. It looks too much like a hack. The solution proposed in the comments below is much more easier to look at, albeit relying on the dreaded anonymous functions. It is also more general. As you'll see in the explanation for the old solution, this only works for static objects, and uglier hacks are required for regular objects or XBL inner objects.

The old solution - do not use!

You rewrite "function1" like this:

function1 : function() {
if(this != MyObject) {
return MyObject.function1();
}
alert(this.attribute1);
},

This odd hack is enough to fix the problem. The "if" statement checks whether the method is being called in the right scope. If it isn't, the right call is performed, and the first call returns without executing the rest of the code. It's the second one, the method inside our object, that does all the work. Now we'll see the "10" we wanted.

You may feel tempted to just add this code to all of your functions and don't worry about this problem ever again. Don't. This is not a pretty hack, and adding so much code is very unnecessary. It's also better that you realize when it's required and when it isn't.The linked reference, Preserving Scope in JavaScript, shows a wider array of solutions which you may find handy for other situations of lost scope.

The new solution

Rewrite the asynchronous scheduling like so:

window.setTimeout(function(){MyObject.function1();}, 1000);
Easy, right? Now the asynchronous call will be performed on the anonymous function, which will in turn call the function in our object, this time in the way we expect. The same will work with instances of regular classes, or functions inside XBL bindings.
You may feel tempted to add more and more code inside the anonymous function, but I strongly recommend against this practice. Anonymous functions make it very hard to debug and read Javascript code. Please use them with moderation, preferably only as proxies for a single function or method call.

Labels: , , , ,


2006/07/13

A simple color picker input on XUL

Today I'll show how to develop a binding that renders a simple color picker widget. This isn't something most people will need, but I did, so maybe you will too. This post also serves as a nice little example of the power and flexibility of XBL bindings.
I require a color picker input for an extension I'm currently working on. This extension requires that most interface elements to be customizable, styles included. This made having a color picker a mandatory requirement. So I started my quest for the information I'm about to give you. For free ;).
As usual, I thought at first that this was going to be real easy. I had some vague recollection of a XUL element that had this very purpose. So I looked it up and found it: the XUL colorpicker element. But alas, this was far from what I needed. This element is a little to low-level, if you look at it from the bright side. If you show the element as a button, you will see that this button opens as little popup that lets the user pick a color from a small grid. The problem is that clicking on any color on the grid does nothing. No color is picked. The grid remains open. It's just not fully functional. Or maybe I forgot to set the "pleasework" attribute to "true". Here's a screenshot of the element after being clicked:

But I'm not impatient, or too proud, so plan B sprung into action: looking for some extension that implements a color picker similar to the one I needed. After browsing around for a while, I found color pickers in the Colorzilla extension and the Nvu web authoring tool. Those two were a little too much for me. They required a new window and were way more advanced than what I needed. So that was a dead end too.
Well, not really. It kind of gave me the idea of what I needed to do to solve this problem: wrap the partly functional XUL element with a binding that gave me what I really needed. The question was, what did I need? I wanted something simple, so a functional colorpicker element might do. But then I thought that I also wanted something a little more flexible, that would allow more advanced users to pick any color they wanted. I had to find a compromise between complexity and functionality, and in the end I settled with this:

My idea was to have a colorpicker widget that included a textbox showing the picked color. Nothing particularly innovative, but it's not part of the current element set, as far as I know. This solution would allow novice users to pick basic colors from the grid, and the color they picked would be shown on the textbox. More advanced users would be able to type any valid CSS color they wanted in the textbox (including colors such as "black" or "rgb(234, 12, 0)"), and the button would show that color the user typed.
Since my color input required to behave as a single unit (I wanted a single "value" attribute that could be set from a template or read from a script), an XBL binding was in order. Here's the code for the binding and a little explanation about it:
<binding id="color-input">
<content>
<xul:hbox>
<xul:textbox anonid="color-textbox" inherits="value"
onchange="this.parentNode.parentNode.value = this.value; return true;" />
<xul:colorpicker anonid="color-picker" type="button" inherits="color=value"
onselect="this.parentNode.parentNode.value = this.mPicker.color; return true;" />
</xul:hbox>
</content>
<implementation>
<constructor><![CDATA[
var picker =
document.getAnonymousElementByAttribute(this, "anonid", "color-picker");
var color = this.getAttribute("value");
picker.initialize();
/* avoid problems with templates. */
if (color && (color.search("rdf:") < 0)) {
this.value = color;
} else {
this.value = "#000000";
}
]]></constructor>
<property name="value"
onget="return this.getAttribute('value');">
<setter><![CDATA[
var picker =
document.getAnonymousElementByAttribute(this, "anonid", "color-picker");
var textbox =
document.getAnonymousElementByAttribute(this, "anonid", "color-textbox");
picker.color = val;
textbox.value = val;
this.setAttribute("value", val);
]]></setter>
</property>
</implementation>
<handlers>
<handler event="DOMAttrModified"><![CDATA[
if(event.attrName == "value") {
this.value = event.newValue;
}
]]></handler>
</handlers>
</binding>
The color input would then be used in my XUL file as follows:
<colorinput value="black" />
Having the proper CSS binding set for the colorinput tag, obviously.
The content of the binding has two simple XUL elements: a textbox and a colorpicker. They both inherit the value attribute from the bound element, and they both implement methods that will update the "value" property when they are changed. The "onselect" handler in the colorpicker is worthy of notice, because that's one of the changes required to add proper functionality to the element. When a color is picked from the grid, this event is fired, but the "color" attribute is not changed, as it should. The "mPicker.color" property is the one that contains the newly selected color, so that's the one you want to use to set the value. The call of the "initialize" method on the picker is the other thing you need to keep in mind. For some reason the element is not properly initialized on the binding.
This binding is "template-friendly", as described on my Attributes and properties post. I also added a little validation on the constructor so that elements inside templates don't throw CSS errors. It's not a big deal, but the cleaner the better.
I've tested this input extensively and I'm very pleased with the end result. Hopefully this will prove to be useful for you as well.

Labels: , , , , , , ,


2006/07/08

Basic CSS handling

XUL applications will rarely lack CSS stylesheets. You will usually need them to style your windows, icons, etc. Pretty much anything the user will be looking at. An important advantage of this approach is that extensions become skinable. Theme developers can create themes that affect all browser appearance, including your extension. If your extension is popular enough, they might even override its specific styles, to make it blend in better with the theme.
But sometimes you'll need to handle CSS from your scripts, perhaps to have user-set styles. Doing this in Firefox is quite easy, because of its compliance with the DOM 2 Specification. The best way to learn about this is reading the DOM Level 2 Style Specification, from the W3. You'll find that most (if not all) objects decribed in this spec can be found in the XUL Planet specifications as well. They allow you to very easily manipulate stylesheets from XUL or any web page. It's the same for both, except that different stylesheets apply for each.
But I'm not the type to post a link and leave you hanging, even though those specifications are extremely helpful. One extension I was working required some style handling, so I'll show some of the stuff I had to do and the little details you need to keep in mind.
Your starting point is the following object:
document.styleSheets
This is a StyleSheetList object (StyleSheetList in the W3). This object is basically an Array of the stylesheet that apply to your page. In my case, I only needed a specific stylesheet I inluded in my extension, so my code looks something like this:
var cssRules;
for (i = 0; i < document.styleSheets.length; i++) {
if (sheets.item(i).href == "chrome://myextension/skin/myextension.css") {
cssRules = document.styleSheets.item(i).cssRules;
break;
}
}
Now the variable cssRules contains all the CSS rules that I had set in that specific file in my extension. I used the href property (from interface StyleSheet) to identify the file, and the cssRules property (from interface CSSStyleSheet) to obtain the rules.
Then I needed to work with some specific selectors (#main-page or .column, for example), so I extracted them from the rules in the following way:
for (i = 0; i < cssRules.length; i++) {
cssRule = cssRules.item(i);

if (cssRule.type == cssRule.STYLE_RULE) {
switch(cssRule.selectorText) {
case "*|#main-page":
case "#main-page":
pageRuleSet = cssRule;
break;
/* ... */
}
}
}
The cssRules object is a list of CSSRule objects, which can have different subtypes. I was only interested in CSSStyleRule objects, which are your typical style declarations, such as:
#main-page {
background-color: black;
margin: 2em;
}
As you can see, I used a switch statement to discern between the different selectors. The case statements are doubled because of some odd quirk I found out during development: the selectors have their normal text set when the page is loading for the first time, but then they would have a "*|" prepended afterwards. Since this script is run on page load and reload, they would both have to catch both types of selectors. I really don't know what that prefix means.
Anyway, this code gives us a set of objects of type CSSStyleRule, which we can store somewhere and use afterwards. Getting and setting CSS properties is very easy now:
bgColor = pageRuleSet.style.getPropertyValue("background-color");
pageRuleSet.style.setProperty("background-color", "black", "");
The third parameter in the setProperty method of interface CSSStyleDeclaration corresponds to the priority of the value. It can be either an empty string or "important". If you want your style to absolutely, positively apply all the time, then you should use "important". Otherwise, it's better to play nice and not use it. The one case where it's necessary to use "important" is when overriding styles for text link selectors in XUL. The applied styles won't work otherwise.
Well, that's all I have for today. I think this is a pretty decent introduction to CSS handling, both in XUL and regular web pages. I will complement this post in the future with another post on how to create a simple color selector.

Labels: , , ,


2006/07/06

Attributes and properties: the essential difference

When reading specifications for XUL elements on XUL Planet or elsewhere, you'll often find both attributes and properties for an element. You'll probably notice that there are several attributes that have a corresponding property, with the exact same name. Examples of this are "hidden" and "value", very commonly used.
If you have a XUL element object on your script, you can get or set an attribute like so:
someValue = element.getAttribute("someAttribute");
element.setAttribute("someAttribute", someValue);
Getting and setting properties is done like this:
someValue = element.someProperty;
element.someProperty = someValue;
There are some differences between attributes and properties. Properties can be read-only, which means that setting them will throw an error. Properties can be handled a little more "naturally" in javascript, because handling of properties is done in the same way it's done with any object. No need to know that the element is, in fact, a XUL element. You just need to know what properties and methods are available in the object. Makes the code nicer.
But that's not such a big deal. Here's the big deal: setting attributes may not yield the result you expect. Suppose you have an empty textbox and want to set its value in a script. Let's execute the following code and see what happens:
textbox.value = "First value";
textbox.setAttribute("value", "Second value");
After this code is executed, the textbox will be showing "First value", but if you look at the textbox element with the DOM Inspector, you'll see that it's attribute "value" will have a value of "Second value". Now that's weird. Or so I think.
What happened? Well, element attributes will be used to set the element's internal state at load time, but you have no guarantees that changing them at runtime will have any effect. Properties are the opposite: you can be sure that setting a property or running a method will change the internal state of the object like you would expect.
OK, so this means you should favor using properties over attributes at all times. That's no problem, right? I already mentioned some advantages of properties, and the code will look much cleaner using them instead of attributes. It's a no-brainer. Problem solved.
Oh, wait. I forgot about templates. Using templates you can make an element change the value of its attributes dynamically, depending on a datasource. On my last post, I had a textbox that uses a template to show its value:
<textbox value="rdf:http://mypage.com/my-schema#some-value" />
This won't work. I mean, it will show the value from the template properly, but changing the value in the datasource will have no effect in the generated element. Maybe doing a full template rebuild will work, but this shouldn't be necessary. The problem is that the template knows nothing about properties. It only deals with element attributes, so changing the value in the datasource will in effect cause a setAttribute call, which does nothing for the state of the element, as mentioned previously.
To work around this issue, an event called DOMAttrModified comes to the rescue. This event is triggered every time an attribute is changed in the element. The improved textbox looks like this:
<textbox value="rdf:http://mypage.com/my-schema#some-value"
DOMAttrModified="if(event.attrName == 'value') this.value = event.newValue; return true;" />
This works. The code in the event handler can be taken out to a script, obviously. The "attrName" property of the event object tells us which attribute was changed, and the "prevValue" and "newValue" properties give us the previous value and new value, respectively. This snippet of code turns the setting of the attribute into the setting of a property. This way all updates in the datasource will be properly reflected in the page.
This event has proved to be quite handy, specially when working with XBL. Bindings will let you add a constructor, with which you can set the internal state of your element with the values of the different attributes it has set. But the only way you'll know an attribute has been changed afterwards is to add a DOMAttrModified handler to your binding. Then you can do something similar as with the textbox example, and convert the setting of an attribute to a setting of a property.
I'm not quite sure why is it that attributes can be independent to the state of the object, but I guess there's a decent reason behind it. Perhaps it's just standard. I don't know.
But now you know how to work around it.

Labels: , , , , ,


2006/07/05

How to handle text and keep your sanity

Showing text in simple ways is very easy in XUL. You have two tags at your disposal: label and description. Even though they are implemented just the same, their semantic is different, so you should use them accordingly.
Labels are supposed to be used for the small bits of text you see all around an interface: menu labels, titles, form fields, etc. They're usually short (2 or 3 words) and need to be shown without breaks. And that's what a label will do. The following:
<label value="Some text here" />
Will display whatever you type in the value attribute in one line. The Gecko engine will try its best to show the full label, and in the very worst case, it will be cropped. No line breaks. No way, no how. And it makes sense to have such an element in your interface language. You don't want things like menu items or titles to be multiline. Things would get pretty ugly.
Description elements are used for longer texts. They are also used for text that could be short or long, and you just don't know. This is the tag to use when you're adding explanations or other pieces of text that may break into multiple lines. You'll find some of those in the Options window. Now here's where the quirks start to appear.
<description value="Some very, very long text here" />
Will this text break under the right circumstances? No. Using the value attribute will always produce a single-line block of text. It's exactly the same as using the label element. What you want to do is the following:
<description>Some very, very long text here</description>
That breaks, when the available width is insufficient. As a general rule of thumb, you should remember to always use the label element with the value attribute, and always use the description element with the inner text node. Use label when you don't want breaks. Use description otherwise.
Now, how about introducing text with breaks? That's a little different. Doing the following makes no difference:
<description>Some very, very
long text here</description>
All breaks and white space is collapsed into a single white space, just like with HTML. We're missing something here. After a little quest for information I found a solution in the following blog post: XUL Description Revisited. A few solutions are reviewed, and the most elegant by far consists on doing the following:
<description style="white-space: pre;">Some very, very
long text here</description>
A little stylin' saved the day :). This is the same as using the HTML "pre" tag, so have to be careful about all white space and breaks in the text, including before and after the text.

Dynamically generated text

So far things have been fairly easy. But sometimes you'll need to handle text in more complicated ways, and things get very tricky. This is something I had to deal with a lot of frustration while developing a relatively complex extension. This issue relates directly with templates, but may also be relevant for XBL.
Creating text with breaks on a template proved to be quite a headache for me. I ran into a bit of a paradox. It's easy to make an attribute of an element be assigned dynamically from the datasource:
<label value="rdf:http://mypage.com/my-schema#some-value" />
Clearly this won't work with the description element, at least not in the way we need it. But worry not, template designers thought of this and came with a rather elegant solution. You can use a "text" element to represent a text node. So all you need to do is this:
<description>
<text value="rdf:http://mypage.com/my-schema#some-value" />
</description>
That's not so hard, now is it? This is where the irony kicks in: the "text" element generates a text node that doesn't break! I couldn't help but laugh... oh, well. I tried a million different ways to get the damn thing to break, but I just couldn't. When is this element useful then? Maybe to do stuff in HTML, I don't know.
Back to the drawing board. For a while I used a workaround and just divided the text into multiple single-line description elements (should've been label according to my own rule, :P), but this just didn't cut it. My extension required the text field to have relatively arbitrary widths, so this was not an acceptable solution.
So it came down to using the multi-purpose textbox element. I left it as a last resource because I see it more as an input element, so I feel it's kind of a bad practice to use it for read-only text. But this was my last hope, so I had to give it a try. And here it is, in all its wonder:
<textbox class="plain" style="background-color: transparent;" flex="1"
multiline="true" readonly="true"
onoverflow="this.height = this.inputField.scrollHeight;"
DOMAttrModified="if(event.attrName == 'value') this.value = event.newValue; return true;"
value="rdf:http://mypage.com/my-schema#some-value" />
This yields exactly what I needed: a read-only, vertically flexible text field that wraps intelligently. Nirvana.
The class is set to "plain" so that the border doesn't show. The background color is set explicitly to counter the ugly color that's set for read-only textboxes. The onoverflow event makes the textbox adjust its height dynamically, according to the text it contains. Finally, the DOMAttrModified keeps the contents of the textbox consistent with the value that's set by the template. Changing the value attribute does not change the displayed value automatically. I'll dig a little deeper into this in a future post.
So there you have it. Labels, descriptions and textboxes. There's always a way, though not always pretty.

Labels: , , , , , ,


2006/07/04

Creating an uninstall script for an extension

On my last post I explained one way in which you can create a post-install script for an extension. I used a preference to store the current version of the just-installed extension so that future upgrades can use this preference to maybe run different code in case of an upgrade from version X to version Y, as well as running different code when doing a fresh install.
Here's where we run into a little problem: when the extension is uninstalled, the preference remains because it doesn't have it's default value set, and Firefox keeps those just in case they're needed for future installs of the same or even other extensions. So, if I install version X, uninstall it, and then install version Y, it looks like I'm doing an upgrade from X to Y, when in reality I'm doing a fresh install. So now we're need of an uninstall script, to clear this preference.
Uninstall scripts can certainly be needed for many other purposes, specially in the more complex extensions. You may need to delete files you created and won't be using anymore, make sure your preferences are removed, or undo other changes that were done on install. I would say it's professional courtesy to cleanly remove your extension and not leave lots of junk behind.
So let's get to it. As with the install script, you'll need to overlay the main window, adding a script element:
<overlay id="homepage-overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript"
src="chrome://myextension/content/overlay.js" />
</overlay>
Clearly, you can use the same overlay and script you used for the post-install script, if that's the case. The add something like this:
const MY_EXTENSION_UUID = "{CEDB5187-8A58-4958-ACF5-F6CD3BEF1927}";
function initializeOverlay() {
UninstallObserver.register();
}
var UninstallObserver = {
_uninstall : false,
observe : function(subject, topic, data) {
if (topic == "em-action-requested") {
subject.QueryInterface(Components.interfaces.nsIUpdateItem);

if (subject.id == MY_EXTENSION_UUID) {
if (data == "item-uninstalled") {
this._uninstall = true;
} else if (data == "item-cancel-action") {
this._uninstall = false;
}
}
} else if (topic == "quit-application-granted") {
if (this._uninstall) {
/* uninstall stuff. */
}
this.unregister();
}
},
register : function() {
var observerService =
Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);

observerService.addObserver(this, "em-action-requested", false);
observerService.addObserver(this, "quit-application-granted", false);
},
unregister : function() {
var observerService =
Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);

observerService.removeObserver(this,"em-action-requested");
observerService.removeObserver(this,"quit-application-granted");
}
}
window.addEventListener("load", initializeOverlay, false);
I remove all documentation from the code due to space constraints. I don't want these posts to be longer than necessary. Rest assured that I'm quite obsesive about proper documentation. I just think that these little tips are more readable without it. But I'll always explain the general idea behind the code I post.
First, notice the UninstallObserver object. It's declared as a javascript singleton because we don't need more than one instance. The register and unregister methods basically add and remove this object as an observer for the "em-action-requested" (the "em" stands for Extension Manager) and the "quit-application-granted" topics. This means that the object's observe method will be called every time an action is performed in the Extension Manager, such as installation, uninstallation, enabling, or disabling of extensions, and when the application is going to exit.
In our observe method, we look for the id (UUID) of our extension. This way we know we're dealing with our extension alone. Then we look for the actions we want to observe. In this case we're interested in the "item-uninstalled" action. When the extension is set to be uninstalled, our _uninstall flag will be set to true. Also note we listen to the "item-cancel-action" action. This is very important, because the user can actually cancel uninstallation by right-clicking on the extension in the Extension Manager and selecting the Enable option. This way we know for sure the flag represents the final decision of the user.
Note that we used the "quit-application-granted" topic instead of the "unload" event to trigger the uninstall script. I do it this way because closing the browser window is not the same as quitting the application. You can have more than one browser window open, or other windows open, such as View Page Source or Javascript Console. It's unlikely that going with the "unload" event will cause problems, but it's better to stay on the safe side. There is one downside, though. An observer is created for each browser window (again, it's unlikely to have more than one when you're uninstalling the extension), so the uninstall script will be called for all observers if the user chooses the Exit menu option, closing all windows at once (very, very unlikely). This means you should plan your uninstall script so that it can be executed more than once without causing any damage.
That's it! Now you have an nice uninstall script where you can do all the cleanup you need. This solution was based on code found on a comment in the Two Ells blog, but it does have a couple of improvements.

Labels: , , , , ,


2006/07/02

Creating a post-install script for an extension

If you used XUL Planet's version of the XUL Tutorial, you must have come to realize that the extension installation part is significantly outdated. The installation process was significantly changed on Firefox, and the infamous install.js script has been put to sleep. It still lives in the old Mozilla Suite and Seamonkey, I think.
Here's the deal: extensions used to use a script (install.js) to perform certain installation operations. Most of them were really repetitive and predictable chrome registrations, while on some cases extension developers would add some specific post-install operations that were required for their extension to work properly.
All of that got the axe. Firefox extensions no longer include that script, and including it will have no effect on installation. It won't be executed, period. Chrome registration has been moved elsewhere (the chrome.manifest file) and post-installation scripts can no longer be bundled this way. It was a good move in my opinion. Most extensions don't require post-install scripts, so this makes life easier for the majority.
But what happens when you do need a post-install script? Well, don't worry. Creating one is not very complicated, and I'll explain how to do it.
Most (all?) extensions overlay the main browser window (chrome://browser/content/browser.xul). If yours doesn't, add one, it's not going to kill you. The same applies to extensions on other applications, as long as there's a main xul file you can overlay. The least you'll need to add is a script:
<overlay id="homepage-overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript"
src="chrome://myextension/content/overlay.js" />
</overlay>
Next thing you'll need is your overlay script (on overlay.js, or whatever name you prefer):
const MY_EXTENSION_VERSION = "0.1";

function initializeOverlay() {
var pref =
Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
var finished = "";

try {
finished = pref.getCharPref("myextension.install.finished");
} catch(e) {}

if (finished != MY_EXTENSION_VERSION) {
/* add post-install stuff here. */
pref.setCharPref("myextension.install.finished", MY_EXTENSION_VERSION);
}
}

window.addEventListener("load", initializeOverlay, false);
A little explaining is in order. First of all, the initializeOverlay function is added as a load event handler. This will make the browser execute this function when it's main interface is about to be drawn and shown to the user. This means this function is executed shortly before the user can actually see the window. You can access DOM elements though, because the DOM for the window is already loaded.
Secondly, we need to make sure our script is only executed once, right after installation. That's why I'm using a preference (myextension.install.finished) to serve as a flag that indicates whether the post-install script has been executed or not.
Note that I didn't use a boolean preference, but a string preference that holds the current version of the extension. The purpose of this is to make this script a little more robust. You never know how this script is going to change in future versions, so you may need to do different things when doing a fresh install or upgrading from version X to version Y. Things are just nicer this way. On the not-so-nice side, you'll have to remember to change that constant every time you change version numbers. You'll also want to clear this preference when the extension is being uninstalled. But that's the topic of the next blog: Creating an uninstall script for an extension.

Labels: , , ,


First post!

Welcome to the XUL Solutions blog. This blog has the intention of documenting and publicizing much of my findings as a XUL developer, as I've gone through great pains to find much of this knowledge.
I'm a XUL developer working at Glaxstar, a company responsible for very important Firefox extensions such as del.icio.us, Paypal, Zoep, OwnArea and the Joga Companion. I'm rather new to the company, so I've had little to do with those products, but I've had my share of development in Firefox extensions, and I have learned a whole lot along the way.
The first thing I've learned is that finding answers to XUL problems online is like finding a needle in a haystack, blindfolded, while free falling from a jetliner. This comes as no surprise as this is a relatively new development technology, and open source is not exactly famous for its copious documentation. I guess everybody's too busy developing... but that's a little hypocritical, because I could very well be helping on that. Maybe some day I will.
You don't have to take my word on this. Here's a list of the different ways you can get answers to your XUL problems:
I'll start posting some of the XUL knowledge I've acquired and I feel is not properly documented elsewhere. It's plenty. I think I have material for at least 10 initial posts. More will definitely come as I become more experienced on this platform and produce more software for Glaxstar and the world. But if you have any questions, feel free to post a comment here. I can't guarantee to know the answer, but every question is valuable, and most will merit new posts on this blog. Hopefully I will make life easier to many of you extension and XUL application developers.
And now... the solutions.

Labels: , , ,


This page is powered by Blogger. Isn't yours?