In onDataAvailable, just use BinaryXmlElementReader.
In expressInterest, do the same as WebSocketTransport, including setting the interest timeout.
diff --git a/js/XpcomTransport.js b/js/XpcomTransport.js
index 14d72f2..f165ddf 100644
--- a/js/XpcomTransport.js
+++ b/js/XpcomTransport.js
@@ -25,9 +25,9 @@
 /*
  * Connect to the host and port in ndn.  This replaces a previous connection.
  * Listen on the port to read an entire binary XML encoded element and call
- *    listener.onReceivedElement(element) where element is Uint8Array.
+ *    elementListener.onReceivedElement(element) where element is Uint8Array.
  */
-XpcomTransport.prototype.connect = function(ndn, listener) {
+XpcomTransport.prototype.connect = function(ndn, elementListener) {
     if (this.socket != null) {
         try {
             this.socket.close(0);
@@ -49,8 +49,7 @@
 
     var inStream = this.socket.openInputStream(0, 0, 0);
 	var dataListener = {
-		dataParts: [],
-        structureDecoder: new BinaryXMLStructureDecoder(),
+        elementReader: new BinaryXmlElementReader(elementListener),
 		
 		onStartRequest: function (request, context) {
 		},
@@ -60,36 +59,10 @@
 			try {
 				// Use readInputStreamToString to handle binary data.
                 // TODO: Can we go directly from the stream to Uint8Array?
-				var rawData = DataUtils.toNumbersFromString
-                    (NetUtil.readInputStreamToString(inStream, count));
-				
-                // Process multiple objects in this packet.
-                while(true) {
-                    // Scan the input to check if a whole ccnb object has been read.
-                    this.structureDecoder.seek(0);
-                    if (this.structureDecoder.findElementEnd(rawData)) {
-                        // Got the remainder of an object.  Report to the caller.
-                        this.dataParts.push(rawData.subarray(0, this.structureDecoder.offset));
-                        listener.onReceivedElement(DataUtils.concatArrays(this.dataParts));
-                    
-                        // Need to read a new object.
-                        rawData = rawData.subarray(this.structureDecoder.offset, rawData.length);
-                        this.dataParts = [];
-                        this.structureDecoder = new BinaryXMLStructureDecoder();
-                        if (rawData.length == 0)
-                            // No more data in the packet.
-                            return;
-                        
-                        // else loop back to decode.
-                    }
-                    else {
-                        // Save for a later call to concatArrays so that we only copy data once.
-                        this.dataParts.push(rawData);
-                        return;
-                    }
-                }
+                this.elementReader.onReceivedData(DataUtils.toNumbersFromString
+                    (NetUtil.readInputStreamToString(inStream, count)));
 			} catch (ex) {
-				console.log("XpcomTransport.onDataAvailable exception: " + ex);
+				console.log("XpcomTransport.onDataAvailable exception: " + ex + "\n" + ex.stack);
 			}
 		}
     };
@@ -116,21 +89,11 @@
     var thisXpcomTransport = this;
     
     if (this.socket == null || this.connectedHost != ndn.host || this.connectedPort != ndn.port) {
-      var dataListener = {
+      var elementListener = {
 		onReceivedElement : function(element) {
             var decoder = new BinaryXMLDecoder(element);
             if (decoder.peekStartElement(CCNProtocolDTags.Interest)) {
-                // TODO: handle interest properly.  For now, assume the only use in getting
-                //   an interest is knowing that the host is alive from NDN.ccndIdFetcher.
-                var pitEntry = NDN.getEntryForExpressedInterest(NDN.ccndIdFetcher);
-				if (pitEntry != null) {
-					// Remove PIT entry from NDN.PITTable.
-					var index = NDN.PITTable.indexOf(pitEntry);
-					if (index >= 0)
-						NDN.PITTable.splice(index, 1);
-                        
-                    pitEntry.closure.upcall(Closure.UPCALL_INTEREST, null);
-                }
+                // TODO: handle interest properly. 
             }
             else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) {
                 var co = new ContentObject();
@@ -147,6 +110,9 @@
    				if (pitEntry != null) {
 					var currentClosure = pitEntry.closure;
                         
+                    // Cancel interest timer
+                    clearTimeout(pitEntry.timerID);
+                    
                     // TODO: verify the content object and set kind to UPCALL_CONTENT.
                     var result = currentClosure.upcall(Closure.UPCALL_CONTENT_UNVERIFIED,
                                 new UpcallInfo(thisXpcomTransport.ndn, null, 0, co));
@@ -172,14 +138,37 @@
 		}
 	  }
       
-      this.connect(ndn, dataListener);
+      this.connect(ndn, elementListener);
     }
     
-    var binaryInterest = encodeToBinaryInterest(interest);
-    
-                        // TODO: This needs to be a single thread-safe transaction.
-	var pitEntry = new PITEntry(interest, closure);
-	NDN.PITTable.push(pitEntry);
+	//TODO: check local content store first
+	if (closure != null) {
+		var pitEntry = new PITEntry(interest, closure);
+        // TODO: This needs to be a single thread-safe transaction on a global object.
+		NDN.PITTable.push(pitEntry);
+		closure.pitEntry = pitEntry;
+	}
 
-    this.send(binaryInterest);
+	// Set interest timer
+	if (closure != null) {
+		pitEntry.timerID = setTimeout(function() {
+			if (LOG > 3) console.log("Interest time out.");
+				
+			// Remove PIT entry from NDN.PITTable.
+            // TODO: Make this a thread-safe operation on the global PITTable.
+			var index = NDN.PITTable.indexOf(pitEntry);
+			//console.log(NDN.PITTable);
+			if (index >= 0) 
+	            NDN.PITTable.splice(index, 1);
+			//console.log(NDN.PITTable);
+			//console.log(pitEntry.interest.name.getName());
+				
+			// Raise closure callback
+			closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
+		}, interest.interestLifetime);  // interestLifetime is in milliseconds.
+		//console.log(closure.timerID);
+	}
+
+	this.send(encodeToBinaryInterest(interest));
 };
+