From c4b3096e166a0a9a60fd27c8f2dc06e688f13172 Mon Sep 17 00:00:00 2001 From: williamt Date: Fri, 05 Jan 2007 10:06:01 +0000 Subject: Initial revision --- (limited to 'src/components/xriProtocolHandler.js') diff --git a/src/components/xriProtocolHandler.js b/src/components/xriProtocolHandler.js new file mode 100755 index 0000000..7046cde --- a/dev/null +++ b/src/components/xriProtocolHandler.js @@ -0,0 +1,1060 @@ +/*********************************************************** + constants + ***********************************************************/ + +// The interface we implement - nsIProtocolHandler +const nsIProtocolHandler = Components.interfaces.nsIProtocolHandler; + +// Interfaces that we require +const nsISupports = Components.interfaces.nsISupports; +const nsIIOService = Components.interfaces.nsIIOService; +const nsIURI = Components.interfaces.nsIURI; +const nsIURL = Components.interfaces.nsIURL; +const nsIRequest = Components.interfaces.nsIRequest; +const nsIRequestObserver = Components.interfaces.nsIRequestObserver; +const nsIChannel = Components.interfaces.nsIChannel; +const nsIHttpChannel = Components.interfaces.nsIHttpChannel; +const nsIStreamListener = Components.interfaces.nsIStreamListener; + + +// UUID uniquely identifying our component +// You can get from: http://kruithof.xs4all.nl/uuid/uuidgen here +const CLASS_ID = Components.ID("{ea00b610-215a-11db-a98b-0800200c9a66}"); + +// textual unique identifier +const CONTRACT_ID = "@mozilla.org/network/protocol;1?name=xri"; + +// Components that we require +const CID_URI = "@mozilla.org/network/simple-uri;1"; +const kIOSERVICE_CID_STR = "{9ac9e770-18bc-11d3-9337-00104ba0fd40}"; +const CID_URL = "@mozilla.org/network/standard-url;1"; + +// description +const CLASS_NAME = "XRI Protocol Handler"; + +const PROXY_URI = "http://xri.net/"; + +const XP_ANY_TYPE = 0; +const XP_NUMBER_TYPE = 1; +const XP_STRING_TYPE = 2; +const XP_BOOLEAN_TYPE = 3; +const XP_UNORDERED_NODE_ITERATOR_TYPE = 4; +const XP_ORDERED_NODE_ITERATOR_TYPE = 5; +const XP_UNORDERED_NODE_SNAPSHOT_TYPE = 6; +const XP_ORDERED_NODE_SNAPSHOT_TYPE = 7; +const XP_ANY_UNORDERED_NODE_TYPE = 8; +const XP_FIRST_ORDERED_NODE_TYPE = 9; + + +var SERVICE_CLASSES = { + 'xri://+i-service*(+contact)*($v*1.0)': 'i-contact', + 'http://openid.net/signon/1.0': 'openid', + 'xri://$res*auth*($v*2.0)': 'res-auth', + 'xri://+i-service*(+authn)*(+saml)*($v*1.0)': 'authn-saml', + 'xri://+i-service*(+metadata)*(+saml)*($v*1.0)' : 'metadata-saml', + 'xri://+i-service*(+forwarding)*($v*1.0)': 'i-forwarding' +}; + + +const HTML_HEAD = "\n\ + \n\ + \n\ + FoXRI Explorer - #QXRI#\n\ + \n\ + \n\ +

FoXRI Explorer

\n\ +
\n"; + +const HTML_FOOT = "
\n\ + \n\ + "; + + + +/// Generic object method wrapper +function methodWrapper(obj, method) +{ + return ( + function() { + /* pass it this inner closure's arguments */ + obj[method](arguments); + } + ); +} + + + +/// XRDS utility functions + + +var nsResolver = { + lookupNamespaceURI: function(prefix) + { + if (prefix == "xrds") + return "xri://$xrds"; + else if (prefix == "xrd") + return "xri://$xrd*($v*2.0)"; + return ""; + } +}; + + + +function runExpr(doc, context, expr, returnType) +{ + if (!returnType) + returnType = XP_ANY_TYPE; + var res = doc.evaluate(expr, context, nsResolver, returnType, null); + return res; +} + + +function getNumeric(doc, context, expr) +{ + var res = runExpr(doc, context, expr, XP_NUMBER_TYPE); + if (res) + return res.numberValue; + return null; +} + + +function getString(doc, context, expr) +{ + // var res = runExpr(doc, context, expr, XPathResult.STRING_TYPE); + var res = runExpr(doc, context, expr, XP_STRING_TYPE); + if (res) + return res.stringValue; + return null; +} + +function getNode(doc, context, expr) +{ + var res = runExpr(doc, context, expr, XP_FIRST_ORDERED_NODE_TYPE); + if (res) + return res.singleNodeValue; + return null; +} + + +function getFinalXRD(doc) +{ + var lastNode = doc.firstChild; + while (true) { + var node = getNode(doc, lastNode, "xrds:XRDS[position()=last()]"); + if (!node) + break; + lastNode = node; + } + + return getNode(doc, lastNode, "xrd:XRD[position()=last()]"); +} + + +function isIName(xri) +{ + if (xri.match('^xri://.!', 'i')) { + return false; + } + if (xri.match('^.!', 'i')) { + return false; + } + return true; +} + + +function arraySearch(a, re) +{ + var returnArr = new Array(); + var i; + for (i = 0; i < a.length; i++) { + if (a[i].match(re)) { + returnArr.push(a[i]); + } + } + + return returnArr; +} + + +function renderService(srv, doc, qxri) +{ + var html_types = ''; + var html_paths = ''; + var html_mediatypes = ''; + var html_uris = ''; + var html_actions = ''; + + var serviceName = friendlyServiceName(null); + var serviceType; // the last non-null Type + var knownServiceType; // first recognized service type + + // get the types + var res = runExpr(doc, srv, "xrd:Type/text()"); + var t; + while (t = res.iterateNext()) { + if (t.nodeValue) { + if (!knownServiceType && isKnownServiceType(t.nodeValue)) { + knownServiceType = t.nodeValue; + } + + serviceType = t.nodeValue; + html_types += "Type: " + t.nodeValue + "
"; + } + } + + // get the paths + res = runExpr(doc, srv, "xrd:Path/text()"); + var p; + var qxri_prefix = qxri; + if (qxri_prefix.charAt(qxri_prefix.length - 1) != '/') { + qxri_prefix += '/'; + } + + while (p = res.iterateNext()) { + if (p.nodeValue) { + html_paths += "Path: " + p.nodeValue + + " [ " + + qxri_prefix + p.nodeValue + " ]" + + "
\n"; + } + } + + + // get the mediatypes + mediaTypes = new Array(); + res = runExpr(doc, srv, "xrd:MediaType/text()"); + var m; + while (m = res.iterateNext()) { + if (!knownServiceType) { + var srvType = guessServiceTypeByMime(m.nodeValue); + knownServiceType = srvType? srvType : null; + } + + mediaTypes.push(m.nodeValue); + if (m.nodeValue) { + html_mediatypes += "Media Type: " + m.nodeValue + "
"; + } + } + + + res = runExpr(doc, srv, "xrd:URI/text()"); + var u; + while (u = res.iterateNext()) { + if (!u.nodeValue) + continue; + + var srvType = guessServiceTypeByURI(u.nodeValue); + if (!knownServiceType) { + knownServiceType = srvType; + } + + html_uris += "
"; + + var linkContent = u.nodeValue; + var uriParts = u.nodeValue.match('^(.*):(.*)$'); + if (!uriParts) + continue; + + if (uriParts[1] == 'data') { + uriParts = uriParts[2].match('^(.*/.*),(.*)'); + if (uriParts && uriParts[1].match('^image/', 'i')) { + linkContent = ""; + } + else if (uriParts) { + linkContent = uriParts[1] + " data"; + } + } + else if (uriParts[1] == 'skype') { + uriParts = uriParts[2].match('^(.*)\\?(.*)'); + if (uriParts) { + if (uriParts[2] == "call") { + linkContent = "\"Call"; + } + else if (uriParts[2] == "chat") { + linkContent = "\"Chat"; + } + else if (uriParts[2] == "add") { + linkContent = "\"Add"; + } + } + } + else if (uriParts[1] == 'aim') { + uriParts = uriParts[2].match('^(.*)\\?.*screenname=([^&]*)', 'i'); + if (uriParts) { + linkContent = "\"Chat Chat with " + uriParts[2]; + } + } + + html_uris += "" + + linkContent + ""; + html_uris += "
"; + } + + var html = "
\n"; + html += html_types; + html += html_paths; + html += html_mediatypes; + if (html_uris) { + html += "URI(s):
\n"; + html += html_uris; + } + html += "
"; + + return html; +} + + + +function isKnownServiceType(type) +{ + if (type.toLowerCase() in SERVICE_CLASSES) { + return true; + } + return false; +} + +function getServiceClass(type) +{ + if (isKnownServiceType(type)) { + return SERVICE_CLASSES[type.toLowerCase()]; + } + return type; +} + + +function guessServiceTypeByURI(uri) +{ + if (uri == null || uri == "") { + return "unknown"; + } + if (uri.match(/^https?:/i)) { + return "www"; + } + else if (uri.match(/^skype:/i)) { + return "skype"; + } + else if (uri.match(/^aim:/i)) { + return "aim"; + } + else if (uri.match(/^xmpp:/i)) { + return "jabber"; + } + else if (uri.match(/^tel:/i)) { + return "tel"; + } + else if (uri.match(/^callto:/i)) { + return "callto"; + } + else if (uri.match(/^telnet:/i)) { + return "telnet"; + } + else if (uri.match(/^news:/i)) { + return "news"; + } + else if (uri.match(/^nntp:/i)) { + return "nntp"; + } + else if (uri.match(/^ftp:/i)) { + return "ftp"; + } + else if (uri.match(/^mailto:/i)) { + return "email"; + } + else if (uri.match(/^urn:/i)) { + return "urn"; + } + else if (uri.match(/^data:/i)) { + return "data"; + } + else if (uri.match(/^feed:/i)) { + return "feed"; + } + return "unknown"; +} + + +function guessServiceTypeByMime(mimeType) +{ + if (mimeType.match(/^application\/(rss|atom)\+xml/i)) { + dump("feed detected!\n"); + return "feed"; + } + else if (mimeType.match(/^image\//i)) { + return "image"; + } + return null; +} + + + +function friendlyServiceName(srvType, uri) +{ + if (srvType && srvType == "xri://+i-service*(+contact)*($v*1.0)") { + return "Contact Service"; + } + else if (srvType && srvType == "http://openid.net/signon/1.0") { + return "OpenID Authentication Service"; + } + else if (srvType && srvType == "xri://$res*auth*($v*2.0)") { + return "Authority Resolution Service"; + } + else { + if (uri == null) { + return "Generic Service"; + } + if (uri.match(/^https?:/i)) { + return "Web Link"; + } + else if (uri.match(/^skype:/i)) { + var user = uri.substring("skype:".length, uri.indexOf('?')); + return "Skype "; + } + else if (uri.match(/^mailto:/i)) { + var qmark = uri.indexOf('?'); + var email = (qmark == -1)? + uri.substr("mailto:".length) : + uri.substring("mailto:".length, qmark); + return "Email (address: " + email + ")"; + } + else if (srvType != null) { + return srvType; // return verbatim + } + return "Generic Service"; + } +} + + + + +function subHTML(template, vars) +{ + for (key in vars) { + template = template.replace(key, vars[key], 'g'); + } + return template; +} + + +/// Given the completed XMLHttpRequest object, renders the XRDS +function renderXRDS(xmlDoc) +{ + var x = xmlDoc; + var qxri = getString(x, x, "/xrds:XRDS/@ref"); + + var html = subHTML(HTML_HEAD, { '#QXRI#': qxri }); + + // TODO: render parents as well + + var lastNode = getFinalXRD(x); + if (lastNode) { + var stat = getString(x, lastNode, "xrd:Status/@code"); + if (stat == "100") { + html += "

Exploring " + qxri + "

"; + } + else { + var msg = getString(x, lastNode, "xrd:Status/text()"); + html += "

" + qxri + " failed to resolve (reason: " + stat + " - " + msg + ")

"; + } + + html += "
"; + + var services = runExpr(x, lastNode, "xrd:Service"); + var s; + var count = getNumeric(x, lastNode, "count(xrd:Service)"); + if (count > 0) { + while (s = services.iterateNext()) { + count++; + html += renderService(s, x, qxri); + } + } + else if (stat == '222') { + var xriType = isIName(qxri)? 'I-name' : 'I-number'; + html += "

" + xriType + " does not exist.

\n"; + } + else { + html += "

No service has been configured for this XRI

"; + } + + } + + html += ""; + + return html; +} + + + + + + +/*********************************************************** + XriServiceExplorer class definition + ***********************************************************/ + + +function XRIChannel(uri) { + this.URI = uri; + var r = uri.spec.indexOf('#'); + if (r >= 0) { + this.qxri = uri.spec.substring(0, r); + this.fragment = uri.spec.substring(r); + } + else { + this.qxri = uri.spec; + } +}; + + +XRIChannel.prototype = { + + fragment: null, + +/* private fields used internally */ + qxri: null, + + xmlRequest: null, + + renderedHTML: null, + + scriptableInStream: null, + + buf: null, + + mChannel: null, + + + copyFields: function(request) + { + dump("copyFields(loadFlags=" + request.loadFlags + ")\n"); + dump("loadGroup = " + request.loadGroup + "\n"); + dump("notificationCallbacks = " + request.notificationCallbacks + "\n"); + + // copy request fields + this.loadFlags = request.loadFlags; + this.loadGroup = request.loadGroup; + this.name = request.name; + this.status = request.status; + + var channel = request.QueryInterface(nsIChannel); + if (channel) { + this.contentCharset = channel.contentCharset; + this.contentLength = channel.contentLength; + this.contentType = channel.contentType; // XXX + this.contentType = "text/html"; + this.notificationCallbacks = channel.notificationCallbacks; + this.originalURI = this.originalURI; + this.URI = this.URI; + this.owner = channel.owner; + this.securityInfo = channel.securityInfo; + + channel = channel.QueryInterface(nsIHttpChannel); + if (channel) { + this.allowPipelining = channel.allowPipelining; + this.redirectionLimit = channel.redirectionLimit; + this.referrer = channel.referrer; + this.requestMethod = channel.requestMethod; + this.requestSucceeded = channel.requestSucceeded; + this.responseStatus = channel.responseStatus; + this.responseStatusText = channel.responseStatusText; + } + } + + }, + + /* nsIStreamListener */ + asyncOpenListener: null, + + /* nsISupports (but we really don't care) */ + asyncOpenContext: null, + + + /* has the XML finished loading? */ + loadDone: false, + + + +/* public fields (nsIStreamListener implementation) */ + onDataAvailable : function(request, ctx, inputStream, offset, count) + { + dump("\nonDataAvailable, offset=" + offset + ", count=" + count + "\n"); + + // XXX +/* + this.copyFields(request); + this.asyncOpenListener.onDataAvailable(this, this.asyncOpenContext, inputStream, offset, count); + return; +*/ + + + if (offset == 0) { + this.scriptableInStream.init(inputStream); + } + + this.buf += this.scriptableInStream.read(count); + + if (!request.isPending()) { + dump("request finished, buf = " + this.buf + "\n"); + + this.scriptableInStream = null; + } + else { + dump("request pending...\n"); + dump("buf so far = " + this.buf + "\n"); + } + }, + + +/* public fields (nsIRequestObserver implementation) */ + onStartRequest : function(request, ctx) + { + dump("\nonStartRequest called\n"); + // XXX + + this.copyFields(request); + this.asyncOpenListener.onStartRequest(this, this.asyncOpenContext); + }, + + + onStopRequest : function(request, ctx, status) + { + dump("\nonStopRequest called - status " + status + "\n"); + + // XXX +/* + this.asyncOpenListener.onStopRequest(this, this.asyncOpenContext, status); + return; +*/ + + this.copyFields(request); + this.loadDone = true; + + if (status == 0) { + + var domParser = Components.classes["@mozilla.org/xmlextras/domparser;1"].createInstance(Components.interfaces.nsIDOMParser); + var xmlDoc = domParser.parseFromString(this.buf, "text/xml"); + + // make fake inputstream + var renderedHTML = renderXRDS(xmlDoc); + + this.contentCharset = "UTF-8"; + this.contentLength = renderedHTML.length; + this.contentType = "text/html"; + + dump("rendered HTML = \n" + renderedHTML + "\n"); + + dump("\nCalling asyncOpenListener.onStartRequest\n\n"); + + + var strIStream = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(Components.interfaces.nsIStringInputStream); + if (strIStream) { + strIStream.setData(renderedHTML, renderedHTML.length); +/* + strIStream.setData(this.buf, this.buf.length); +*/ + dump("\nleftovers in string-input-stream = " + strIStream.available() + "\n"); + dump("\nCalling asyncOpenListener.onDataAvailable\n\n"); + this.asyncOpenListener.onDataAvailable(this, this.asyncOpenContext, strIStream, 0, renderedHTML.length); +/* + this.asyncOpenListener.onDataAvailable(this, this.asyncOpenContext, strIStream, 0, this.buf.length); +*/ + + dump("\nleftovers in string-input-stream = " + strIStream.available() + "\n"); + } + } + else { + dump("\nStatus = " + status + "\n"); + dump("Calling asyncOpenListener.onStartRequest\n\n"); + this.asyncOpenListener.onStartRequest(this, this.asyncOpenContext); + } + + dump("stopping request for underlying asyncOpenListener\n"); + + this.asyncOpenListener.onStopRequest(this, this.asyncOpenContext, status); + + // copied from nsIWyciwygChannel + this.asyncOpenListener = null; + this.asyncOpenContext = null; + +/* + if (this.loadGroup) { + this.loadGroup.removeRequest(request, null, status); + } +*/ + + this.notificationCallbacks = null; + this.mChannel = null; + + dump("stopped request\n"); + }, + + +/* public fields (nsIInputStream implementation) */ + available: function() + { + dump("nsIInputStream::available called\n"); + return renderedHTML.length; + }, + + close: function() + { + dump("nsIInputStream::close called\n"); + }, + + isNonBlocking: function() { + dump("nsIInputStream::isNonBlocking called\n"); + return true; + }, + + read: function() { dump("nsIInputStream::read() called!!!\n"); }, + + + + + +/* public fields (nsIRequest implmentation) */ + + loadFlags: 0, + + loadGroup: null, + + name: "xri://request", + + status: 0, + + cancel: function(status) { dump("\ncancel called...\n"); }, + + isPending: function() { + dump("isPending called\n\n"); + return !this.loadDone; + }, + + resume: function() { dump("resume called\n"); }, + + suspend: function() { dump("suspend called\n"); }, + + + +/* public fields (nsIChannel implmentation) */ + + contentCharset: null, + + contentLength: -1, + + contentType: null, + + notificationCallbacks: null, + + originalURI: null, + + owner: null, + + securityInfo: null, + + URI: null, + + open: function() + { + dump("open not supporteD!!!!!!\n"); + }, + + asyncOpen: function(listener, context) + { + dump("asyncOpen called!!!!!!\n"); + this.asyncOpenListener = listener; + this.asyncOpenContext = context; + + var hxri = PROXY_URI + this.qxri + + "?_xrd_r=application/xrds%2Bxml;sep=false"; + var ioService = Components.classesByID[kIOSERVICE_CID_STR].getService(); + ioService = ioService.QueryInterface(nsIIOService); + var channel = ioService.newChannel(hxri, null, null); + + if (this.scriptableInStream) { + dump("Hey! You can't possibly be reusing this handler?!\n"); + return; + } + + dump("making scriptableInStream\n"); + this.scriptableInStream = Components.classes["@mozilla.org/scriptableinputstream;1"] + .createInstance(Components.interfaces.nsIScriptableInputStream); + + this.buf = ''; + + dump("notificationCallbacks = " + this.notificationCallbacks + "\n"); + dump("loadFlags = " + this.loadFlags + "\n"); + dump("loadGroup = " + this.loadGroup + "\n"); + dump("owner = " + this.owner + "\n"); + dump("securityInfo = " + this.securityInfo + "\n"); + + // these nsIRequest attributes must be copied to the stub + // channel that we created + channel.notificationCallbacks = this.notificationCallbacks; + channel.loadGroup = this.loadGroup; + channel.loadFlags = this.loadFlags; + + this.mChannel = channel; + channel.asyncOpen(this, null); + }, + + +/* public fields (nsIChannel implmentation) */ + allowPipelining: false, + redirectionLimit: 5, + referrer: "", + requestMethod: "GET", + requestSucceeded: true, + responseStatus: 200, + responseStatusText: "OK", + getRequestHeader: function(header) { + dump("getRequestHeader(" + header + ")\n"); + var httpChannel = this.mChannel.QueryInterface(nsIHttpChannel); + + try { + var val = httpChannel.getRequestHeader(header); + dump("getRequestHeader(" + header + ") = " + val + "\n"); + return val; + } + catch (e) { + throw e; + } + }, + getResponseHeader: function(header) { + dump("getResponseHeader(" + header + ")\n"); + var httpChannel = this.mChannel.QueryInterface(nsIHttpChannel); + + try { + var val = httpChannel.getResponseHeader(header); + dump("getResponseHeader(" + header + ") = " + val + "\n"); + return val; + } + catch (e) { + throw e; + } +/* XXX + if (header == "Content-Type") + return "text/html"; +*/ + return null; + }, + isNoCacheResponse: function() { + dump("isNoCacheResponse()\n"); + var httpChannel = this.mChannel.QueryInterface(nsIHttpChannel); + return httpChannel.isNoCacheResponse(); + }, + isNoStoreResponse: function() { + dump("isNoStoreResponse()\n"); + var httpChannel = this.mChannel.QueryInterface(nsIHttpChannel); + return httpChannel.isNoStoreResponse(); + return true; + }, + setRequestHeader: function(header, value, merge) { + dump("setRequestHeader(" + header + ", " + value + ")\n"); + var httpChannel = this.mChannel.QueryInterface(nsIHttpChannel); + return httpChannel.setRequestHeader(header, value, merge); + }, + setResponseHeader: function(header, value, merge) { + dump("setResponseHeader(" + header + ", " + value + ")\n"); + var httpChannel = this.mChannel.QueryInterface(nsIHttpChannel); + return httpChannel.setResponseHeader(header, value, merge); + }, + visitRequestHeaders: function(visitor) { + dump("visitRequestHeaders()\n"); + }, + visitResponseHeaders: function(visitor) { + dump("visitResponseHeaders()\n"); + }, + + QueryInterface: function(iid) + { + dump("QI.. \n"); + if (iid.equals(nsIChannel)) + dump("QI(nsIChannel)\n"); + else if (iid.equals(nsIHttpChannel)) + dump("QI(nsIHttpChannel)\n"); + else if (iid.equals(Components.interfaces.nsIUploadChannel)) + dump("QI(nsIUploadChannel) - not supported\n"); + else if (iid.equals(Components.interfaces.nsICachingChannel)) + dump("QI(nsICachingChannel) - not supported\n"); + else if (iid.equals(Components.interfaces.nsIClassInfo)) + dump("QI(nsIClassInfo) - not supported\n"); + else if (iid.equals(Components.interfaces.nsISecurityCheckedComponent)) + dump("QI(nsISecurityCheckedComponent) - not supported\n"); + else if (iid.equals(Components.interfaces.nsIWyciwygChannel)) + dump("QI(nsIWyciwygChannel) - not supported\n"); + else if (iid.equals(Components.interfaces.nsIMultiPartChannel)) + dump("QI(nsIMultiPartChannel) - not supported\n"); + else if (iid.equals(Components.interfaces.nsIHttpChannelInternal)) + dump("QI(nsIHttpChannelInternal) - not supported\n"); + else if (iid.equals(Components.interfaces.nsIWritablePropertyBag2)) + dump("QI(nsIWritablePropertyBag2) - not supported\n"); + else if (iid.equals(nsIRequest)) + dump("QI(nsIRequest)\n"); + else if (iid.equals(nsIRequestObserver)) + dump("QI(nsIRequestObserver)\n"); + else if (iid.equals(nsISupports)) + dump("QI(nsISupports)\n"); + else if (iid.equals(nsIStreamListener)) + dump("QI(nsIStreamListener)\n"); + else + dump("unknown " + iid + "\n"); + + if (iid.equals(nsISupports) || + iid.equals(nsIRequest) || + iid.equals(nsIRequestObserver) || + iid.equals(nsIChannel) || + iid.equals(nsIHttpChannel) || + iid.equals(nsIStreamListener) + ) { + return this; + } + + throw Components.results.NS_ERROR_NO_INTERFACE; + } +}; + + + +/*********************************************************** + XriProtocolHandler class definition + ***********************************************************/ + +//class constructor +function XriProtocolHandler() { +}; + +// class definition +XriProtocolHandler.prototype = { + defaultPort: 80, // HTTP + + protocolFlags : nsIProtocolHandler.ALLOWS_PROXY | nsIProtocolHandler.ALLOWS_PROXY_HTTP, + + scheme: "xri", + + allowPort: function() { + return false; // only called for blacklisted ports, should respect + }, + + _newHttpChannel: function(aURI) + { + var HXRI = PROXY_URI + aURI.spec; + var ioService = Components.classesByID[kIOSERVICE_CID_STR].getService(); + ioService = ioService.QueryInterface(nsIIOService); + var channel = ioService.newChannel(HXRI, null, null); + return channel; + }, + + newChannel: function(aURI) + { + // leave alone if path is not empty or just a single slash or query exists + + dump("path='" + aURI.path + "'\n"); + dump("query='" + aURI.query + "'\n"); + dump("spec='" + aURI.spec + "'\n"); + + var slashPos = aURI.spec.indexOf('/', 'xri://'.length); + var qmarkPos = aURI.spec.indexOf('?'); + dump("slashPos='" + slashPos + "'\n"); + dump("qmarkPos='" + qmarkPos + "'\n"); + if ((slashPos > 0 && slashPos < aURI.spec.length - 1) || qmarkPos > -1) { + return this._newHttpChannel(aURI); + } + + var explorer = new XRIChannel(aURI); + return explorer; + }, + + + newURI: function(spec, originCharset, baseURI) + { + var newSpec = spec; + if (baseURI != null) { + // standard-url (nsIURL) does not work with @-GCS + var baseURL = Components.classes[CID_URL].createInstance(nsIURL); + baseURL.spec = baseURI.spec; + newSpec = baseURL.resolve(spec); + } + + var uri = Components.classes[CID_URI].createInstance(nsIURI); + uri.spec = newSpec; + return uri; + }, + + QueryInterface: function(aIID) + { + if (!aIID.equals(nsIProtocolHandler) && + !aIID.equals(nsISupports)) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + } +}; + + +/*********************************************************** + class factory + + This object is a member of the global-scope Components.classes. + It is keyed off of the contract ID. Eg: + + myXriProtocolHandler = Components.classes["@dietrich.ganx4.com/helloworld;1"]. + createInstance(Components.interfaces.nsIXriProtocolHandler); + + ***********************************************************/ +var XriProtocolHandlerFactory = { + createInstance: function (aOuter, aIID) + { + if (aOuter != null) + throw Components.results.NS_ERROR_NO_AGGREGATION; + return (new XriProtocolHandler()).QueryInterface(aIID); + } +}; + + +/*********************************************************** + module definition (xpcom registration) + ***********************************************************/ +var XriProtocolHandlerModule = { + + _firstTime: true, + + registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) + { + if (this._firstTime) { + this._firstTime = false; + throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN; + } + aCompMgr = aCompMgr. + QueryInterface(Components.interfaces.nsIComponentRegistrar); + aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, + CONTRACT_ID, aFileSpec, aLocation, aType); + }, + + unregisterSelf: function(aCompMgr, aLocation, aType) + { + aCompMgr = aCompMgr. + QueryInterface(Components.interfaces.nsIComponentRegistrar); + aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation); + }, + + getClassObject: function(aCompMgr, aCID, aIID) + { + if (!aIID.equals(Components.interfaces.nsIFactory)) + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + + if (aCID.equals(CLASS_ID)) + return XriProtocolHandlerFactory; + + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + canUnload: function(aCompMgr) { return true; } +}; + + +/*********************************************************** + module initialization + + When the application registers the component, this function + is called. + ***********************************************************/ +function NSGetModule(aCompMgr, aFileSpec) { return XriProtocolHandlerModule; } + -- cgit v0.9.0.2