Steps for merging WebSocket and Xpcom code: Moved the processing of Interest and ContentObject from WebSocketTransport onmessages to NDN.onReceivedData.
Added method WebSocketTransport.send and use in expressInterest, etc.
Moved CSEntry definition and CSTable to NDN.js.
Moved registerPrefix from WebSocketTransport to NDN.
diff --git a/js/WebSocketTransport.js b/js/WebSocketTransport.js
index 594aff4..dbda8b7 100644
--- a/js/WebSocketTransport.js
+++ b/js/WebSocketTransport.js
@@ -5,7 +5,6 @@
 
 var WebSocketTransport = function WebSocketTransport() {    
 	this.ws = null;
-	this.ccndid = null;
 	this.maxBufferSize = 10000;  // Currently support 10000 bytes data input, consistent with BinaryXMLEncoder
 	this.buffer = new Uint8Array(this.maxBufferSize);
 	this.bufferOffset = 0;
@@ -70,199 +69,8 @@
 				return;
 			}
 			
-			var decoder = new BinaryXMLDecoder(self.buffer);
-			// Dispatch according to packet type
-			if (decoder.peekStartElement(CCNProtocolDTags.Interest)) {  // Interest packet
-				if (LOG > 3) console.log('Interest packet received.');
-				
-				var interest = new Interest();
-				interest.from_ccnb(decoder);
-				if (LOG > 3) console.log(interest);
-				var nameStr = escape(interest.name.getName());
-				if (LOG > 3) console.log(nameStr);
-				
-				var entry = getEntryForRegisteredPrefix(nameStr);
-				if (entry != null) {
-					//console.log(entry);
-					var info = new UpcallInfo(ndn, interest, 0, null);
-					var ret = entry.closure.upcall(Closure.UPCALL_INTEREST, info);
-					if (ret == Closure.RESULT_INTEREST_CONSUMED && info.contentObject != null) { 
-						var coBinary = encodeToBinaryContentObject(info.contentObject);
-						// If we directly use coBinary.buffer to feed ws.send(), WebSocket 
-						// will end up sending a packet with 10000 bytes of data. That 
-						// is, WebSocket will flush the entire buffer in BinaryXMLEncoder
-						// regardless of the offset of the Uint8Array. So we have to
-						// create a new Uint8Array buffer with just the right size and
-						// copy the content from coBinary to the new buffer.
-						//    ---Wentao
-						var bytearray = new Uint8Array(coBinary.length);
-						bytearray.set(coBinary);
-						
-						self.ws.send(bytearray.buffer);
-					}
-				}
-				
-			} else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) {  // Content packet
-				if (LOG > 3) console.log('ContentObject packet received.');
-				
-				var co = new ContentObject();
-				co.from_ccnb(decoder);
-				//console.log(co);
-				//var nameStr = co.name.getName();
-				//console.log(nameStr);
-				
-				if (self.ccndid == null && NDN.ccndIdFetcher.match(co.name)) {
-					// We are in starting phase, record publisherPublicKeyDigest in self.ccndid
-					if(!co.signedInfo || !co.signedInfo.publisher 
-						|| !co.signedInfo.publisher.publisherPublicKeyDigest) {
-						console.log("Cannot contact router, close NDN now.");
-						
-						// Close NDN if we fail to connect to a ccn router
-						ndn.readyStatus = NDN.CLOSED;
-						ndn.onclose();
-						//console.log("NDN.onclose event fired.");
-					} else {
-						//console.log('Connected to ccnd.');
-						self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
-						if (LOG>3) console.log(self.ccndid);
-						
-						// Call NDN.onopen after success
-						ndn.readyStatus = NDN.OPENED;
-						ndn.onopen();
-						//console.log("NDN.onopen event fired.");
-					}
-				} else {
-					var pitEntry = NDN.getEntryForExpressedInterest(co.name);
-					if (pitEntry != null) {
-						//console.log(pitEntry);
-						// Remove PIT entry from NDN.PITTable
-						var index = NDN.PITTable.indexOf(pitEntry);
-						if (index >= 0)
-							NDN.PITTable.splice(index, 1);
-						
-						var currentClosure = pitEntry.closure;
-						
-						// Cancel interest timer
-						clearTimeout(pitEntry.timerID);
-						//console.log("Clear interest timer");
-						//console.log(currentClosure.timerID);
-						
-						// Key verification
-						
-						// Recursive key fetching & verification closure
-						var KeyFetchClosure = function KeyFetchClosure(content, closure, key, sig, wit) {
-							this.contentObject = content;  // unverified content object
-							this.closure = closure;  // closure corresponding to the contentObject
-							this.keyName = key;  // name of current key to be fetched
-							this.sigHex = sig;  // hex signature string to be verified
-							this.witness = wit;
-							
-							Closure.call(this);
-						};
-						
-						KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
-							if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
-								console.log("In KeyFetchClosure.upcall: interest time out.");
-								console.log(this.keyName.contentName.getName());
-							} else if (kind == Closure.UPCALL_CONTENT) {
-								//console.log("In KeyFetchClosure.upcall: signature verification passed");
-								
-								var rsakey = decodeSubjectPublicKeyInfo(upcallInfo.contentObject.content);
-								var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.witness, this.sigHex);
-								
-								var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-								//console.log("raise encapsulated closure");
-								this.closure.upcall(flag, new UpcallInfo(ndn, null, 0, this.contentObject));
-								
-								// Store key in cache
-								var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
-								NDN.addKeyEntry(keyEntry);
-								//console.log(NDN.KeyStore);
-							} else if (kind == Closure.UPCALL_CONTENT_BAD) {
-								console.log("In KeyFetchClosure.upcall: signature verification failed");
-							}
-						};
-						
-						if (co.signedInfo && co.signedInfo.locator && co.signature) {
-							if (LOG > 3) console.log("Key verification...");
-							var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
-							
-							var wit = null;
-							if (co.signature.Witness != null) {
-								wit = new Witness();
-								wit.decode(co.signature.Witness);
-							}
-							
-							var keylocator = co.signedInfo.locator;
-							if (keylocator.type == KeyLocatorType.KEYNAME) {
-								if (LOG > 3) console.log("KeyLocator contains KEYNAME");
-								//var keyname = keylocator.keyName.contentName.getName();
-								//console.log(nameStr);
-								//console.log(keyname);
-								
-								if (keylocator.keyName.contentName.match(co.name)) {
-									if (LOG > 3) console.log("Content is key itself");
-									
-									var rsakey = decodeSubjectPublicKeyInfo(co.content);
-									var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-									var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-									
-									currentClosure.upcall(flag, new UpcallInfo(ndn, null, 0, co));
-									
-									// SWT: We don't need to store key here since the same key will be
-									//      stored again in the closure.
-									//var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
-									//NDN.addKeyEntry(keyEntry);
-									//console.log(NDN.KeyStore);
-								} else {
-									// Check local key store
-									var keyEntry = NDN.getKeyByName(keylocator.keyName);
-									if (keyEntry) {
-										// Key found, verify now
-										if (LOG > 3) console.log("Local key cache hit");
-										var rsakey = keyEntry.rsaKey;
-										var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-										var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-										
-										// Raise callback
-										currentClosure.upcall(flag, new UpcallInfo(ndn, null, 0, co));
-									} else {
-										// Not found, fetch now
-										if (LOG > 3) console.log("Fetch key according to keylocator");
-										var nextClosure = new KeyFetchClosure(co, currentClosure, keylocator.keyName, sigHex, wit);
-										var interest = new Interest(keylocator.keyName.contentName.getPrefix(4));
-										interest.interestLifetime = 4000;  // milliseconds
-										self.expressInterest(ndn, interest, nextClosure);
-									}
-								}
-							} else if (keylocator.type == KeyLocatorType.KEY) {
-								if (LOG > 3) console.log("Keylocator contains KEY");
-								
-								var rsakey = decodeSubjectPublicKeyInfo(co.signedInfo.locator.publicKey);
-								var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
-								
-								var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
-								// Raise callback
-								currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
-								
-								// Since KeyLocator does not contain key name for this key,
-								// we have no way to store it as a key entry in KeyStore.
-							} else {
-								var cert = keylocator.certificate;
-								console.log("KeyLocator contains CERT");
-								console.log(cert);
-								
-								// TODO: verify certificate
-							}
-						}
-					}
-				}
-			} else {
-				console.log('Incoming packet is not Interest or ContentObject. Discard now.');
-			}
-			
-			delete decoder;
-			
+            ndn.onReceivedElement(self.buffer);
+
 			// Renew StrcutureDecoder and buffer after we process a full packet
 			delete self.structureDecoder;
 			delete self.buffer;
@@ -280,12 +88,7 @@
 		// Fetch ccndid now
 		var interest = new Interest(NDN.ccndIdFetcher);
 		interest.interestLifetime = 4000; // milliseconds
-		var subarray = encodeToBinaryInterest(interest);
-		
-		var bytes = new Uint8Array(subarray.length);
-		bytes.set(subarray);
-		
-		self.ws.send(bytes.buffer);
+        this.send(encodeToBinaryInterest(interest));
 	}
 	
 	this.ws.onerror = function(ev) {
@@ -305,107 +108,61 @@
 	}
 };
 
-WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
+/*
+ * Send the Uint8Array data.
+ */
+WebSocketTransport.prototype.send = function(data) {
 	if (this.ws != null) {
-		//TODO: check local content store first
-
-        var binaryInterest = encodeToBinaryInterest(interest);
-		var bytearray = new Uint8Array(binaryInterest.length);
-		bytearray.set(binaryInterest);
-		
-		if (closure != null) {
-			var pitEntry = new PITEntry(interest, closure);
-			NDN.PITTable.push(pitEntry);
-			closure.pitEntry = pitEntry;
-		}
-		
-		this.ws.send(bytearray.buffer);
+        // If we directly use data.buffer to feed ws.send(), 
+        // WebSocket may end up sending a packet with 10000 bytes of data.
+        // That is, WebSocket will flush the entire buffer
+        // regardless of the offset of the Uint8Array. So we have to create
+        // a new Uint8Array buffer with just the right size and copy the 
+        // content from binaryInterest to the new buffer.
+        //    ---Wentao
+        var bytearray = new Uint8Array(data.length);
+        bytearray.set(data);
+        this.ws.send(bytearray.buffer);
 		if (LOG > 3) console.log('ws.send() returned.');
-		
-		// Set interest timer
-		if (closure != null) {
-			pitEntry.timerID = setTimeout(function() {
-				if (LOG > 3) console.log("Interest time out.");
-				
-				// Remove PIT entry from NDN.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);
-		}
 	}
 	else
 		console.log('WebSocket connection is not established.');
-};
-
-
-// For publishing data
-var CSTable = new Array();
-
-var CSEntry = function CSEntry(name, closure) {
-	this.name = name;        // String
-	this.closure = closure;  // Closure
-};
-
-function getEntryForRegisteredPrefix(name) {
-	for (var i = 0; i < CSTable.length; i++) {
-		if (CSTable[i].name.match(name) != null)
-			return CSTable[i];
-	}
-	return null;
 }
 
-WebSocketTransport.prototype.registerPrefix = function(ndn, name, closure, flag) {
-	if (this.ws != null) {
-		if (this.ccndid == null) {
-			console.log('ccnd node ID unkonwn. Cannot register prefix.');
-			return -1;
-		}
-		
-		var fe = new ForwardingEntry('selfreg', name, null, null, 3, 2147483647);
-		var bytes = encodeForwardingEntry(fe);
-		
-		var si = new SignedInfo();
-		si.setFields();
-		
-		var co = new ContentObject(new Name(), si, bytes, new Signature()); 
-		co.sign();
-		var coBinary = encodeToBinaryContentObject(co);
-		
-		//var nodename = unescape('%00%88%E2%F4%9C%91%16%16%D6%21%8E%A0c%95%A5%A6r%11%E0%A0%82%89%A6%A9%85%AB%D6%E2%065%DB%AF');
-		var nodename = this.ccndid;
-		var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
-
-		var interest = new Interest(interestName);
-		interest.scope = 1;
-		var binaryInterest = encodeToBinaryInterest(interest);
-		// If we directly use binaryInterest.buffer to feed ws.send(), 
-		// WebSocket will end up sending a packet with 10000 bytes of data.
-		// That is, WebSocket will flush the entire buffer in BinaryXMLEncoder
-		// regardless of the offset of the Uint8Array. So we have to create
-		// a new Uint8Array buffer with just the right size and copy the 
-		// content from binaryInterest to the new buffer.
-		//    ---Wentao
-    	var bytearray = new Uint8Array(binaryInterest.length);
-		bytearray.set(binaryInterest);
-		if (LOG > 3) console.log('Send Interest registration packet.');
-    	
-    	var csEntry = new CSEntry(name.getName(), closure);
-		CSTable.push(csEntry);
-    	
-    	this.ws.send(bytearray.buffer);
-		
-		return 0;
-	} else {
-		console.log('WebSocket connection is not established.');
-		return -1;
+WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
+    if (ndn.readyStatus != NDN.OPENED) {
+		console.log('Connection is not established.');
+        return;
+    }
+    
+	//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;
 	}
+
+	// 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));
 };