summaryrefslogtreecommitdiffabout
path: root/src
Side-by-side diff
Diffstat (limited to 'src') (more/less context) (show whitespace changes)
-rwxr-xr-xsrc/chrome.manifest2
-rwxr-xr-xsrc/chrome/content/aim_logo.gifbin0 -> 1247 bytes
-rwxr-xr-xsrc/chrome/content/email_icon.gifbin0 -> 476 bytes
-rwxr-xr-xsrc/chrome/content/feed-icon-14x14.pngbin0 -> 689 bytes
-rwxr-xr-xsrc/chrome/content/feed-icon-28x28.pngbin0 -> 1737 bytes
-rwxr-xr-xsrc/chrome/content/foxri.xul32
-rwxr-xr-xsrc/chrome/content/foxri_explorer.css124
-rwxr-xr-xsrc/chrome/content/i-contact_logo.gifbin0 -> 2391 bytes
-rwxr-xr-xsrc/chrome/content/i-forwarding_logo.gifbin0 -> 1293 bytes
-rwxr-xr-xsrc/chrome/content/i-sso_logo.gifbin0 -> 1917 bytes
-rwxr-xr-xsrc/chrome/content/icn_sso_lg.gifbin0 -> 1917 bytes
-rwxr-xr-xsrc/chrome/content/jabber_logo.gifbin0 -> 1199 bytes
-rwxr-xr-xsrc/chrome/content/msn_logo.gifbin0 -> 1190 bytes
-rwxr-xr-xsrc/chrome/content/openid_logo.pngbin0 -> 2489 bytes
-rwxr-xr-xsrc/chrome/content/skype_add_large.pngbin0 -> 2928 bytes
-rwxr-xr-xsrc/chrome/content/skype_call.pngbin0 -> 910 bytes
-rwxr-xr-xsrc/chrome/content/skype_call_large.pngbin0 -> 2348 bytes
-rwxr-xr-xsrc/chrome/content/skype_chat_large.pngbin0 -> 2721 bytes
-rwxr-xr-xsrc/chrome/content/skype_logo.pngbin0 -> 4331 bytes
-rwxr-xr-xsrc/chrome/content/xrdsHandler.js258
-rwxr-xr-xsrc/chrome/content/yahoo_logo.gifbin0 -> 1292 bytes
-rwxr-xr-xsrc/components/xriProtocolHandler.js1060
-rwxr-xr-xsrc/install.rdf40
23 files changed, 1516 insertions, 0 deletions
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="foxri.css" type="text/css"?>
+
+
+<overlay id="foxri" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+
+<!--
+<script type="application/x-javascript" src="chrome://foxri/content/xrdsHandler.js"/>
+-->
+
+<script>
+function xriFix(event)
+{
+ var txt = gURLBar.value;
+ if (/^(=|@|!)\S+/.test(txt)) {
+ gURLBar.value = "xri://" + txt;
+ }
+}
+</script>
+
+
+ <toolbaritem id="urlbar-container">
+ <textbox id="urlbar" ontextentered="xriFix(); return handleURLBarCommand(param);" />
+ </toolbaritem>
+
+ <toolbaritem id="go-container">
+ <toolbarbutton id="go-button" oncommand="xriFix(); return handleURLBarCommand(event);" />
+ </toolbaritem>
+
+</overlay>
+
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 = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\
+ <html xmlns=\"http://www.w3.org/1999/xhtml\">\n\
+ <head>\n\
+ <title>FoXRI Explorer - #QXRI#</title>\n\
+ <link href=\"chrome://foxri/content/foxri_explorer.css\" rel=\"stylesheet\" type=\"text/css\" />\n\
+ <body>\n\
+ <h1>FoXRI Explorer</h1>\n\
+ <div id=\"explorer_body\">\n";
+
+const HTML_FOOT = "</div>\n\
+ </body>\n\
+ </html>";
+
+
+
+/// 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 += "<strong>Type:</strong> " + t.nodeValue + "<br/>";
+ }
+ }
+
+ // 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 += "<strong>Path:</strong> " + p.nodeValue
+ + " [ <tt><a href=\"" + qxri_prefix + p.nodeValue + "\">"
+ + qxri_prefix + p.nodeValue + "</a></tt> ]"
+ + "<br/>\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 += "<strong>Media Type:</strong> " + m.nodeValue + "<br/>";
+ }
+ }
+
+
+ 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 += "<div class=\"" + getServiceClass(srvType) + "\">";
+
+ 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 = "<img src=\"" + u.nodeValue + "\"/>";
+ }
+ else if (uriParts) {
+ linkContent = uriParts[1] + " data";
+ }
+ }
+ else if (uriParts[1] == 'skype') {
+ uriParts = uriParts[2].match('^(.*)\\?(.*)');
+ if (uriParts) {
+ if (uriParts[2] == "call") {
+ linkContent = "<img src=\"chrome://foxri/content/skype_call_large.png\" alt=\"Call " + uriParts[1] + "\"/>";
+ }
+ else if (uriParts[2] == "chat") {
+ linkContent = "<img src=\"chrome://foxri/content/skype_chat_large.png\" alt=\"Chat with " + uriParts[1] + "\"/>";
+ }
+ else if (uriParts[2] == "add") {
+ linkContent = "<img src=\"chrome://foxri/content/skype_add_large.png\" alt=\"Add " + uriParts[1] + " to Skype\"/>";
+ }
+ }
+ }
+ else if (uriParts[1] == 'aim') {
+ uriParts = uriParts[2].match('^(.*)\\?.*screenname=([^&]*)', 'i');
+ if (uriParts) {
+ linkContent = "<img src=\"chrome://foxri/content/aim_logo.gif\" alt=\"Chat with " + uriParts[2] + "\"/> Chat with " + uriParts[2];
+ }
+ }
+
+ html_uris += "<a href=\""+u.nodeValue+"\">"
+ + linkContent + "</a>";
+ html_uris += "</div>";
+ }
+
+ var html = "<div class=\"service srv_" + getServiceClass(knownServiceType) + "\">\n";
+ html += html_types;
+ html += html_paths;
+ html += html_mediatypes;
+ if (html_uris) {
+ html += "<strong>URI(s):</strong><br/>\n";
+ html += html_uris;
+ }
+ html += "</div>";
+
+ 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 <a href=\"" + uri + "\"><img src=\"chrome://foxri/content/skype_call.png\"></a>";
+ }
+ 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 += "<h3>Exploring <strong>" + qxri + "</strong></h3>";
+ }
+ else {
+ var msg = getString(x, lastNode, "xrd:Status/text()");
+ html += "<h3 class=\"error\"><strong>" + qxri + "</strong> failed to resolve (reason: " + stat + " - " + msg + ")</h3>";
+ }
+
+ html += "<br/>";
+
+ 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 += "<p class='error'>" + xriType + " does not exist.</p>\n";
+ }
+ else {
+ html += "<p>No service has been configured for this XRI</p>";
+ }
+
+ }
+
+ html += "</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 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>foxri@foxri.net</em:id>
+ <em:version>1.1.1</em:version>
+ <em:type>2</em:type>
+
+ <!-- Target Application this extension can install into,
+ with minimum and maximum supported versions. -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+ <em:minVersion>1.0</em:minVersion>
+ <em:maxVersion>1.5.0.*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>{a463f10c-3994-11da-9945-000d60ca027b}</em:id>
+ <em:minVersion>0.5</em:minVersion>
+ <em:maxVersion>0.8</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+
+ <!-- Front End MetaData -->
+ <em:name>FoXRI</em:name>
+ <em:description>XRI Extension</em:description>
+ <em:creator>William Tan</em:creator>
+ <em:homepageURL>http://dready.org/projects/foxri</em:homepageURL>
+<!--
+ <em:updateURL>http://dready.org/projects/foxri/update.rdf</em:updateURL>
+-->
+ </Description>
+</RDF>
+