<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ashita.org &#187; Pirate Questing</title>
	<atom:link href="http://www.ashita.org/category/piratequesting/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.ashita.org</link>
	<description></description>
	<lastBuildDate>Thu, 22 Apr 2010 17:21:34 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Howto: XHR Listening by a Firefox Addon</title>
		<link>http://www.ashita.org/howto-xhr-listening-by-a-firefox-addon/</link>
		<comments>http://www.ashita.org/howto-xhr-listening-by-a-firefox-addon/#comments</comments>
		<pubDate>Sun, 11 Oct 2009 04:00:38 +0000</pubDate>
		<dc:creator>Jonathan Fingland</dc:creator>
				<category><![CDATA[Firefox]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Pirate Questing]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Firefox Extension]]></category>
		<category><![CDATA[XmlHttpRequest]]></category>

		<guid isPermaLink="false">http://ashita.org/?p=307</guid>
		<description><![CDATA[The following post draws significantly from a post by Jan Odvarko at http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/ but goes a bit further. There are also some sections which were inspired by Firebug, but are heavily modified.


What you need to know
Before I get into the code, understand that one of the most important things in this process to understand is [...]]]></description>
			<content:encoded><![CDATA[<p>The following post draws significantly from a post by Jan Odvarko at <a href="http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/">http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/</a> but goes a bit further. There are also some sections which were inspired by <a href="http://www.getfirebug.com">Firebug</a>, but are heavily modified.</p>
<ul class='structure'>
<li>
<h3>What you need to know</h3>
<p>Before I get into the code, understand that one of the most important things in this process to understand is that your extension&#8217;s listener is just one in a chain. It is the responsibility of every listener in the chain to pass on the information. Failure to do this has some amusing consequences&#8230;. like nothing loading in the browser.</p>
<p>Just to make it really clear &#8212; <em>Don&#8217;t drop the ball</em>. (Edit: And while you <em>can</em> edit the data in the stream &#8212; don&#8217;t do it unless you have a really good reason.)
</li>
<li>
<h3>Convenience methods and aliases</h3>
<p>A lot of the Firefox internals are accessed using <code>Components.classes</code> and <code>Components.interfaces</code>. While the verbosity makes it clear, it can at times be overly repetitive and, honestly, can take a long time to write out. A fairly common shorthand is in use, <code>Cc</code> and <code>Ci</code> with a few other less common shorthands like <code>CCIN</code> (for creating instances of a class based on a class name and an interface name) and <code>CCSV</code> (similarly creating a service based on a class name and interface name)</p>
<pre><code class='javascript'>
if (typeof Cc == "undefined") {
	var Cc = Components.classes;
}
if (typeof Ci == "undefined") {
        var Ci = Components.interfaces;
}
if (typeof CCIN == "undefined") {
	function CCIN(cName, ifaceName){
		return Cc[cName].createInstance(Ci[ifaceName]);
	}
}
if (typeof CCSV == "undefined") {
	function CCSV(cName, ifaceName){
		if (Cc[cName])
			// if fbs fails to load, the error can be _CC[cName] has no properties
			return Cc[cName].getService(Ci[ifaceName]);
		else
			dumpError("CCSV fails for cName:" + cName);
	};
}</code></pre>
<ul class='structure'>
<li>
<h4>What&#8217;s with all of the <code>typeof</code> checks?</h4>
<p>Firebug, gotta love it, but it declares the same things using <code>const</code>. Inside of an <code>if()</code> block, a <code>const</code> is still seen and conflicts, even when the if condition evaluates to <code>false</code>. The code above is essentially a workaround to satisfy both possibilities. <em>If the user has firebug installed, then carry on; if the user doesn&#8217;t have firebug installed, declare those shorthands</em>
</li>
</ul>
</li>
<li>
<h3>The constructor</h3>
<pre><code class='javascript'>function TracingListener() {
}</code></pre>
<p>Above is a (very) simple constructor function for us to create objects from. The methods and properties on the prototype are below. Note that while I could have changed the structure to accommodate better data-hiding, the method below reduces the number of new functions created by making them all declared only once on the prototype. Functions in the constructor are recreated every time the constructor is called with <code>new yourConstructor()</code> whereas functions on the prototype are shared by all instances.
</li>
<li>
<h3>The prototype definition</h3>
<ul class='structure'>
<li>
<h4>Basic properties</h4>
<pre><code class='javascript'>TracingListener.prototype =
{
    originalListener: null,
    receivedData: null,   //will be an array for incoming data.
</code></pre>
<p>The first part of the prototype definition is setting up some basic properties. Note that both are assigned <code>null</code>. These properties will exist on all instances of <code>TracingListener</code>, and thus not be <code>undefined</code> if/when checking. In the case of <code>receivedData</code>, do not be tempted to make it an array here. Remember that methods and properties on the prototype are shared by <em>all</em> instances of the same type &#8212; and we don&#8217;t want all instances to share the same array for data.</p>
<p>Also worth note is that <code>receivedData</code> is a good candidate for data-hiding and declaring it local to the constructor&#8230; but scope and visibility limitations would mean the functions requiring access to it would either need to be in the constructor as well, or have accessor and mutator methods for it. If you&#8217;re making a Singleton or a small number of instances, declaring functions in the constructor is no big deal, but this listener will be instantiated hundreds or thousands of times and it&#8217;s important to keep the duplication to a minimum.
</li>
<li>
<h4>Methods on the prototype</h4>
<ul class='structure'>
<li>
<h5>Interface Requirements</h5>
<pre><code class='javascript'>    //For the listener this is step 1.
    onStartRequest: function(request, context) {
    	this.receivedData = []; //initialize the array

	//Pass on the onStartRequest call to the next listener in the chain -- VERY IMPORTANT
	this.originalListener.onStartRequest(request, context);
    },</code></pre>
<p><code>onStartRequest</code> is the first thing called when the actual request processing begins. This is also the best opportunity to initialize the array on <em>this</em> listener.</p>
<pre><code class='javascript'>    //This is step 2. This gets called every time additional data is available
    onDataAvailable: function(request, context, inputStream, offset, count)
    {
       var binaryInputStream = CCIN("@mozilla.org/binaryinputstream;1",
                                 "nsIBinaryInputStream");
        binaryInputStream.setInputStream(inputStream);

        var storageStream = CCIN("@mozilla.org/storagestream;1",
                                 "nsIStorageStream");
        //8192 is the segment size in bytes, count is the maximum size of the stream in bytes
        storageStream.init(8192, count, null); 

	var binaryOutputStream = CCIN("@mozilla.org/binaryoutputstream;1",
                                 "nsIBinaryOutputStream");
        binaryOutputStream.setOutputStream(storageStream.getOutputStream(0));

        // Copy received data as they come.
        var data = binaryInputStream.readBytes(count);

        this.receivedData.push(data);

        binaryOutputStream.writeBytes(data, count);

        //Pass it on down the chain
        this.originalListener.onDataAvailable(request,
                                          context,
                                          storageStream.newInputStream(0),
                                          offset,
                                          count);
    },</code></pre>
<p><code>onDataAvailable</code> essentially copies the data from the <a href="http://www.oxymoronical.com/experiments/apidocs/interface/nsIBinaryInputStream"><code>binaryInputStream</code></a> to our <code>receivedData</code> array and to the <a href="http://www.oxymoronical.com/experiments/apidocs/interface/nsIStorageStream"><code>storageStream</code></a> (via the <a href="http://www.oxymoronical.com/experiments/apidocs/interface/nsIBinaryOutputStream"><code>binaryOutputStream</code></a>). Then we pass a new <code>InputStream</code> from our <code>storageStream</code> onto the next listener in the chain.</p>
<pre><code class='javascript'>    onStopRequest: function(request, context, statusCode)
    {
	try
	{
                //QueryInterface into HttpChannel to access originalURI and requestMethod properties
		request.QueryInterface(Ci.nsIHttpChannel);

                //this is specific to the PirateQuesting Add-on, but is left here as an example of how to modify behaviour based on the requested URL
		if (request.originalURI
                    &amp;&amp; piratequesting.baseURL == request.originalURI.prePath
                    &amp;&amp; request.originalURI.path.indexOf("/index.php?ajax=") == 0)
		{

			var data = null;
			if (request.requestMethod.toLowerCase() == "post")
			{
				var postText = this.readPostTextFromRequest(request, context);
				if (postText)
					data = ((String)(postText)).parseQuery();

			}

                        //Combine the response into a single string
			var responseSource = this.receivedData.join('');

			//fix leading spaces bug
			//(FM occasionally adds spaces to the beginning of their ajax responses...
                        //which breaks the XML)
			responseSource = responseSource.replace(/^\s+(\S[\s\S]+)/, "$1");

                        //gets the date from the response headers on the request.
                        //For PirateQuesting this was preferred over the date on the user's machine
			var date = Date.parse(request.getResponseHeader("Date"));

                        //Again a PQ specific function call, but left as an example.
                        //This just passes a string URL, the text of the response,
                        //the date, and the data in the POST request (if applicable)
			piratequesting.ProcessRawResponse(request.originalURI.spec,
                                               responseSource,
                                               date,
                                               data);
		}
	}
	catch (e)
	{
		//standard function to dump a formatted version of the error to console
		dumpError(e);
	}
	//Pass it on down the chain
	this.originalListener.onStopRequest(request,
                                         context,
                                         statusCode);
    },</code></pre>
<p>The <code>onStopRequest</code> above has a few tricky parts. The first is the <code>QueryInterface</code> to <a href="http://www.oxymoronical.com/experiments/apidocs/interface/nsIHttpChannel"><code>nsIHttpChannel</code> </a>&#8211; this is critical to getting the info needed. The second tricky part is to get the posted variables. To do so, you need to check that the <code>requestMethod</code> was indeed post, and then we call <code>readPostTextFromRequest</code> which I&#8217;ll introduce in a bit. The last tricky bit is getting the Date header from the response. <code>Date.parse()</code> plays nicely with those (assuming the server response conforms)</p>
<pre><code class='javascript'>    QueryInterface: function (aIID) {
        if (aIID.equals(Ci.nsIStreamListener) ||
            aIID.equals(Ci.nsISupports)) {
            return this;
        }
        throw Components.results.NS_NOINTERFACE;
    },</code></pre>
<p>This is pretty standard for anything fulfilling an interface contract for Firefox (or other mozilla-based browsers). <code>QueryInterface</code> is part of the <a href="http://www.oxymoronical.com/experiments/apidocs/interface/nsISupports"><code>nsISupports</code></a> interface and is the only part which is scriptable. All interfaces are derived from <code>nsISupports</code>, so it has to be there.
</li>
<li>
<h5>Utility methods</h5>
<p>The following methods are required by our TracingListener but are not part of the interface contract. (It would also have been possible to define them globally or within a pseudo-namespace.)</p>
<pre><code class='javascript'>    readPostTextFromRequest : function(request, context) {
        try
        {
	        var is = request.QueryInterface(Ci.nsIUploadChannel).uploadStream;
	        if (is)
	        {
	            var ss = is.QueryInterface(Ci.nsISeekableStream);
	            var prevOffset;
	            if (ss)
	            {
	                prevOffset = ss.tell();
	                ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
	            }

	            // Read data from the stream..
		    var charset = "UTF-8";
		    var text = this.readFromStream(is, charset, true);

	            if (ss &amp;&amp; prevOffset == 0)
	                ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);

	            return text;
	        }
		else {
			dump("Failed to Query Interface for upload stream.\n");
		}
	    }
	    catch(exc)
	    {
			dumpError(exc);
	    }

	    return null;
	},</code></pre>
<p>I will readily admit that <code>readPostTextFromRequest</code> is mostly taken from Firebug, though there are a few changes. Basically, we have to do the same thing as before and <code>QueryInterface</code> into the appropriate interface. In this case we need <a href="http://www.oxymoronical.com/experiments/apidocs/interface/nsIUploadChannel">nsIUploadChannel</a> to get access to <code>uploadStream</code>. And then we <code>QueryInterface</code> the <code>uploadStream</code> into a <a href="http://www.oxymoronical.com/experiments/apidocs/interface/nsISeekableStream">nsISeekableStream</a> (noticing a pattern, yet? <code>QueryInterface</code> is your best friend.. and worst enemy.). After that we store the original offset in the stream in <code>prevOffset</code>, and then seek to the beginning of the stream. Then we read the data and, if the stream was at position 0 originally, we seek to the beginning again.</p>
<pre><code class='javascript'>	readFromStream : function(stream, charset, noClose)	{

	    var sis = CCSV("@mozilla.org/binaryinputstream;1",
                            "nsIBinaryInputStream");
	    sis.setInputStream(stream);

	    var segments = [];
	    for (var count = stream.available(); count; count = stream.available())
	        segments.push(sis.readBytes(count));

	    if (!noClose)
	        sis.close();

	    var text = segments.join("");
	    return text;
	}

}</code></pre>
<p><code>readFromStream</code> is also largely from Firebug with a few modifications. It is however remarkably similar to what is done in <code>onDataAvailable</code> and <code>onStopRequest</code>. Basically, we get a <code>BinaryInputStream</code> to work with the stream given. Then we loop through the segments of the stream (size provided by <code>available()</code>) and add them to an array. When finished with that, we join the segments and return the text.</p>
<pre><code class='javascript'>httpRequestObserver = {

	observe: function(request, aTopic, aData){
		if (typeof Cc == "undefined") {
			var Cc = Components.classes;
		}
		if (typeof Ci == "undefined") {
			var Ci = Components.interfaces;
		}
	    	if (aTopic == "http-on-examine-response") {
	    		request.QueryInterface(Ci.nsIHttpChannel);

			if (request.originalURI
                            &amp;&amp; piratequesting.baseURL == request.originalURI.prePath
                            &amp;&amp; request.originalURI.path.indexOf("/index.php?ajax=") == 0) {
				var newListener = new TracingListener();
    				request.QueryInterface(Ci.nsITraceableChannel);
    				newListener.originalListener = request.setNewListener(newListener);
			}
		}
	},

	QueryInterface: function(aIID){
		if (typeof Cc == "undefined") {
			var Cc = Components.classes;
		}
		if (typeof Ci == "undefined") {
			var Ci = Components.interfaces;
		}
		if (aIID.equals(Ci.nsIObserver) ||
		aIID.equals(Ci.nsISupports)) {
			return this;
		}

		throw Components.results.NS_NOINTERFACE;

	},
};</code></pre>
<p>This part is fairly straightforward. The object httpRequestObserver has to fulfill the contract for the nsIObserver interface &#8212; which only has two methods: observe and QueryInterface.
</li>
</ul>
</li>
</ul>
</li>
<li>
<h3>Observer registration</h3>
<p>Finally, we need to register the observer:</p>
<pre><code class='javascript'>var observerService = Cc["@mozilla.org/observer-service;1"]
    .getService(Ci.nsIObserverService);

observerService.addObserver(httpRequestObserver,
    "http-on-examine-response", false);</code></pre>
<p>Now the <a href="http://www.oxymoronical.com/experiments/apidocs/interface/nsIObserverService"><code>observerService</code></a> will call the <code>observe</code> method on <code>httpRequestObserver</code> whenever it notifies observers with the <code>http-on-examine-response</code> topic.</p>
<p>When you want to unregister the observer, use:</p>
<pre><code class='javascript'>observerService.removeObserver(httpRequestObserver,
    "http-on-examine-response");</code></pre>
<p>As you can see, getting the text and post variables from an http request is non-trivial.
</li>
</ul>
<p>Note, though, that this code does not check the context to determine whether the http request is <em>for</em> a browser window, or <em>from</em> a browser window so depending on the complexity of your situation, you may want to do that as well. Perhaps, I&#8217;ll add that in another post.</p>
<p>(See Firebug license <a href="http://code.google.com/p/fbug/source/browse/branches/firebug1.5/license.txt">here</a>. Special thanks to the Firebug team and to Jon Odvarko for providing so much useful material. The interface docs at <a href="http://www.oxymoronical.com/experiments/apidocs/platform/1.9.2a1pre">oxymoronical</a> are a great resource. The <a href="https://developer.mozilla.org">Mozilla Developer Center</a> also deserves special credit for great documentation. )</p>
<p><strong>Update (Jan 17, 2010):</strong> Corrected a small bug in <code>onStopRequest</code> (Thanks Broady!). See <a href="http://www.ashita.org/howto-xhr-listening-by-a-firefox-addon/#comment-419">below</a> for details.</p>
<p><strong>Update (April 22, 2010):</strong> Corrected a bug which doesn&#8217;t occur if Firebug is installed (Thanks Harini!). See <a href="http://www.ashita.org/howto-xhr-listening-by-a-firefox-addon/#comment-1125">below</a> for details.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ashita.org/howto-xhr-listening-by-a-firefox-addon/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>The loadOverlay Dilemma</title>
		<link>http://www.ashita.org/the-loadoverlay-dilemma/</link>
		<comments>http://www.ashita.org/the-loadoverlay-dilemma/#comments</comments>
		<pubDate>Wed, 25 Mar 2009 04:51:41 +0000</pubDate>
		<dc:creator>Jonathan Fingland</dc:creator>
				<category><![CDATA[Firefox]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Pirate Questing]]></category>
		<category><![CDATA[XUL]]></category>

		<guid isPermaLink="false">http://ashita.org/?p=256</guid>
		<description><![CDATA[When making Firefox extensions with modular components, it&#8217;s nice to be able to include overlays based on preferences or some other criteria at loadtime. One of the problems that quickly comes up is that sequential loadOverlay calls will fail. This bug is documented here. The solution described there, and elsewhere, is to use chained observers [...]]]></description>
			<content:encoded><![CDATA[<p>When making Firefox extensions with modular components, it&#8217;s nice to be able to include overlays based on preferences or some other criteria at loadtime. One of the problems that quickly comes up is that sequential loadOverlay calls will fail. This bug is documented <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=330458">here</a>. The solution described there, and elsewhere, is to use chained observers and a custom queue. Strangely, I never came across an implementation example of such an observer/queue combination. So&#8230; with that stunning introduction, I give you the system used in PirateQuesting 2.</p>
<p>The first part here is an overlay registry. Essentially, the queue portion of the solution.</p>
<pre>
piratequesting.overlayRegistry = function() {
	var overlays = [];
	var index = 0;

	function conflicts(tabid, tabpanelid, overlayFile) {
		for (var i = 0, len = overlays.length; i&lt;len;++i)
			if ((tabid == overlays[i].getTabId() &#038;&#038; tabid != null )  || (tabpanelid == overlays[i].getTabPanelId() &#038;&#038; tabpanelid != null) || overlayFile == overlays[i].getOverlayFile())
				return true;
		return false;
	}

	function overlay(tabid, tabpanelid, overlayFile) {
		var tabid = tabid;
		var tabpanelid = tabpanelid;
		var overlayFile = overlayFile;
		var added = false;

		return {
			toString : function() {
				return "tabid: " + tabid + "\ntabpanelid: " + tabpanelid
						+ "\noverlayFile: " + overlayFile;
			},
			getTabId : function() {
				return tabid;
			},
			getTabPanelId : function() {
				return tabpanelid;
			},
			getOverlayFile : function() {
				return overlayFile;
			},
			getAdded : function() {
				return added;
			},
			setAdded : function(val) {
				added = !!val; // ensure boolean
			}
		}

	}

	return {
		addOverlay : function(tabid, tabpanelid, stringFile) {
			if (!conflicts(tabid, tabpanelid, stringFile))
				overlays.push(new overlay(tabid, tabpanelid, stringFile));
			else
				dump("\nOverlay conflict occurred on: " + tabid + ", " + tabpanelid + ", " + stringFile);
		},
		getOverlayByIndex : function(index) {
			return overlays[index];
		},
		getOverlayByTabId : function(tabid) {
			var ol = overlays.length;
			for (var i = 0; i &lt; ol; i++) {
				if (overlays[i].getTabId() == tabid)
					return overlays[i];
			}
			return false;
		},
		count : function() {
			return overlays.length;
		},
		reset : function() {
			index = 0;
		},
		next : function() {
			if (index &lt; overlays.length) {
				return overlays[index++];
			} else
				return null;
		},
		progress : function() {
			return Math.ceil(index * 100 / overlays.length);
		},
		resetAll : function() {
			this.reset();
			var nex;
			while (nex = this.next()) {
				nex.setAdded(false);
			}
			this.reset();
		}

	}
}();
</pre>
<p>Now, that is a somewhat specialized case. The tabid and tabpanelid are arguably unnecessary but have been included to prevent two modules having the same tab ids, and, more importantly, to be able to refer to the overlay by a known value, in this case the tabid.</p>
<p>A somewhat stripped down version might look like:</p>
<pre>
var overlayRegistry = function() {
	var overlays = [];
	var index = 0;

	function conflicts(overlayFile) {
		for (var i = 0, len = overlays.length; i&lt;len;++i)
			if (overlayFile == overlays[i].getOverlayFile())
				return true;
		return false;
	}

	function overlay(overlayFile) {
		var overlayFile = overlayFile;
		var added = false;

		return {
			toString : function() {
				return "\noverlayFile: " + overlayFile;
			},
			getOverlayFile : function() {
				return overlayFile;
			},
			getAdded : function() {
				return added;
			},
			setAdded : function(val) {
				added = !!val; // ensure boolean
			}
		}

	}

	return {
		addOverlay : function(stringFile) {
			if (!conflicts(stringFile))
				overlays.push(new overlay(stringFile));
			else
				dump("\nOverlay conflict occurred on: " + stringFile);
		},
		getOverlayByIndex : function(index) {
			return overlays[index];
		},
		count : function() {
			return overlays.length;
		},
		reset : function() {
			index = 0;
		},
		next : function() {
			if (index &lt; overlays.length) {
				return overlays[index++];
			} else
				return null;
		},
		progress : function() {
			return Math.ceil(index * 100 / overlays.length);
		},
		resetAll : function() {
			this.reset();
			var nex;
			while (nex = this.next()) {
				nex.setAdded(false);
			}
			this.reset();
		}

	}
}();
</pre>
<p>The overlay registry is simply an iterator-style queue. This makes walking through the items very easy for the observer (shown next) which doesn&#8217;t (and shouldn&#8217;t) really have any idea of the state of the queue. The queue, overlayRegistry, makes a number of methods available for getting basic info about the queue (size, progress, etc) for use in progress bars or the like. It also provides ways of resetting the queue. Obviously, since it&#8217;s designed to be an iterator, there are ways of getting the next item and advancing the queue. </p>
<p>The second part of the solution is using a chained observer. Again, the piratequesting implementation is:</p>
<pre>
function overlayObserver()
{
  this.register();
}
overlayObserver.prototype = {
  observe: function(subject, topic, data) {

  	function cleanUp() {
		sidebar.contentDocument.getElementById("pqmain_deck").selectedIndex="1";

		var mod_boxes = sidebar.contentDocument.getElementsByTagName("tabbox");
		for (var i=0,len=mod_boxes.length;i&lt;len;++i) {
			if (hasClassName(mod_boxes[i],"moduleBox")) {
				mod_boxes[i].selectedIndex = 0;
			}
		}
  	}

  	if (topic == "xul-overlay-merged") {
		try {
			var nex = piratequesting.overlayRegistry.next();
			if (nex) {
  				sidebar.contentDocument.getElementById("pqloadprogress").value = piratequesting.overlayRegistry.progress();
  				try {
  					sidebar.contentDocument.loadOverlay(nex.getOverlayFile(),this);
	  			} catch (error) {
  					cleanUp();
  					dump("Failed to load: " + nex.getOverlayFile() + "\nReported Error " + getErrorString(error));
	  			}
  			} else {
  				cleanUp();
	  		}
  		} catch (error) { alert(getErrorString(error)); }
  	}
  },
  register: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                          .getService(Components.interfaces.nsIObserverService);
    observerService.addObserver(this, "xul-overlay-merged", false);
  },
  unregister: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                            .getService(Components.interfaces.nsIObserverService);
    observerService.removeObserver(this, "xul-overlay-merged");
  }
}
</pre>
<p>And a stripped down version would look something like this: </p>
<pre>
function overlayObserver()
{
  this.register();
}
overlayObserver.prototype = {
  observe: function(subject, topic, data) {

  	if (topic == "xul-overlay-merged") {
		try {
			var nex = overlayRegistry.next();
			if (nex) {
  				try {
  					document.loadOverlay(nex.getOverlayFile(),this);
	  			} catch (error) {
  					dump("Failed to load: " + nex.getOverlayFile() + "\nReported Error " + getErrorString(error));
	  			}
  			} else {
  				cleanUp();
	  		}
  		} catch (error) { alert(getErrorString(error)); }
  	}
  },
  register: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                          .getService(Components.interfaces.nsIObserverService);
    observerService.addObserver(this, "xul-overlay-merged", false);
  },
  unregister: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                            .getService(Components.interfaces.nsIObserverService);
    observerService.removeObserver(this, "xul-overlay-merged");
  }
}
</pre>
<p>This observer chain is started with:</p>
<pre>
overlayObserver.observe(null,"xul-overlay-merged", null);
</pre>
<p>The observer code is also fairly simple but relies on some things that were not terribly well documented. The key thing to know about loadOverlay is that it raises an <code>xul-overlay-merged</code> observer notification. <a href="http://developer.mozilla.org>MDC</a> has a pretty good list of the observer notifications <a href="http://developer.mozilla.org/en/Observer_Notifications">here</a>, but you&#8217;ll notice what&#8217;s missing. <a href="https://developer.mozilla.org/en/Document.loadOverlay">This entry</a>, however, has that piece of info. If you&#8217;ve never used observers before, <a href="https://developer.mozilla.org/en/nsIObserver">this</a> gives a good rundown.</p>
<p>Sooo&#8230;. What happens? how does it work? Basically, at some point during the initialization of the sidebar (not important in this discussion), the observer chain is started and will loop as follows:</p>
<ol>
<li>receive notification of the last loadOverlay finishing</li>
<li>get the next item in the iterator</li>
<li>if the &#8220;next item&#8221; is null, stop</li>
<li>load the overlay file specified by said item</li>
</ol>
<p>And you&#8217;re done. One Note I would make is that if there are multiple extensions making use of this at the same time, you&#8217;re very likely to have problems when both extensions receive the notifications and both start loading their next items. As firefox still lacks a built-in queue for overlay loading, we&#8217;re stuck hoping that nobody else will use this at the same time.</p>
<p>Note: You may notice that a lot of the piratequesting code makes use of a function <code>getErrorString()</code>. This is a very siple function that puts all of the error info I want into a string. <code>dump()</code> is a function available in firefox for dumping text to the console. I am also currently working on an error logging system and will cover all of these issues in more detail when that is finished. For the time being, ignore how I handle the errors but for obvious reasons, you will want to have some kind of error handling in place.</p>
<p>Edit 2009/04/04:<br />
It has since occurred to me that by using additional information, that is, <code>subject</code>, from the observer notification, I can reduce the chances of conflict by ensuring it only fires on the completion of it&#8217;s own loadOverlay calls.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ashita.org/the-loadoverlay-dilemma/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Passing parameters into a created dialog, and retrieving them on exit</title>
		<link>http://www.ashita.org/passing-parameters-into-a-created-dialog-and-retrieving-them-on-exit/</link>
		<comments>http://www.ashita.org/passing-parameters-into-a-created-dialog-and-retrieving-them-on-exit/#comments</comments>
		<pubDate>Sun, 26 Oct 2008 07:50:05 +0000</pubDate>
		<dc:creator>Jonathan Fingland</dc:creator>
				<category><![CDATA[DOM]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Pirate Questing]]></category>
		<category><![CDATA[XUL]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Firefox Extension]]></category>

		<guid isPermaLink="false">http://ashita.org/?p=251</guid>
		<description><![CDATA[This is going to be a pretty short tutorial today to help explain how to pass information to an window when opening it. The following is taken from PirateQuesting.
First, here is the function for entering the code when PirateQuest asks the user for verification.
function enterCode(url,imgsrc, func) {
	var params = {in:imgsrc, out:null};
	window.openDialog("chrome://piratequesting/content/codeDialog.xul", "",
		"chrome, dialog, modal, resizable=no, [...]]]></description>
			<content:encoded><![CDATA[<p>This is going to be a pretty short tutorial today to help explain how to pass information to an window when opening it. The following is taken from PirateQuesting.</p>
<p>First, here is the function for entering the code when PirateQuest asks the user for verification.</p>
<pre>function enterCode(url,imgsrc, func) {
	var params = {in:imgsrc, out:null};
	window.openDialog("chrome://piratequesting/content/codeDialog.xul", "",
		"chrome, dialog, modal, resizable=no, status=no,
		height=250, width=400", params).focus();
	if (params.out) {
		piratequesting.Code.submit(url,params.out,imgsrc,func);
	}
	else {
	    // User clicked cancel. stop here
	}
}</pre>
<p>So, as we can see in the first line the params variable stores a hash. A has is useful here as it allows us to easily pass more than one variable in without goign to the work of creating an object. There are actually much more significant differences between a hash and an object but, for this tutorial, know that it stores values in name:value pairs separated by commas and all of it enclosed by curly braces. The last element must not be followed by a comma.</p>
<p>Next, when we use openDialog we pass params into the dialog.</p>
<p>After the user has clicked OK, the value of params.out is checked. The condition will be true unless the value is still null or by some strange miracle taken on a value like &#8216;false&#8217;.</p>
<p>Now, let&#8217;s look at the code behind the dialog itself</p>
<pre>function codeDialogOnLoad() {

    // Use the arguments passed to us by the caller
    document.getElementById("codeImage").setAttribute('src',
            window.arguments[0].in);
}

// Called once if and only if the user clicks OK
function onOK() {
    window.arguments[0].out = document.getElementById("codeValue").value;
    return true;
}</pre>
<p>Ok, so what do we have here? well, when the dialog first loads we call codeDialogOnLoad which then sets the image source on the dialog based on the value passed in params.in. Note that it is now referred to as window.arguments[0].in.</p>
<p>When the user presses OK, the value of an input box, codeValue, is assigned to params.out (a.k.a. window.arguments[0].out).</p>
<p>Last thing to look at is the codeDialog.xul</p>
<pre>&lt;?xml version="1.0"?&gt;
&lt;?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?&gt;
&lt;dialog
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  id="codeDialog"
  title="Enter the Code"
  ondialogaccept="return onOK();"
  buttonlabelaccept="Submit"
  onload="codeDialogOnLoad();"
  persist="screenX screenY width height"&gt;

	&lt;script type="application/x-javascript" src="chrome://piratequesting/content/codeDialog.js"/&gt;
	&lt;vbox&gt;
		&lt;label value="Enter the code shown below" /&gt;
		&lt;image id="codeImage" /&gt;
		&lt;textbox width="50" id="codeValue" /&gt;
	&lt;/vbox&gt;
&lt;/dialog&gt;</pre>
<p>As you can see, codeDialog.xul is very simple and contains only three elements inside a vbox. This is really one of the simplest examples you could use and was chosen to illustrate how to simply and easily pass information into and out of a dialog</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ashita.org/passing-parameters-into-a-created-dialog-and-retrieving-them-on-exit/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Pirate Questing</title>
		<link>http://www.ashita.org/pirate-questing/</link>
		<comments>http://www.ashita.org/pirate-questing/#comments</comments>
		<pubDate>Wed, 13 Aug 2008 05:24:58 +0000</pubDate>
		<dc:creator>Jonathan Fingland</dc:creator>
				<category><![CDATA[DOM]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Pirate Questing]]></category>
		<category><![CDATA[XUL]]></category>

		<guid isPermaLink="false">http://ashita.org/?p=3</guid>
		<description><![CDATA[Pirate Questing is a firefox addon thatI&#8217;ve been devloping for a while. It&#8217;s available at AMO here
I&#8217;ll mostly be using this space to document interesting solutions I come across.
]]></description>
			<content:encoded><![CDATA[<p>Pirate Questing is a firefox addon thatI&#8217;ve been devloping for a while. It&#8217;s available at AMO <a href="http://addons.mozilla.org/en-US/firefox/addon/6801">here</a></p>
<p>I&#8217;ll mostly be using this space to document interesting solutions I come across.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ashita.org/pirate-questing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
