[Pkg-mozext-commits] [SCM] lightweight RSS and Atom feed reader for Iceweasel/Firefox branch, master, updated. upstream/1.4.5-24-gc398483

Andrea Veri av at src.gnome.org
Tue Aug 16 21:08:37 UTC 2011


The following commit has been merged in the master branch:
commit c398483aa1b170d9a82b09ae7bb9df3547323543
Author: Andrea Veri <av at src.gnome.org>
Date:   Tue Aug 16 23:08:02 2011 +0200

    New upstream release. (sage-1.4.12)

diff --git a/chrome.manifest b/chrome.manifest
index 8b33504..de00f35 100644
--- a/chrome.manifest
+++ b/chrome.manifest
@@ -1,3 +1,42 @@
+interfaces components/sageIDateFormatter.xpt
+interfaces components/sageIDateParser.xpt
+interfaces components/sageIFeed.xpt
+interfaces components/sageIFeedItem.xpt
+interfaces components/sageIFeedItemEnclosure.xpt
+interfaces components/sageIFeedParser.xpt
+interfaces components/sageIFeedParserFactory.xpt
+interfaces components/sageILogger.xpt
+
+component {B315E9D2-7300-4C70-A2A3-B2BE328813AE} components/sageAtom03Parser.js
+contract @sage.mozdev.org/sage/atom03parser;1 {B315E9D2-7300-4C70-A2A3-B2BE328813AE}
+
+component {C48B7642-5C72-45C7-AA88-1AB550B0AB9B} components/sageAtomParser.js
+contract @sage.mozdev.org/sage/atomparser;1 {C48B7642-5C72-45C7-AA88-1AB550B0AB9B}
+
+component {E5D15F67-C113-497D-AC6F-40296FEAD058} components/sageDateFormatter.js
+contract @sage.mozdev.org/sage/dateformatter;1 {E5D15F67-C113-497D-AC6F-40296FEAD058}
+
+component {16077429-E1DE-434C-BCDB-D0AD6BE13AEE} components/sageDateParser.js
+contract @sage.mozdev.org/sage/dateparser;1 {16077429-E1DE-434C-BCDB-D0AD6BE13AEE}
+
+component {E7E23F87-1993-4D04-8663-25946CB58153} components/sageFeed.js
+contract @sage.mozdev.org/sage/feed;1 {E7E23F87-1993-4D04-8663-25946CB58153}
+
+component {BBAEEF86-C8B5-4B03-A250-2C433CB8EC5B} components/sageFeedItem.js
+contract @sage.mozdev.org/sage/feeditem;1 {BBAEEF86-C8B5-4B03-A250-2C433CB8EC5B}
+
+component {E32528AA-6F29-4763-AB2A-2182CA6649FA} components/sageFeedItemEnclosure.js
+contract @sage.mozdev.org/sage/feeditemenclosure;1 {E32528AA-6F29-4763-AB2A-2182CA6649FA}
+
+component {9C464FBF-590A-4BD0-A0F9-D72A44A505BB} components/sageFeedParserFactory.js
+contract @sage.mozdev.org/sage/feedparserfactory;1 {9C464FBF-590A-4BD0-A0F9-D72A44A505BB}
+
+component {DA518D7D-6C3E-4507-99E2-6102EB3BD031} components/sageLogger.js
+contract @sage.mozdev.org/sage/logger;1 {DA518D7D-6C3E-4507-99E2-6102EB3BD031}
+
+component {69F7C75E-9A41-4E7B-9E6F-620158D2DE09} components/sageRSSParser.js
+contract @sage.mozdev.org/sage/rssparser;1 {69F7C75E-9A41-4E7B-9E6F-620158D2DE09}
+
 content	sage	jar:chrome/sage.jar!/content/
 
 skin	sage	classic/1.0		jar:chrome/sage.jar!/skin/classic/
diff --git a/chrome/sage.jar!/content/commonfunc.js b/chrome/sage.jar!/content/commonfunc.js
index ceda23c..2caa1f4 100644
--- a/chrome/sage.jar!/content/commonfunc.js
+++ b/chrome/sage.jar!/content/commonfunc.js
@@ -38,7 +38,7 @@
 
 var SageUtils = {
 
-	VERSION : "1.4.2",
+	VERSION : "1.4.12",
 	
 	USER_AGENT : "Mozilla/5.0 (Sage)",
 
@@ -68,8 +68,6 @@ var SageUtils = {
 	ANNO_SIG : "sage/signature", // string
 	ANNO_LASTVISIT : "sage/lastvisit", // Epoch seconds
 	ANNO_FEEDTITLE : "sage/feedtitle", // string
-		
-	ORGANIZER_QUERY_ANNO : "PlacesOrganizer/OrganizerQuery", // Not Sage-specific
 
 	NC_NS: "http://home.netscape.com/NC-rdf#",
 	XUL_NS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
@@ -78,7 +76,8 @@ var SageUtils = {
 	STATUS_NO_UPDATE: "no-updated",
 	STATUS_UNKNOWN: "unknown",
 	STATUS_ERROR: "error",
-	STATUS_CHECKING: "checking",
+	
+	SAGE_ROOT_TITLE : "Sage Feeds",
 
 	convertCharCodeFrom : function(aString, aCharCode) {
 		var UConvID = "@mozilla.org/intl/scriptableunicodeconverter";
@@ -195,16 +194,9 @@ var SageUtils = {
 			if (results[0] != folderId) {
 				annotationService.removeItemAnnotation(results[0], this.ANNO_ROOT);
 				annotationService.setItemAnnotation(folderId, this.ANNO_ROOT, "Sage Root Folder", 0, annotationService.EXPIRE_NEVER);
-				try {
-					annotationService.removeItemAnnotation(results[0], this.ORGANIZER_QUERY_ANNO);
-				} catch (e) {
-					// The annotation didn't exist
-				}
-				annotationService.setItemAnnotation(folderId, this.ORGANIZER_QUERY_ANNO, "SageRoot", 0, annotationService.EXPIRE_NEVER);
 			}
 		} else if (results.length == 0) {
 			annotationService.setItemAnnotation(folderId, this.ANNO_ROOT, "Sage Root Folder", 0, annotationService.EXPIRE_NEVER);
-			annotationService.setItemAnnotation(folderId, this.ORGANIZER_QUERY_ANNO, "SageRoot", 0, annotationService.EXPIRE_NEVER);
 		} else if (results.length > 1) {
 			throw "Multiple root folders found";
 		}
diff --git a/chrome/sage.jar!/content/createhtml.js b/chrome/sage.jar!/content/createhtml.js
deleted file mode 100644
index d0be5bf..0000000
--- a/chrome/sage.jar!/content/createhtml.js
+++ /dev/null
@@ -1,364 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Sage.
- *
- * The Initial Developer of the Original Code is
- * Peter Andrews <petea at jhu.edu>.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Peter Andrews <petea at jhu.edu>
- * Erik Arvidsson <erik at eae.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-var CreateHTML = {
-
-	HTML_SOURCE: SageUtils.loadText("chrome://sage/content/res/template-html.txt"),
-	ITEM_SOURCE: SageUtils.loadText("chrome://sage/content/res/template-item.txt"),
-	DEFAULT_CSS: "chrome://sage/content/res/sage.css",
-
-	_tabbed: false,
-
-	set tabbed(aValue) { this._tabbed = aValue },
-
-	openHTML: function(feed) {
-		if (!feed) {
-			return;
-		}
-
-		try {
-			var htmlURL = this.createHTML(feed);
-			if (this._tabbed) {
-				getContentBrowser().addTab(htmlURL);
-			} else {
-				getContentBrowser().loadURI(htmlURL);
-			}
-		} catch(e) {
-			dump(e);
-		}
-	},
-
-	createHTML: function(feed) {
-		var tmpFile = this.getSpecialDir("UChrm");
-		tmpFile.appendRelativePath("sage.html");
-
-		var ioService = Components.classes["@mozilla.org/network/io-service;1"]
-							.getService(Components.interfaces.nsIIOService);
-		var xmlFilePath = ioService.newFileURI(tmpFile).spec;
-
-		if (tmpFile.exists()) {
-			tmpFile.remove(true);
-		}
-		tmpFile.create(tmpFile.NORMAL_FILE_TYPE, 0666);
-
-		var stream = Components.classes['@mozilla.org/network/file-output-stream;1']
-						.createInstance(Components.interfaces.nsIFileOutputStream);
-		stream.init(tmpFile, 2, 0x200, false); // open as "write only"
-
-		var content = this.createHTMLSource(feed);
-		stream.write(content, content.length);
-		stream.flush();
-		stream.close();
-
-		return xmlFilePath;
-	},
-
-	getUserCssURL: function() {
-		var userCssEnable = SageUtils.getSagePrefValue(SageUtils.PREF_USER_CSS_ENABLE);
-		var userCssPath = SageUtils.getSagePrefValue(SageUtils.PREF_USER_CSS_PATH);
-		if (!userCssEnable || !userCssPath) {
-			return null;
-		}
-		var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
-		var tmpFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
-		try {
-			tmpFile.initWithPath(userCssPath);
-			var cssUrl = ioService.newFileURI(tmpFile);
-			var contentType = ioService.newChannelFromURI(cssUrl).contentType;
-			return cssUrl.spec;
-		} catch(e) {
-			return null;
-		}
-	},
-
-	formatFileSize:	function (n) {
-		if (n > 1048576) {
-			return Math.round(n / 1048576) + " MB";
-		} else if (n > 1024) {
-			return Math.round(n / 1024) + " KB";
-		} else {
-			return n + " B";
-		}
-	},
-
-	createHTMLSource: function(feed) {
-		return this.HTML_SOURCE.replace(/\*\*[^\*]+\*\*/g, function (s) {
-				return CreateHTML.replaceFeedKeyword(feed, s);
-			});
-
-		return SageUtils.convertCharCodeFrom(
-			this.HTML_SOURCE.replace(/\*\*[^\*]+\*\*/g, function (s) {
-				return CreateHTML.replaceFeedKeyword(feed, s);
-			}),
-			"UTF-8");
-	},
-
-	replaceFeedKeyword:	function (feed, s) {
-		var footer;
-
-		switch (s) {
-			case "**TITLE**":
-				// Entity encode is correct here - we shouldn't let any HTML through
-				return this.entityEncode(SageUtils.htmlToText(feed.getTitle()));
-
-			case "**LINK**":
-				// Partial fix for CVE-2009-4102
-				// Clean href is correct here - there is HTML in what gets returned by getLink, but it's all Sage generated and anything which can break out of it should be escaped
-				return this.entityEncode(this.cleanHref(feed.getLink()));
-				break;
-
-			case "**AUTHOR**":
-				if (feed.hasAuthor()) {
-					// Entity encode is correct - we don't want any HTML back from this
-					return "<div class=\"feed-author\">" + this.entityEncode(SageUtils.htmlToText(feed.getAuthor())) + "</div>";
-				}
-				return "";
-
-			case "**DESCRIPTION**":
-				if (feed.hasDescription()) {
-					var allowEContent = SageUtils.getSagePrefValue(SageUtils.PREF_ALLOW_ENCODED_CONTENT);
-					var ds;
-					if (allowEContent) {
-						var sanitizer = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML);
-						var fragment = sanitizer.parseFragment(feed.getDescription(), false, null, document.documentElement);
-						ds = new XMLSerializer().serializeToString(fragment);
-					} else {
-						ds = this.entityEncode(SageUtils.htmlToText(feed.getDescription()));
-					}
-					return ds;
-				}
-				return "";
-
-/*
-			case "**LOGOLINK**":
-				// need to be sure we can't escape the href="..." part this gets enclosed in
-				return feed.getLogo().link;
-
-			case "**LOGOALT**":
-				// need to be sure we can't escape the alt="..."
-				return feed.getLogo().alt;
-
-			case "**COPYRIGHT**":
-				return feed.getFooter().copyright;
-
-			case "**GENERATOR**":
-				return feed.getFooter().generator;
-
-			case "**EDITOR**":
-				var editor = "";
-				footer = feed.getFooter();
-				if (footer.editor) {
-					editor = "<a href=\"mailto:" + footer.editor + "\">Editor</a>";
-					if (footer.webmaster) {
-						editor += ", ";
-					}
-				}
-				return editor;
-
-			case "**WEBMASTER**":
-				footer = feed.getFooter();
-				if (footer.webmaster) {
-					return "<a href=\"mailto:" + footer.webmaster + "\">" +
-						strRes.GetStringFromName("feed_summary_webmaster") +
-						"</a>";
-				}
-				return "";
-*/
-			case "**ITEMS**":
-				// Correct - getItemsHtml is already escaped/quoted internally
-				return this.getItemsHtml(feed);
-		}
-
-		return s;
-	},
-
-	getItemsHtml:	function (feed) {
-		var feedItemOrder = SageUtils.getSagePrefValue(SageUtils.PREF_FEED_ITEM_ORDER);
-		switch (feedItemOrder) {
-			case "chrono": feed.setSort(feed.SORT_CHRONO); break;
-			case "source": feed.setSort(feed.SORT_SOURCE); break;
-		}
-		var sb = [];
-		for (var i = 0; i < feed.getItemCount(); i++) {
-			// Correct - already quoted/escaped
-			sb.push(this.getItemHtml(feed, feed.getItem(i), i));
-		}
-		return sb.join("");
-	},
-
-	getItemHtml:	function (feed, item, i) {
-		return  this.ITEM_SOURCE.replace(/\*\*[^\*]+\*\*/g, function (s) {
-			return CreateHTML.replaceFeedItemKeyword(feed, item, i, s);
-		});
-	},
-
-	replaceFeedItemKeyword:	function (feed, item, i, s) {
-		switch (s) {
-			case "**NUMBER**":
-				return i + 1;
-
-			case "**LINK**":
-				// Partial fix for CVE-2009-4102
-				// Correct - be careful of breaking out of the href="..." though
-				return this.entityEncode(this.cleanHref(item.getLink()));
-
-			case "**TITLE**":
-				if (item.hasTitle()) {
-					// correct - this doesn't let any HTML through
-					return this.entityEncode(SageUtils.htmlToText(item.getTitle()));
-				} else if (item.getTitle()) {
-					// correct - no HTML through
-					return this.entityEncode(SageUtils.htmlToText(item.getTitle()));
-				} else {
-					// No HTML here eitther, but it's not input anyway
-					return this.entityEncode(strRes.GetStringFromName("feed_item_no_title"));
-				}
-
-			case "**AUTHOR**":
-				if (item.hasAuthor()) {
-					// Correct - no HTML permitted here
-					return "<div class=\"item-author\">" + this.entityEncode(SageUtils.htmlToText(item.getAuthor())) + "</div>";
-				}
-				return "";
-
-			case "**DESCRIPTION**":
-				if (item.hasContent()) {
-					var allowEContent = SageUtils.getSagePrefValue(SageUtils.PREF_ALLOW_ENCODED_CONTENT);
-					var ds;
-					if (allowEContent) {
-						var sanitizer = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML);
-						var fragment = sanitizer.parseFragment(item.getContent(), false, null, document.documentElement);
-						ds = new XMLSerializer().serializeToString(fragment);
-					} else {
-						ds = this.entityEncode(SageUtils.htmlToText(item.getContent()));
-					}
-					return "<div class=\"item-desc\">" + ds + "</div>";
-				}
-				return "";
-
-			case "**PUBDATE**":
-				if (item.hasPubDate()) {
-					var twelveHourClock = SageUtils.getSagePrefValue(SageUtils.PREF_TWELVE_HOUR_CLOCK);
-					var formatter = Components.classes["@sage.mozdev.org/sage/dateformatter;1"].getService(Components.interfaces.sageIDateFormatter);
-					formatter.setFormat(formatter.FORMAT_LONG, formatter.ABBREVIATED_FALSE, twelveHourClock ? formatter.CLOCK_12HOUR : formatter.CLOCK_24HOUR);
-					var dateString = this.entityEncode(formatter.formatDate(item.getPubDate()));
-					return "<div class=\"item-pubDate\">" + dateString + "</div>";
-				}
-				return "";
-
-			case "**ENCLOSURE**":
-				if (item.hasEnclosure()) {
-					// ??
-					var enc = item.getEnclosure();
-					function createDescriptionFromURL(url) {
-						var array = url.split("/");
-						var description = "";
-						if (array.length > 0) {
-							description = array[array.length - 1];
-						}
-						return description;
-					}
-					return "<div class=\"item-enclosure\">" +
-						"<a href=\"" + this.entityEncode(enc.getLink()) + "\" title=\"" +
-						createDescriptionFromURL(this.entityEncode(enc.getLink())) +
-						"\"><img src=\"" +
-							(enc.hasMimeType() ?
-								"moz-icon://dummy?size=16&contentType=" + this.entityEncode(enc.getMimeType()) :
-								"chrome://sage/skin/enclosure.png") +
-						"\">" + strRes.GetStringFromName("feed_summary_enclosure") + "</a>" +
-						(enc.hasLength() ? " (" + this.formatFileSize(enc.getLength()) + ")" : "") +
-						"</div>";
-				}
-				return "";
-		}
-
-		return s;
-	},
-
-	getSpecialDir: function(aProp) {
-		var dirService = Components.classes['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties);
-		return dirService.get(aProp, Components.interfaces.nsILocalFile);
-	},
-
-	// Partial fix for CVE-2009-4102
-	cleanHref: function(aUrl) {
-		// We only want to allow http, ftp, news and mailto before :
-		var ltype = aUrl.split(":")[0];
-		// Make it greedy so there cannot be any surplus :'s left after filtering
-		// This was an error in my original patch
-		aUrl = aUrl.replace(/^.*:/, "");
-		switch(ltype.toLowerCase()) {
-			case "http":
-				aUrl = ltype + ":" + aUrl;
-				break;
-			case "nntp":
-				aUrl = ltype + ":" + aUrl;
-				break;
-			case "mailto":
-				aUrl = ltype + ":" + aUrl;
-				break;
-			case "ftp":
-				aUrl = ltype + ":" + aUrl;
-				break;
-		}
-		// Did I miss some safe ones?
-		return aUrl
-	},
-
-	entityEncode: function(aStr) {
-
-		function replacechar(match) {
-			if (match == "<")
-				return "<";
-			else if (match == ">")
-				return ">";
-			else if (match == "\"")
-				return """;
-			else if (match == "'")
-				return "'";
-			else if (match == "&")
-				return "&";
-			else
-				return "";
-		}
-
-		var re = /[<>"'&]/g;
-		return aStr.replace(re, function(m) { return replacechar(m); });
-	}
-
-};
diff --git a/chrome/sage.jar!/content/feedsummary.html b/chrome/sage.jar!/content/feedsummary.html
index 0f5d222..20e7375 100644
--- a/chrome/sage.jar!/content/feedsummary.html
+++ b/chrome/sage.jar!/content/feedsummary.html
@@ -47,7 +47,6 @@
 <link rel="stylesheet" href="chrome://sage/skin/feedsummary.css" type="text/css">
 <script type="application/x-javascript" src="chrome://sage/content/commonfunc.js"></script>
 <script type="application/x-javascript" src="chrome://sage/content/feedloader.js"></script>
-<script type="application/x-javascript" src="chrome://sage/content/createhtml.js"></script>
 <script type="application/x-javascript" src="chrome://sage/content/feedsummary.js"></script>
 </head>
 <body>
diff --git a/chrome/sage.jar!/content/feedsummary.js b/chrome/sage.jar!/content/feedsummary.js
index 46eb34c..5b2d2d0 100644
--- a/chrome/sage.jar!/content/feedsummary.js
+++ b/chrome/sage.jar!/content/feedsummary.js
@@ -47,6 +47,8 @@ var strBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"].ge
 var strRes = strBundleService.createBundle("chrome://sage/locale/sage.properties");
 
 var feedSummary = {
+		
+	DEFAULT_CSS: "chrome://sage/skin/feedsummary-content.css",
 
 	_uri:			null,
 	_feedLoader:	null,
@@ -78,7 +80,7 @@ var feedSummary = {
 
 		// if the sidebar has the same uri then we should reuse that
 		var ssb = this.findSageSideBar();
-		if (ssb != null && ssb.feedLoader.uri == this.uri)
+		if (ssb != null && ssb.feedLoader && ssb.feedLoader.uri == this.uri)
 		{
 			this._ownFeedLoader = false;
 			return this._feedLoader = ssb.feedLoader;
@@ -189,10 +191,10 @@ var feedSummary = {
 		// This should be handled in a better way.
 		if (aFeed.getFeedURI() == feedSummary.uri)
 		{
-			feedSummary.displayFeed(aFeed);
-
 			document.body.removeAttribute("loading");
 			document.body.removeAttribute("error");
+			
+			feedSummary.displayFeed(aFeed);
 		}
 		else
 		{
@@ -233,7 +235,131 @@ var feedSummary = {
 	displayFeed:	function (feed)
 	{
 		document.title = feed.getTitle() + " - Sage";
-		document.body.innerHTML = CreateHTML.createHTMLSource(feed);
+		
+		var allowEContent = SageUtils.getSagePrefValue(SageUtils.PREF_ALLOW_ENCODED_CONTENT);
+		var twelveHourClock = SageUtils.getSagePrefValue(SageUtils.PREF_TWELVE_HOUR_CLOCK);
+		var parser = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML);
+		var dateFormatter = Cc["@sage.mozdev.org/sage/dateformatter;1"].getService(Ci.sageIDateFormatter);
+		dateFormatter.setFormat(dateFormatter.FORMAT_LONG, dateFormatter.ABBREVIATED_FALSE, twelveHourClock ? dateFormatter.CLOCK_12HOUR : dateFormatter.CLOCK_24HOUR);
+		var content;
+		
+		var header = document.createElement("div");
+		header.setAttribute("id", "rss-header");
+		var h1 = document.createElement("h1");
+		h1.setAttribute("id", "rss-title");
+		var feedLink = document.createElement("a");
+		feedLink.setAttribute("id", "rss-link");
+		this.setURIAttributeSafe(feedLink, "href", feed.getLink());
+		feedLink.appendChild(document.createTextNode(feed.getTitle()));
+		h1.appendChild(feedLink);
+		header.appendChild(h1);
+		if (feed.hasDescription()) {
+			var p = document.createElement("p");
+			p.setAttribute("id", "rss-desc");
+			if (allowEContent) {
+				content = feed.getDescription();
+			} else {
+				content = SageUtils.htmlToText(feed.getDescription());
+			}
+			p.appendChild(this.sanitizeFragment(parser.parseFragment(content, false, null, document.documentElement), feed.getLink()));
+			header.appendChild(p);
+		}
+		document.body.appendChild(header);
+		
+		function renderFeedItem(feedItem, index) {
+			var item = document.createElement("div");
+			item.setAttribute("class", "item");
+			var h2 = document.createElement("h2");
+			h2.setAttribute("class", "item-title");
+			var span = document.createElement("span");
+			span.setAttribute("class", "item-ordinal");
+			span.appendChild(document.createTextNode((index + 1) + ". " ));
+			h2.appendChild(span);
+			var a = document.createElement("a");
+			this.setURIAttributeSafe(a, "href", feedItem.getLink());
+			function getFeedItemTitle(feedItem) {
+				if (feedItem.hasTitle()) {
+					return feedItem.getTitle();
+				} else if (feedItem.getTitle()) {
+					return feedItem.getTitle();
+				} else {
+					return strRes.GetStringFromName("feed_item_no_title");
+				}
+			}
+			a.appendChild(document.createTextNode(getFeedItemTitle(feedItem)));
+			h2.appendChild(a);
+			item.appendChild(h2);
+			if (feedItem.hasContent()) {
+				var description = document.createElement("div");
+				description.setAttribute("class", "item-desc");
+				if (allowEContent) {
+					content = feedItem.getContent();
+				} else {
+					content = SageUtils.htmlToText(feedItem.getContent());
+				}
+				description.appendChild(this.sanitizeFragment(parser.parseFragment(content, false, null, document.documentElement), feedItem.hasBaseURI() ? feedItem.getBaseURI() : feed.getLink()));
+				item.appendChild(description);
+			}
+			if (feedItem.hasEnclosure()) {
+				var enc = feedItem.getEnclosure();
+				var enclosure = document.createElement("div");
+				enclosure.setAttribute("class", "item-enclosure");
+				a = document.createElement("a");
+				this.setURIAttributeSafe(a, "href", enc.getLink());
+				function createDescriptionFromURL(url) {
+					var array = url.split("/");
+					var description = "";
+					if (array.length > 0) {
+						description = array[array.length - 1];
+					}
+					return description;
+				}
+				a.setAttribute("title", createDescriptionFromURL(enc.getLink()));
+				var img = document.createElement("img");
+				img.setAttribute("src", enc.hasMimeType() ? "moz-icon://dummy?size=16&contentType=" + enc.getMimeType() : "chrome://sage/skin/enclosure.png");
+				a.appendChild(img);
+				a.appendChild(document.createTextNode(strRes.GetStringFromName("feed_summary_enclosure")));
+				enclosure.appendChild(a);
+				function formatFileSize(n) {
+					if (n > 1048576) {
+						return Math.round(n / 1048576) + " MB";
+					} else if (n > 1024) {
+						return Math.round(n / 1024) + " KB";
+					} else {
+						return n + " B";
+					}
+				}
+				enclosure.appendChild(document.createTextNode(enc.hasLength() ? " (" + formatFileSize(enc.getLength()) + ")" : ""));
+				item.appendChild(enclosure);
+			}
+			if (feedItem.hasPubDate()) {
+				var pubdate = document.createElement("div");
+				pubdate.setAttribute("class", "item-pubDate");
+				pubdate.appendChild(document.createTextNode(dateFormatter.formatDate(feedItem.getPubDate())));
+				item.appendChild(pubdate);
+			}
+			if (feedItem.hasAuthor()) {
+				var author = document.createElement("div");
+				author.setAttribute("class", "item-author");
+				author.appendChild(document.createTextNode(feedItem.getAuthor()));
+				item.appendChild(author);
+			}
+			document.body.appendChild(item);
+		}
+		
+		if (feed.getItemCount() > 0) {
+			var items = document.createElement("div");
+			items.setAttribute("class", "items");
+			var feedItemOrder = SageUtils.getSagePrefValue(SageUtils.PREF_FEED_ITEM_ORDER);
+			switch (feedItemOrder) {
+				case "chrono": feed.setSort(feed.SORT_CHRONO); break;
+				case "source": feed.setSort(feed.SORT_SOURCE); break;
+			}
+			for (var i = 0; i < feed.getItemCount(); i++) {
+				renderFeedItem.call(this, feed.getItem(i), i);
+			}
+			document.body.appendChild(items);
+		}
 	},
 
 	findSageSideBar:	function ()
@@ -346,8 +472,88 @@ var feedSummary = {
 		}
 
 		return false;
+	},
+		
+	setURIAttributeSafe: function(element, attribute, uri) {
+		var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+		var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+		
+		const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL;
+		
+		try {
+			feedURI = ios.newURI(this.uri, null, null);
+			attrURI = ios.newURI(uri, null, null);
+			secman.checkLoadURI(feedURI, attrURI, flags);
+			element.setAttribute(attribute, attrURI.spec);
+		} catch (e) { }
+	},
+	
+	decorateAnchorElement: function(element) {
+		var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+		var url;
+		
+		if (element.hasAttribute("href")) {
+			try {
+				url = ios.newURI(element.getAttribute("href"), null, null).QueryInterface(Ci.nsIURL);
+				if (url.host.match(/(.*\.)?amazon\.(com|[a-z]{2}(\.[a-z]{2})?)$/i)) {
+					if (!(url.path.match(/-[0-9]{2}$/i) || url.path == "/" || url.query.match(/(^|&)tag=/i))) {
+						url.query += (url.query == "" ? "" : "&") + "tag=sagerss-20";
+						element.setAttribute("href", url.spec);
+					}
+				}
+			} catch(e) { }
+		}
+	},
+	
+	sanitizeFragment: function(fragment, baseURI) {
+		var walker = document.createTreeWalker(fragment, Ci.nsIDOMNodeFilter.SHOW_ELEMENT, null, false);
+		var elem, attrName, attr, value;
+		
+		const URIFixup = Components.classes["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
+		
+		const URI_ATTR_LIST = ["action", "href", "src", "longdesc", "usemap", "cite", "background"];
+		
+		while (walker.nextNode()) {
+			elem = walker.currentNode;
+			for each (attrName in URI_ATTR_LIST) {
+				attr = elem.attributes.getNamedItem(attrName);
+				if (attr) {
+					value = elem.getAttribute(attrName);
+					elem.removeAttribute(attrName);
+					if (baseURI) {
+						try {
+							value = URIFixup.createFixupURI(baseURI, Ci.nsIURIFixup.FIXUP_FLAG_NONE).resolve(value);
+						} catch (e) { }
+					}
+					this.setURIAttributeSafe(elem, attrName, value);
+				}
+			}
+			if (elem.tagName.toLowerCase() == "a") {
+				this.decorateAnchorElement(elem);
+			}
+		}
+		
+		return fragment;
+	},
+	
+	getUserCssURL: function() {
+		var userCssEnable = SageUtils.getSagePrefValue(SageUtils.PREF_USER_CSS_ENABLE);
+		var userCssPath = SageUtils.getSagePrefValue(SageUtils.PREF_USER_CSS_PATH);
+		if (!userCssEnable || !userCssPath) {
+			return null;
+		}
+		var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+		var tmpFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+		try {
+			tmpFile.initWithPath(userCssPath);
+			var cssUrl = ioService.newFileURI(tmpFile);
+			var contentType = ioService.newChannelFromURI(cssUrl).contentType;
+			return cssUrl.spec;
+		} catch(e) {
+			return null;
+		}
 	}
-
+	
 };
 
 window.addEventListener("load", function (e)
@@ -361,15 +567,10 @@ window.addEventListener("unload", function (e)
 }, false);
 
 
-// Cannot use DOM to set base
-if (feedSummary.uri) {
-	document.write("<base href=\"" + feedSummary.uri + "\">");
-}
-
 // set feed style sheet before content loads
-var cssUrl	= CreateHTML.getUserCssURL();
+var cssUrl	= feedSummary.getUserCssURL();
 if (!cssUrl) {
-	cssUrl = CreateHTML.DEFAULT_CSS;
+	cssUrl = feedSummary.DEFAULT_CSS;
 }
 var headEl = document.getElementsByTagName("head")[0];
 var linkEl = document.createElement("link");
diff --git a/chrome/sage.jar!/content/opml/opml.js b/chrome/sage.jar!/content/opml/opml.js
index 837e557..285dbdf 100644
--- a/chrome/sage.jar!/content/opml/opml.js
+++ b/chrome/sage.jar!/content/opml/opml.js
@@ -44,6 +44,7 @@ var winMain, txtImportFile, txtExportFile;
 var strRes;
 
 var bookmarksService = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
+var livemarkService = Cc["@mozilla.org/browser/livemark-service;2"].getService(Ci.nsILivemarkService);
 
 var g_errorMesage = "";
 
@@ -134,7 +135,7 @@ function importOPML() {
 		return false;
 	}
 
-	opmlDoc = httpReq.responseXML;
+	var opmlDoc = httpReq.responseXML;
 	if(opmlDoc.documentElement.localName != "opml") {
 		reportError(strRes.getString("opml_import_badfile"));
 		return false;
@@ -151,16 +152,19 @@ function importOPML() {
 
 	var treeWalker = opmlDoc.createTreeWalker(opmlDoc, NodeFilter.SHOW_ELEMENT, outlineFilter, true);
 
-	while(treeWalker.nextNode()) {
+	function isFolder(node) {
+		return !(node.hasAttribute('xmlUrl') || node.hasAttribute('xmlurl'));
+	}
+	
+	while (treeWalker.nextNode()) {
 		var cNode = treeWalker.currentNode;
 		var pNode = cNode.parentNode;
 		var parentFolderId = ("_folderId" in pNode) ? pNode._folderId : rootFolderId;
-		if(cNode.hasChildNodes()) {
+		if (isFolder(cNode)) {
 			var title = cNode.getAttribute("title");
-			if(!title) title = cNode.getAttribute("text");
-			if(!title) title = "folder";
+			if (!title) title = cNode.getAttribute("text");
+			if (!title) title = "folder";
 			cNode._folderId = bookmarksService.createFolder(parentFolderId, title, bookmarksService.DEFAULT_INDEX);
-			
 		} else {
 			createRssItem(cNode, parentFolderId);
 		}
@@ -260,13 +264,18 @@ function createOpmlOutline(aOpmlDoc, aResultNode) {
 
 	var outlineNode = aOpmlDoc.createElement("outline");
 
-	if (type == bmsvc.TYPE_FOLDER) {
+	var childNode, childNodeType;
+	if (type == bmsvc.TYPE_FOLDER && !livemarkService.isLivemark(aResultNode.itemId)) {
 		outlineNode.setAttribute("text", title);
 
 		aResultNode.QueryInterface(Components.interfaces.nsINavHistoryContainerResultNode);
 		aResultNode.containerOpen = true;
 		for (var i = 0; i < aResultNode.childCount; i ++) {
-			outlineNode.appendChild(createOpmlOutline(aOpmlDoc, aResultNode.getChild(i)));
+			childNode = aResultNode.getChild(i);
+			childNodeType = bmsvc.getItemType(childNode.itemId);
+			if (childNodeType == bmsvc.TYPE_FOLDER || childNodeType == bmsvc.TYPE_BOOKMARK) {
+				outlineNode.appendChild(createOpmlOutline(aOpmlDoc, childNode));				
+			}
 		}
 		aResultNode.containerOpen = false;
 	} else if (type == bmsvc.TYPE_BOOKMARK) {
@@ -275,6 +284,11 @@ function createOpmlOutline(aOpmlDoc, aResultNode) {
 		outlineNode.setAttribute("text", title);
 		outlineNode.setAttribute("title", title);
 		outlineNode.setAttribute("xmlUrl", url);
+	} else if (livemarkService.isLivemark(aResultNode.itemId)) {
+		outlineNode.setAttribute("type", "rss");
+		outlineNode.setAttribute("text", title);
+		outlineNode.setAttribute("title", title);
+		outlineNode.setAttribute("xmlUrl", livemarkService.getFeedURI(aResultNode.itemId).spec);
 	}
 	return outlineNode;
 }
@@ -328,8 +342,8 @@ function reportError(s)
 
 // Page initializers
 function onPageStartShow() {
-	winMain.getButton("cancel").disabled = false;
-	winMain.canAdvance = true;
+	document.documentElement.getButton("cancel").disabled = false;
+	document.documentElement.canAdvance = true;
 }
 
 function onPageImportShow() {
diff --git a/chrome/sage.jar!/content/res/template-html.txt b/chrome/sage.jar!/content/res/template-html.txt
deleted file mode 100644
index 76eecc4..0000000
--- a/chrome/sage.jar!/content/res/template-html.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-<div id="rss-header">
-	<h1 id="rss-title"><a id="rss-link" href="**LINK**">**TITLE**</a></h1>
-	<p id="rss-desc">**DESCRIPTION**</p>
-</div>
-
-<div class="items">
-	**ITEMS**
-</div>
diff --git a/chrome/sage.jar!/content/res/template-item.txt b/chrome/sage.jar!/content/res/template-item.txt
deleted file mode 100644
index 7b679a0..0000000
--- a/chrome/sage.jar!/content/res/template-item.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-	<div class="item">
-		<h2 class="item-title">
-			<span class="item-ordinal">**NUMBER**.</span>
-			<a href="**LINK**">**TITLE**</a>
-		</h2>
-		**DESCRIPTION**
-		**ENCLOSURE**
-		**PUBDATE**
-		**AUTHOR**
-	</div>
diff --git a/chrome/sage.jar!/content/sage.js b/chrome/sage.jar!/content/sage.js
index 5a43905..b839470 100644
--- a/chrome/sage.jar!/content/sage.js
+++ b/chrome/sage.jar!/content/sage.js
@@ -58,12 +58,13 @@ var annotationObserver = {
 	onPageAnnotationSet : function(aURI, aName) { },
 	
 	onItemAnnotationSet : function(aItemId, aName) {
+		logger.debug("onItemAnnotationSet: " + aName);
 		switch (aName) {
 			case SageUtils.ANNO_ROOT:
-				bookmarksTree.place = "place:queryType=1&folder=" + aItemId;
+				bookmarksTree.place = sidebarController.bookmarksTreeQueryURI(aItemId);
 				break;
 			case SageUtils.ANNO_STATUS:
-				bookmarksTree.getResultView().invalidateAll();
+				bookmarksTree.view.invalidateContainer(bookmarksTree.getResultNode ? /* Firefox 3.x */ bookmarksTree.getResultNode() : bookmarksTree.result.root);
 				break;
 		}
 	},
@@ -91,7 +92,7 @@ var sidebarController = {
 		
 		try {
 			var sageRootFolderId = SageUtils.getSageRootFolderId();
-			bookmarksTree.place = "place:queryType=1&folder=" + sageRootFolderId;
+			bookmarksTree.place = this.bookmarksTreeQueryURI(sageRootFolderId);
 		} catch(e) {
 			logger.error(e);
 		}
@@ -122,7 +123,7 @@ var sidebarController = {
 		feedLoader.addListener("abort", onFeedAbort);
 		
 		linkVisitor.init();
-	
+		
 		logger.info("sidebar open");
 	},
 	
@@ -134,37 +135,54 @@ var sidebarController = {
 		linkVisitor.uninit();
 		PlacesUtils.annotations.removeObserver(annotationObserver);
 				
-		SidebarUtils.clearURLFromStatusBar();
+		SidebarUtils.setMouseoverURL ? SidebarUtils.setMouseoverURL("") : /* FF 3.x */ SidebarUtils.clearURLFromStatusBar();
 	
 		logger.info("sidebar closed");
 	},
 	
 	_extendPlacesTreeView : function() {
+		
 		PlacesTreeView.prototype.getCellPropertiesBase = PlacesTreeView.prototype.getCellProperties;
 		PlacesTreeView.prototype.getCellProperties =
 		function sage_getCellProperties(aRow, aColumn, aProperties) {
-			var properties = this._visibleElements[aRow].properties;
+			if (this._ensureValidRow) { // FF 3.x
+				this._ensureValidRow(aRow);
+			}
+			
+			var rows;
+			if (this._rows) { // FF 4
+				rows = this._rows;
+			} else { // FF 3.x
+				rows = this._visibleElements;
+			}
+			
+			var cached = false;
+			if (rows[aRow].properties !== undefined) {
+				if (rows[aRow].properties) {
+					cached = true;
+				}
+			}
 			
 			var propertiesBase = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
 			this.getCellPropertiesBase(aRow, aColumn, propertiesBase);
-			var proptery;
+			var property;
 			for (var i = 0; i < propertiesBase.Count(); i++) {
 				property = propertiesBase.GetElementAt(i);
 				if (property != this._getAtomFor("livemark")) {
 					aProperties.AppendElement(propertiesBase.GetElementAt(i));
 				}
 			}
-					
+			
 			if (aColumn.id != "title")
 			  return;
 			
-			if (!properties) {
-				properties = [];
-				var node = this._visibleElements[aRow].node || this._visibleElements[aRow];
+			if (!cached) {
+				var properties = [];
+				var node = rows[aRow].node || rows[aRow]; // FF 3.0 - 3.5 / 3.6 - 4.0
 				var nodeType = node.type;
 				var itemId = node.itemId;
 				if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI) {
-					if (!PlacesUtils.nodeIsLivemarkContainer(node.parent)) {
+					if (!PlacesUtils.nodeIsLivemarkItem(node)) {
 						try {
 							var state = PlacesUtils.annotations.getItemAnnotation(itemId, SageUtils.ANNO_STATUS);
 							properties.push(this._getAtomFor("sage_state_" + state));
@@ -179,8 +197,10 @@ var sidebarController = {
 					}
 				}
 				for (var i = 0; i < properties.length; i++) {
+					if (rows[aRow].properties !== undefined) {
+						rows[aRow].properties.push(properties[i]);
+					}
 					aProperties.AppendElement(properties[i]);
-					this._visibleElements[aRow].properties.push(properties[i]);
 				}
 			}
 		}
@@ -188,10 +208,21 @@ var sidebarController = {
 		PlacesTreeView.prototype.isContainerBase = PlacesTreeView.prototype.isContainer;
 		PlacesTreeView.prototype.isContainer =
 		function sage_isContainer(aRow) {
+			if (this._ensureValidRow) { // FF 3.x
+				this._ensureValidRow(aRow);
+			}
+			
+			var rows;
+			if (this._rows) { // FF 4
+				rows = this._rows;
+			} else { // FF 3.x
+				rows = this._visibleElements;
+			}
+			
 			var baseValue = this.isContainerBase(aRow);
  			if (baseValue) {
- 				var node = this._visibleElements[aRow].node || this._visibleElements[aRow];
- 				if (PlacesUtils.annotations.itemHasAnnotation(node.itemId, LMANNO_FEEDURI)) {
+ 				var node = rows[aRow].node || rows[aRow]; // FF 3.0 - 3.5 / 3.6 - 4.0
+ 				if (PlacesUtils.nodeIsLivemarkContainer(node)) {
  					return false;
  				} else {
  					return true;
@@ -203,8 +234,17 @@ var sidebarController = {
 		
 		PlacesTreeView.prototype.getImageSrc =
 		function sage_getImageSrc(aRow, aColumn) {
+			if (this._ensureValidRow) { // FF 3.x
+				this._ensureValidRow(aRow);
+			}
+			
 			return "";
-		}	
+		}
+		
+	},
+	
+	bookmarksTreeQueryURI : function(rootFolderId) {
+		return "place:queryType=1&excludeItemIfParentHasAnnotation=livemark%2FfeedURI&folder=" + rootFolderId;
 	},
 		
 	bookmarksTreeClick : function(aEvent) {
@@ -290,7 +330,7 @@ var sidebarController = {
 	},
 	
 	openOrganizeFeedsDialog : function() {
-		var query = "SageRoot";
+		var query = "BookmarksMenu";
 		var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
 		var organizer = wm.getMostRecentWindow("Places:Organizer");
 		if (!organizer) {
@@ -304,9 +344,18 @@ var sidebarController = {
 	},
 	
 	openAboutDialog : function() {
-		var extensionManager = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
-		openDialog("chrome://mozapps/content/extensions/about.xul", "",
-			"chrome,centerscreen,modal", "urn:mozilla:item:{a6ca9b3b-5e52-4f47-85d8-cca35bb57596}", extensionManager.datasource);
+		var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
+		var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"].getService(Components.interfaces.nsIVersionComparator);
+		if (versionChecker.compare(appInfo.version, "4.0a") >= 0) {
+			Components.utils.import("resource://gre/modules/AddonManager.jsm");
+			AddonManager.getAddonByID("{a6ca9b3b-5e52-4f47-85d8-cca35bb57596}", function(aAddon) {
+				openDialog("chrome://mozapps/content/extensions/about.xul", "", "chrome,centerscreen,modal", aAddon);
+			});
+		} else {
+			var extensionManager = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
+			openDialog("chrome://mozapps/content/extensions/about.xul", "",
+				"chrome,centerscreen,modal", "urn:mozilla:item:{a6ca9b3b-5e52-4f47-85d8-cca35bb57596}", extensionManager.datasource);
+		}
 	}
 
 }
@@ -532,6 +581,22 @@ function newURI(spec) {
  * @returns	void
  */
 function openURI(aURI, aEvent) {
+	var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+	var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);		
+	
+	var sidebarURI = null;
+	try {
+		sidebarURI = ios.newURI("chrome://sage/content/sage.xul", null, null);
+	} catch (e) { }
+	
+	var sidebarPrincipal = secman.getCodebasePrincipal(sidebarURI);
+	const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL;
+	try {
+		secman.checkLoadURIStrWithPrincipal(sidebarPrincipal, aURI, flags);
+	} catch (e) {
+		return;
+	}
+
 	switch (getWindowType(aEvent)) {
 		case "tab":
 			getContentBrowser().addTab(aURI);
@@ -659,11 +724,7 @@ var linkVisitor = {
 	onURIChanged: function (aURI) {
 		if (aURI in this._items) {
 			var listItem = this._items[aURI];
-			if (this.getVisited(aURI)) {
-				listItem.setAttribute("visited", "true");
-			} else {
-				listItem.removeAttribute("visited");
-			}
+			listItem.setAttribute("visited", "true");
 		}
 	}
 };
diff --git a/chrome/sage.jar!/content/sage.xul b/chrome/sage.jar!/content/sage.xul
index 2eb49c4..00f95a2 100644
--- a/chrome/sage.jar!/content/sage.xul
+++ b/chrome/sage.jar!/content/sage.xul
@@ -65,7 +65,7 @@
 	
 	<commandset id="placesCommands"/>
 	<commandset id="editMenuCommands"/>
-	<popup id="placesContext"/>
+	<menupopup id="placesContext"/>
 	
 	<keyset>
 		<key id="key_toggleread" key="&toggleReadState.command.key;" command="cmd_toggleread"/>
@@ -141,7 +141,7 @@
 				onkeypress="SidebarUtils.handleTreeKeyPress(event);"
 				onclick="sidebarController.bookmarksTreeClick(event);"
 				onmousemove="SidebarUtils.handleTreeMouseMove(event);"
-				onmouseout="SidebarUtils.clearURLFromStatusBar();">
+				onmouseout="SidebarUtils.setMouseoverURL ? SidebarUtils.setMouseoverURL('') : /* FF 3.x */ SidebarUtils.clearURLFromStatusBar();">
 				<treecols>
 					<treecol id="title" flex="1" primary="true" hideheader="true"/>
 				</treecols>
@@ -156,7 +156,7 @@
 				</hbox>
 			</sidebarheader>
 			<toolbox class="plain">
-				<toolbar id="itemListToolbar" iconsize="small" mode="icons">
+				<toolbar id="itemListToolbar">
 					<toolbarbutton id="markAsReadButton" class="toolbarbutton-1" command="cmd_markasread"/>
 					<toolbarbutton id="markAsUnreadButton" class="toolbarbutton-1" command="cmd_markasunread"/>
 					<toolbarbutton id="markAllAsReadButton" class="toolbarbutton-1" command="cmd_markallasread"/>
@@ -175,15 +175,7 @@
 
 	<popupset>
 		<tooltip id="rssItemToolTip" onpopupshowing="populateToolTip(event)" noautohide="true"/>
-		<popup id="popSearchEngine" datasources="rdf:null" ref="urn:rrp:searchengine:root">
-			<template>
-				<menuitem type="checkbox" uri="..." value="..."
-					oncommand="FeedSearch.popSearchEngineClick(event);"
-					label="rdf:http://sage.mozdev.org/#name"
-					image="rdf:http://sage.mozdev.org/#icon"/>
-			</template>
-		</popup>
-		<popup id="rssItemListBoxContextMenu"
+		<menupopup id="rssItemListBoxContextMenu"
 			onpopupshowing="updateItemContextMenu();">
 			<menuitem id="rssOpenItem" default="true" oncommand="openListItem();"
 				label="&openLinkInWindow.label;" accesskey="&openLinkInWindow.accesskey;"/>
@@ -196,7 +188,7 @@
 			<menuitem id="rssMarkAsUnreadItem" command="cmd_markasunread"/>
 			<menuitem id="rssMarkAllAsReadItem" command="cmd_markallasread"/>
 			<menuitem id="rssMarkAllAsUnreadItem" command="cmd_markallasunread"/>
-		</popup>
+		</menupopup>
 	</popupset>
 
 </page>
diff --git a/chrome/sage.jar!/content/sage_main.js b/chrome/sage.jar!/content/sage_main.js
index 85844b6..5a6dac8 100644
--- a/chrome/sage.jar!/content/sage_main.js
+++ b/chrome/sage.jar!/content/sage_main.js
@@ -37,7 +37,7 @@
  * ***** END LICENSE BLOCK ***** */
 
 var sageOverlay = {
-	
+
 	logger : null,
 	needsRestart : null,
 
@@ -69,7 +69,7 @@ var sageOverlay = {
 			prefService.setBoolPref("browser.sessionstore.resume_session_once", true);
 			Cc["@mozilla.org/toolkit/app-startup;1"]
 				.getService(Ci.nsIAppStartup)
-				.quit(Ci.nsIAppStartup.eForceQuit | Ci.nsIAppStartup.eRestart);
+				.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
 		}
 		this.logger.info("initialized");
 	},
@@ -78,11 +78,11 @@ var sageOverlay = {
 	
 	createRoot : function() {
 		var bookmarksService = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
-		var folderId = bookmarksService.createFolder(bookmarksService.bookmarksMenuFolder, "Sage Feeds", bookmarksService.DEFAULT_INDEX);
+		var folderId = bookmarksService.createFolder(bookmarksService.bookmarksMenuFolder, SageUtils.SAGE_ROOT_TITLE, bookmarksService.DEFAULT_INDEX);
 		SageUtils.setSageRootFolderId(folderId);
 		SageUtils.addFeed("BBC News | News Front Page | World Edition", "http://news.bbc.co.uk/rss/newsonline_world_edition/front_page/rss091.xml");
 		SageUtils.addFeed("Yahoo! News - Sports", "http://rss.news.yahoo.com/rss/sports");
-		SageUtils.addFeed("Sage Project News", "http://sage.mozdev.org/rss.xml");
+		SageUtils.addFeed("Sage", "http://sagerss.com/feed/");
 	},
 	
 	getVersion : function() {
@@ -247,6 +247,18 @@ var sageOverlay = {
 				// add content handler
 				self.addContentHandler();
 				self.needsRestart = true;
+			},
+			
+			"1.4.6" : function() {
+				var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
+				var as = Cc["@mozilla.org/browser/annotation-service;1"].getService(Ci.nsIAnnotationService);
+				var root = SageUtils.getSageRootFolderId(); 
+				try {
+					as.removeItemAnnotation(root, "PlacesOrganizer/OrganizerQuery");
+				} catch (e) { }
+				if (bs.getItemTitle(root) == null) {
+					bs.setItemTitle(root, SageUtils.SAGE_ROOT_TITLE);
+				}
 			}
 			
 		}
diff --git a/chrome/sage.jar!/content/settings/settings.js b/chrome/sage.jar!/content/settings/settings.js
index 4de5f41..9534ec8 100644
--- a/chrome/sage.jar!/content/settings/settings.js
+++ b/chrome/sage.jar!/content/settings/settings.js
@@ -88,7 +88,7 @@ function init() {
 
 	setDisabled();
 
-	setTimeout(fillSelectFolderMenupopup, 0);
+	fillSelectFolderMenupopup();
 }
 
 function accept() {
@@ -124,7 +124,7 @@ function browseCss() {
 	}
 }
 
-function fillSelectFolderMenupopup () {
+function fillSelectFolderMenupopup() {
 	var popup = document.getElementById("select-folder");
 
 	// clearing the old menupopup
diff --git a/chrome/sage.jar!/content/updatechecker.js b/chrome/sage.jar!/content/updatechecker.js
index 847b883..833d7f6 100644
--- a/chrome/sage.jar!/content/updatechecker.js
+++ b/chrome/sage.jar!/content/updatechecker.js
@@ -112,9 +112,10 @@ var UpdateChecker = {
 	},
 
 	done: function() {
-		if(this.checking) {
+		if (this.checking) {
 			this.httpReq.abort();
 			this.setStatusFlag(this.lastItemId, SageUtils.STATUS_NO_UPDATE);
+			this.logger.info("aborted check in progress")
 		}
 	},
 
@@ -146,7 +147,6 @@ var UpdateChecker = {
 			this.httpReq.setRequestHeader("User-Agent", SageUtils.USER_AGENT);
 			this.httpReq.overrideMimeType("application/xml");
 			this.httpReq.send(null);
-			this.setStatusFlag(this.lastItemId, SageUtils.STATUS_CHECKING);
 			this.onCheck(name, url);
 		} catch(e) {
 				// FAILURE
@@ -233,14 +233,15 @@ var UpdateChecker = {
 		if (this.checkList.length == 0) {
 			this.checking = false;
 			this.onChecked(name, url);
-			return;
+			this.logger.info("finished checking feeds");
 		} else {
+			this.logger.info(this.checkList.length + " feed(s) remaining to be checked");
 			this.check();
 		}
 	},
 
 	setStatusFlag: function(aItemId, aState) {
-		logger.info("setting " + SageUtils.ANNO_STATUS + " => " + aState + " on item " + aItemId);
+		logger.debug("setting " + SageUtils.ANNO_STATUS + " => " + aState + " on item " + aItemId);
 		PlacesUtils.annotations.setItemAnnotation(aItemId, SageUtils.ANNO_STATUS, aState, 0, PlacesUtils.annotations.EXPIRE_NEVER);
 	},
 	
diff --git a/chrome/sage.jar!/content/res/sage.css b/chrome/sage.jar!/skin/classic/feedsummary-content.css
similarity index 86%
rename from chrome/sage.jar!/content/res/sage.css
rename to chrome/sage.jar!/skin/classic/feedsummary-content.css
index 34cb722..3a0da23 100644
--- a/chrome/sage.jar!/content/res/sage.css
+++ b/chrome/sage.jar!/skin/classic/feedsummary-content.css
@@ -13,12 +13,13 @@ body {
 	font-family: Verdana, Sans-Serif;
 }
 
-a:link		{ text-decoration: none; color: #436976; font-weight: bold; }
-a:visited	{ text-decoration: none;  color: #537986; }
-a:hover		{ text-decoration: underline; }
-a:active	{ text-decoration: none; }
-
-p.item-desc a:link { color: #029; }
+a {
+	color: #436976;
+	font-weight: bold;
+	text-decoration: none;
+}
+a:visited { color: #7399a6; }
+a:hover { text-decoration: underline; }
 
 #rss-header {
 	background-color: #dee7ec;
@@ -33,6 +34,7 @@ p.item-desc a:link { color: #029; }
 	margin: 0px;
 	padding: 0px;
 }
+#rss-title a:visited { color: #436976; }
 
 #rss-desc {
 	font-size: small;
@@ -122,7 +124,7 @@ div.item-author {
 	float: right;
 }
 
-img {
+img, video {
 	max-width: 100%;
 	height: auto;
 }
diff --git a/chrome/sage.jar!/skin/classic/feedsummary.css b/chrome/sage.jar!/skin/classic/feedsummary.css
index f5c3f64..e776fd8 100644
--- a/chrome/sage.jar!/skin/classic/feedsummary.css
+++ b/chrome/sage.jar!/skin/classic/feedsummary.css
@@ -3,7 +3,8 @@
 	margin:	3em auto 0.5em auto;
 	width:	200px;
 	font:	Icon;
-	white-space:	nowrap
+	white-space:	nowrap;
+	display: none;
 }
 
 #loading-progress-meter {
@@ -12,6 +13,10 @@
 	display:	none;
 }
 
+[loading] #loading-text {
+	display:	block;
+}
+
 [loading] #loading-progress-meter {
 	display:	block;
 }
diff --git a/chrome/sage.jar!/skin/classic/sage-button-mac.css b/chrome/sage.jar!/skin/classic/sage-button-mac.css
index 532ca84..e07d61c 100644
--- a/chrome/sage.jar!/skin/classic/sage-button-mac.css
+++ b/chrome/sage.jar!/skin/classic/sage-button-mac.css
@@ -42,6 +42,16 @@
 	list-style-image: url("chrome://sage/skin/sage_leaf_32.png");
 }
 
+#sage-button[checked="true"] {
+	list-style-image: url("chrome://sage/skin/sage_leaf_32-selected.png");
+	background-color: transparent;
+	border-color: transparent;
+}
+
 toolbar[iconsize="small"] #sage-button {
 	list-style-image: url("chrome://sage/skin/sage_leaf_24.png");
 }
+
+toolbar[iconsize="small"] #sage-button[checked="true"] {
+	list-style-image: url("chrome://sage/skin/sage_leaf_24-selected.png");
+}
diff --git a/chrome/sage.jar!/skin/classic/sage-mac.css b/chrome/sage.jar!/skin/classic/sage-mac.css
index c8b99d1..c7f0c16 100644
--- a/chrome/sage.jar!/skin/classic/sage-mac.css
+++ b/chrome/sage.jar!/skin/classic/sage-mac.css
@@ -38,12 +38,15 @@
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
+#sagePanel {
+	-moz-appearance: none;
+}
+
 sidebarheader {
 	background-color: #eee;
 }
 
 #bookmarks-view {
-	background-color: #fff;
 	border-top: 1px solid #aaa !important;
 }
 
@@ -52,8 +55,14 @@ sidebarheader {
 	background-color: #ddd;
 	border-top: 1px solid #aaa;
 	border-bottom: 1px solid #aaa;
+	-moz-appearance: none;
 }
 
 #feedListToolbar toolbarbutton {
 	padding: 5px 3px 2px 3px;
 }
+
+#sage-splitter {
+	border-top: 1px solid ThreeDLightShadow;
+	background-color: #eee;
+}
diff --git a/chrome/sage.jar!/skin/classic/sage.css b/chrome/sage.jar!/skin/classic/sage.css
index 81541f0..2786e24 100644
--- a/chrome/sage.jar!/skin/classic/sage.css
+++ b/chrome/sage.jar!/skin/classic/sage.css
@@ -115,10 +115,6 @@
   -moz-image-region: auto !important;
   list-style-image: url("chrome://sage/skin/icon/unknown.png") !important;
 }
-#bookmarks-view treechildren::-moz-tree-image(leaf, sage_state_checking) {
-  -moz-image-region: auto !important;
-  list-style-image: url("chrome://global/skin/icons/loading_16.png") !important;
-}
 #bookmarks-view treechildren::-moz-tree-image(leaf, sage_state_error) {
   -moz-image-region: auto !important;
   list-style-image: url("chrome://sage/skin/icon/error.gif") !important;
diff --git a/chrome/sage.jar!/skin/classic/sage_leaf_24-selected.png b/chrome/sage.jar!/skin/classic/sage_leaf_24-selected.png
new file mode 100644
index 0000000..c0a68ed
Binary files /dev/null and b/chrome/sage.jar!/skin/classic/sage_leaf_24-selected.png differ
diff --git a/chrome/sage.jar!/skin/classic/sage_leaf_32-selected.png b/chrome/sage.jar!/skin/classic/sage_leaf_32-selected.png
new file mode 100644
index 0000000..0155c0a
Binary files /dev/null and b/chrome/sage.jar!/skin/classic/sage_leaf_32-selected.png differ
diff --git a/components/sageAtom03Parser.js b/components/sageAtom03Parser.js
index 49b98d3..2d7afaa 100644
--- a/components/sageAtom03Parser.js
+++ b/components/sageAtom03Parser.js
@@ -36,9 +36,8 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{B315E9D2-7300-4C70-A2A3-B2BE328813AE}");
-const CLASS_NAME = "Sage Atom 0.3 Parser Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/atom03parser;1";
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 const sageIFeedParser = Components.interfaces.sageIFeedParser;
 
 /******************************************************************************
@@ -47,8 +46,13 @@ const sageIFeedParser = Components.interfaces.sageIFeedParser;
 function sageAtom03Parser() {};
 sageAtom03Parser.prototype = {
 
+	classDescription: "Sage Atom 0.3 Parser Component",
+	classID: Components.ID("{B315E9D2-7300-4C70-A2A3-B2BE328813AE}"),
+	contractID: "@sage.mozdev.org/sage/atom03parser;1",		
+
 	discover: function(feedDocument)
 	{
+	  feedDocument = feedDocument.QueryInterface(Components.interfaces.nsIDOMDocument);
 		var rootNode = feedDocument.documentElement;
 		if (rootNode.localName.toLowerCase() == "feed"  && rootNode.namespaceURI == "http://purl.org/atom/ns#") {
 			return true;
@@ -59,6 +63,7 @@ sageAtom03Parser.prototype = {
 
 	parse: function(feedDocument)
 	{
+	  feedDocument = feedDocument.QueryInterface(Components.interfaces.nsIDOMDocument);
 		var Feed = new Components.Constructor("@sage.mozdev.org/sage/feed;1", "sageIFeed", "init");
 		var FeedItem = new Components.Constructor("@sage.mozdev.org/sage/feeditem;1", "sageIFeedItem", "init");
 		var FeedItemEnclosure = new Components.Constructor("@sage.mozdev.org/sage/feeditemenclosure;1", "sageIFeedItemEnclosure", "init");
@@ -308,54 +313,12 @@ sageAtom03Parser.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageIFeedParser) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
-
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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);
-	},
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageIFeedParser])
 
-	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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
-
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageAtom03Parser()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageAtom03Parser]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageAtom03Parser]);
+}
diff --git a/components/sageAtomParser.js b/components/sageAtomParser.js
index 5fdb7e6..cc670bb 100644
--- a/components/sageAtomParser.js
+++ b/components/sageAtomParser.js
@@ -36,9 +36,8 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{C48B7642-5C72-45C7-AA88-1AB550B0AB9B}");
-const CLASS_NAME = "Sage Atom Parser Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/atomparser;1";
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 const sageIFeedParser = Components.interfaces.sageIFeedParser;
 
 /******************************************************************************
@@ -47,8 +46,13 @@ const sageIFeedParser = Components.interfaces.sageIFeedParser;
 function sageAtomParser() {};
 sageAtomParser.prototype = {
 
+	classDescription: "Sage Atom Parser Component",
+	classID: Components.ID("{C48B7642-5C72-45C7-AA88-1AB550B0AB9B}"),
+	contractID: "@sage.mozdev.org/sage/atomparser;1",
+
 	discover: function(feedDocument)
 	{
+	  feedDocument = feedDocument.QueryInterface(Components.interfaces.nsIDOMDocument);
 		var rootNode = feedDocument.documentElement;
 		if (rootNode.localName.toLowerCase() == "feed"  && rootNode.namespaceURI == "http://www.w3.org/2005/Atom") {
 			return true;
@@ -59,6 +63,7 @@ sageAtomParser.prototype = {
 
 	parse: function(feedDocument)
 	{
+	  feedDocument = feedDocument.QueryInterface(Components.interfaces.nsIDOMDocument);
 		var Feed = new Components.Constructor("@sage.mozdev.org/sage/feed;1", "sageIFeed", "init");
 		var FeedItem = new Components.Constructor("@sage.mozdev.org/sage/feeditem;1", "sageIFeedItem", "init");
 		var FeedItemEnclosure = new Components.Constructor("@sage.mozdev.org/sage/feeditemenclosure;1", "sageIFeedItemEnclosure", "init");
@@ -194,6 +199,10 @@ sageAtomParser.prototype = {
 			var authorNodes = entryNodes[i].getElementsByTagNameNS(ATOM_NS, "author");
 			if (authorNodes.length) {
 				node = authorNodes[0];
+				var nameNodes = node.getElementsByTagNameNS(ATOM_NS, "name");
+				if (nameNodes.length) {
+					node = nameNodes[0];
+				}
 				if (node.hasAttribute("type") && (node.getAttribute("type").toLowerCase() == "html" || node.getAttribute("type").toLowerCase() == "xhtml")) {
 					item.author = this._entityDecode(this._getInnerText(node));
 				} else {
@@ -306,54 +315,12 @@ sageAtomParser.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageIFeedParser) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageIFeedParser])
 
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
-
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageAtomParser()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageAtomParser]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageAtomParser]);
+}
diff --git a/components/sageDateFormatter.js b/components/sageDateFormatter.js
index b98e7c7..20c05b1 100644
--- a/components/sageDateFormatter.js
+++ b/components/sageDateFormatter.js
@@ -36,9 +36,8 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{E5D15F67-C113-497D-AC6F-40296FEAD058}");
-const CLASS_NAME = "Sage Date Formatter Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/dateformatter;1";
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 const sageIDateFormatter = Components.interfaces.sageIDateFormatter;
 
 /******************************************************************************
@@ -46,6 +45,11 @@ const sageIDateFormatter = Components.interfaces.sageIDateFormatter;
  ******************************************************************************/
 function sageDateFormatter() {};
 sageDateFormatter.prototype = {
+
+	classDescription: "Sage Date Formatter Component",
+	classID: Components.ID("{E5D15F67-C113-497D-AC6F-40296FEAD058}"),
+	contractID: "@sage.mozdev.org/sage/dateformatter;1",
+
 	_format: sageIDateFormatter.FORMAT_LONG,
 	_abbreviated: sageIDateFormatter.ABBREVIATED_TRUE,
 	_clock: sageIDateFormatter.CLOCK_12HOUR,
@@ -133,54 +137,12 @@ sageDateFormatter.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageIDateFormatter) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageIDateFormatter])
 
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
-
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageDateFormatter()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageDateFormatter]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageDateFormatter]);
+}
diff --git a/components/sageDateParser.js b/components/sageDateParser.js
index 79aaecc..edaeb24 100644
--- a/components/sageDateParser.js
+++ b/components/sageDateParser.js
@@ -36,9 +36,8 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{16077429-E1DE-434C-BCDB-D0AD6BE13AEE}");
-const CLASS_NAME = "Sage Date Parser Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/dateparser;1";
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 const sageIDateParser = Components.interfaces.sageIDateParser;
 
 /******************************************************************************
@@ -46,7 +45,11 @@ const sageIDateParser = Components.interfaces.sageIDateParser;
  ******************************************************************************/
 function sageDateParser() {};
 sageDateParser.prototype = {
-	
+
+	classDescription: "Sage Date Parser Component",
+	classID: Components.ID("{16077429-E1DE-434C-BCDB-D0AD6BE13AEE}"),
+	contractID: "@sage.mozdev.org/sage/dateparser;1",
+
 	parseRFC822: function(aDateString)
 	{
 		date_array = aDateString.split(" ");
@@ -134,21 +137,21 @@ sageDateParser.prototype = {
 	parseISO8601: function(aDateString)
 	{
 		// trims leading spaces
-		String.prototype.lTrim = function () {
-			return this.replace(/^\s+/gm, '');
+		function lTrim(string) {
+			return string.replace(/^\s+/gm, '');
 		}
 		
 		// trims trailing spaces
-		String.prototype.rTrim = function () {
-			return this.replace(/\s+$/gm, '');
+		function rTrim(string) {
+			return string.replace(/\s+$/gm, '');
 		}
 		
 		// trims spaces
-		String.prototype.trim = function () {
-			return this.rTrim().lTrim();
+		function trim(string) {
+			return lTrim(rTrim(string));
 		}
 	
-		var tmp = aDateString.trim(); // remove any leding and/or trailing spaces
+		var tmp = trim(aDateString); // remove any leding and/or trailing spaces
 		if (tmp.indexOf('T') > -1) {
 			tmp = tmp.split('T');
 		} else {
@@ -247,54 +250,12 @@ sageDateParser.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageIDateParser) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
-
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageIDateParser])
 
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageDateParser()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageDateParser]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageDateParser]);
+}
diff --git a/components/sageFeed.js b/components/sageFeed.js
index 31dfd75..bdf1336 100644
--- a/components/sageFeed.js
+++ b/components/sageFeed.js
@@ -36,9 +36,8 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{E7E23F87-1993-4D04-8663-25946CB58153}");
-const CLASS_NAME = "Sage Feed Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/feed;1";
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 const sageIFeed = Components.interfaces.sageIFeed;
 
 /******************************************************************************
@@ -46,6 +45,11 @@ const sageIFeed = Components.interfaces.sageIFeed;
  ******************************************************************************/
 function sageFeed() {};
 sageFeed.prototype = {
+
+	classDescription: "Sage Feed Component",
+	classID: Components.ID("{E7E23F87-1993-4D04-8663-25946CB58153}"),
+	contractID: "@sage.mozdev.org/sage/feed;1",
+
 	_title: null,
 	_link: null,
 	_description: null,
@@ -219,54 +223,12 @@ sageFeed.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageIFeed) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageIFeed])
 
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
-
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageFeed()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageFeed]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageFeed]);
+}
diff --git a/components/sageFeedItem.js b/components/sageFeedItem.js
index aeee08f..5bd4507 100644
--- a/components/sageFeedItem.js
+++ b/components/sageFeedItem.js
@@ -36,9 +36,8 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{BBAEEF86-C8B5-4B03-A250-2C433CB8EC5B}");
-const CLASS_NAME = "Sage Feed Item Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/feeditem;1";
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 const sageIFeedItem = Components.interfaces.sageIFeedItem;
 
 /******************************************************************************
@@ -46,6 +45,11 @@ const sageIFeedItem = Components.interfaces.sageIFeedItem;
  ******************************************************************************/
 function sageFeedItem() {};
 sageFeedItem.prototype = {
+
+	classDescription: "Sage Feed Item Component",
+	classID: Components.ID("{BBAEEF86-C8B5-4B03-A250-2C433CB8EC5B}"),
+	contractID: "@sage.mozdev.org/sage/feeditem;1",
+		
 	_title: null,
 	_link: null,
 	_author: null,
@@ -167,54 +171,12 @@ sageFeedItem.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageIFeedItem) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
-
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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);
-	},
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageIFeedItem])
 
-	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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
-
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageFeedItem()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageFeedItem]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageFeedItem]);
+}
diff --git a/components/sageFeedItemEnclosure.js b/components/sageFeedItemEnclosure.js
index e84eead..587c913 100644
--- a/components/sageFeedItemEnclosure.js
+++ b/components/sageFeedItemEnclosure.js
@@ -36,9 +36,8 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{E32528AA-6F29-4763-AB2A-2182CA6649FA}");
-const CLASS_NAME = "Sage Feed Item Enclosure Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/feeditemenclosure;1";
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 const sageIFeedItemEnclosure = Components.interfaces.sageIFeedItemEnclosure;
 
 /******************************************************************************
@@ -46,6 +45,11 @@ const sageIFeedItemEnclosure = Components.interfaces.sageIFeedItemEnclosure;
  ******************************************************************************/
 function sageFeedItemEnclosure() {};
 sageFeedItemEnclosure.prototype = {
+	
+	classDescription: "Sage Feed Item Enclosure Component",
+	classID: Components.ID("{E32528AA-6F29-4763-AB2A-2182CA6649FA}"),
+	contractID: "@sage.mozdev.org/sage/feeditemenclosure;1",
+	
 	_link: null,
 	_length: null,
 	_mimeType: null,
@@ -141,54 +145,12 @@ sageFeedItemEnclosure.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageIFeedItemEnclosure) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageIFeedItemEnclosure])
 
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
-
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageFeedItemEnclosure()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageFeedItemEnclosure]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageFeedItemEnclosure]);
+}
diff --git a/components/sageFeedParserFactory.js b/components/sageFeedParserFactory.js
index c4bd3f4..698282d 100644
--- a/components/sageFeedParserFactory.js
+++ b/components/sageFeedParserFactory.js
@@ -36,9 +36,8 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{9C464FBF-590A-4BD0-A0F9-D72A44A505BB}");
-const CLASS_NAME = "Sage Feed Parser Factory Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/feedparserfactory;1";
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 const sageIFeedParserFactory = Components.interfaces.sageIFeedParserFactory;
 
 /******************************************************************************
@@ -46,9 +45,14 @@ const sageIFeedParserFactory = Components.interfaces.sageIFeedParserFactory;
  ******************************************************************************/
 function sageFeedParserFactory() {};
 sageFeedParserFactory.prototype = {
+		
+	classDescription: "Sage Feed Parser Factory Component",
+	classID: Components.ID("{9C464FBF-590A-4BD0-A0F9-D72A44A505BB}"),
+	contractID: "@sage.mozdev.org/sage/feedparserfactory;1",
 
 	createFeedParser: function(feedDocument)
 	{
+	  feedDocument = feedDocument.QueryInterface(Components.interfaces.nsIDOMDocument);
 		if (!feedDocument) {
 			throw "Feed document empty";
 		}
@@ -79,54 +83,12 @@ sageFeedParserFactory.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageIFeedParserFactory) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
-
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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);
-	},
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageIFeedParserFactory])
 
-	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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
-
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageFeedParserFactory()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageFeedParserFactory]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageFeedParserFactory]);
+}
diff --git a/components/sageIFeedParser.xpt b/components/sageIFeedParser.xpt
index 8b6603e..8e998fb 100644
Binary files a/components/sageIFeedParser.xpt and b/components/sageIFeedParser.xpt differ
diff --git a/components/sageIFeedParserFactory.xpt b/components/sageIFeedParserFactory.xpt
index c35e18e..103d0f5 100644
Binary files a/components/sageIFeedParserFactory.xpt and b/components/sageIFeedParserFactory.xpt differ
diff --git a/components/sageLogger.js b/components/sageLogger.js
index c190e8b..c92b0f6 100644
--- a/components/sageLogger.js
+++ b/components/sageLogger.js
@@ -36,11 +36,9 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{DA518D7D-6C3E-4507-99E2-6102EB3BD031}");
-const CLASS_NAME = "Sage Logger Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/logger;1";
-const sageILogger = Components.interfaces.sageILogger;
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
+const sageILogger = Components.interfaces.sageILogger;
 const PREF_LOG_LEVEL = "extensions.sage.logLevel";
 
 /******************************************************************************
@@ -48,6 +46,11 @@ const PREF_LOG_LEVEL = "extensions.sage.logLevel";
  ******************************************************************************/
 function sageLogger() {};
 sageLogger.prototype = {
+	
+	classDescription: "Sage Logger Component",
+	classID: Components.ID("{DA518D7D-6C3E-4507-99E2-6102EB3BD031}"),
+	contractID: "@sage.mozdev.org/sage/logger;1",
+		
 	_level: sageILogger.LEVEL_WARN,
 	_consoleService: Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService),
 	
@@ -100,54 +103,12 @@ sageLogger.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageILogger) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageILogger])
 
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
-
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageLogger()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageLogger]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageLogger]);
+}
diff --git a/components/sageRSSParser.js b/components/sageRSSParser.js
index 7a2229c..303bfb9 100644
--- a/components/sageRSSParser.js
+++ b/components/sageRSSParser.js
@@ -36,9 +36,8 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CLASS_ID = Components.ID("{69F7C75E-9A41-4E7B-9E6F-620158D2DE09}");
-const CLASS_NAME = "Sage RSS Parser Component";
-const CONTRACT_ID = "@sage.mozdev.org/sage/rssparser;1";
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
 const sageIFeedParser = Components.interfaces.sageIFeedParser;
 
 /******************************************************************************
@@ -47,8 +46,13 @@ const sageIFeedParser = Components.interfaces.sageIFeedParser;
 function sageRSSParser() {};
 sageRSSParser.prototype = {
 
+	classDescription: "Sage RSS Parser Component",
+	classID: Components.ID("{69F7C75E-9A41-4E7B-9E6F-620158D2DE09}"),
+	contractID: "@sage.mozdev.org/sage/rssparser;1",
+
 	discover: function(feedDocument)
 	{
+	  feedDocument = feedDocument.QueryInterface(Components.interfaces.nsIDOMDocument);
 		var rootNodeName = feedDocument.documentElement.localName.toLowerCase();
 		if (rootNodeName == "rss" || rootNodeName == "rdf") {
 			return true;
@@ -59,6 +63,7 @@ sageRSSParser.prototype = {
 
 	parse: function(feedDocument)
 	{
+	  feedDocument = feedDocument.QueryInterface(Components.interfaces.nsIDOMDocument);
 		var Feed = new Components.Constructor("@sage.mozdev.org/sage/feed;1", "sageIFeed", "init");
 		var FeedItem = new Components.Constructor("@sage.mozdev.org/sage/feeditem;1", "sageIFeedItem", "init");
 		var FeedItemEnclosure = new Components.Constructor("@sage.mozdev.org/sage/feeditemenclosure;1", "sageIFeedItemEnclosure", "init");
@@ -268,54 +273,12 @@ sageRSSParser.prototype = {
 	},
 	
 	// nsISupports
-	QueryInterface: function(aIID)
-	{
-		if (!aIID.equals(Components.interfaces.sageIFeedParser) && !aIID.equals(Components.interfaces.nsISupports))
-			throw Components.results.NS_ERROR_NO_INTERFACE;
-		return this;
-	}
-};
-
-/******************************************************************************
- * XPCOM Functions for construction and registration
- ******************************************************************************/
-var Module = {
-	_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);
-	},
+	QueryInterface: XPCOMUtils.generateQI([Components.interfaces.sageIFeedParser])
 
-	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 Factory;
-		throw Components.results.NS_ERROR_NO_INTERFACE;
-	},
-
-	canUnload: function(aCompMgr) { return true; }
-};
-
-var Factory = {
-	createInstance: function(aOuter, aIID)
-	{
-		if (aOuter != null)
-			throw Components.results.NS_ERROR_NO_AGGREGATION;
-		return (new sageRSSParser()).QueryInterface(aIID);
-	}
 };
 
-function NSGetModule(aCompMgr, aFileSpec) { return Module; }
\ No newline at end of file
+if (XPCOMUtils.generateNSGetFactory) {
+    var NSGetFactory = XPCOMUtils.generateNSGetFactory([sageRSSParser]);
+} else {
+	var NSGetModule = XPCOMUtils.generateNSGetModule([sageRSSParser]);
+}
diff --git a/defaults/preferences/sage.js b/defaults/preferences/sage.js
index 8fae841..38bee95 100644
--- a/defaults/preferences/sage.js
+++ b/defaults/preferences/sage.js
@@ -39,8 +39,7 @@
 pref("extensions.sage.version", "");
 pref("extensions.sage.userCss.enable", false);
 pref("extensions.sage.userCss.path", "");
-pref("extensions.sage.allowEncodedContent", false);
-pref("extensions.sage.autoFeedTitle", true);
+pref("extensions.sage.allowEncodedContent", true);
 pref("extensions.sage.renderFeeds", true);
 pref("extensions.sage.twelveHourClock", true);
 pref("extensions.sage.feedItemOrder", "chrono");
diff --git a/install.rdf b/install.rdf
index dbcebc7..c78309d 100644
--- a/install.rdf
+++ b/install.rdf
@@ -42,7 +42,7 @@
 	<Description about="urn:mozilla:install-manifest">
 	
 		<em:id>{a6ca9b3b-5e52-4f47-85d8-cca35bb57596}</em:id>
-		<em:version>1.4.5</em:version>
+		<em:version>1.4.12</em:version>
 		<em:type>2</em:type>
 	
 		<em:name>Sage</em:name>
@@ -61,7 +61,7 @@
 			<Description>
 				<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
 				<em:minVersion>3.0b1</em:minVersion>
-				<em:maxVersion>3.6.*</em:maxVersion>
+				<em:maxVersion>5.*</em:maxVersion>
 			</Description>
 		</em:targetApplication>
 		

-- 
lightweight RSS and Atom feed reader for Iceweasel/Firefox



More information about the Pkg-mozext-commits mailing list