1 // Copyright (c) 2008-2012 Kris Maglione <maglione.k@gmail.com>
3 // This work is licensed for reuse under an MIT license. Details are
4 // given in the LICENSE.txt file included with this file.
7 defineModule("protocol", {
8 exports: ["LocaleChannel", "Protocol", "RedirectChannel", "StringChannel", "XMLChannel"],
9 require: ["services", "util"]
12 var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal);
14 function Channel(url, orig, noErrorChannel, unprivileged) {
17 return noErrorChannel ? null : NetError(orig);
19 if (url instanceof Ci.nsIChannel)
22 if (typeof url === "function")
23 return let ([type, data] = url(orig)) StringChannel(data, type, orig);
26 return let ([type, data] = url) StringChannel(data, type, orig);
28 let uri = services.io.newURI(url, null, null);
29 return (new XMLChannel(uri, null, noErrorChannel)).channel;
37 function NetError(orig, error) {
38 return services.InterfacePointer({
39 QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel]),
47 asyncOpen: function () { throw error || Cr.NS_ERROR_FILE_NOT_FOUND; },
49 open: function () { throw error || Cr.NS_ERROR_FILE_NOT_FOUND; }
50 }).data.QueryInterface(Ci.nsIChannel);
52 function RedirectChannel(to, orig, time, message) {
56 ["meta", { "http-equiv": "Refresh", content: (time || 0) + ";" + to }]],
58 ["h2", { style: "text-align: center" }, message || ""]]]);
59 return StringChannel(html, "text/html", services.io.newURI(to, null, null));
62 function Protocol(scheme, classID, contentBase) {
64 ProtocolBase.call(this);
66 Protocol.prototype = {
67 __proto__: ProtocolBase.prototype,
69 classID: Components.ID(classID),
73 contentBase: contentBase,
75 _xpcom_factory: JSMLoader.Factory(Protocol)
80 function ProtocolBase() {
81 this.wrappedJSObject = this;
85 "content": function (uri, path) this.pages[path] || this.contentBase + path,
87 "data": function (uri) {
88 var channel = services.io.newChannel(uri.path.replace(/^\/(.*)(?:#.*)?/, "data:$1"),
91 channel.contentCharset = "UTF-8";
92 channel.owner = systemPrincipal;
93 channel.originalURI = uri;
98 ProtocolBase.prototype = {
99 get contractID() services.PROTOCOL + this.scheme,
100 get classDescription() this.scheme + " utility protocol",
101 QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
103 purge: function purge() {
104 for (let doc in util.iterDocuments())
106 if (doc.documentURIObject.scheme == this.scheme)
107 doc.defaultView.close();
115 allowPort: function (port, scheme) false,
117 | Ci.nsIProtocolHandler.URI_IS_UI_RESOURCE
118 | Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE,
120 newURI: function newURI(spec, charset, baseURI) {
121 if (baseURI && (!(baseURI instanceof Ci.nsIURL) || baseURI.host === "data"))
123 return services.URL(services.URL.URLTYPE_AUTHORITY,
124 this.defaultPort, spec, charset, baseURI);
127 newChannel: function newChannel(uri) {
129 uri.QueryInterface(Ci.nsIURL);
131 let path = decodeURIComponent(uri.filePath.substr(1));
132 if (uri.host in this.providers)
133 return Channel(this.providers[uri.host].call(this, uri, path),
136 return NetError(uri);
145 function LocaleChannel(pkg, locale, path, orig) {
146 for each (let locale in [locale, "en-US"])
147 for each (let sep in "-/") {
148 var channel = Channel(["resource:/", pkg + sep + locale, path].join("/"), orig, true, true);
153 return NetError(orig);
156 function StringChannel(data, contentType, uri) {
157 let channel = services.StreamChannel(uri);
158 channel.contentStream = services.CharsetConv("UTF-8").convertToInputStream(data);
160 channel.contentType = contentType;
161 channel.contentCharset = "UTF-8";
162 channel.owner = systemPrincipal;
164 channel.originalURI = uri;
168 function XMLChannel(uri, contentType, noErrorChannel, unprivileged) {
170 var channel = services.io.newChannelFromURI(uri);
171 var channelStream = channel.open();
174 this.channel = noErrorChannel ? null : NetError(uri);
179 this.sourceChannel = services.io.newChannelFromURI(uri);
180 this.pipe = services.Pipe(true, true, 0, 0, null);
183 this.channel = services.StreamChannel(uri);
184 this.channel.contentStream = this.pipe.inputStream;
185 this.channel.contentType = contentType || channel.contentType;
186 this.channel.contentCharset = "UTF-8";
188 this.channel.owner = systemPrincipal;
190 let type = this.channel.contentType;
191 if (/^text\/|[\/+]xml$/.test(type)) {
192 let stream = services.InputStream(channelStream);
193 let [, pre, doctype, url, extra, open, post] = util.regexp(literal(/*
196 (<!DOCTYPE \s+ \S+ \s+) (?:SYSTEM \s+ "([^"]*)" | ((?:[^[>\s]|\s[^[])*))
201 */), "x").exec(stream.read(4096));
202 this.writes.push(pre);
204 this.writes.push(doctype + (extra || "") + " [\n");
206 this.addChannel(url);
209 this.writes.push("\n]");
211 for (let [, pre, url] in util.regexp.iterate(/([^]*?)(?:%include\s+"([^"]*)";|$)/gy, post)) {
212 this.writes.push(pre);
214 this.addChannel(url);
218 this.writes.push(channelStream);
222 XMLChannel.prototype = {
223 QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver]),
225 addChannel: function addChannel(url) {
227 this.writes.push(services.io.newChannel(url, null, this.uri).open());
230 util.dump("addChannel('" + url + "'):");
235 writeNext: function () {
237 if (!this.writes.length)
238 this.pipe.outputStream.close();
240 let stream = this.writes.shift();
241 if (isString(stream))
242 stream = services.StringStream(stream);
244 services.StreamCopier(stream, this.pipe.outputStream, null,
245 false, true, 4096, true, false)
246 .asyncCopy(this, null);
254 onStartRequest: function (request, context) {},
255 onStopRequest: function (request, context, statusCode) {
262 // vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: