1 // Copyright (c) 2008-2011 by 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 Components.utils.import("resource://dactyl/bootstrap.jsm");
8 defineModule("protocol", {
9 exports: ["LocaleChannel", "Protocol", "RedirectChannel", "StringChannel", "XMLChannel"],
10 require: ["services", "util"]
13 var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal);
15 function Channel(url, orig, noErrorChannel, unprivileged) {
18 return noErrorChannel ? null : NetError(orig);
20 if (url instanceof Ci.nsIChannel)
23 if (typeof url === "function")
24 return let ([type, data] = url(orig)) StringChannel(data, type, orig);
27 return let ([type, data] = url) StringChannel(data, type, orig);
29 let uri = services.io.newURI(url, null, null);
30 return (new XMLChannel(uri, null, noErrorChannel)).channel;
38 function NetError(orig, error) {
39 return services.InterfacePointer({
40 QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel]),
48 asyncOpen: function () { throw error || Cr.NS_ERROR_FILE_NOT_FOUND },
50 open: function () { throw error || Cr.NS_ERROR_FILE_NOT_FOUND }
51 }).data.QueryInterface(Ci.nsIChannel);
53 function RedirectChannel(to, orig, time, message) {
54 let html = <html><head><meta http-equiv="Refresh" content={(time || 0) + ";" + to}/></head>
55 <body><h2 style="text-align: center">{message || ""}</h2></body></html>.toXMLString();
56 return StringChannel(html, "text/html", services.io.newURI(to, null, null));
59 function Protocol(scheme, classID, contentBase) {
61 ProtocolBase.call(this);
63 Protocol.prototype = {
64 __proto__: ProtocolBase.prototype,
66 classID: Components.ID(classID),
70 contentBase: contentBase,
72 _xpcom_factory: JSMLoader.Factory(Protocol),
77 function ProtocolBase() {
78 this.wrappedJSObject = this;
82 "content": function (uri, path) this.pages[path] || this.contentBase + path,
84 "data": function (uri) {
85 var channel = services.io.newChannel(uri.path.replace(/^\/(.*)(?:#.*)?/, "data:$1"),
88 channel.contentCharset = "UTF-8";
89 channel.owner = systemPrincipal;
90 channel.originalURI = uri;
95 ProtocolBase.prototype = {
96 get contractID() services.PROTOCOL + this.scheme,
97 get classDescription() this.scheme + " utility protocol",
98 QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
100 purge: function purge() {
101 for (let doc in util.iterDocuments())
103 if (doc.documentURIObject.scheme == this.scheme)
104 doc.defaultView.close();
112 allowPort: function (port, scheme) false,
114 | Ci.nsIProtocolHandler.URI_IS_UI_RESOURCE
115 | Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE,
117 newURI: function newURI(spec, charset, baseURI) {
118 if (baseURI && (!(baseURI instanceof Ci.nsIURL) || baseURI.host === "data"))
120 return services.URL(services.URL.URLTYPE_AUTHORITY,
121 this.defaultPort, spec, charset, baseURI);
124 newChannel: function newChannel(uri) {
126 uri.QueryInterface(Ci.nsIURL);
128 let path = decodeURIComponent(uri.filePath.substr(1));
129 if (uri.host in this.providers)
130 return Channel(this.providers[uri.host].call(this, uri, path),
133 return NetError(uri);
142 function LocaleChannel(pkg, locale, path, orig) {
143 for each (let locale in [locale, "en-US"])
144 for each (let sep in "-/") {
145 var channel = Channel(["resource:/", pkg + sep + locale, path].join("/"), orig, true);
150 return NetError(orig);
153 function StringChannel(data, contentType, uri) {
154 let channel = services.StreamChannel(uri);
155 channel.contentStream = services.CharsetConv("UTF-8").convertToInputStream(data);
157 channel.contentType = contentType;
158 channel.contentCharset = "UTF-8";
159 channel.owner = systemPrincipal;
161 channel.originalURI = uri;
165 function XMLChannel(uri, contentType, noErrorChannel, unprivileged) {
167 var channel = services.io.newChannelFromURI(uri);
168 var channelStream = channel.open();
171 this.channel = noErrorChannel ? null : NetError(uri);
176 this.sourceChannel = services.io.newChannelFromURI(uri);
177 this.pipe = services.Pipe(true, true, 0, 0, null);
180 this.channel = services.StreamChannel(uri);
181 this.channel.contentStream = this.pipe.inputStream;
182 this.channel.contentType = contentType || channel.contentType;
183 this.channel.contentCharset = "UTF-8";
185 this.channel.owner = systemPrincipal;
187 let stream = services.InputStream(channelStream);
188 let [, pre, doctype, url, extra, open, post] = util.regexp(<![CDATA[
191 (<!DOCTYPE \s+ \S+ \s+) (?:SYSTEM \s+ "([^"]*)" | ((?:[^[>\s]|\s[^[])*))
196 ]]>, "x").exec(stream.read(4096));
197 this.writes.push(pre);
199 this.writes.push(doctype + (extra || "") + " [\n");
201 this.addChannel(url);
204 this.writes.push("\n]");
206 for (let [, pre, url] in util.regexp.iterate(/([^]*?)(?:%include\s+"([^"]*)";|$)/gy, post)) {
207 this.writes.push(pre);
209 this.addChannel(url);
212 this.writes.push(channelStream);
216 XMLChannel.prototype = {
217 QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver]),
219 addChannel: function addChannel(url) {
221 this.writes.push(services.io.newChannel(url, null, this.uri).open());
228 writeNext: function () {
230 if (!this.writes.length)
231 this.pipe.outputStream.close();
233 let stream = this.writes.shift();
234 if (isString(stream))
235 stream = services.StringStream(stream);
237 services.StreamCopier(stream, this.pipe.outputStream, null,
238 false, true, 4096, true, false)
239 .asyncCopy(this, null);
247 onStartRequest: function (request, context) {},
248 onStopRequest: function (request, context, statusCode) {
255 // vim: set fdm=marker sw=4 ts=4 et ft=javascript: