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.
customEvent
, which lets you dispatch custom events with arbitrary data from JavaScript. (see bug 427537)Requirements
In order to do this you must be able to do all of the following:
- Download Mozilla Source Code
- Build Mozilla
- Creating Custom Firefox Extensions with the Mozilla Build System. This is the foundation for everything we'll do.
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.
mozilla/dom/base/nsDOMClassInfoClasses.h
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/dom/src/base/nsDOMClassInfo.cpp
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:
- Keep your stuff at the back. You know never know what depends on that enum you modified
- Keep your stuff organized. Same reason as above.
You need to make the following two modifications:
- around line 1000:
NS_DEFINE_CLASSINFO_DATA({truncated name}, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS)
- around line 2900:
DOM_CLASSINFO_MAP_BEGIN({truncated name}, nsIDOM{truncatedName}) DOM_CLASSINFO_MAP_ENTRY(nsIDOM{truncated name}) DOM_CLASSINFO_EVENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END
Remember, {truncated name}
is the same as above.
mozilla/content/events/src/nsEventDispatcher.cpp
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 NS_ERROR_OUT_OF_MEMORY; } 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 fromnsIDOMEvent
. 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); window.dispatchEvent(event);
Dispatching your event in C++
The following shows how to dispatch your event in C++:
nsCOMPtr<nsIWindowWatcher> wwatcher (do_GetService("@mozilla.org/embedcomp/window-watcher;1")); // 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 inteface :/ nsCOMPtr<nsIDOMEvent> event; //this will be the event we throw. nsCOMPtr<nsIDOMMyEvent> myEvent; //this is a handle to the your inteface to the event. wwatcher->GetActiveWindow(getter_AddRefs(aWindow)); //get the Active window. aWindow->GetDocument(getter_AddRefs(aDoc)); //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); myEvent->SetMyProperty(45); //set my property of my event to (e.g.) 45. PRBool defaultActionEnabledWin; PRBool defaultActionEnabledDoc; nsCOMPtr<nsIPrivateDOMEvent> privEvt(do_QueryInterface(event)); privEvt->SetTrusted(PR_TRUE); //make the event trusted tWindow->DispatchEvent(event, &defaultActionEnabledWin); //dispatch it (i.e. send it out into the wild)