| /* |
| * @author: Jeff Thompson |
| * See COPYING for copyright and distribution information. |
| */ |
| |
| var EXPORTED_SYMBOLS = ["ContentChannel"]; |
| |
| const Cc = Components.classes; |
| const Ci = Components.interfaces; |
| const Cr = Components.results; |
| |
| Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); |
| |
| /* Create an nsIChannel for returning content to the caller of asyncOpen. |
| * For requestContent detail, see asyncOpen. |
| */ |
| function ContentChannel(uri, requestContent) { |
| this.requestContent = requestContent; |
| |
| this.done = false; |
| |
| this.name = uri.spec; |
| // Bit 18 "LOAD_REPLACE" means the window.location should use the URI set by onStart. |
| // loadFlags is updated by the caller of asyncOpen. |
| this.loadFlags = (1<<18); |
| this.loadGroup = null; |
| this.status = 200; |
| |
| // We don't know these yet. |
| this.contentLength = -1; |
| this.contentType = null; |
| this.contentCharset = null; |
| this.URI = uri; |
| this.originalURI = uri; |
| this.owner = null; |
| this.notificationCallback = null; |
| this.securityInfo = null; |
| |
| // Save the mostRecentWindow from the moment of creating the channel. |
| var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); |
| this.mostRecentWindow = wm.getMostRecentWindow("navigator:browser"); |
| } |
| |
| ContentChannel.prototype = { |
| QueryInterface: function(aIID) { |
| if (aIID.equals(Ci.nsISupports)) |
| return this; |
| |
| if (aIID.equals(Ci.nsIRequest)) |
| return this; |
| |
| if (aIID.equals(Ci.nsIChannel)) |
| return this; |
| |
| throw Cr.NS_ERROR_NO_INTERFACE; |
| }, |
| |
| isPending: function() { |
| return !this.done; |
| }, |
| |
| cancel: function(aStatus){ |
| this.status = aStatus; |
| this.done = true; |
| }, |
| |
| suspend: function(aStatus){ |
| this.status = aStatus; |
| }, |
| |
| resume: function(aStatus){ |
| this.status = aStatus; |
| }, |
| |
| open: function() { |
| throw Cr.NS_ERROR_NOT_IMPLEMENTED; |
| } |
| }; |
| |
| /* Call requestContent(contentListener). When the content is available, you should call |
| * contentListener funtions as follows: |
| * onStart(contentType, contentCharset, uri) |
| * Set the contentType and contentCharset and call aListener.onStartRequest. If uri |
| * is not null, update this.URI and if this.loadFlags LOAD_INITIAL_DOCUMENT_URI bit is set, |
| * then update the URL bar of the mostRecentWindow. (Note that the caller of asyncOpen |
| * sets this.loadFlags.) |
| * onReceivedContent(content) |
| * Call aListener.onDataAvailable. |
| * onStop() |
| * Call aListener.onStopRequest. |
| */ |
| ContentChannel.prototype.asyncOpen = function(aListener, aContext) { |
| try { |
| var thisContentChannel = this; |
| |
| var threadManager = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); |
| var callingThread = threadManager.currentThread; |
| |
| var contentListener = { |
| onStart: function(contentType, contentCharset, uri) { |
| if (uri) |
| thisContentChannel.URI = uri; |
| thisContentChannel.contentType = contentType; |
| thisContentChannel.contentCharset = contentCharset; |
| |
| // nsIChannel requires us to call aListener on its calling thread. |
| callingThread.dispatch({ |
| run: function() { |
| aListener.onStartRequest(thisContentChannel, aContext); |
| // Load flags bit 19 "LOAD_INITIAL_DOCUMENT_URI" means this channel is |
| // for the main window with the URL bar. |
| if (uri && thisContentChannel.loadFlags & (1<<19)) |
| // aListener.onStartRequest may set the URL bar but now we update it. |
| thisContentChannel.mostRecentWindow.gURLBar.value = |
| thisContentChannel.URI.spec; |
| } |
| }, 0); |
| }, |
| |
| onReceivedContent: function(content) { |
| var pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe); |
| pipe.init(true, true, 0, 0, null); |
| pipe.outputStream.write(content, content.length); |
| pipe.outputStream.close(); |
| |
| // nsIChannel requires us to call aListener on its calling thread. |
| // Assume calls to dispatch are eventually executed in order. |
| callingThread.dispatch({ |
| run: function() { |
| aListener.onDataAvailable(thisContentChannel, aContext, |
| pipe.inputStream, 0, content.length); |
| } |
| }, 0); |
| }, |
| |
| onStop: function() { |
| thisContentChannel.done = true; |
| |
| // nsIChannel requires us to call aListener on its calling thread. |
| callingThread.dispatch({ |
| run: function() { |
| aListener.onStopRequest(thisContentChannel, aContext, |
| thisContentChannel.status); |
| } |
| }, 0); |
| }, |
| |
| isDone: function() { return thisContentChannel.done; } |
| }; |
| |
| this.requestContent(contentListener); |
| } catch (ex) { |
| dump("ContentChannel.asyncOpen exception: " + ex + "\n" + ex.stack); |
| } |
| }; |
| |