4

I have a Firefox extension that needs to check for the onUnload event. Basically I want to send a message to my server when the user disables the extension.

What I tried doing, was to send a message to one of my content scripts, which would then call XMLHttpRequest. This works fine for any other event the extension triggers, but it would appear that the content scripts get unloaded before the message can even get passed.

main.js

Here is the code from the main.js script:

exports.onUnload = function(reason) {
    //unloadWorker comes from a PageMod 'onAttach: function(worker){}'
    //That is called every time a page loads, so it will a recent worker.
    if(unloadWorker != null) { 
        unloadWorker.port.emit("sendOnUnloadEvent", settings, reason);
    }
};

content script

Here is the code from the content script that I attach to every page that gets loaded.

self.port.on("sendOnUnloadEvent", function(settings, reason) {
    console.log("sending on unload event to servers");
    settings.subid = reason;
    if(reason != "shutdown") {
        sendEvent(("on_unload"), settings);
    }
});

Send event code

Finally, here is the send event code just for a reference for how I originally planned on using XMLHttpRequest:

sendEvent = function(eventName, settings) {

    if (!eventName) {
        eventName = "ping"; 
    }
    //Not the actual URL, but you get the idea.
    var url = 'http://example.com/sendData/?variables=value&var2=value2'

    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {

    }

    xhr.open("GET", url, true);
    xhr.send();
}

Is there anyway to use XMLHttpRequest from main.js?

Or perhaps a way to trigger the onUnload event, but have it trigger before the extension actually get unloaded? (Like a beforeOnUnload type event)

serv-inc
  • 35,772
  • 9
  • 166
  • 188
Garrett
  • 545
  • 1
  • 7
  • 21

3 Answers3

7

The preferred way to make network requests from main.js is to use the Request object in module sdk/request.

However, Request can only make asynchronous requests, which means that the object will be out of scope when the function ends and the request will not occur.

Instead, you can use sdk/net/xhr to be able to use XMLHttpRequest and make a synchronous GET request on unload like so:

const {XMLHttpRequest} = require("sdk/net/xhr");
exports.onUnload = function(reason) {
    var url = 'http://mysite.com/sendData/?variables=value&var2=value2'
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, false);
    xhr.send(null);
});

But do note that sdk/net/xhr is marked as unstable and synchronous requests are blocking and frowned upon.

  • I came up with a different solution that I will post in a moment, but it seems to me that I can keep my request async. You gave me what I was looking for, but my solution uses "require("chrome")" Basically I loaded it like this: const {Cc, Ci} = require("chrome"); var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); – Garrett Nov 06 '13 at 18:35
  • Hmmm... Your answer was to the point, until you got to the part where you proposed synchronous requests. Synchronous requests are not just "frowned upon", they are actually bad. Either they will block the whole user interface, or these days they will usually spin the event loop, making the code re-entrant which isn't really supported in privileged code (`main.js`, etc.). Because of this, they are not allowed for addons.mozilla.org hosted extensions, which is something else you should be aware of. – nmaier Nov 06 '13 at 18:59
  • I proposed asynchronous, not synchronous. – Garrett Nov 06 '13 at 19:58
  • 2
    I was talking about the answer itself, not your comment to it ;) – nmaier Nov 06 '13 at 20:21
1

My solution is slightly different than user2958125's, so I'll post it below.

const {Cc, Ci} = require("chrome");
var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
//var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Components.interfaces.nsIXMLHttpRequest);

xhr.onreadystatechange = function() {

}

xhr.open("GET", url, true);
xhr.send();
Garrett
  • 545
  • 1
  • 7
  • 21
  • You should prefer using the `request` module, or at least the `net/xhr` module, as @user2958125 proposed in the other answer (but without the synchronous part). They have callbacks, too. Also, when using plain `XMLHttpRequest`, most of the time `onload`, `onerror` and/or `onloadend` would be preferable to `onreadystatechange`. See [Using XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest) – nmaier Nov 06 '13 at 19:04
  • 1
    I think using the components might be better. I've read a few documents talking about how the modules you are talking about have no guarantee of being supported in the future. While the components I'm using are basically the same as what is used when making an extension using XUL. – Garrett Nov 06 '13 at 19:57
  • 1
    You don't actually have any guarantee that the `chrome` module will be supported in the future, or that `nsIXMLHttpRequest` will be, for that matter. However, the `request` module is marked *stable* and hence will be supported in the future. – nmaier Nov 06 '13 at 20:19
1
function reqListener () {
  console.log(this.responseText);
}

const {XMLHttpRequest} = require("sdk/net/xhr");
var url = "http://ya.ru";
var xhr = new XMLHttpRequest();
xhr.onload = reqListener;
xhr.open('GET', url, false);
xhr.send(null);
serv-inc
  • 35,772
  • 9
  • 166
  • 188
Alex M
  • 77
  • 8