Mozilla's getting a new look. What do you think? https://mzl.la/brandsurvey

Adding a new event

This document is a working draft.

What type of event do you want?

Roughly, there are 3 types of event. First, you need to choose which type you need.

Case 1: A simple DOM event which can be implemented with WebIDL and WidgetEvent or something. This type of event is useful if the event isn't handled by C++ code.

Case 2: A DOM event which represents a native event like a user action such as keyboard or mouse input. This type of event allows C++ code to handle it easier.

Case 3: It's not a DOM event. However, C++ code may need to dispatch an event to DOM tree and handle it with C++ event handler. This is useful for widget notifying DOM tree of something or retrieving something from DOM tree.

How to implement an internal event class

If you're in the case 1, you can skip this section.

Add event messages

You need to add event messages which are stored by WidgetEvent::message. All messages are defined by macro in "messages" section of BasicEvents.h.

Define event class name

You need to add an event class name in EventClassList.h.

Use NS_EVENT_CLASS macro for adding it. The macro takes two arguments, aPrefix and aName.

aPrefix defines prefix of event class name which should be Widget or Internal. Widget should be used if the event class is dispatched from widget. Otherwise, i.e., the event class is just used for internal event class of a DOM event class, it should be Internal.

aName defines its event class specific name and it must end with "Event" or "EventBase". The latter is used only for abstruct super event class whose instance will never be created.

Please note that aName must not be same as other event classes even if aPrefix is different. I.e., defining both WidgetFooEvent and InternalFooEvent is invalid.

Define and implement an event class

Then, you need to define and implement an event class. You should choose a good header file from what your event class represents.

BasicEvents.h
This header file should be used only for defining generic purpose event class such as a common super class of various event classes. Probably, you shouldn't use this for your class.
ContentEvents.h
This header file should be used for defining internal event classes which are dispatched in content and do not represent user action. Typically, events in this header file never cross process boundary.
MouseEvents.h
This header file should be used for defining input events from pointing devices such as mouse.
TextEvents.h
This header file should be used for defining input events from keyboard or IME and also other text edit related events like querying focused content information.
TouchEvents.h
This header file should be used for defining input events from touch devices.
MiscEvents.h
If the new event class isn't proper any header files above, you should use this.

Each event class should implement following methods manually.

virtual InternalFooEvent* AsFooEvent() MOZ_OVERRIDE
This method should just return this. This method is used for retrieving sub class pointer avoiding to use static_cast.
virtual WidgetEvent* Duplicate() const MOZ_OVERRIDE
This method should create a new instance with copying its members except widget. For copying the members, this should use AssignFooEventData() with true for aCopyTargets. And also, mFlags should be just copied since AssignFooEventData() doesn't copy mFlags. This method is basically used for duplicating an internal event class instance of a DOM event when the DOM event is stored by content.
void AssignFooEventData(const InternalFooEvent* aEvent, bool aCopyTargets)
This method should copy members from aEvent. First, this method should call its super class's this method. Then, copy the additional members which are defined in the class. If aCopyTargets is false, don't copy its event target related members.

All new member variables of event classes must be with "m" prefix. Although, a lot of existing members don't have "m" prefix, but you shouldn't use the legacy local rules.

If you need to add methods, it's okay to implement them as inline methods. I.e., you can implement new methods in the header files directly.

However, if implementing method isn't small and called from a lot of places, implementing it as inline causes to increase binary size. In this case, you should implement the method in WidgetEventImple.cpp. And also, if implementing a method needs to include other header files, you should implement it in WidgetEventImple.cpp too for avoiding include hell.

Make new event class IPC aware

For example, new class is caused by native user input event and needed to be dispatch to content, the class should be able to cross process boundary. In such cases, you may need to add new ParamTraits for the new event class in nsGUIEventIPC.h.

Modify utility methods of WidgetEvent

WidgetEvent class has some utility mehtods and all of them are implemented in WidgetEventImple.cpp. E.g., if your event shouldn't cause DOM event, you need to modify WidgetEvent::IsAllowedToDispatchDOMEvent(). If your event should be fired on focused node, you need to modify WidgetEvent::IsTargetedAtFocusedContent().

How to implement a DOM event class

If you're in the case 3, you can skip this section.

Create WebIDL of the event

Write a DOM event definition with WebIDL. It should be created under dom/webidl/. See WebIDL bindings for the detail.

If your new event shouldn't be created with event constructor such as:

var event = new FooEvent("bar", { bubbles: true, cancelable: true, detail: 5 });

you shouldn't define construnctor in it.

Create XPCOM interface for the DOM event

If your event class should be accessible via XPCOM interface, you should create a new nsIDOMFooEvent interface in dom/interfaces/events/. However, in these days, this is not necessary. If all information of the event is stored by its internal event, C++ event handlers can acess them with following code:

NS_IMETHODIMP
AnEventListener::HandleEvent(nsIDOMEvent* aEvent)
{
  InternalFooEvent* internalEvent =
    aEvent->GetInternalNSEvent()->AsFooEvent();
  if (NS_WARN_IF(!internalEvent)) {
    return NS_ERROR_UNEXPECTED;
  }
  DoSomethingWith(internalEvent->mBar);
  aEvent->PreventDefault();
  return NS_OK;
}

Implement DOM event class

Generate DOM event implementation if it's possible

If you're creating simple DOM event class, it might be generated automatically. If it's possible, you just need to add an entry to GENERATED_EVENTS_WEBIDL_FILES in dom/webidl/moz.build.

Define and create DOM event class manually

Otherwise, you need to implement a DOM event class yourself. The best place to create FooEvent.h and FooEvent.cpp is in dom/events/. Otherwise, you need to specify the path to the header file in dom/bindings/Bindings.conf.

When you create a DOM event class, its name should be same as the name defined in its standard specification. And it should be in mozilla::dom namespace. For example, KeyboardEvent which is defined in DOM Level 3 Events should be implemented as mozilla::dom::KeyboardEvent.

And the header file should be exported as "mozilla/dom/FooEvent.h". Add the header file to EXPORTS.mozilla.dom in dom/events/moz.build.

You probably need to implement a lot of methods to the event class, but this document doesn't explain all of them. Please refer existing DOM event implementation for example.

However, there are some hints:

  1. Set mEventIsInternal in the constructor of the most descendant class because each constructor creates dummy internal event instance at calling constructors of its super class. Therefore, super classes always set it false.
  2. Don't override methods of its super classes as far as possible since overriding the method may cause harder to maintain.
  3. When you need to check if the caller content or chrome, you can use implicitJSContext attribute in dom/bindings/Bindings.conf. Then, the method can have a pointer of JSContext in its argument.

Create DOM event factory method

If your DOM event implementation is generatable, you can skip this section.

In nsIDOMEvent.idl, you need to define NS_NewDOMFooEvent(). It's last argument should be the most subclass. Then, the implementation of the DOM event can access internal event class instance safer.

Then, the factory method should be implemented at the bottom of FooEvent.cpp in the global namespace.

Modify EventDispatcher::CreateEvent()

If your DOM event implementation is generatable or you're in case 3, you can skip this section.

The first half of EventDispatcher::CreateEvent() is a factory to create a DOM event instance from internal event instance. You need to modify this.

The last half of it is the implementation of legacy event creator of Javascript such as:

var event = document.createEvent("FooEvent");

If you support this feature, you need to modify here. However, this feature shouldn't be implemented for compabitility with other browsers since web applications should use event constructor.

Register each event into EventNameList.h

If you're in case 3, you can skip this section.

EventNameList.h defines mapping between DOM event name, internal event message, event handler attribute owner and internal event class. See the comments of the head of the file for the detail.

Modify tests

If you're in case 3, you can skip this section.

You need to modify test_all_synthetic_events.html which tests if calling getModifierState() doesn't cause crash.

You need to modify test_assign_event_data.html which tests if each attribute of DOM event is copied by AssignFooEventData() properly. If your event cannot be fired easy, you should add the test but canRun should return false and call todo().

If the new DOM event is creatable with constructor, you need to modify test_eventctors.html or test_eventctors.xul which tests the behavior of event constructor.

Document Tags and Contributors

 Contributors to this page: michaelweghorn, Masayuki
 Last updated by: michaelweghorn,