javascriptfirefoxfirefox-addon

Firefox Extension/Addon : Reading a text file from remote server(URL)


After wasting my two days to find out what's going wrong with this script, finally I decide to ask it.

What I am trying to do

I am trying to read a text file from remote server. Then storing all text file updates to an SQLITE database at the time of my Firefox Extension/Addon get loaded.

What I tried

var updatereader = {

    start: function () {
    //alert('reading update');
        var fURL = null;
        var ioService = null;
        var fURI = null;
        var httpChannel = null;

        fURL = "http://www.example.com/addon/mlist.txt";
        ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
        fURI = ioService.newURI(fURL, null, null);
        httpChannel = ioService.newChannelFromURI(fURI).QueryInterface(Components.interfaces.nsIHttpChannel);
        httpChannel.asyncOpen(updatereader.StreamReader, null);
    },

    onUpdateCompleted: function () {

    },

    StreamReader:
    {
        fOutputStream: null,
        fPointer: null,
        tempFile: "mlist.txt",

        onStartRequest: function (aRequest, aContext) {
        //alert('onStart');
            updatereader.StreamReader.fOutputStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
            updatereader.StreamReader.fPointer = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsIFile);
            updatereader.StreamReader.fPointer.append(updatereader.StreamReader.tempFile);
            updatereader.StreamReader.fOutputStream.init(updatereader.StreamReader.fPointer, 0x02 | 0x20 | 0x08, 0644, 0);
        },

        onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) {
        //control flow is not entering here - may be here is somehting missing
            var sStream = null;
            var tempBuffer = null;
            sStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
            sStream.init(aInputStream);
            tempBuffer = sStream.read(aCount);
            updatereader.StreamReader.fOutputStream.write(tempBuffer, aCount);
        },

        onStopRequest: function (aRequest, aContext, aStatusCode) {
        //alert('onStop');
            var currentDate = new Date();
            if (aStatusCode == 0) {
                fileInputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);

                updatereader.StreamReader.fOutputStream.close();
                fileInputStream.init(updatereader.StreamReader.fPointer, 0x01, 0, 0);
                lineInputStream = fileInputStream.QueryInterface(Components.interfaces.nsILineInputStream);

             //pass data to somewhere 
              var dbH = new dbstore();
              dbH.updateData(lineInputStream);

                lineInputStream.close();

                updatereader.StreamReader.fPointer.remove(false);


                updatereader.onUpdateCompleted();
            } else {

            }
        }
    }
}

Problem:

Getting nothing in lineInputStream which passes the read data to somewhere else for storing it.

Area of problem:

Program control flow is not entring to this section

 onDataAvailable:

Not getting any error.


Solution

  • First of all, there doesn't really seem to be any need to read the file to the disk first (unless it is really, really big).

    I'd just use XMLHttpRequest to get the file, which when run from a privileged context (e.g. add-on code, but not a website) can access any and every valid URI. XMLHttpRequest will simplify almost everything, e.g. no more onDataAvailable, (usually) no more manual text converting, etc. Also, no need to ever hit the disk during the transfer.

    Code would look something like this:

    var req = new XMLHttpRequest();
    req.open("GET", "http://www.example.com/addon/mlist.txt"); // file:/// would work too, BTW
    req.overrideMimeType("text/plain");
    req.addEventListener("load", function() {
      // Do something with req.responseText
    }, false);
    req.addEventListener("error", function() {
      // Handle error
    }, false);
    req.send();
    

    If you want to use XMLHttpRequest in a non-window, e.g. js code module or js components, then you need to first initialize a constructor. This is not required for windows, including XUL windows and by that XUL overlays.

    // Add XMLHttpRequest constructor, if not already present
    if (!('XMLHttpRequest' in this)) {
        this.XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest");
    }
    

    SDK users should use the request module, or net/xhr if a lower-level API is required.

    PS: If you're still interested in using raw channels, here is a minimal example I coded up in a Scratchpad (to run, open a Scratchpad for a privileged location, e.g. about:newtab). You shouldn't alert from your own implementation: alert() will spin the event loop and causes reentrant code, which is not supported in this context.

    var {
        classes: Cc,
        interfaces: Ci,
        results: Cr,
        utils: Cu
    } = Components;
    
    Cu.import("resource://gre/modules/XPCOMUtils.jsm")
    Cu.import("resource://gre/modules/Services.jsm");
    
    var ConverterStream = Components.Constructor(
        "@mozilla.org/intl/converter-input-stream;1",
        "nsIConverterInputStream",
        "init");
    var RC = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
    
    function Listener() {
        this.content = "";
    }
    Listener.prototype = {
        QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener]),
        onStartRequest: function(req, ctx) {
            console.log("start");
        },
        onDataAvailable: function(req, ctx, stream, offset, count) {
            console.log("data", count);
            try {
                var cs = new ConverterStream(stream, null /* utf-8 */, 4096, RC);
                try {
                    var str = {};
                    while (cs.readString(4096, str)) {
                        this.content += str.value;
                    }
                }
                finally {
                    cs.close();
                }
            }
            catch (ex) {
                console.error("data", ex.message, ex);
            }
        },
        onStopRequest: function(req, ctx, status) {
            console.log("stop", status,
                        this.content.substr(0, 20), this.content.length);
        }
    };
    
    var uri = Services.io.newURI("http://example.org", null, null);
    Services.io.newChannelFromURI(uri).asyncOpen(new Listener(), null);