Avoiding namespace leaks in event handlers

By | September 8, 2011

A common pattern that we see in review of Mozilla addons is code that looks like the following:

var myExtension = {
  saveMe: null,
  onLoad: function () {
    this.saveMe = "I am saved";
  }
}
window.addEventListener("load", myExtension.onLoad);

Unfortunately the value of “this.saveMe” that is used in onLoad is the value from the global window, not the value from the myExtension object. Really the event listener is looking for an object of type nsIDOMEventListener, with a method handleEvent. When it does not find that, it applies some magic and just uses the entered function. Unfortunately, in the process the “this” does not get defined from the parent object, and you are stuck with “this” being the global object.

Here are three alternatives that don’t leak:

1) give it what it wants, namely a handleEvent method:

var myExtension = {
  saveMe: null,
  handleEvent: function (evt) {
    if (evt.type == "load")
      this.saveMe = "I am saved";
  }
}
window.addEventListener("load", myExtension);

2) wrap the function call with a function:

var myExtension = {
  saveMe: null,
  onLoad: function () {
    this.saveMe = "I am saved";
  }
}
window.addEventListener("load", function() { myExtension.onLoad(); });

3) use the bind function to manually specify what “this” will be used:

var myExtension = {
  saveMe: null,
  onLoad: function () {
    this.saveMe = "I am saved";
  }
}
window.addEventListener("load", myExtension.onLoad.bind(myExtension));

These kinds of issue can be difficult to spot just by looking at code. Addon authors are strongly encouraged to use the “Extension Test” addon to catch leaks of names to global namespaces.