Accelerating Firefox Add-On Development with JavaScript IntelliSense in VS 2008

Some folks on the Visual Studio team have done a phenomenal job of enhancing IntelliSense for JavaScript in Visual Studio 2008 and Visual Studio 2008 SP1. So far, I have found numerous articles and videos that discuss how it significantly improves the development experience in ASP.NET, but I wanted to share the impact it has on what I do—Firefox add-ons.

If you’re not familiar with the architecture of Firefox add-ons, here’s an extremely abridged top-down view:

  • Most add-ons are implemented with a combination of XUL (XML User Interface Language) for the UI and JavaScript to support it.
  • Instead of running the context of a web page, the JavaScript runs in the context of a window.
  • In order to share state across multiple windows, you must use and/or implement XPCOM components.

XPCOM components provide services or objects that JavaScript running in a Firefox web page normally does not have privileges for. For example, there are services for file I/O, preference reading and writing, and writing to Firefox’s JavaScript console. You can also build your own XPCOM components (in JavaScript, C++, among other languages) that enable you to share data across the entire Firefox session or place logic that doesn’t fit well within the context of a single window.

My use of IDEs for add-ons shifted from Notepad++ to Visual Studio in early 2007 because I needed a better way to organize and search my files, but now I am glad I made the switch for other reasons. The hard about using XPCOM is that unless you have memorized your own interfaces as well as the interfaces of all the components you use, you often find yourself referring back to your IDL files or Mozilla’s documentation for the exact name of the attributes and functions you need. This arguably slows your momentum when working with XPCOM. But with the new improvements to Visual Studio’s JavaScript IntelliSense, this problem (almost) fades away completely.

Consider the following XPCOM interface:

[scriptable, uuid(770E43E2-7213-4639-ABEB-DED12A7188A9)]

interface ITrace : nsISupports

{

    /// <summary>

    /// Logs an exception as an error to the JS Console.

    /// </summary>

    /// <param name="e">The exception to log.</param>

    void error(in nsIException e);

 

    /// <summary>

    /// Logs a message to the JS Console and the dump window as an informational message.

    /// </summary>

    /// <param name="message">The message to write.</param>

    void info(in wstring message);

 

    /// <summary>

    /// Logs an exception to the JS Console as a warning.

    /// </summary>

    /// <param name="e">The exception to log.</param>

    void warn(in nsIException e);

 

    /// <summary>

    /// Logs a message to the JS Console and the dump window as a verbose message.

    /// </summary>

    /// <param name="message">The message to write.</param>

    void verbose(in wstring message);

};

Creating an XPCOM component requires an interface specified in XPIDL (shown above) as well as an implementation (not important for this discussion). The interface above should be straightforward to interpret even if you are not familiar with XPIDL. The example shows a simple tracing interface which abstracts away the API pain points of logging data to Firefox’s JavaScript console. It inherits from nsISupports (the XPCOM equivalent of IUnknown), and its methods take very simple parameters. The scriptable attribute specifies that this interface is callable from JavaScript and hence usable in our add-on. Here’s some sample code to show from JavaScript how we can invoke this interface.

try {

    // Do something that can throw

}

catch (e) {

    Components.classes["@example.org/trace;1"].getService(Components.interfaces.ITrace).error(e);

}

The indexer on Components.classes returns the object that follows the given contract ID. The contract ID is defined in the implementation (and we hope the documentation); from there we get the service that corresponds to the ITrace interface (this is like a QueryInterface call) and call the error function with the exception that we caught. Of course, there is zero IntelliSense help because Visual Studio doesn’t know what Components.classes and Components.interfaces are, nor would it have any idea how to find the XPIDL file for a given service to find what functions ITrace actually exposes. We can do much, much better.

Let’s expose a simple function called "error" that we can include from any other JavaScript file that allows us to do the same thing:

// xpcom-interop.js

 

function logError(e) {

    /// <summary>

    /// Logs an exception as an error to the JS Console.

    /// </summary>

    /// <param name="e" type="nsIException">The exception to log.</param>

    Components.classes["@example.org/trace;1"].getService(Components.interfaces.ITrace).error(e);

}

Great! Now we have a utility function, we can replace our ugly code before:

try {

    // Do something that can throw

}

catch (e) {

    logError(e);

}

Still, though, no love from IntelliSense:

No JS IntelliSense

Here we can use the the reference tag in our JS file to enable Intellisense, like this:

JS Function IntelliSense

JS Parameter IntelliSense

Very nice! If we flesh out this xpcom-interop file more, our development time can really improve.

Something you need to be aware of is that sometimes the XPCOM interop file can become too complicated for Visual Studio to parse correctly because it references objects that it might not know exist (e.g. Components.classes, Components.interfaces, Components.results). If this is the case, add an xxx.debug.js file in the same directory as the xxx.js file and write all of your documentation there. For example, I could keep xpcom-interop.js like this:

// xpcom-interop.js

 

function logError(e) {

    /// <summary>

    /// Logs an exception as an error to the JS Console.

    /// </summary>

    /// <param name="e" type="nsIException">The exception to log.</param>

    Components.classes["@example.org/trace;1"].getService(Components.interfaces.ITrace).error(e);

}

 

var prefService = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);

(Note that because we reference Components.classes outside of the logError function we see this when updating IntelliSense (Ctrl+Shift+J).)

IntelliSense Update Error

And I could also create a new xpcom-interop.debug.js file for documentation purposes:

// xpcom-interop.debug.js

 

function logError(e) {

    /// <summary>

    /// Logs an exception as an error to the JS Console.

    /// </summary>

    /// <param name="e" type="nsIException">The exception to log.</param>

}

 

var prefService = {};

Notice that I don’t include any code for the logError function this time. This file is intended for documentation purposes only; it will never be included with the final Firefox add-on because it doesn’t do anything. If you go back to the default.js file and refresh IntelliSense (Ctrl+Shift+J), IntelliSense will display the same help for the logError function in addition to displaying the prefService variable. When you place the reference tag at the top of a file, Visual Studio will look for filename.debug.js before checking the actual file that the tag references, which is very useful in these situations.

prefService shown in IntelliSense 

Ctrl+Shift+J Is Your Friend!

Fiddling with JS IntelliSense can be frustrating if you’re not aware of how to get it to refresh itself. Ctrl+Shift+J should work. If not, you can find it in the Edit menu under the IntelliSense menu item. Also, you can bind a shortcut to the command "Edit.UpdateJScriptIntelliSense".

Resources

  1. Format for JavaScript Doc Comments
  2. Hotfix to Enable -vsdoc JS files

DBML Fixup Preview

Back in February I blogged about DBML Fixup—a tool whose goal was to sync LINQ to SQL models with their respective database schemas from within the Visual Studio environment, as well as to handle running various fixup tasks on models. Now it’s six months later, and I have a much clearer vision of what the tool is meant to be as well as a firmer command on VSX. The following screencast is a preview of the features of DBML Fixup.

 

 

Here are some previous posts which (briefly) describe the domain:

http://blogs.rev-net.com/ddewinter/2008/02/16/the-linq-to-sql-model/

http://blogs.rev-net.com/ddewinter/2008/02/16/linq-to-sql-and-database-schema-sync/

 

Any ideas about directions for the tool? Or maybe something that wasn’t covered in the screencast? I’d love to hear your ideas.

Generate Serialization Classes As Part of Your Build (Part 1)

Yes, I am back from the grave. The past two months have been infuriatingly busy for me (and you’ll see why in a later post), but I finally have some time to write again. The topic? Generating serialization classes as part of your build process (Part 1) and in your own way (Part 2).

I might reach only a niche of .NET developers with this post, but it’s something that niche should be aware of. There is a tool in the .NET framework called xsd.exe, and one of its functions is to generate code, specifically .NET classes, from an XML Schema file (XSD). The tool decorates these classes with XML serialization attributes such that when .NET serializes an instance of the generated class that represents the root element, the output conforms to that XML schema. Often, developers using XML schemas will need to change them throughout the course of a project. Leveraging xsd.exe in a project thus requires the generated classes to be updated when an XML schema is updated. This is a bit dangerous in larger projects because not all developers may realize that the tool must be re-run, especially in the case of extremely minor changes to the schema.

Wouldn’t it be better to have a tighter integration among the XML schema, the generated classes, and the build environment? Fortunately, we can achieve this with a few simple steps, thanks to the MSBuild system for projects inside Visual Studio.

Continue reading

Dynamic Menu Commands in Visual Studio Packages – Part 3

This is the final post in a series detailing how to build dynamic menu commands in Visual Studio packages. The previous posts are located here:

  • Part 1 – Discusses UI Contexts and how to utilize the built-in ones for dynamic menu commands.
  • Part 2 – Discusses the use of the BeforyQueryStatus event to provide more flexibility than built-in UI contexts.

What we’ve explored to date is a way to provide dynamic menu commands (e.g. dynamic visibility, enabled state) in our own package, and the techniques that I’ve shown thus far have worked well for this scenario. However, if you want to develop multiple menu commands or even multiple packages that rely on a custom condition (as shown in Part 2), then you’re stuck implementing the same logic in an event handler for the BeforeQueryStatus event for each OleMenuCommand.

Wouldn’t it be great if we could create our own UI Context, similar to the built-in ones like UIContext_NoSolution or UIContext_FullScreenMode? That way we can create multiple menu commands that rely on that context or even multiple packages which rely on it.

And that’s exactly what this post will cover. We’ll use the solution starting from where we left off at the end of Part 2.

Continue reading

Dynamic Menu Commands in Visual Studio Packages – Part 2

In this post I’ll be building on the principles of my last post with dynamic menu development in Visual Studio, so if you haven’t read that post please go back and check it out!

If you remember from last time, we were able to create a dynamic menu command without any lines of procedural code (other than those generated by the wizard). The result was a menu command that showed itself only when no solution was loaded in Visual Studio. Immediately a solution was loaded, the menu command disappeared. The limitations of this approach are that you’re reliant on built-in UI contexts, such as UICONTEXT_NoSolution. However, what I needed for my DBML Fixup project was a way to add menu commands when right clicking on .dbml files (LINQ to SQL Classes) in the Solution Explorer. The built-in UI contexts that we reviewed in the previous post are not enough to give us this functionality. In this tutorial, I take you through step-by-step on how to add this feature to your VS packages. We have to do a bit of digging though, so let’s get started!

Continue reading

Dynamic Menu Commands in Visual Studio Packages – Part 1

As I get further into my DBML Fixup project, I’m learning more and more about Visual Studio Extensibility (VSX). Along the way, I have found multiple blogs which have helped me to learn a lot more than I would than just stepping through the documentation. (Those blogs are now listed on the sidebar.) The one I find especially helpful for beginners like me is DiveDeeper, whose LearnVSXNow tutorials are great stepping stones in learning the basics of VSX Packages. He currently has 14 tutorials, the first of which is located here. Although his blog is very helpful for learning about how to work with menu commands, he hasn’t (yet?) delved into making them dynamic. I needed to do this for my project, so I had to "dive deeper," as it were.

In this series of posts I’m going to focus on three ways to make menu commands dynamic—that is, how to make menu commands invisible or disabled in certain contexts, but visible and enabled in others. Two of these ways I found through some experimentation. Consequently, I’m not sure whether these represent VSX best practices, so if anyone has any more input, it would be greatly appreciated. With that said, let’s get started.

Continue reading

Profiling the Visual Studio Web Server

With JetBrains’ dotTrace and Red Gate Software’s ANTS Profiler, developers can find bottlenecks in their code as well as profile memory usage. Both tools also support tracing the Visual Studio (2005) Web Server out-of-the-box. However, tracing with Visual Studio 2008 Web Server is a little different.

Continue reading