1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-19 16:27:58 +01:00
Files
pentadactyl-pm/common/modules/protocol.jsm

267 lines
7.9 KiB
JavaScript

// Copyright (c) 2008-2015 Kris Maglione <maglione.k@gmail.com>
//
// This work is licensed for reuse under an MIT license. Details are
// given in the LICENSE.txt file included with this file.
"use strict";
defineModule("protocol", {
exports: ["LocaleChannel", "Protocol", "RedirectChannel", "StringChannel", "XMLChannel"],
require: ["services", "util"]
});
var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal);
function Channel(url, orig, noErrorChannel, unprivileged) {
try {
if (url == null)
return noErrorChannel ? null : NetError(orig);
if (url instanceof Ci.nsIChannel)
return url;
if (typeof url === "function") {
let [type, data] = url(orig);
return StringChannel(data, type, orig);
}
if (isArray(url)) {
let [type, data] = url;
return StringChannel(data, type, orig);
}
let uri = services.io.newURI(url, null, null);
return (new XMLChannel(uri, null, noErrorChannel)).channel;
}
catch (e) {
util.reportError(e);
util.dump(url);
throw e;
}
}
function NetError(orig, error) {
return services.InterfacePointer({
QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel]),
name: orig.spec,
URI: orig,
originalURI: orig,
asyncOpen: function () { throw error || Cr.NS_ERROR_FILE_NOT_FOUND; },
open: function () { throw error || Cr.NS_ERROR_FILE_NOT_FOUND; }
}).data.QueryInterface(Ci.nsIChannel);
}
function RedirectChannel(to, orig, time, message) {
let html = DOM.toXML(
["html", {},
["head", {},
["meta", { "http-equiv": "Refresh", content: (time || 0) + ";" + to }]],
["body", {},
["h2", { style: "text-align: center" }, message || ""]]]);
return StringChannel(html, "text/html", services.io.newURI(to, null, null));
}
function Protocol(scheme, classID, contentBase) {
function Protocol() {
ProtocolBase.call(this);
}
Protocol.prototype = {
__proto__: ProtocolBase.prototype,
classID: Components.ID(classID),
scheme: scheme,
contentBase: contentBase,
_xpcom_factory: JSMLoader.Factory(Protocol)
};
return Protocol;
}
function ProtocolBase() {
this.wrappedJSObject = this;
this.pages = {};
this.providers = {
"content": function (uri, path) this.pages[path] || this.contentBase + path,
"data": function (uri) {
var channel = services.io.newChannel(uri.path.replace(/^\/(.*)(?:#.*)?/, "data:$1"),
null, null);
channel.contentCharset = "UTF-8";
channel.owner = systemPrincipal;
channel.originalURI = uri;
return channel;
}
};
}
ProtocolBase.prototype = {
get contractID() services.PROTOCOL + this.scheme,
get classDescription() this.scheme + " utility protocol",
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
purge: function purge() {
for (let doc of util.iterDocuments())
try {
if (doc.documentURIObject.scheme == this.scheme)
doc.defaultView.close();
}
catch (e) {
util.reportError(e);
}
},
defaultPort: -1,
allowPort: function (port, scheme) false,
protocolFlags: 0
| Ci.nsIProtocolHandler.URI_IS_UI_RESOURCE
| Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE,
newURI: function newURI(spec, charset, baseURI) {
if (baseURI && (!(baseURI instanceof Ci.nsIURL) || baseURI.host === "data"))
baseURI = null;
return services.URL(services.URL.URLTYPE_AUTHORITY,
this.defaultPort, spec, charset, baseURI);
},
newChannel: function newChannel(uri) {
try {
uri.QueryInterface(Ci.nsIURL);
let path = decodeURIComponent(uri.filePath.substr(1));
if (uri.host in this.providers)
return Channel(this.providers[uri.host].call(this, uri, path),
uri);
return NetError(uri);
}
catch (e) {
util.reportError(e);
throw e;
}
}
};
function LocaleChannel(pkg, locale, path, orig) {
for (let locale of [locale, "en-US"])
for (let sep of "-/") {
var channel = Channel(["resource:/", pkg + sep + locale, path].join("/"), orig, true, true);
if (channel)
return channel;
}
return NetError(orig);
}
function StringChannel(data, contentType, uri) {
let channel = services.StreamChannel(uri);
channel.contentStream = services.CharsetConv("UTF-8").convertToInputStream(data);
if (contentType)
channel.contentType = contentType;
channel.contentCharset = "UTF-8";
channel.owner = systemPrincipal;
if (uri)
channel.originalURI = uri;
return channel;
}
function XMLChannel(uri, contentType, noErrorChannel, unprivileged) {
try {
var channel = services.io.newChannelFromURI(uri);
var channelStream = channel.open();
}
catch (e) {
this.channel = noErrorChannel ? null : NetError(uri);
return;
}
this.uri = uri;
this.sourceChannel = services.io.newChannelFromURI(uri);
this.pipe = services.Pipe(true, true, 0, 0, null);
this.writes = [];
this.channel = services.StreamChannel(uri);
this.channel.contentStream = this.pipe.inputStream;
this.channel.contentType = contentType || channel.contentType;
this.channel.contentCharset = "UTF-8";
if (!unprivileged)
this.channel.owner = systemPrincipal;
let type = this.channel.contentType;
if (/^text\/|[\/+]xml$/.test(type)) {
let stream = services.InputStream(channelStream);
let [, pre, doctype, url, extra, open, post] = util.regexp(literal(function () /*
^ ([^]*?)
(?:
(<!DOCTYPE \s+ \S+ \s+) (?:SYSTEM \s+ "([^"]*)" | ((?:[^[>\s]|\s[^[])*))
(\s+ \[)?
([^]*)
)?
$
*/$), "x").exec(stream.read(4096));
this.writes.push(pre);
if (doctype) {
this.writes.push(doctype + (extra || "") + " [\n");
if (url)
this.addChannel(url);
if (!open)
this.writes.push("\n]");
for (let [, pre, url] of util.regexp.iterate(/([^]*?)(?:%include\s+"([^"]*)";|$)/gy, post)) {
this.writes.push(pre);
if (url)
this.addChannel(url);
}
}
}
this.writes.push(channelStream);
this.writeNext();
}
XMLChannel.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver]),
addChannel: function addChannel(url) {
try {
this.writes.push(services.io.newChannel(url, null, this.uri).open());
}
catch (e) {
util.dump("addChannel('" + url + "'):");
util.reportError(e);
}
},
writeNext: function () {
try {
if (!this.writes.length)
this.pipe.outputStream.close();
else {
let stream = this.writes.shift();
if (isString(stream))
stream = services.StringStream(stream);
services.StreamCopier(stream, this.pipe.outputStream, null,
false, true, 4096, true, false)
.asyncCopy(this, null);
}
}
catch (e) {
util.reportError(e);
}
},
onStartRequest: function (request, context) {},
onStopRequest: function (request, context, statusCode) {
this.writeNext();
}
};
endModule();
// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: