Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

eventListeners unload scope anonymous functions

28 views
Skip to first unread message

foudfou

unread,
Dec 29, 2011, 2:06:54 PM12/29/11
to
Hi All,

Suppose I have a minimal extension that alert("UNLOADED") on the
'unload' event.
Suppose the overlay.xul imports a JS module that only does:

if ("undefined" == typeof(myExt)) {
var myExt = {};
};

Now, if I open 2 FF windows and close them, on the second close, the
"UNLOADED" popup is not fired because alert() is not defined (?!)

I know the problem resides in the fact that 'myExt' gets defined in
the imported module, but what is happening exactly ? And am I doing
something wrong ?


Some remarks:

1. eventListeners are defined with anonymous functions

2. some possible "fixes" include:
- bind onQuit() to the main object and use it in the eventListener:

var myExtMainOnQuit = myExt.Main.onQuit.bind(myExt.Main);

- use handlers instead of anonymous functions:

window.addEventListener('unload', myExt.Main.onQuit, false);

3. the minimal addon is available for testing here:
https://github.com/downloads/foudfou/FireTray/bug_listeners_unload.xpi

Dave Townsend

unread,
Dec 29, 2011, 2:33:55 PM12/29/11
to
On 12/29/11 11:06, foudfou wrote:
> Hi All,
>
> Suppose I have a minimal extension that alert("UNLOADED") on the
> 'unload' event.
> Suppose the overlay.xul imports a JS module that only does:
>
> if ("undefined" == typeof(myExt)) {
> var myExt = {};
> };
>
> Now, if I open 2 FF windows and close them, on the second close, the
> "UNLOADED" popup is not fired because alert() is not defined (?!)
>
> I know the problem resides in the fact that 'myExt' gets defined in
> the imported module, but what is happening exactly ? And am I doing
> something wrong ?

You haven't really provided enough code or explanation here to explain
what is going on and how the JS module relates to the event handler.
Sorry I'm too lazy to download and extract an XPI.

My guess is that you aren't understanding that JS modules are
singletons, the first code to import it causes it to execute, any other
code that imports it just gets a reference to the already existing JS
module, none of its code is executed again.

foudfou

unread,
Dec 29, 2011, 3:40:06 PM12/29/11
to
> Sorry I'm too lazy to download and extract an XPI.

<!-- overlay.xul -->
<?xml version="1.0"?>
<overlay id="xulschoolhello-browser-overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/
there.is.only.xul">

<script type="application/x-javascript">
Components.utils.import("resource://myext/commons.js");

myExt.Main = {
onQuit: function(win) {
alert("UNLOADED");
},
}

window.addEventListener(
'unload', function (e) {
myExt.Main.onQuit(this); },
false);
</script>
</overlay>

// commons.js
var EXPORTED_SYMBOLS = [ "myExt" ];
var myExt = {};

> My guess is that you aren't understanding that JS modules are
> singletons [...]

I was aware of that, but how does it relate to the fact that alert()
is not defined on the second window unload ?

Also, I figured out there are 2 conditions for this behaviour:
- import of module which defines the global namespace
- use of sub-namespaces (myExt.Main)

Dave Townsend

unread,
Dec 29, 2011, 4:02:19 PM12/29/11
to
Your problem is that myExt is a singleton yet you try to set myExt.Main
to something different for each window, only the last thing you set to
myExt.Main will stick.

On 12/29/11 12:40, foudfou wrote:
>> Sorry I'm too lazy to download and extract an XPI.
>
> <!-- overlay.xul -->
> <?xml version="1.0"?>
> <overlay id="xulschoolhello-browser-overlay"
> xmlns="http://www.mozilla.org/keymaster/gatekeeper/
> there.is.only.xul">
>
> <script type="application/x-javascript">
> Components.utils.import("resource://myext/commons.js");
>
> myExt.Main = {
> onQuit: function(win) {
> alert("UNLOADED");
> },
> }

Here you are updating the myExt singleton with a new object that has the
current window as its parent. Everytime this code runs (for every
browser window opened) it replaces the current myExt.Main with the new
object. So "alert" here tries to call "alert" on the last browser window
that was opened. If that browser window is no longer around (i.e. if you
close the second browser window then close the first browser window)
then alert would be undefined.

I imagine if you did win.alert it would work fine because you are
passing in a live window, the one that is currently closing.

Not sure what the end goal here is but the correct thing to do is define
myExt.Main in the JSM in the first place rather than redefine it
everytime a browser window is opened, that is bound to lead to confusing
problems like this.

foudfou

unread,
Dec 29, 2011, 5:19:17 PM12/29/11
to
Thank you for the enlightening, detailed explanations !
And you're right, win.alert works fine.

I understand the chrome-specific window object gets destroyed with the
most recent window, but consider:

myExt.Main = {
sayHi: function() {
alert("Hi!");
},
onQuit: function(win) {
myExt.Main.sayHi();
alert("UNLOADED");
},
}

why does this produce "myExt undefined" on the second close ? myExt
isn't supposed to be a singleton shared by all windows, and thus
persistent ?

Dave Townsend

unread,
Dec 29, 2011, 5:52:40 PM12/29/11
to
The problem is that the window that defined that has gone away, taking
with it the JS scope that is defined in including its reference to myExt.
0 new messages