Creating Custom Events That Can Pass Data

This page describes how to implement custom DOM events that can be used to pass data. Using this technique you can add extra parameters and query them. For example, if you want Firefox to perform an action whenever something happens (i.e., something other than the standard mouse/keyboard events) and, depending on the data passed along with this event, you want Firefox to react differently.

Note that starting with version 6, Firefox supports DOM Level 3 customEvent, which lets you dispatch custom events with arbitrary data from JavaScript. (see bug 427537)


In order to do this you must be able to do all of the following:

What's in a name

Despite whatever you may have been taught in your English class, there's a lot in a name when it's an event name. As of Gecko 1.8, if your event names do not start with "nsDOM" and their interfaces do not start with "nsIDOM" then you can forget about passing data. You will still be able to throw events, but that's it.

The trunk

You needed to download (or check out) the trunk source because you will have to modify it in order to implement your event. Be aware that this means your stuff won't work with everyone else's version of Firefox unless you get your patches into the trunk. At the time of writing the author is not aware of a way to do this that doesn't involve modifications to trunk.

What follows a list of the files you'll need to modify, as well as a discussion of each change.


The change you make here is really rather small but it is incredibly important. If you peruse nsDOMClassInfoClasses.h you'll see of list of macros of the form DOMCI_CLASS(foo). You'll want to add an entry to it which looks like this:

DOMCI_CLASS({truncated name})

Here's the fun part: Above I mentioned that you must name your events as nsDOM. However, here you want to put the other part of the name (e.g. if you have an event named nsDOMMyFirstEvent your nsDOMClassInfoClasses entry would be DOMCI_CLASS(MyFirstEvent)).


Mozilla contains many convenience macros to make changes like the one you're making easier. Your modification of this file is basically just two macro calls. Try to make sure of two things:

  1. Keep your stuff at the back. You know never know what depends on that enum you modified
  2. Keep your stuff organized. Same reason as above.

You need to make the following two modifications:

  • around line 1000:
  • around line 2900:
DOM_CLASSINFO_MAP_BEGIN({truncated name}, nsIDOM{truncatedName})
  DOM_CLASSINFO_MAP_ENTRY(nsIDOM{truncated name})

Remember, {truncated name} is the same as above.


Note: In the mozilla 1.8.x branch this code is actually in mozilla/content/events/src/nsEventListenerManager.cpp

This is quite an important file since this holds the CreateEvent method which acts as a factory method DOM events. The change you want to make is in nsEventDispatcher::CreateEvent(). You will find that there is a bunch of code like:

if (aEventType.LowerCaseEqualsLiteral("{somethingsomething}event"))
  return NS_{somethingSomething}Event(aDOMEvent, aPresContext, nsnull);

You can either have a function like this or write the code straight in nsEventListenerManager::CreateEvent() like this:

if (aEventType.LowerCaseEqualsLiteral("nsmyevent")){ //note: the lowercase is important!
  nsDOMEvent* it = new nsDOMMyEvent(aPresContext, aEvent);
  if (nsnull == it) {
  return CallQueryInterface(it, aDOMEvent);

In general though I'd strongly recommend using a function the way that everyone else does. You can find the prototypes for the function in nsIPrivateDOMEvent.

Your event

In order for your event to work you must do the following:

  • Create a scriptable interface called nsIDOM{YourEventName} inheriting from nsIDOMEvent. A good place to put this .idl is in mozilla/dom/public/idl/events/.
#include "nsIDOMEvent.idl"
[scriptable, uuid(08bea243-8a7b-4554-9ee9-70d7785d741b)]
interface nsIDOMMyEvent: nsIDOMEvent
   //put members here!
  • Implement the interface you created with a class that inherits from nsDOMEvent. Use nsDOMEvent.h's NS_FORWARD_TO_NSDOMEVENT macro so that you don't have to forward manually (unless you plan on overriding one of nsDOMEvent's original functions).

Example TBP

Note for extension developers

In order for your event to work the way it is described here it must be derived from nsDOMEvent. The problem that extension developers will hit is that you are not allowed derive from nsDOMEvent in an extension. You can try to rewrite code but it will have to be a lot of code because nsDOMEvent uses a lot of code outside of itself which you, again, cannot access from an extension. This remains true even if you mark you extension code as "internal".

Dispatching your event in JavaScript

Here is how to dispatch your event in JavaScript.

var event = document.createEvent("nsDOMMyEvent");
event.initEvent("nsDOMMyEvent", true, true);

Dispatching your event in C++

The following shows how to dispatch your event in C++:

 nsCOMPtr<nsIWindowWatcher> wwatcher
 // The window watcher will be able to give me a handle to the window

  nsCOMPtr<nsIDOMWindow> aWindow;
  // a handle to the window

  nsCOMPtr<nsIDOMDocument> aDoc;
  // a handle to the document

  nsCOMPtr<nsIDOMEventTarget> tWindow;
  // the target window (really the same window as above but you need
  // a different interface :/

  nsCOMPtr<nsIDOMEvent> event;
  //this will be the event we throw.

  nsCOMPtr<nsIDOMMyEvent> myEvent;
  //this is a handle to the your interface to the event.

  //get the Active window.

  //get the document from the window.

  nsCOMPtr<nsIDOMDocumentEvent> docEvent = do_QueryInterface(aDoc);
  //change interfaces so you can create interfaces.

  docEvent->CreateEvent(NS_LITERAL_STRING("nsDOMMyEvent"), getter_AddRefs(event));
  //create the event

  event->InitEvent(NS_LITERAL_STRING("nsDOMMyEvent"), PR_TRUE, PR_TRUE);
  //initialize it

  tWindow = do_QueryInterface(aWindow);
  //use the window for a target.

  myEvent = do_QueryInterface(event);

  //set my property of my event to (e.g.) 45.

  PRBool defaultActionEnabledWin;
  PRBool defaultActionEnabledDoc;

  nsCOMPtr<nsIPrivateDOMEvent> privEvt(do_QueryInterface(event));
  //make the event trusted

  tWindow->DispatchEvent(event, &defaultActionEnabledWin);
  //dispatch it (i.e. send it out into the wild)