Running Add-on Code at First Run (and Upgrade)
Running code when your add-on is first installed or upgraded is pretty common. You might need to install a toolbar button or display a web page. I've seen a few different ways to do this, so I thought I'd show how I do it in my add-ons.
The best way to demonstrate this is with some sample code. I'll make some comments on at the end.
var firstrun = Services.prefs.getBoolPref("extensions.YOUREXT.firstrun");
var curVersion = "0.0.0";
if (firstrun) {
Services.prefs.setBoolPref("extensions.YOUREXT.firstrun", false);
Services.prefs.setCharPref("extensions.YOUREXT.installedVersion", curVersion);
/* Code related to firstrun */
} else {
try {
var installedVersion = Services.prefs.getCharPref("extensions.YOUREXT.installedVersion");
if (curVersion > installedVersion) {
Services.prefs.setCharPref("extensions.YOUREXT.installedVersion", curVersion);
/* Code related to upgrade */
}
} catch (ex) {
/* Code related to a reinstall */
}
}
Some comments on the code.
- YOUREXT is some arbitrary string to uniquely identify your add-on. It can be the add-on ID or just some name.
- I usually put this code in the JS file included in my XUL overlay. I don't put it in a module. This makes it much easier to test it, since I can simply reset the preference and open a new window.
- I used Services.jsm in this example to make it simple. I don't use Services.jsm, I roll my own custom Services file. I'll show how in a future post.
- During development, I hardcode my version to 0.0.0. In my previous add-ons, I queried the version from the extension manager, but someone pointed out to me that this was a waste of code. Now what I do is parse install.rdf to get the current version and replace 0.0.0 with that version at runtime.
- I use a simple comparison for my versions, but if your versions are more complex (involving letters for instance), you should use nsIVersionComparator.
- There is no need for a try/catch around my call to get the firstrun preference because I put this line in my default preferences file.
pref("extensions.YOUREXT.firstrun", true); - In order to tell when a user has reinstalled my add-on, I remove the installedVersion preference when my add-on is uninstalled. I leave the firstrun preference behind.
-
If you're going to display a web page at first run, here's how you do it:
window.setTimeout(function(){ gBrowser.selectedTab = gBrowser.addTab("http://example.com"); }, 1000);I found that if you don't use a timeout, the page will intermittently not display.
Hopefully this code is useful to you. Let me know in the comments if you have any suggestions.
To detect the first run, you can use also the firstRun property of the extIExtension object in the FUEL API https://developer.mozilla.org/en/Toolkit_API/extIExtension
Thank you! That solved my issue.
I'm building a new extension for firefox, and I'm having difficulties doing the first run script. When I put
service = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
firstrun = service.prefs.getBoolPref("extensions.newflashswitcher.firstrun");
I get service.prefs is undefined, I'm not sure if i'm doing this correct.
In my example, I was using the built-in Services module. So that's where Services.prefs comes from.
If you're using the prefs service directly, it looks like this:
var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService)
firstrun = prefs.getBoolPref("extensions.newflashswitcher.firstrun");
Thanks for the reply, so basically I followed your instructions, but I'm using Console2 to debug the javascript, and I get the following error.
Erro: Component returned failure code: 0x8000ffff (NS_ERROR_UNEXPECTED) [nsIPrefBranch2.getBoolPref]
Source file: chrome://newflashswitcher/content/newfs.js
Line: 12
I don't know why exactly I get this return failure. I looked at your example and I also get the same error.
You either have to put your firstrun pref in your default preference file or put a try/catch around it.
See the fifth bullet at the end of the post.
The default preferences file is in your add-on:
defaults/preferences/prefs.js (it can actually have any name)
I created a preference file, but how do I load it into the application, I looked thru your example, and it's not in the chrome manifest, or in the service module, or in the javascript file. Does firefox automatically sees it?
(WordPress won't let me reply to the last post).
Yes, any JS file in defaults/preferences will be automatically loaded by Firefox as preferences.
You can verify the prefs in about:config
https://developer.mozilla.org/en/Default_Preferences
The last problem I'm having is that I add the button to the addon-bar, and I have a part that
document.getElementById("addon-bar").collapsed = false, when I first insert the button, the addon-bar won't show, I have to manually turn it on, I don't know if there is another settings so that when It can make it visible.
The other question is that if I uninstall, and reinstall, I put some code around the catch bracket, but for some reason it won't execute the code.
Thanks, I just had to get my head around the architecture of firefox.
Bruno, in reference to your last comment, are you saying setting collapsed to false isn't working for you?
As far as the uninstall/reinstall, I'll need to see the code to help you figure it out.
in extensions.YOUREXT.firstrun
If I use a guid as my addon id, do I put that in YOUREXT? I don't understand what you are supposed to put here.
I've updated the post to clarify.
YOUREXT is some arbitrary string to uniquely identify your add-on. It can be the add-on ID or just some name.
Thanks Mike for your help. I'm having the same issue as Bruno was above but I'm really not sure how he fixed his problem. I do have firstrun pref set in the defaults/preferences/prefs.js file, but I'm receiving the same error 'component returned failure code 0x8000ffff' right at the line where it calls Services.prefs.getBoolPref() for first run.
Per a conversation on stack overflow, the issue is the location of the preferences file.
The defaults/preferences directory has to be at the top level of your add-on.
Hi Mike,
I followed your example and it works, but only for the first installation. For the subsequent installations, the "var firstrun" will always be false. How do I solve this problem?
That's correct. Subsequent installations won't be firstrun, they will be upgrades.
You catch those in the else cases.
Firstrun code only runs the very first time the add-on is installed.
Oh because I need my extension to be able to be uninstalled and then installed again, showing the firstrun page again. Is there anyway to do it?
Sure. What you would want to do is clear the preference when your add-on is uninstalled.
What I do is just clear the installedVersion preferences on uninstall so I can detect this exact case. If I see firstrun but I don't see installedVersion, I know they uninstalled the add-on.
let listener = {
onUninstalling: function(addon) {
if (addon.id == "YUOUR ADDON ID") {
/* unset pref */
}
},
onOperationCancelled: function(addon) {
if (addon.id == "YOUR ADDON ID") {
/* reset pref (they cancelled the onuninstall */
}
}
}
Components.utils.import("resource://gre/modules/AddonManager.jsm");
AddonManager.addAddonListener(listener);
Hi Mike,
My extension is for Firefox 3.6, Gecko 1.9.2. So I won't be able to use AddonManager,instead, I should use nsIExtentionManager. I found a blog that might be able to solve this issue. http://blog.iosart.com/2004/08/20/how-to-run-a-clean-up-script-when-your-extension-is-uninstalled/
The problem is that I do not know how to ask the nsIExtentionManager whether the extenstion is in toBeUninstalled state as mentioned in the blog. Please advise. Thanks!
Hey Mike,
I've got it! thanks man. you've been of great help. Thanks!:)
3.6 is dead, haven't you heard?
But seriously, I had 3.6 code as well, just didn't think you'd need it.
Glad you figured it out.
If you need anything else, don't hesitate to ask.
I got an message like this: Services is not defined.
Right now my FF is V17.0
Any idea, please share.
Thanks
Add
Components.utils.import("resource://gre/modules/Services.jsm");
at the top.