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') diff --git a/src/chrome.manifest b/src/chrome.manifest new file mode 100755 index 0000000..8ff2425 --- a/dev/null +++ b/src/chrome.manifest @@ -0,0 +1,2 @@ +content foxri chrome/content/ +overlay chrome://browser/content/browser.xul chrome://foxri/content/foxri.xul diff --git a/src/chrome/content/aim_logo.gif b/src/chrome/content/aim_logo.gif new file mode 100755 index 0000000..9d5bffe --- a/dev/null +++ b/src/chrome/content/aim_logo.gif Binary files differ diff --git a/src/chrome/content/email_icon.gif b/src/chrome/content/email_icon.gif new file mode 100755 index 0000000..1c62b57 --- a/dev/null +++ b/src/chrome/content/email_icon.gif Binary files differ diff --git a/src/chrome/content/feed-icon-14x14.png b/src/chrome/content/feed-icon-14x14.png new file mode 100755 index 0000000..b3c949d --- a/dev/null +++ b/src/chrome/content/feed-icon-14x14.png Binary files differ diff --git a/src/chrome/content/feed-icon-28x28.png b/src/chrome/content/feed-icon-28x28.png new file mode 100755 index 0000000..d64c669 --- a/dev/null +++ b/src/chrome/content/feed-icon-28x28.png Binary files differ diff --git a/src/chrome/content/foxri.xul b/src/chrome/content/foxri.xul new file mode 100755 index 0000000..dc1cc37 --- a/dev/null +++ b/src/chrome/content/foxri.xul @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/chrome/content/foxri_explorer.css b/src/chrome/content/foxri_explorer.css new file mode 100755 index 0000000..9e68e95 --- a/dev/null +++ b/src/chrome/content/foxri_explorer.css @@ -0,0 +1,124 @@ + + +* { + margin: 0; + padding: 0; +} + + +body { + background-color: #b1d8ff; + font-family: Trebuchet MS, Verdana, Helvetica, Arial, sans-serif; +} + + +a { + text-decoration: none; +} + +a:link, a:visited { + color: #D65B5B; /* inames.net logo color */ +} + +a:hover { + border-bottom: 1px dashed #555A5C; +} + +a img { + border: none; + text-decoration: none; +} + + +h1,h2,h3,h4,h5,h6,p { + margin-left: 100px; +} + +h1 { + margin: 0 100px; + padding: 0; + color: #D65B5B; /* inames.net logo color */ +} + +h3 { + margin: 0 100px; + padding: 0; + color: #666666; +} + +h3 strong { + color: #FF0000; /* inames.net logo color */ +} + + +.service { + background-color: #ffffff; /* inames.net */ + color: #333333; + + margin: 20px 100px; + padding: 10px 150px; + min-height: 100px; + background-repeat: no-repeat; + background-position: 15px 10px; + + -moz-border-radius: 10px; +} + + +.srv_skype { + background-image: url(chrome://foxri/content/skype_logo.png); +} + +.srv_aim { + background-image: url(chrome://foxri/content/aim_logo.gif); +} + +.srv_yahoo { + background-image: url(chrome://foxri/content/yahoo_logo.gif); +} + +.srv_msn { + background-image: url(chrome://foxri/content/msn_logo.gif); +} + +.srv_jabber { + background-image: url(chrome://foxri/content/jabber_logo.gif); +} + +.srv_openid { + background-image: url(chrome://foxri/content/openid_logo.png); +} + +.srv_i-contact { + background-image: url(chrome://foxri/content/i-contact_logo.gif); +} + +.srv_i-forwarding { + background-image: url(chrome://foxri/content/i-forwarding_logo.gif); +} + +.srv_authn-saml { + background-image: url(chrome://foxri/content/i-sso_logo.gif); +} + +.srv_email { + background-image: url(chrome://foxri/content/email_icon.gif); +} + +.srv_feed { + background-image: url(chrome://foxri/content/feed-icon-28x28.png); +} + +.service a { + color: #D65B5B; /* inames.net logo color */ +} + + +div a:hover { + text-decoration: none; + border: none; +} + +.error { + color: #FF5B5B; +} diff --git a/src/chrome/content/i-contact_logo.gif b/src/chrome/content/i-contact_logo.gif new file mode 100755 index 0000000..9775919 --- a/dev/null +++ b/src/chrome/content/i-contact_logo.gif Binary files differ diff --git a/src/chrome/content/i-forwarding_logo.gif b/src/chrome/content/i-forwarding_logo.gif new file mode 100755 index 0000000..125b1de --- a/dev/null +++ b/src/chrome/content/i-forwarding_logo.gif Binary files differ diff --git a/src/chrome/content/i-sso_logo.gif b/src/chrome/content/i-sso_logo.gif new file mode 100755 index 0000000..f948dd6 --- a/dev/null +++ b/src/chrome/content/i-sso_logo.gif Binary files differ diff --git a/src/chrome/content/icn_sso_lg.gif b/src/chrome/content/icn_sso_lg.gif new file mode 100755 index 0000000..f948dd6 --- a/dev/null +++ b/src/chrome/content/icn_sso_lg.gif Binary files differ diff --git a/src/chrome/content/jabber_logo.gif b/src/chrome/content/jabber_logo.gif new file mode 100755 index 0000000..01628d1 --- a/dev/null +++ b/src/chrome/content/jabber_logo.gif Binary files differ diff --git a/src/chrome/content/msn_logo.gif b/src/chrome/content/msn_logo.gif new file mode 100755 index 0000000..19dd647 --- a/dev/null +++ b/src/chrome/content/msn_logo.gif Binary files differ diff --git a/src/chrome/content/openid_logo.png b/src/chrome/content/openid_logo.png new file mode 100755 index 0000000..51d3654 --- a/dev/null +++ b/src/chrome/content/openid_logo.png Binary files differ diff --git a/src/chrome/content/skype_add_large.png b/src/chrome/content/skype_add_large.png new file mode 100755 index 0000000..2cfb5dc --- a/dev/null +++ b/src/chrome/content/skype_add_large.png Binary files differ diff --git a/src/chrome/content/skype_call.png b/src/chrome/content/skype_call.png new file mode 100755 index 0000000..bbb9807 --- a/dev/null +++ b/src/chrome/content/skype_call.png Binary files differ diff --git a/src/chrome/content/skype_call_large.png b/src/chrome/content/skype_call_large.png new file mode 100755 index 0000000..65ce0dd --- a/dev/null +++ b/src/chrome/content/skype_call_large.png Binary files differ diff --git a/src/chrome/content/skype_chat_large.png b/src/chrome/content/skype_chat_large.png new file mode 100755 index 0000000..9642d8a --- a/dev/null +++ b/src/chrome/content/skype_chat_large.png Binary files differ diff --git a/src/chrome/content/skype_logo.png b/src/chrome/content/skype_logo.png new file mode 100755 index 0000000..a190417 --- a/dev/null +++ b/src/chrome/content/skype_logo.png Binary files differ diff --git a/src/chrome/content/xrdsHandler.js b/src/chrome/content/xrdsHandler.js new file mode 100755 index 0000000..d4b0d35 --- a/dev/null +++ b/src/chrome/content/xrdsHandler.js @@ -0,0 +1,258 @@ + +const nsISupports = Components.interfaces.nsISupports; +const nsIChannel = Components.interfaces.nsIChannel; +const nsIContentHandler = Components.interfaces.nsIContentHandler; +const nsIDOMWindow = Components.interfaces.nsIDOMWindow; +const nsIInterfaceRequestor = Components.interfaces.nsIInterfaceRequestor; +const nsIStreamListener = Components.interfaces.nsIStreamListener; +const nsIRequestObserver = Components.interfaces.nsIRequestObserver; +const nsIURILoader = Components.interfaces.nsIURILoader; +const nsIURIContentListener = Components.interfaces.nsIURIContentListener; + + + + +var g_xrdsHandler = null; + + + + + + +XrdsContentHandler.prototype = { + + /** browser window */ + contentWindow: null, + + /** XRDS buffer */ + buf: null, + + scriptableInStream: null, + + init: function(contentWin) { + dump("XrdsContentHandler.init()\n"); + this.contentWindow = contentWin; + + var uriLoader = Components.classes["@mozilla.org/uriloader;1"] + .getService(Components.interfaces.nsIURILoader); + uriLoader.registerContentListener(this); + dump("XrdsContentHandler.init() returning\n"); + }, + + close: function() { + this.contentWindow = null; + var uriLoader = Components.classes["@mozilla.org/uriloader;1"] + .getService(Components.interfaces.nsIURILoader); + uriLoader.unRegisterContentListener(g_xrdsHandler); + }, + + /* nsISupports */ + QueryInterface : function(iid) { + dump("xrdsCH ... "); + if (iid.equals(nsISupports)) { + dump("QI(nsISupports)\n"); + } + else if (iid.equals(nsIContentHandler)) { + dump("QI(nsIContentHandler)\n"); + } + else if (iid.equals(nsIStreamListener)) { + dump("QI(nsIStreamListener)\n"); + } + else if (iid.equals(nsIRequestObserver)) { + dump("QI(nsIRequestObserver)\n"); + } + else if (iid.equals(Components.interfaces.nsIURIContentListener)) { + dump("QI(nsIURIContentListener)\n"); + } + else if (iid.equals(nsIFactory)) { + dump("QI(nsIFactory)\n"); + } + else { + dump("QI(" + iid + ") - IDONTKNOW!!!!!\n"); + } + + if (!iid.equals(nsISupports) && +// !iid.equals(nsIContentHandler) && + !iid.equals(nsIURIContentListener) && + !iid.equals(nsIStreamListener) && +// !iid.equals(nsIRequestObserver) && + !iid.equals(nsIFactory)) + throw Components.results.NS_ERROR_NO_INTERFACE; + + dump("QI returning this..\n"); + return this; + }, + + + /* nsIURIContentListener */ + loadCookie: null, + + parentContentListener: null, + + onStartURIOpen: function(uri) + { + dump("xrdsCH onStartURIOpen '" + uri + "'"); + // ignore and don't abort + return false; + }, + + doContent: function(contentType, isContentPreferred, request, contentHandler) + { + dump("doContent called\n"); + // forward the doContent to our content area webshell + var docShell = this.contentWindow.docShell; + var contentListener; + try { + contentListener = + docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIURIContentListener); + } catch (ex) { + dump(ex); + } + + dump("no content listener from docShell!\n"); + if (!contentListener) return false; + + var rv = contentListener.doContent(contentType, isContentPreferred, request, contentHandler); + + if (rv) { + dump("docShell wants to handle the load..\n"); + } + else { + dump("docShell does NOT want to handle the load..\n"); + + contentHandler = contentHandler.value; + } + + return rv; + + }, + + isPreferred: function(contentType, desiredContentType) + { + dump("isPreferred '" + contentType + "'\n"); + switch(contentType) { + case "application/xrds+xml": + case "application/xrd+xml": + dump("yes!!!\n"); + return true; + } + dump("erm.. nope!\n"); + return false; + }, + + canHandleContent: function(contentType, isContentPreferred, desiredContentType) + { + dump("canHandleContent '" + contentType + "'\n"); + return this.isPreferred(contentType, desiredContentType); + }, + + + + /* nsIRequestObserver */ + onStartRequest: function(request, ctx) + { + dump("xrdsContentHandler - onStartRequest\n"); + }, + + onStopRequest: function(request, ctx, status) + { + dump("xrdsContentHandler - onStopRequest\n"); + this.scriptableInStream.close(); + }, + + + /* nsIStreamListener */ + onDataAvailable: function(request, domWindow, inputStream, offset, count) + { + dump("onDataAvailable, offset=" + offset + ", count=" + count + "\n"); + if (offset == 0) { + this.scriptableInStream.init(inputStream); + } + + buf += this.scriptableInStream.read(count); + + if (!request.isPending()) { + dump("request finished, buf = " + buf + "\n"); + var html = domWindow.document.createElement("html"); + html.createTextNode(buf); + this.scriptableInStream = null; + } + else { + dump("request pending...\n"); + } + }, + + /* nsIContentHandler */ + + handleContent : function(contentType, context, request) + { + dump("handleContent " + contentType + "\n"); + var parentWin; + try { + parentWin = context.getInterface(nsIDOMWindow); + } + catch (e) { + alert("no parent!!!"); // XXX + return; + } + + dump("getting channel\n"); + var channel = request.QueryInterface(nsIChannel); + if (!channel) { + dump("no channel!!!\n"); + return; + } + + 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); + + buf = ''; + + } + +}; + + +function XrdsContentHandler(contentWindow) +{ + this.init(contentWindow); +} + + + + + + + + + + + + + +function foxri_startup() { + // our XRDS content-handler also does nsIURIContentListener + var xrdsHandler = new XrdsContentHandler(getBrowser()); + g_xrdsHandler = xrdsHandler; + +} + + +function foxri_shutdown() { + if (!g_xrdsHandler) + return; + + g_xrdsHandler.close(); +} + + + + +foxri_startup(); diff --git a/src/chrome/content/yahoo_logo.gif b/src/chrome/content/yahoo_logo.gif new file mode 100755 index 0000000..34e6cd2 --- a/dev/null +++ b/src/chrome/content/yahoo_logo.gif Binary files differ 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; } + diff --git a/src/install.rdf b/src/install.rdf new file mode 100755 index 0000000..a482755 --- a/dev/null +++ b/src/install.rdf @@ -0,0 +1,40 @@ + + + + + + foxri@foxri.net + 1.1.1 + 2 + + + + + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + 1.0 + 1.5.0.* + + + + + + {a463f10c-3994-11da-9945-000d60ca027b} + 0.5 + 0.8 + + + + + + FoXRI + XRI Extension + William Tan + http://dready.org/projects/foxri + + + + -- cgit v0.9.0.2