utility code in toolkit

So this has been mulling around in my mind for a while, but with 1.5 in RC status I actually have time to work on this now.

Basically, my plan is to create a set of generic toolkit files to act as wrappers for commonly used functions, in order to facilitate simpler and more future-proof app and extension code. This type of wrapper exists in a lot of places, from jslib to calendar to small-scale bits and pieces (getBoolPref() and makeURI()) in toolkit. I want to do this a lot more, because we get to write simpler code, which helps us iterate faster, and it helps extension authors write smaller extensions that should age better since if we keep these wrappers up to date, API updates stay transparent. Take the following example (getting the home page URL from the pref):


var url;
try {
url = gPrefService.getComplexValue("browser.startup.homepage",
Components.interfaces.nsIPrefLocalizedString).data;
} catch (e) {
}

vs.


var url = gPrefUtils.getPref("browser.startup.homepage");

or the following use for constructing URIs (we’ve started to do this, but makeURI still throws, and we don’t handle that well/as consistently as we should).


try {
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var baseURI = ioService.newURI(aURL, null, null);
} catch(ex) {
}

vs.

var baseURI = makeURI(aURL);

My current plan is to start with pref handling, xpconnect calls, and promptservice. There’s some ongoing discussion around the jslib file utils, so that might be more low-hanging fruit as well. On the subject of prompts (alerts, confirm prompts, etc) on trunk we’ll be breaking/mangling how window.alert() and window.confirm() works from chrome so as to discourage using those methods, so the promptservice wrapper should be very useful to reduce the amount of extra work to use a proper dialog.

Thoughts?

Posted November 3rd, 2005.

12 comments:

  1. Mossop:

    Sounds good. I assume the utilitys will be in their own script file, not dependant on anything in browser.js so extension authors can include them at will in their own windows scope?

  2. Ben Basson:

    Mike, see bugs 293187, 298695 and maybe 281278 for things that I think would be useful to extension authors in the toolkit.

    As a general rule, I’ve found that there’s a huge amount of code in Firefox that is entirely not reusable. This can be a pain, as it means that extension authors have to inject their own function rather than simply calling one that is native to the toolkit. You’ll probably find that this is one of the major reasons that extensions break Firefox when things change.

    In addition, there are parts of the interface that change or are likely to change in the future. Exposing those via getter functions will also lead to less breakage…

    i.e. getSearchBar(), getURLBar()

    Some documented functions for other activities would be really useful as well, such as appending new toolbar items to a toolbar programatically.

    More later. This lab is being emptied.

  3. Robert Accettura:

    I’m loving the idea.

    IMHO on the same page, something like jslib should be included in firefox. Would save tons of time if we had that. Lots of extensions rely on it anyway. Solves lots of I/O and other complexity issues.

  4. Gemal's Psyched Blog:

    Utility functions for extensions in toolkit

    Basically, my plan is to create a set of generic toolkit files to act as wrappers for commonly used functions, in order to facilitate simpler and more future-proof app and extension code. Read more This is want I’ve been requesting…

  5. Ben Basson:

    Ok, as promised – here’s some more things that I’d love to see addressed.

    1) promptService.confirmEx is possibly the worst interface I’ve ever had to use. Multiplication is required to set the button order, which is also not what you’d expect it to be at all. What’s really needed is a JavaScript function which totally masks confirmEx.

    Ideally, this function would accept a title, some body text and an array containing two or more strings (for button labels) in the order that they are to be displayed and returns the clicked button (i.e. corresponding to the array element passed).

    Example:

    var buttonClicked = confirmEx(“Do you want to use confirmEx?”,”Please click one of the buttons…”,["Yes","No","Maybe"]).

    2) Lots of functions in Firefox are relatively inflexible. For example, most things that call File pickers specify a default directory inline. To use the native filepicker functionality, but also specify my own default, I have a choice of overlaying a key function and then flipping the process when I’m done, or duplicating the code, only to have it break (or be different) later.

    It would be really nice to have some load and save functions functions that don’t rely on inline values, so we can simply use a function with a prototype similar to:

    function saveFile (aInput, aDefaultDir, aReferrer, aBypassFilePicker, aOverwrite, aTitle, aFilters);

    Where:

    i) aInput is a nsIFile, nsILocalFile or nsIURI
    ii) aDefaultDir is a nsILocalFile or a pref string to convert to an nsILocalFile
    iii) aReferrer is an optional nsIURI
    iv) aBypassFilePicker is a boolean value to show or not show the file picker during the save process
    v) aOverwrite is a boolean value to specify whether files should be overwritten (or the filename incremented) when saving without the filepicker
    vi) aTitle is an optional string value for the title of the file picker
    vii) aFilters is an array of filters to apply to the filepicker

    3) The aggressive removal of any OS-specific ifdefs, to be replaced with code that simply gives authors what they want in each case would be wonderful.

  6. Henrik Gemal:

    - Getting th URL of the current loaded document would be a nice thing.

    - Opening a URL would also. Trying to do that so that it works in both Firefox and Thunderbird is a pain.

  7. Christian Weiske:

    getPref() should take an optional parameter for the default value in case the key doesn’t exist.
    And either there are multiple functions as getIntPref(), getBooleanPref() and getStringPref(), or the one getPref() function should switch according to the type.

  8. Jed:

    Awesome.
    Thank you for taking the time to do this, it will help extension authors greatly!

    If you need help documenting what you do, please hollar as documentation on what utilities are available will be crucial.

    Also, for file access, there is a great IO lib you could look into:
    http://kb.mozillazine.org/Io.js

    Cheers
    -Jed

  9. Mike:

    Christian:

    getPref is based on the implementation in inspector, and checks the pref type and uses the right method. It also definitely has that optional param for a default value.

    Ben:

    I’m not sure I understand the comments about OS-specific ifdefs. In some cases we have things that aren’t implemented (i.e. nsILocalFile) where we need to do hackarounds for that platform. As long as extension authors can call foo(argA, argB) on any platform and get a reasonable and expected action, I don’t think it matters what the underlying implementation looks like, and whether its preprocessed.

  10. Ted Mielczarek:

    Finally, giving some love to the extension authors. Let me know if you want any help on this!

  11. Ben Basson:

    Hi Mike, thanks for the reply (and obviously thanks for doing this!)…

    I was referring to functions like getSpecialFolderKey(aFolderType), of which there may be similar instances. These are functions declared inside other functions and cannot usually be directly accessed.

    Perhaps what I meant to say was “no functions inside functions” instead… that would probably be a lot more constructive ;)

  12. Davide Ficano:

    The idea is great, in my extensions always I have a js module called XXXCommon which contains reusable code.
    But there are some problems:
    1. Namespace must be guaranteed due to possible version signature problems, consider a version may clash with superior revision, oh yes this is always possible :-)
    I wrap common functions in “namespace” prefixed with extension name but this is bad practice for real reusable code.
    2. Modules must be considered snippets of code instead of real library, I prefer to “import” only functions I really use instead of tons of unused routines.
    I hate JSLib because is too big
    I have my access file functions without using JSLib , I have Url access function and many specific functions

Leave a response: