Move out the ContentClosure for expressInterest to be its own class.
Load files with multiple segments if needed, and updated the README to explain how it works.
diff --git a/README b/README
index 28a09fa..ad9e4ba 100644
--- a/README
+++ b/README
@@ -1,9 +1,33 @@
 First native version of the NDN protocol written in Javascript ( Also refereed to as CCN )
 
-The goal of this project is to improve the current implementation of the lwNDN API that allows users to create applications running on top of the NDN network. The goal is to have a lightweight version of the protocol, which can run on browsers. The main intent is to enable browser-based applications to use NDN directly without requiring a binary build of the CCNx code. In particular, the goal is to have an AJAX-style dynamic data access. The goal is also to have a lighter version of the protocol, which would be better suited for embedded systems. Furthermore, the goal is that lwNDN communicates with CCNx nodes (routers).
+The goal of this project is to improve the current implementation of the NDN-JS API that allows users to create applications running on top of the NDN network. The goal is to have a lightweight version of the protocol, which can run on browsers. The main intent is to enable browser-based applications to use NDN directly without requiring a binary build of the CCNx code. In particular, the goal is to have an AJAX-style dynamic data access. The goal is also to have a lighter version of the protocol, which would be better suited for embedded systems. Furthermore, the goal is that NDN-JS communicates with CCNx nodes (routers).
 
-The current status of lwNDN allows for JavaScript applications running on browsers to send interest packets and retrieve data packets. This includes encoding and decoding data packets.
+The current status of NDN-JS allows for JavaScript applications running on browsers to send interest packets and retrieve data packets. This includes encoding and decoding data packets.
 
 This is currently done in the following way:
 
-createRoute('borges.metwi.ucla.edu', 9695); var contentObject = queryPrefix('/ndn/ucla.edu/apps/hydra/mainvideo'); console.log(contentObject.content);
\ No newline at end of file
+var ndn = new NDN();
+var contentObject = ndn.get('/ndn/ucla.edu/apps/hydra/mainvideo'); 
+console.log(contentObject.content);
+
+* Firefox extension for ccnx protocol
+
+NDN-JS also includes a Firefox extension for the ccnx protocol.  To install in Firefox, open
+Tools > Add-ons.  In the "gear" or "wrench" menu, click Install Add-on From File and open
+js/ccnxProtocol.xpi in this distribution.  Restart Firefox.
+
+Firefox uses the extension to load any URI starting with ccnx, for example
+ccnx:/ndn/ucla.edu/apps/lwndn-test/trig-table
+
+When the page is loaded, the extension updates address bar with the full matched name from the
+retrieved content object including the version, but without the implicit digest or segment number 
+(see below).
+
+A URI for content with multiple segments is handled as follows.
+If the URI has a segment number, just retrieve that segment and return the content to the browser.
+
+Otherwise look at the name in the returned ContentObject.  If the returned name has no segment number,
+just return the content to the browser. If the name has a segment number which isn't 0, store it 
+and express an interest for segment 0. Read segments in order and return each content to the browser 
+as we go until we get the segment for FinalBlockID.
+
diff --git a/js/ccnxProtocol.xpi b/js/ccnxProtocol.xpi
index b90ab65..4cf7ed4 100644
--- a/js/ccnxProtocol.xpi
+++ b/js/ccnxProtocol.xpi
Binary files differ
diff --git a/js/ccnxProtocol/components/ccnxProtocolService.js b/js/ccnxProtocol/components/ccnxProtocolService.js
index 0bafb5d..8c5c0fa 100644
--- a/js/ccnxProtocol/components/ccnxProtocolService.js
+++ b/js/ccnxProtocol/components/ccnxProtocolService.js
@@ -19,25 +19,25 @@
 }

 

 CcnxProtocol.prototype = {

-	scheme: "ccnx",

-	protocolFlags: nsIProtocolHandler.URI_NORELATIVE |

+    scheme: "ccnx",

+    protocolFlags: nsIProtocolHandler.URI_NORELATIVE |

                    nsIProtocolHandler.URI_NOAUTH |

                    nsIProtocolHandler.URI_LOADABLE_BY_ANYONE,

 

-	newURI: function(aSpec, aOriginCharset, aBaseURI)

-	{

-		var uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);

-		uri.spec = aSpec;

-		return uri;

-	},

+    newURI: function(aSpec, aOriginCharset, aBaseURI)

+    {

+        var uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);

+        uri.spec = aSpec;

+        return uri;

+    },

 

-	newChannel: function(aURI)

-	{

-		try {

+    newChannel: function(aURI)

+    {

+        try {

             var trimmedSpec = aURI.spec.trim();

     

             var contentChannel;

-			var requestContent = function(contentListener) {

+            var requestContent = function(contentListener) {

                 // Set nameString to the URI without the protocol.

                 var nameString = trimmedSpec;

                 var colonIndex = nameString.indexOf(':');

@@ -45,70 +45,140 @@
                     nameString = nameString.substr

                         (colonIndex + 1, nameString.length - colonIndex - 1).trim();

                 

-				var name = new Name(nameString);

+                var name = new Name(nameString);

                 // TODO: Strip off an ending implicit digest before checking the last component?

-                var uriEndsWithSequence = endsWithSequence(name);

+                var uriEndsWithSegmentNumber = endsWithSegmentNumber(name);

                 

-			    // 131.179.141.18 is lioncub.metwi.ucla.edu .

-				var ndn = new NDN('131.179.141.18');

-                				

-				var ContentClosure = function ContentClosure() {

-                    // Inherit from Closure.

-                    Closure.call(this);

-                }

-                ContentClosure.prototype.upcall = function(kind, upcallInfo) {

-                    if (!(kind == Closure.UPCALL_CONTENT ||

-                          kind == Closure.UPCALL_CONTENT_UNVERIFIED))

-                        // The upcall is not for us.

-                        return Closure.RESULT_ERR;

-                        

-                    var contentObject = upcallInfo.contentObject;

-                    if (contentObject.content == null) {

-                        dump("CcnxProtocol.newChannel: contentObject.content is null\n");

-                        return Closure.RESULT_ERR;

-                    }

+                var ndn = new NDN("lioncub.metwi.ucla.edu");

+                ndn.expressInterest(name, new ContentClosure

+                    (ndn, contentListener, uriEndsWithSegmentNumber, aURI.originCharset));

+            };

 

-                    // Get the URI from the ContentObject including the version.

-                    var contentUriSpec;

-                    if (!uriEndsWithSequence && endsWithSequence(contentObject.name)) {

-                        var nameWithoutSequence = new Name

-                            (contentObject.name.components.slice

-                             (0, contentObject.name.components.length - 1));

-                        contentUriSpec = "ccnx:" + nameWithoutSequence.to_uri();

-                    }

-                    else

-                        contentUriSpec = "ccnx:" + contentObject.name.to_uri();

-

-                    var content = DataUtils.toString(contentObject.content);

-                    var contentTypeEtc = getContentTypeAndCharset(contentObject.name);

-                    

-                    var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);

-					contentListener.onReceivedContent(content, 

-                        contentTypeEtc.contentType, contentTypeEtc.contentCharset, 

-                        ioService.newURI(contentUriSpec, aURI.originCharset, null));                    

-                    return Closure.RESULT_OK;

-				};

-			

-				ndn.expressInterest(name, new ContentClosure());

-			};

-

-			contentChannel = new ContentChannel(aURI, requestContent);

+            contentChannel = new ContentChannel(aURI, requestContent);

             return contentChannel;

-		} catch (ex) {

-			dump("CcnxProtocol.newChannel exception: " + ex + "\n");

-		}

-	},

+        } catch (ex) {

+            dump("CcnxProtocol.newChannel exception: " + ex + "\n");

+        }

+    },

 

-	classDescription: "ccnx Protocol Handler",

-	contractID: "@mozilla.org/network/protocol;1?name=" + "ccnx",

-	classID: Components.ID('{8122e660-1012-11e2-892e-0800200c9a66}'),

-	QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])

+    classDescription: "ccnx Protocol Handler",

+    contractID: "@mozilla.org/network/protocol;1?name=" + "ccnx",

+    classID: Components.ID('{8122e660-1012-11e2-892e-0800200c9a66}'),

+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])

 }

 

 if (XPCOMUtils.generateNSGetFactory)

-	var NSGetFactory = XPCOMUtils.generateNSGetFactory([CcnxProtocol]);

+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([CcnxProtocol]);

 else

-	var NSGetModule = XPCOMUtils.generateNSGetModule([CcnxProtocol]);

+    var NSGetModule = XPCOMUtils.generateNSGetModule([CcnxProtocol]);

+                

+/*

+ * Create a closure for calling expressInterest.

+ * contentListener is from the call to requestContent.

+ * uriEndsWithSegmentNumber is true if the URI passed to newChannel has a segment number

+ *    (used to determine whether to request only that segment number and for updating the URL bar).

+ * uriOriginCharset is the charset of the URI passed to newChannel (used for making a new URI)

+ */                                                

+var ContentClosure = function ContentClosure

+        (ndn, contentListener, uriEndsWithSegmentNumber, uriOriginCharset) {

+    // Inherit from Closure.

+    Closure.call(this);

+    

+    this.ndn = ndn;

+    this.contentListener = contentListener;

+    this.uriEndsWithSegmentNumber = uriEndsWithSegmentNumber;

+    this.uriOriginCharset = uriOriginCharset;

+    this.firstReceivedSegmentNumber = null;

+    this.firstReceivedContentObject = null;

+}

+

+ContentClosure.prototype.upcall = function(kind, upcallInfo) {

+    if (!(kind == Closure.UPCALL_CONTENT ||

+          kind == Closure.UPCALL_CONTENT_UNVERIFIED))

+        // The upcall is not for us.

+        return Closure.RESULT_ERR;

+        

+    var contentObject = upcallInfo.contentObject;

+    if (contentObject.content == null) {

+        dump("CcnxProtocol.ContentClosure: contentObject.content is null\n");

+        return Closure.RESULT_ERR;

+    }

+

+    // If !this.uriEndsWithSegmentNumber, we use the segmentNumber to load multiple segments.

+    var segmentNumber = null;

+    if (!this.uriEndsWithSegmentNumber && endsWithSegmentNumber(contentObject.name)) {

+        segmentNumber = DataUtils.bigEndianToUnsignedInt

+            (contentObject.name.components[contentObject.name.components.length - 1]);

+        if (this.firstReceivedSegmentNumber == null) {

+            // This is the first call.

+            this.firstReceivedSegmentNumber = segmentNumber;

+            if (segmentNumber != 0) {

+                // Special case: Save this content object for later and request segment zero.

+                this.firstReceivedContentObject = contentObject;

+                var componentsForZero = contentObject.name.components.slice

+                    (0, contentObject.name.components.length - 1);

+                componentsForZero.push([0]);

+                this.ndn.expressInterest(new Name(componentsForZero), this); 

+                return Closure.RESULT_OK;

+            }

+        }

+    }

+    

+    if (this.uriEndsWithSegmentNumber || segmentNumber == null || segmentNumber == 0) {

+        // This is the first or only segment, so start.

+        // Get the URI from the ContentObject including the version.

+        var contentUriSpec;

+        if (!this.uriEndsWithSegmentNumber && endsWithSegmentNumber(contentObject.name)) {

+            var nameWithoutSegmentNumber = new Name

+                (contentObject.name.components.slice

+                 (0, contentObject.name.components.length - 1));

+            contentUriSpec = "ccnx:" + nameWithoutSegmentNumber.to_uri();

+        }

+        else

+            contentUriSpec = "ccnx:" + contentObject.name.to_uri();

+    

+        var contentTypeEtc = getContentTypeAndCharset(contentObject.name);

+        var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);

+        this.contentListener.onStart(contentTypeEtc.contentType, contentTypeEtc.contentCharset, 

+            ioService.newURI(contentUriSpec, this.uriOriginCharset, null));

+    }

+

+    this.contentListener.onReceivedContent(DataUtils.toString(contentObject.content));

+    

+    // Check for the special case if the saved content is for the next segment that we need.

+    if (this.firstReceivedContentObject != null && 

+        this.firstReceivedSegmentNumber == segmentNumber + 1) {

+        // Substitute the saved contentObject send its content and keep going.

+        contentObject = this.firstReceivedContentObject;

+        segmentNumber = segmentNumber + 1;

+        // Clear firstReceivedContentObject to save memory.

+        this.firstReceivedContentObject = null;

+        

+        this.contentListener.onReceivedContent(DataUtils.toString(contentObject.content));        

+    }

+

+    var finalSegmentNumber = null;

+    if (contentObject.signedInfo != null && contentObject.signedInfo.finalBlockID != null)

+        finalSegmentNumber = DataUtils.bigEndianToUnsignedInt(contentObject.signedInfo.finalBlockID);

+            

+    if (!this.uriEndsWithSegmentNumber &&

+        segmentNumber != null && 

+        (finalSegmentNumber == null || segmentNumber != finalSegmentNumber)) {

+        // Make a name for the next segment and get it.

+        var nextSegmentNumber = DataUtils.nonNegativeIntToBigEndian(segmentNumber + 1);

+        nextSegmentNumber.unshift(0);

+        var components = contentObject.name.components.slice

+            (0, contentObject.name.components.length - 1);

+        components.push(nextSegmentNumber);

+        this.ndn.expressInterest(new Name(components), this);

+    }

+    else

+        // Finished.

+        this.contentListener.onStop(); 

+        

+    return Closure.RESULT_OK;

+};

+            

  

 /*

  * Scan the name from the last component to the first (skipping special CCNx components)

@@ -128,14 +198,14 @@
         var str = DataUtils.toString(component).toLowerCase();

         if (str.indexOf(".gif") >= 0) 

             return { contentType: "image/gif", charset: "ISO-8859-1" }

-    	else if (str.indexOf(".jpg") >= 0 ||

-    			 str.indexOf(".jpeg") >= 0) 

+        else if (str.indexOf(".jpg") >= 0 ||

+                 str.indexOf(".jpeg") >= 0) 

             return { contentType: "image/jpeg", charset: "ISO-8859-1" }

-    	else if (str.indexOf(".png") >= 0) 

+        else if (str.indexOf(".png") >= 0) 

             return { contentType: "image/png", charset: "ISO-8859-1" }

         else if (str.indexOf(".bmp") >= 0) 

             return { contentType: "image/bmp", charset: "ISO-8859-1" }

-    	else if (str.indexOf(".css") >= 0) 

+        else if (str.indexOf(".css") >= 0) 

             return { contentType: "text/css", charset: "utf-8" }

     }

     

@@ -144,9 +214,9 @@
 }

 

 /*

- * Return true if the last component in the name is a sequence..

+ * Return true if the last component in the name is a segment number..

  */

-function endsWithSequence(name) {

+function endsWithSegmentNumber(name) {

     return name.components != null && name.components.length >= 1 &&

         name.components[name.components.length - 1].length >= 1 &&

         name.components[name.components.length - 1][0] == 0;