Home > Software > Using nsISAXXMLReader and my nsIChannel Woes

Using nsISAXXMLReader and my nsIChannel Woes

My first pass was to use nsIIOService to create an nsIChannel. This worked and gave me time to focus on getting my parser methods correct.

function runRestoreFromChannel( urlSpec, progress )
{
 var ioService = CC["@mozilla.org/network/io-service;1"].getService(CI.nsIIOService);
 var reader = CC["@mozilla.org/saxparser/xmlreader;1"].createInstance(CI.nsISAXXMLReader);
 var channel = ioService.newChannel( urlSpec,null,null );

 channel.notificationCallbacks = progress;
 reader.contentHandler = new RestoreParser( broker, progress );

 reader.parseAsync( null );
 channel.contentType = "text/xml";
 //channel.loadFlags = CI.nsIRequest.LOAD_BACKGROUND;
 channel.asyncOpen( reader, null );
}

This solution has one major flaw. The nsIFileInputStream that’s created reads in very large chunks from the filesystem for performance. The problem is during my parse I do some fairly intensive I/O operations as I read my data in. But since, almost all the data is read in by the nsIInputStream, it means my GUI is locked up while I’m processing the data. To get around this I thought about writing my own nsIStreamListener and controlling how the data is passed to my nsISAXXMLReader. The problem there is I’d either have to buffer all the data or still be stuck in a loop on the main thread blocking the UI. Ideally, I’d be able to run the parser in a background thread at full speed. As a simple workaround I can use nsIInputStreamPump. It works almost identically to using the nsIChannel, except I can specify how much data to read at a time (i.e. the chunk size). There was one minor little problem that can be worked around using a custom nsIStreamListener around the nsISAXXMLReader.

Here’s the code for setting up the parser:

var reader = CC["@mozilla.org/saxparser/xmlreader;1"].createInstance(CI.nsISAXXMLReader);
var filestream = CC["@mozilla.org/network/file-input-stream;1"].createInstance(CI.nsIFileInputStream);
var pump = CC["@mozilla.org/network/input-stream-pump;1"].createInstance(CI.nsIInputStreamPump);
var ioService = CC["@mozilla.org/network/io-service;1"].getService(CI.nsIIOService);
var channel = ioService.newChannel( urlSpec,null,null );
var uri = ioService.newURI( urlSpec, null, null );

filestream.init(file, 0x01, 0444, 0);

reader.contentHandler = new RestoreParser( broker, progress );

reader.parseAsync( null );

pump.init( filestream, -1, -1, StreamChunkSize, 1, false );
reader.baseURI = uri;
pump.asyncRead( new StreamListener( reader, progress, channel ), null );

Here’s the code for wrapping the nsIStreamListener:

function StreamListener( reader, progress, channel )
{
 this.reader = reader;
 this.progress = progress;
 this.channel = channel;
}
StreamListener.prototype = {
 reader:null,
 onStartRequest: function( request,context ){
   dump( "starting\n" );
   // XXX: passing channel here to work around bug in nsParser.cpp
   this.reader.onStartRequest( this.channel, context );
 },
 onDataAvailable: function( request, context, input, offset, count ){
   dump( "offset: " + offset + ", count: " + count + "\n" );
   // XXX: passing channel here to work around bug in nsParser.cpp
   this.reader.onDataAvailable( this.channel, context, input, offset, count );
   this.progress.onProgress( request, context, offset, offset + count );
 },
 onStopRequest: function( request, context, status ){
   // XXX: passing channel here to work around bug in nsParser.cpp
   this.reader.onStopRequest( this.channel, context, status );
 }
};

Software , , ,

  1. phreed
    May 16th, 2008 at 15:31 | #1

    I am trying to do a similar thing but reading from a socket rather than a file.

    this.host = host.toLowerCase();
    this.port = port;

    try {
    // Set up the transport
    var transportService = Components
    .classes["@mozilla.org/network/socket-transport-service;1"]
    .getService(Components.interfaces.nsISocketTransportService);

    this.transport = transportService
    .createTransport(null,0,this.host,this.port,null);

    // What will the output stream be used for?
    this.outstream = this.transport.openOutputStream(0,0,0);

    // We will at least receive requests to manage the sprites representing
    // equipment and units. ADD, CHANGE, DELETE
    this.instream = this.transport.openInputStream(0,0,0);
    this.sinstream = Components
    .classes["@mozilla.org/scriptableinputstream;1"]
    .createInstance(Components.interfaces.nsIScriptableInputStream);
    this.sinstream.init(this.instream);

    } catch(ex) {
    jsdump(ex);
    }

  2. phreed
    May 16th, 2008 at 16:03 | #2

    I got it working.
    So the answer to your question is, yes nsISAXXMLReader works with FireFox3.

  1. No trackbacks yet.
CommentLuv Enabled

Comments links could be nofollow free.