From 502cebd462856fc0ad2502e5ff355aa4be387af7 Mon Sep 17 00:00:00 2001 From: alta88 Date: Wed, 18 Apr 2012 09:00:46 -0600 Subject: [PATCH] Bug 721517 - Convert mailnews/extensions/newsblog/content to Services.jsm and mailServices.js. Also fix global scope. r=dbienvenu, neil --- mail/base/content/mailWindowOverlay.js | 14 +- mailnews/extensions/newsblog/content/Feed.js | 399 ++++--- .../extensions/newsblog/content/FeedItem.js | 324 +++--- .../newsblog/content/debug-utils.js | 48 - .../newsblog/content/feed-parser.js | 635 ++++++---- .../newsblog/content/feed-subscriptions.js | 83 +- .../newsblog/content/feed-subscriptions.xul | 2 - mailnews/extensions/newsblog/content/utils.js | 1026 ++++++++--------- mailnews/extensions/newsblog/jar.mn | 1 - mailnews/extensions/newsblog/js/newsblog.js | 39 +- suite/mailnews/mailWindowOverlay.js | 10 +- 11 files changed, 1323 insertions(+), 1258 deletions(-) delete mode 100644 mailnews/extensions/newsblog/content/debug-utils.js diff --git a/mail/base/content/mailWindowOverlay.js b/mail/base/content/mailWindowOverlay.js index dc30ffcc52..4d36add7b8 100644 --- a/mail/base/content/mailWindowOverlay.js +++ b/mail/base/content/mailWindowOverlay.js @@ -3249,16 +3249,14 @@ function FeedSetContentView(val) if (wintype == "mail:3pane") { // Get quickmode per feed pref from feeds.rdf var quickMode, targetRes; - var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Components.interfaces.mozIJSSubScriptLoader); - if (scriptLoader && typeof FZ_NS == 'undefined') - scriptLoader.loadSubScript("chrome://messenger-newsblog/content/utils.js"); + if (!("FeedUtils" in window)) + Services.scriptloader.loadSubScript("chrome://messenger-newsblog/content/utils.js"); try { - var targetRes = getParentTargetForChildResource( - gFolderDisplay.displayedFolder.URI, - FZ_QUICKMODE, - gFolderDisplay.displayedFolder.server); + var targetRes = FeedUtils.getParentTargetForChildResource( + gFolderDisplay.displayedFolder.URI, + FeedUtils.FZ_QUICKMODE, + gFolderDisplay.displayedFolder.server); } catch (ex) {}; diff --git a/mailnews/extensions/newsblog/content/Feed.js b/mailnews/extensions/newsblog/content/Feed.js index 9d4a6ad1cc..aa09602886 100755 --- a/mailnews/extensions/newsblog/content/Feed.js +++ b/mailnews/extensions/newsblog/content/Feed.js @@ -35,17 +35,10 @@ # # ***** END LICENSE BLOCK ***** */ - -// error codes used to inform the consumer about attempts to download a feed -const kNewsBlogSuccess = 0; -const kNewsBlogInvalidFeed = 1; // usually means there was an error trying to parse the feed... -const kNewsBlogRequestFailure = 2; // generic networking failure when trying to download the feed. -const kNewsBlogFeedIsBusy = 3; -const kNewsBlogNoNewItems = 4; // there are no new articles for this feed - -// Cache for all of the feeds currently being downloaded, indexed by URL, so the load event listener -// can access the Feed objects after it finishes downloading the feed. -var FeedCache = +// Cache for all of the feeds currently being downloaded, indexed by URL, +// so the load event listener can access the Feed objects after it finishes +// downloading the feed. +var FeedCache = { mFeeds: {}, @@ -56,7 +49,7 @@ var FeedCache = getFeed: function (aUrl) { - var index = this.normalizeHost(aUrl); + let index = this.normalizeHost(aUrl); if (index in this.mFeeds) return this.mFeeds[index]; return null; @@ -64,7 +57,7 @@ var FeedCache = removeFeed: function (aUrl) { - var index = this.normalizeHost(aUrl); + let index = this.normalizeHost(aUrl); if (index in this.mFeeds) delete this.mFeeds[index]; }, @@ -84,9 +77,9 @@ var FeedCache = } }; -function Feed(aResource, aRSSServer) +function Feed(aResource, aRSSServer) { - this.resource = aResource.QueryInterface(Components.interfaces.nsIRDFResource); + this.resource = aResource.QueryInterface(Ci.nsIRDFResource); this.server = aRSSServer; } @@ -101,30 +94,33 @@ Feed.prototype = items: new Array(), mFolder: null, mInvalidFeed: false, + mFeedType: null, get folder() { if (!this.mFolder) { - try + try { this.mFolder = this.server.rootMsgFolder.getChildNamed(this.name); - } catch (ex) {} + } + catch (ex) {} } return this.mFolder; }, - set folder (aFolder) + set folder (aFolder) { this.mFolder = aFolder; }, get name() { - var name = this.title || this.description || this.url; + let name = this.title || this.description || this.url; if (!name) - throw("couldn't compute feed name, as feed has no title, description, or URL."); + throw new Error("Feed.name: couldn't compute name, as feed has no title, " + + "description, or URL."); // Make sure the feed name doesn't have any line breaks, since we're going // to use it as the name of the folder in the filesystem. This may not @@ -139,44 +135,52 @@ Feed.prototype = return name; }, - download: function(aParseItems, aCallback) - { - this.downloadCallback = aCallback; // may be null + download: function(aParseItems, aCallback) + { + // May be null. + this.downloadCallback = aCallback; // Whether or not to parse items when downloading and parsing the feed. // Defaults to true, but setting to false is useful for obtaining // just the title of the feed when the user subscribes to it. this.parseItems = aParseItems == null ? true : aParseItems ? true : false; - // Before we do anything...make sure the url is an http url. This is just a sanity check - // so we don't try opening mailto urls, imap urls, etc. that the user may have tried to subscribe to - // as an rss feed.. - var uri = Components.classes["@mozilla.org/network/standard-url;1"]. - createInstance(Components.interfaces.nsIURI); + // Before we do anything, make sure the url is an http url. This is just + // a sanity check so we don't try opening mailto urls, imap urls, etc. that + // the user may have tried to subscribe to as an rss feed. + let uri = Cc["@mozilla.org/network/standard-url;1"]. + createInstance(Ci.nsIURI); uri.spec = this.url; - if (!(uri.schemeIs("http") || uri.schemeIs("https"))) + if (!FeedUtils.isValidScheme(uri)) { - this.onParseError(this); // simulate an invalid feed error + // Simulate an invalid feed error. + FeedUtils.log.debug("Feed.download: invalid protocol for - " + uri.spec); + this.onParseError(this); return; } - // Before we try to download the feed, make sure we aren't already processing the feed - // by looking up the url in our feed cache + // Before we try to download the feed, make sure we aren't already + // processing the feed by looking up the url in our feed cache. if (FeedCache.getFeed(this.url)) { if (this.downloadCallback) - this.downloadCallback.downloaded(this, kNewsBlogFeedIsBusy); - return ; // don't do anything, the feed is already in use + this.downloadCallback.downloaded(this, FeedUtils.kNewsBlogFeedIsBusy); + // Return, the feed is already in use. + return; } - this.request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(Components.interfaces.nsIXMLHttpRequest); - this.request.onprogress = this.onProgress; // must be set before calling .open + this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(Ci.nsIXMLHttpRequest); + // Must set onProgress before calling open. + this.request.onprogress = this.onProgress; this.request.open("GET", this.url, true); - var lastModified = this.lastModified; - if (lastModified) - this.request.setRequestHeader("If-Modified-Since", lastModified); + let lastModified = this.lastModified; + // Some servers, if sent If-Modified-Since, will send 304 if subsequently + // not sent If-Modified-Since, as in the case of an unsubscribe and new + // subscribe. Send 0 to force a download. + this.request.setRequestHeader("If-Modified-Since", + lastModified ? lastModified : 0); // Only order what you're going to eat... this.request.responseType = "document"; @@ -185,95 +189,103 @@ Feed.prototype = this.request.onerror = this.onDownloadError; FeedCache.putFeed(this); this.request.send(null); - }, + }, - onDownloaded: function(aEvent) + onDownloaded: function(aEvent) { - var request = aEvent.target; - var url = request.channel.originalURI.spec; - debug(url + " downloaded"); - if (request.status < 200 || request.status >= 300) + let request = aEvent.target; + let isHttp = /^http(s?)/.test(request.channel.originalURI.scheme); + let url = request.channel.originalURI.spec; + if (isHttp && (request.status < 200 || request.status >= 300)) { Feed.prototype.onDownloadError(aEvent); return; } - var feed = FeedCache.getFeed(url); + + FeedUtils.log.debug("Feed.onDownloaded: got a download - " + url); + let feed = FeedCache.getFeed(url); if (!feed) - throw("error after downloading " + url + ": couldn't retrieve feed from request"); + throw new Error("Feed.onDownloaded: error - couldn't retrieve feed " + + "from cache"); // If the server sends a Last-Modified header, store the property on the // feed so we can use it when making future requests, to avoid downloading // and parsing feeds that have not changed. - var lastModifiedHeader = request.getResponseHeader('Last-Modified'); + let lastModifiedHeader = request.getResponseHeader("Last-Modified"); if (lastModifiedHeader) feed.lastModified = lastModifiedHeader; // The download callback is called asynchronously when parse() is done. feed.parse(); - }, + }, onProgress: function(aEvent) { - var request = aEvent.target; - var url = request.channel.originalURI.spec; - var feed = FeedCache.getFeed(url); + let request = aEvent.target; + let url = request.channel.originalURI.spec; + let feed = FeedCache.getFeed(url); if (feed.downloadCallback) - feed.downloadCallback.onProgress(feed, aEvent.position, aEvent.totalSize); + feed.downloadCallback.onProgress(feed, aEvent.loaded, aEvent.total, + aEvent.lengthComputable); }, - onDownloadError: function(aEvent) + onDownloadError: function(aEvent) { - var request = aEvent.target; - var url = request.channel.originalURI.spec; - var feed = FeedCache.getFeed(url); + let request = aEvent.target; + let url = request.channel.originalURI.spec; + let feed = FeedCache.getFeed(url); if (feed.downloadCallback) { - var error = kNewsBlogRequestFailure; + let error = FeedUtils.kNewsBlogRequestFailure; try { if (request.status == 304) // If the http status code is 304, the feed has not been modified // since we last downloaded it and does not need to be parsed. - error = kNewsBlogNoNewItems; - } catch (ex) {} + error = FeedUtils.kNewsBlogNoNewItems; + } + catch (ex) {} + feed.downloadCallback.downloaded(feed, error); } - + FeedCache.removeFeed(url); }, - onParseError: function(aFeed) + onParseError: function(aFeed) { - if (aFeed) - { - aFeed.mInvalidFeed = true; - aFeed.lastModified = ""; + if (!aFeed) + return; - if (aFeed.downloadCallback) - aFeed.downloadCallback.downloaded(aFeed, kNewsBlogInvalidFeed); + aFeed.mInvalidFeed = true; + aFeed.lastModified = ""; - FeedCache.removeFeed(aFeed.url); - } + if (aFeed.downloadCallback) + aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogInvalidFeed); + + FeedCache.removeFeed(aFeed.url); }, get url() { - var ds = getSubscriptionsDS(this.server); - var url = ds.GetTarget(this.resource, DC_IDENTIFIER, true); + let ds = FeedUtils.getSubscriptionsDS(this.server); + let url = ds.GetTarget(this.resource, FeedUtils.DC_IDENTIFIER, true); if (url) - url = url.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; + url = url.QueryInterface(Ci.nsIRDFLiteral).Value; else url = this.resource.Value; + return url; }, get title() { - var ds = getSubscriptionsDS(this.server); - var title = ds.GetTarget(this.resource, DC_TITLE, true); + let ds = FeedUtils.getSubscriptionsDS(this.server); + let title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true); if (title) - title = title.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; + title = title.QueryInterface(Ci.nsIRDFLiteral).Value; + return title; }, @@ -282,69 +294,78 @@ Feed.prototype = if (!aNewTitle) return; - var ds = getSubscriptionsDS(this.server); - aNewTitle = rdf.GetLiteral(aNewTitle); - var old_title = ds.GetTarget(this.resource, DC_TITLE, true); + let ds = FeedUtils.getSubscriptionsDS(this.server); + aNewTitle = FeedUtils.rdf.GetLiteral(aNewTitle); + let old_title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true); if (old_title) - ds.Change(this.resource, DC_TITLE, old_title, aNewTitle); + ds.Change(this.resource, FeedUtils.DC_TITLE, old_title, aNewTitle); else - ds.Assert(this.resource, DC_TITLE, aNewTitle, true); + ds.Assert(this.resource, FeedUtils.DC_TITLE, aNewTitle, true); }, get lastModified() { - var ds = getSubscriptionsDS(this.server); - var lastModified = ds.GetTarget(this.resource, DC_LASTMODIFIED, true); + let ds = FeedUtils.getSubscriptionsDS(this.server); + let lastModified = ds.GetTarget(this.resource, + FeedUtils.DC_LASTMODIFIED, + true); if (lastModified) - lastModified = lastModified.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; + lastModified = lastModified.QueryInterface(Ci.nsIRDFLiteral).Value; return lastModified; }, set lastModified(aLastModified) { - var ds = getSubscriptionsDS(this.server); - aLastModified = rdf.GetLiteral(aLastModified); - var old_lastmodified = ds.GetTarget(this.resource, DC_LASTMODIFIED, true); + let ds = FeedUtils.getSubscriptionsDS(this.server); + aLastModified = FeedUtils.rdf.GetLiteral(aLastModified); + let old_lastmodified = ds.GetTarget(this.resource, + FeedUtils.DC_LASTMODIFIED, + true); if (old_lastmodified) - ds.Change(this.resource, DC_LASTMODIFIED, old_lastmodified, aLastModified); + ds.Change(this.resource, FeedUtils.DC_LASTMODIFIED, + old_lastmodified, aLastModified); else - ds.Assert(this.resource, DC_LASTMODIFIED, aLastModified, true); + ds.Assert(this.resource, FeedUtils.DC_LASTMODIFIED, aLastModified, true); - // do we need to flush every time this property changes? - ds = ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource); - ds.Flush(); + // Do we need to flush every time this property changes? + ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); }, get quickMode () { - var ds = getSubscriptionsDS(this.server); - var quickMode = ds.GetTarget(this.resource, FZ_QUICKMODE, true); - if (quickMode) + let ds = FeedUtils.getSubscriptionsDS(this.server); + let quickMode = ds.GetTarget(this.resource, FeedUtils.FZ_QUICKMODE, true); + if (quickMode) { - quickMode = quickMode.QueryInterface(Components.interfaces.nsIRDFLiteral); - quickMode = quickMode.Value; - quickMode = eval(quickMode); - } + quickMode = quickMode.QueryInterface(Ci.nsIRDFLiteral); + quickMode = quickMode.Value == "true" ? true : false; + } + return quickMode; }, - set quickMode (aNewQuickMode) + set quickMode (aNewQuickMode) { - var ds = getSubscriptionsDS(this.server); - aNewQuickMode = rdf.GetLiteral(aNewQuickMode); - var old_quickMode = ds.GetTarget(this.resource, FZ_QUICKMODE, true); + let ds = FeedUtils.getSubscriptionsDS(this.server); + aNewQuickMode = FeedUtils.rdf.GetLiteral(aNewQuickMode); + let old_quickMode = ds.GetTarget(this.resource, + FeedUtils.FZ_QUICKMODE, + true); if (old_quickMode) - ds.Change(this.resource, FZ_QUICKMODE, old_quickMode, aNewQuickMode); + ds.Change(this.resource, FeedUtils.FZ_QUICKMODE, + old_quickMode, aNewQuickMode); else - ds.Assert(this.resource, FZ_QUICKMODE, aNewQuickMode, true); + ds.Assert(this.resource, FeedUtils.FZ_QUICKMODE, + aNewQuickMode, true); }, get link () { - var ds = getSubscriptionsDS(this.server); - var link = ds.GetTarget(this.resource, RSS_LINK, true); - if(link) - link = link.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; + let ds = FeedUtils.getSubscriptionsDS(this.server); + let link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true); + if (link) + link = link.QueryInterface(Ci.nsIRDFLiteral).Value; + return link; }, @@ -353,19 +374,19 @@ Feed.prototype = if (!aNewLink) return; - var ds = getSubscriptionsDS(this.server); - aNewLink = rdf.GetLiteral(aNewLink); - var old_link = ds.GetTarget(this.resource, RSS_LINK, true); + let ds = FeedUtils.getSubscriptionsDS(this.server); + aNewLink = FeedUtils.rdf.GetLiteral(aNewLink); + let old_link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true); if (old_link) - ds.Change(this.resource, RSS_LINK, old_link, aNewLink); + ds.Change(this.resource, FeedUtils.RSS_LINK, old_link, aNewLink); else - ds.Assert(this.resource, RSS_LINK, aNewLink, true); + ds.Assert(this.resource, FeedUtils.RSS_LINK, aNewLink, true); }, - parse: function() + parse: function() { // Create a feed parser which will parse the feed. - var parser = new FeedParser(); + let parser = new FeedParser(); this.itemsToStore = parser.parseFeed(this, this.request.responseXML, this.request.channel.URI); @@ -376,144 +397,148 @@ Feed.prototype = return; } - // storeNextItem will iterate through the parsed items, storing each one. + // storeNextItem() will iterate through the parsed items, storing each one. this.itemsToStoreIndex = 0; this.storeNextItem(); }, - invalidateItems: function () + invalidateItems: function () { - var ds = getItemsDS(this.server); - debug("invalidating items for " + this.url); - var items = ds.GetSources(FZ_FEED, this.resource, true); - var item; - - while (items.hasMoreElements()) + let ds = FeedUtils.getItemsDS(this.server); + FeedUtils.log.debug("Feed.invalidateItems: for url - " + this.url); + let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true); + let item; + + while (items.hasMoreElements()) { item = items.getNext(); - item = item.QueryInterface(Components.interfaces.nsIRDFResource); - debug("invalidating " + item.Value); - var valid = ds.GetTarget(item, FZ_VALID, true); + item = item.QueryInterface(Ci.nsIRDFResource); + FeedUtils.log.trace("Feed.invalidateItems: item - " + item.Value); + let valid = ds.GetTarget(item, FeedUtils.FZ_VALID, true); if (valid) - ds.Unassert(item, FZ_VALID, valid, true); + ds.Unassert(item, FeedUtils.FZ_VALID, valid, true); } - }, + }, - removeInvalidItems: function(aDeleteFeed) + removeInvalidItems: function(aDeleteFeed) { - var ds = getItemsDS(this.server); - debug("removing invalid items for " + this.url); - var items = ds.GetSources(FZ_FEED, this.resource, true); - var item; - var currentTime = new Date().getTime(); - while (items.hasMoreElements()) + let ds = FeedUtils.getItemsDS(this.server); + FeedUtils.log.debug("Feed.removeInvalidItems: for url - " + this.url); + let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true); + let item; + let currentTime = new Date().getTime(); + while (items.hasMoreElements()) { item = items.getNext(); - item = item.QueryInterface(Components.interfaces.nsIRDFResource); + item = item.QueryInterface(Ci.nsIRDFResource); - if (ds.HasAssertion(item, FZ_VALID, RDF_LITERAL_TRUE, true)) + if (ds.HasAssertion(item, FeedUtils.FZ_VALID, + FeedUtils.RDF_LITERAL_TRUE, true)) continue; - var lastSeenTime = ds.GetTarget(item, FZ_LAST_SEEN_TIMESTAMP, true); - lastSeenTime = lastSeenTime ? - parseInt(lastSeenTime - .QueryInterface(Components.interfaces.nsIRDFLiteral) - .Value) : 0; - if ((currentTime - lastSeenTime) < INVALID_ITEM_PURGE_DELAY && !aDeleteFeed) + let lastSeenTime = ds.GetTarget(item, FeedUtils.FZ_LAST_SEEN_TIMESTAMP, true); + if (lastSeenTime) + lastSeenTime = parseInt(lastSeenTime.QueryInterface(Ci.nsIRDFLiteral).Value) + else + lastSeenTime = 0; + + if ((currentTime - lastSeenTime) < FeedUtils.INVALID_ITEM_PURGE_DELAY && + !aDeleteFeed) // Don't immediately purge items in active feeds; do so for deleted feeds. continue; - debug("removing " + item.Value); - ds.Unassert(item, FZ_FEED, this.resource, true); - if (ds.hasArcOut(item, FZ_FEED)) - debug(item.Value + " is from more than one feed; only the reference to this feed removed"); + FeedUtils.log.trace("Feed.removeInvalidItems: item - " + item.Value); + ds.Unassert(item, FeedUtils.FZ_FEED, this.resource, true); + if (ds.hasArcOut(item, FeedUtils.FZ_FEED)) + FeedUtils.log.debug("Feed.removeInvalidItems: " + item.Value + + " is from more than one feed; only the reference to" + + " this feed removed"); else - removeAssertions(ds, item); + FeedUtils.removeAssertions(ds, item); } }, createFolder: function() - { - if (!this.folder) - this.server.rootMsgFolder.createSubfolder(this.name, null /* supposed to be a msg window */); + { + if (!this.folder) + this.server.rootMsgFolder.createSubfolder(this.name, null); }, - // gets the next item from gItemsToStore and forces that item to be stored - // to the folder. If more items are left to be stored, fires a timer for the next one. - // otherwise it triggers a download done notification to the UI + // Gets the next item from itemsToStore and forces that item to be stored + // to the folder. If more items are left to be stored, fires a timer for + // the next one, otherwise triggers a download done notification to the UI. storeNextItem: function() { - if (!this.itemsToStore || !this.itemsToStore.length) + if (!this.itemsToStore || !this.itemsToStore.length) { this.createFolder(); this.cleanupParsingState(this); return; } - var item = this.itemsToStore[this.itemsToStoreIndex]; + let item = this.itemsToStore[this.itemsToStoreIndex]; item.store(); this.itemsToStoreIndex++; - // if the listener is tracking progress for storing each item, report it here... + // If the listener is tracking progress for each item, report it here. if (item.feed.downloadCallback && item.feed.downloadCallback.onFeedItemStored) - item.feed.downloadCallback.onFeedItemStored(item.feed, this.itemsToStoreIndex, this.itemsToStore.length); - - // eventually we'll report individual progress here.... + item.feed.downloadCallback.onFeedItemStored(item.feed, + this.itemsToStoreIndex, + this.itemsToStore.length); + + // Eventually we'll report individual progress here. if (this.itemsToStoreIndex < this.itemsToStore.length) { if (!this.storeItemsTimer) - this.storeItemsTimer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); - this.storeItemsTimer.initWithCallback(this, 50, Components.interfaces.nsITimer.TYPE_ONE_SHOT); + this.storeItemsTimer = Cc["@mozilla.org/timer;1"]. + createInstance(Ci.nsITimer); + this.storeItemsTimer.initWithCallback(this, 50, Ci.nsITimer.TYPE_ONE_SHOT); } else { - // we have just finished downloading one or more feed items into the destination folder, - // if the folder is still listed as having new messages in it, then we should set the biff state on the folder - // so the right RDF UI changes happen in the folder pane to indicate new mail. - + // We have just finished downloading one or more feed items into the + // destination folder; if the folder is still listed as having new + // messages in it, then we should set the biff state on the folder so the + // right RDF UI changes happen in the folder pane to indicate new mail. if (item.feed.folder.hasNewMessages) { - item.feed.folder.biffState = Components.interfaces.nsIMsgFolder.nsMsgBiffState_NewMail; - // run the bayesian spam filter, if enabled + item.feed.folder.biffState = Ci.nsIMsgFolder.nsMsgBiffState_NewMail; + // Run the bayesian spam filter, if enabled. item.feed.folder.callFilterPlugins(null); } + this.cleanupParsingState(item.feed); } }, - cleanupParsingState: function(aFeed) + cleanupParsingState: function(aFeed) { - // now that we are done parsing the feed, remove the feed from our feed cache + // Now that we are done parsing the feed, remove the feed from the cache. FeedCache.removeFeed(aFeed.url); aFeed.removeInvalidItems(false); - // let's be sure to flush any feed item changes back to disk - var ds = getItemsDS(aFeed.server); - ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush(); // flush any changes + // Flush any feed item changes to disk. + let ds = FeedUtils.getItemsDS(aFeed.server); + ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); if (aFeed.downloadCallback) - aFeed.downloadCallback.downloaded(aFeed, kNewsBlogSuccess); + aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogSuccess); - this.request = null; // force the xml http request to go away. This helps reduce some nasty assertions on shut down. + // Force the xml http request to go away. This helps reduce some nasty + // assertions on shut down. + this.request = null; this.itemsToStore = ""; this.itemsToStoreIndex = 0; this.storeItemsTimer = null; - }, + }, - notify: function(aTimer) + // nsITimerCallback + notify: function(aTimer) { this.storeNextItem(); - }, - - QueryInterface: function(aIID) - { - if (aIID.equals(Components.interfaces.nsITimerCallback) || aIID.equals(Components.interfaces.nsISupports)) - return this; - - throw Components.results.NS_ERROR_NO_INTERFACE; } }; diff --git a/mailnews/extensions/newsblog/content/FeedItem.js b/mailnews/extensions/newsblog/content/FeedItem.js index 029708d9cf..35f9e5f05f 100755 --- a/mailnews/extensions/newsblog/content/FeedItem.js +++ b/mailnews/extensions/newsblog/content/FeedItem.js @@ -35,52 +35,46 @@ # # ***** END LICENSE BLOCK ***** */ -// Handy conversion values. -const HOURS_TO_MINUTES = 60; -const MINUTES_TO_SECONDS = 60; -const SECONDS_TO_MILLISECONDS = 1000; -const MINUTES_TO_MILLISECONDS = MINUTES_TO_SECONDS * SECONDS_TO_MILLISECONDS; -const HOURS_TO_MILLISECONDS = HOURS_TO_MINUTES * MINUTES_TO_MILLISECONDS; -const ENCLOSURE_BOUNDARY_PREFIX = "--------------"; // 14 dashes -const ENCLOSURE_HEADER_BOUNDARY_PREFIX = "------------"; // 12 dashes - -const MESSAGE_TEMPLATE = "\n\ -\n\ - \n\ - %TITLE%\n\ - \n\ - \n\ - \n\ - %CONTENT%\n\ - \n\ -\n\ -"; - function FeedItem() { this.mDate = new Date().toString(); - this.mUnicodeConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Components.interfaces.nsIScriptableUnicodeConverter); + this.mUnicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. + createInstance(Ci.nsIScriptableUnicodeConverter); + this.mUnescapeHTML = Cc["@mozilla.org/feed-unescapehtml;1"]. + getService(Ci.nsIScriptableUnescapeHTML); } FeedItem.prototype = { - // Currently only for IETF Atom. RSS2 with GUIDs should do this as too. + // Currently only for IETF Atom. RSS2 with GUIDs should do this too. isStoredWithId: false, - // Only for IETF Atom + // Only for IETF Atom. xmlContentBase: null, id: null, feed: null, description: null, content: null, - // Currently only support one enclosure per feed item... + // Currently only support one enclosure per feed item. enclosure: null, - // TO DO: this needs to be localized + // TO DO: this needs to be localized. title: "(no subject)", author: "anonymous", mURL: null, characterSet: "", + ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes + ENCLOSURE_HEADER_BOUNDARY_PREFIX: "------------", // 12 dashes + MESSAGE_TEMPLATE: '\n' + + '\n' + + ' \n' + + ' %TITLE%\n' + + ' \n' + + ' \n' + + ' \n' + + ' %CONTENT%\n' + + ' \n' + + '\n', + get url() { return this.mURL; @@ -90,9 +84,7 @@ FeedItem.prototype = { try { - var ioService = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); - this.mURL = ioService.newURI(aVal, null, null).spec; + this.mURL = Services.io.newURI(aVal, null, null).spec; } catch(ex) { @@ -120,25 +112,26 @@ FeedItem.prototype = get messageID() { - var messageID = this.id || this.mURL || this.title; + let messageID = this.id || this.mURL || this.title; - debug('messageID - id = ' + this.id); - debug('messageID - mURL = ' + this.mURL); - debug('messageID - title = ' + this.title); - debug('messageID - messageID = ' + messageID); + FeedUtils.log.trace("FeedItem.messageID: id - " + this.id); + FeedUtils.log.trace("FeedItem.messageID: mURL - " + this.mURL); + FeedUtils.log.trace("FeedItem.messageID: title - " + this.title); // Escape occurrences of message ID meta characters <, >, and @. messageID.replace(//g, "%3E"); messageID.replace(/@/g, "%40"); messageID = messageID + "@" + "localhost.localdomain"; + + FeedUtils.log.trace("FeedItem.messageID: messageID - " + messageID); return messageID; }, get itemUniqueURI() { - return (this.isStoredWithId && this.id) ? createURN(this.id) : - createURN(this.mURL || this.id); + return this.isStoredWithId && this.id ? this.createURN(this.id) : + this.createURN(this.mURL || this.id); }, get contentBase() @@ -153,25 +146,27 @@ FeedItem.prototype = { this.mUnicodeConverter.charset = this.characterSet; - // this.title and this.content contain HTML - // this.mUrl and this.contentBase contain plain text + // this.title and this.content contain HTML. + // this.mUrl and this.contentBase contain plain text. let resource = this.findStoredResource(); if (resource == null) { - resource = rdf.GetResource(this.itemUniqueURI); + resource = FeedUtils.rdf.GetResource(this.itemUniqueURI); if (!this.content) { - debug(this.identity + " no content; storing"); + FeedUtils.log.trace("FeedItem.store: " + this.identity + + " no content; storing"); this.content = this.description || this.title; } - debug(this.identity + " store both remote/no content and content items"); - var content = MESSAGE_TEMPLATE; + FeedUtils.log.trace("FeedItem.store: " + this.identity + + " store both remote/no content and content items"); + let content = this.MESSAGE_TEMPLATE; content = content.replace(/%TITLE%/, this.title); - content = content.replace(/%BASE%/, htmlEscape(this.contentBase)); + content = content.replace(/%BASE%/, this.htmlEscape(this.contentBase)); content = content.replace(/%CONTENT%/, this.content); - // XXX store it elsewhere, f.e. this.page + // XXX store it elsewhere, f.e. this.page. this.content = content; this.writeToFolder(); this.markStored(resource); @@ -181,37 +176,39 @@ FeedItem.prototype = findStoredResource: function() { - // Checks to see if the item has already been stored in its feed's message folder. + // Checks to see if the item has already been stored in its feed's + // message folder. + FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity + + " checking to see if stored"); - debug(this.identity + " checking to see if stored"); - - var server = this.feed.server; - var folder = this.feed.folder; + let server = this.feed.server; + let folder = this.feed.folder; if (!folder) { - debug(this.feed.name + " folder doesn't exist; creating"); - debug("creating " + this.feed.name + "as child of " + - server.rootMsgFolder + "\n"); - server.rootMsgFolder.createSubfolder(this.feed.name, null /* supposed to be a msg window */); + FeedUtils.log.debug("FeedItem.findStoredResource: " + this.feed.name + + " folder doesn't exist; creating as child of " + + server.rootMsgFolder.prettyName + "\n"); + server.rootMsgFolder.createSubfolder(this.feed.name, null); folder = server.rootMsgFolder.findSubFolder(this.feed.name); - debug(this.identity + " not stored (folder didn't exist)"); + FeedUtils.log.debug("FeedItem.findStoredResource: " + this.identity + + " not stored (folder didn't exist)"); return null; } - var ds = getItemsDS(server); - var itemURI = this.itemUniqueURI; - var itemResource = rdf.GetResource(itemURI); + let ds = FeedUtils.getItemsDS(server); + let itemURI = this.itemUniqueURI; + let itemResource = FeedUtils.rdf.GetResource(itemURI); - var downloaded = ds.GetTarget(itemResource, FZ_STORED, true); + let downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true); // Backward compatibility: we might have stored this item before // isStoredWithId has been turned on for RSS 2.0 (bug 354345). // Check whether this item has been stored with its URL. if (!downloaded && this.mURL && itemURI != this.mURL) { - itemResource = rdf.GetResource(this.mURL); - downloaded = ds.GetTarget(itemResource, FZ_STORED, true); + itemResource = FeedUtils.rdf.GetResource(this.mURL); + downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true); } // Backward compatibility: the item may have been stored @@ -219,97 +216,108 @@ FeedItem.prototype = // (bug 410842 & bug 461109) if (!downloaded) { - itemResource = rdf.GetResource((this.isStoredWithId && this.id) ? - ("urn:" + this.id) : - (this.mURL || ("urn:" + this.id))); - downloaded = ds.GetTarget(itemResource, FZ_STORED, true); + itemResource = FeedUtils.rdf.GetResource((this.isStoredWithId && this.id) ? + ("urn:" + this.id) : + (this.mURL || ("urn:" + this.id))); + downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true); } if (!downloaded || - downloaded.QueryInterface(Components.interfaces.nsIRDFLiteral) - .Value == "false") + downloaded.QueryInterface(Ci.nsIRDFLiteral).Value == "false") { // HACK ALERT: before we give up, try to work around an entity - // escaping bug in RDF. See Bug #258465 for more details + // escaping bug in RDF. See Bug #258465 for more details. itemURI = itemURI.replace(/</g, '<'); itemURI = itemURI.replace(/>/g, '>'); itemURI = itemURI.replace(/"/g, '"'); itemURI = itemURI.replace(/&/g, '&'); - debug('Failed to find item, trying entity replacement version: ' + itemURI); - itemResource = rdf.GetResource(itemURI); - downloaded = ds.GetTarget(itemResource, FZ_STORED, true); + FeedUtils.log.trace("FeedItem.findStoredResource: failed to find item," + + " trying entity replacement version - " + itemURI); + itemResource = FeedUtils.rdf.GetResource(itemURI); + downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true); if (downloaded) { - debug(this.identity + " stored"); + FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity + + " stored"); return itemResource; } - debug(this.identity + " not stored"); + FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity + + " not stored"); return null; } else { - debug(this.identity + " stored"); + FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity + + " stored"); return itemResource; } }, markValid: function(resource) { - var ds = getItemsDS(this.feed.server); + let ds = FeedUtils.getItemsDS(this.feed.server); - var newTimeStamp = rdf.GetLiteral(new Date().getTime()); - var currentTimeStamp = ds.GetTarget(resource, FZ_LAST_SEEN_TIMESTAMP, true); + let newTimeStamp = FeedUtils.rdf.GetLiteral(new Date().getTime()); + let currentTimeStamp = ds.GetTarget(resource, + FeedUtils.FZ_LAST_SEEN_TIMESTAMP, + true); if (currentTimeStamp) - ds.Change(resource, FZ_LAST_SEEN_TIMESTAMP, currentTimeStamp, newTimeStamp); + ds.Change(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP, + currentTimeStamp, newTimeStamp); else - ds.Assert(resource, FZ_LAST_SEEN_TIMESTAMP, newTimeStamp, true); + ds.Assert(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP, + newTimeStamp, true); - if (!ds.HasAssertion(resource, FZ_FEED, rdf.GetResource(this.feed.url), true)) - ds.Assert(resource, FZ_FEED, rdf.GetResource(this.feed.url), true); + if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED, + FeedUtils.rdf.GetResource(this.feed.url), true)) + ds.Assert(resource, FeedUtils.FZ_FEED, + FeedUtils.rdf.GetResource(this.feed.url), true); - if (ds.hasArcOut(resource, FZ_VALID)) + if (ds.hasArcOut(resource, FeedUtils.FZ_VALID)) { - var currentValue = ds.GetTarget(resource, FZ_VALID, true); - ds.Change(resource, FZ_VALID, currentValue, RDF_LITERAL_TRUE); + let currentValue = ds.GetTarget(resource, FeedUtils.FZ_VALID, true); + ds.Change(resource, FeedUtils.FZ_VALID, + currentValue, FeedUtils.RDF_LITERAL_TRUE); } else - ds.Assert(resource, FZ_VALID, RDF_LITERAL_TRUE, true); + ds.Assert(resource, FeedUtils.FZ_VALID, FeedUtils.RDF_LITERAL_TRUE, true); }, markStored: function(resource) { - var ds = getItemsDS(this.feed.server); + let ds = FeedUtils.getItemsDS(this.feed.server); - if (!ds.HasAssertion(resource, FZ_FEED, rdf.GetResource(this.feed.url), true)) - ds.Assert(resource, FZ_FEED, rdf.GetResource(this.feed.url), true); + if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED, + FeedUtils.rdf.GetResource(this.feed.url), true)) + ds.Assert(resource, FeedUtils.FZ_FEED, + FeedUtils.rdf.GetResource(this.feed.url), true); - var currentValue; - if (ds.hasArcOut(resource, FZ_STORED)) + let currentValue; + if (ds.hasArcOut(resource, FeedUtils.FZ_STORED)) { - currentValue = ds.GetTarget(resource, FZ_STORED, true); - ds.Change(resource, FZ_STORED, currentValue, RDF_LITERAL_TRUE); + currentValue = ds.GetTarget(resource, FeedUtils.FZ_STORED, true); + ds.Change(resource, FeedUtils.FZ_STORED, + currentValue, FeedUtils.RDF_LITERAL_TRUE); } else - ds.Assert(resource, FZ_STORED, RDF_LITERAL_TRUE, true); + ds.Assert(resource, FeedUtils.FZ_STORED, + FeedUtils.RDF_LITERAL_TRUE, true); }, mimeEncodeSubject: function(aSubject, aCharset) { - // Get the mime header encoder service - var mimeEncoder = Components.classes["@mozilla.org/messenger/mimeconverter;1"] - .getService(Components.interfaces.nsIMimeConverter); - // This routine sometimes throws exceptions for mis-encoded data so - // wrap it with a try catch for now.. - var newSubject; + // wrap it with a try catch for now. + let newSubject; try { - newSubject = mimeEncoder.encodeMimePartIIStr( - this.mUnicodeConverter.ConvertFromUnicode(aSubject), - false, aCharset, 9, 72); + newSubject = mailServices.mimeConverter.encodeMimePartIIStr( + this.mUnicodeConverter.ConvertFromUnicode(aSubject), + false, + aCharset, 9, 72); } catch (ex) { @@ -321,24 +329,21 @@ FeedItem.prototype = writeToFolder: function() { - debug(this.identity + " writing to message folder" + this.feed.name + "\n"); - - var server = this.feed.server; + FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity + + " writing to message folder " + this.feed.name); this.mUnicodeConverter.charset = this.characterSet; // If the sender isn't a valid email address, quote it so it looks nicer. - if (this.author && this.author.indexOf('@') == -1) - this.author = '<' + this.author + '>'; + if (this.author && this.author.indexOf("@") == -1) + this.author = "<" + this.author + ">"; // Convert the title to UTF-16 before performing our HTML entity // replacement reg expressions. - var title = this.title; + let title = this.title; - // the subject may contain HTML entities. - // Convert these to their unencoded state. i.e. & becomes '&' - title = Components.classes["@mozilla.org/feed-unescapehtml;1"] - .getService(Components.interfaces.nsIScriptableUnescapeHTML) - .unescape(title); + // The subject may contain HTML entities. Convert these to their unencoded + // state. i.e. & becomes '&'. + title = this.mUnescapeHTML.unescape(title); // Compress white space in the subject to make it look better. Trim // leading/trailing spaces to prevent mbox header folding issue at just @@ -364,9 +369,9 @@ FeedItem.prototype = // use it to calculate the offset of the X-Mozilla-Status lines from // the front of the message for the statusOffset property of the // DB header object. - var openingLine = 'From - ' + this.mDate + '\n'; + let openingLine = 'From - ' + this.mDate + '\n'; - var source = + let source = openingLine + 'X-Mozilla-Status: 0000\n' + 'X-Mozilla-Status2: 00000000\n' + @@ -381,9 +386,11 @@ FeedItem.prototype = if (this.enclosure && this.enclosure.mFileName) { - var boundaryID = source.length + this.enclosure.mLength; - source += 'Content-Type: multipart/mixed;\n boundary="' + ENCLOSURE_HEADER_BOUNDARY_PREFIX + boundaryID + '"' + '\n\n' + - 'This is a multi-part message in MIME format.\n' + ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '\n' + + let boundaryID = source.length + this.enclosure.mLength; + source += 'Content-Type: multipart/mixed;\n boundary="' + + this.ENCLOSURE_HEADER_BOUNDARY_PREFIX + boundaryID + '"' + '\n\n' + + 'This is a multi-part message in MIME format.\n' + + this.ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '\n' + 'Content-Type: text/html; charset=' + this.characterSet + '\n' + 'Content-Transfer-Encoding: 8bit\n' + this.content; @@ -396,47 +403,72 @@ FeedItem.prototype = } - debug(this.identity + " is " + source.length + " characters long"); + FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity + + " is " + source.length + " characters long"); // Get the folder and database storing the feed's messages and headers. - var folder = this.feed.folder.QueryInterface(Components.interfaces.nsIMsgLocalMailFolder); - var msgFolder = folder.QueryInterface(Components.interfaces.nsIMsgFolder); + let folder = this.feed.folder.QueryInterface(Ci.nsIMsgLocalMailFolder); + let msgFolder = folder.QueryInterface(Ci.nsIMsgFolder); msgFolder.gettingNewMessages = true; // Source is a unicode string, we want to save a char * string in - // the original charset. So convert back + // the original charset. So convert back. folder.addMessage(this.mUnicodeConverter.ConvertFromUnicode(source)); msgFolder.gettingNewMessages = false; + }, + + htmlEscape: function(s) + { + s = s.replace(/&/g, "&"); + s = s.replace(/>/g, ">"); + s = s.replace(/ value as an URL to avoid linking to non-URL strings + // If attribute isPermaLink is missing, it is good to check the validity + // of value as an URL to avoid linking to non-URL strings. if (!guidNode.hasAttribute("isPermaLink")) { try { - gIOService.newURI(guid, null, null); - if (gIOService.extractScheme(guid) == "tag") + Services.io.newURI(guid, null, null); + if (Services.io.extractScheme(guid) == "tag") isPermaLink = false; } catch (ex) @@ -163,182 +179,215 @@ FeedParser.prototype = } item.url = (guid && isPermaLink) ? guid : link ? link : null; - item.description = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "description")[0]); - item.title = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "title")[0]) - || (item.description ? (this.stripTags(item.description).substr(0, 150)) : null) - || item.title; + tags = this.childrenByTagNameNS(itemNode, nsURI, "description"); + item.description = this.getNodeValue(tags ? tags[0] : null); + tags = this.childrenByTagNameNS(itemNode, nsURI, "title"); + item.title = this.getNodeValue(tags ? tags[0] : null) || + (item.description ? + this.stripTags(item.description).substr(0, 150) : null) || + item.title; - item.author = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "author")[0] - || this.childrenByTagNameNS(itemNode, DC_NS, "creator")[0]) - || aFeed.title - || item.author; - item.date = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "pubDate")[0] - || this.childrenByTagNameNS(itemNode, DC_NS, "date")[0]) - || item.date; + tags = this.childrenByTagNameNS(itemNode, nsURI, "author"); + if (!tags) + tags = this.childrenByTagNameNS(itemNode, FeedUtils.DC_NS, "creator"); + item.author = this.getNodeValue(tags ? tags[0] : null) || + aFeed.title || + item.author; + + tags = this.childrenByTagNameNS(itemNode, nsURI, "pubDate"); + if (!tags) + tags = this.childrenByTagNameNS(itemNode, FeedUtils.DC_NS, "date"); + item.date = this.getNodeValue(tags ? tags[0] : null) || item.date; if (!item.id) - item.id = item.feed.url + '#' + (item.date || item.title); + item.id = item.feed.url + "#" + (item.date || item.title); // If the date is invalid, users will see the beginning of the epoch // unless we reset it here, so they'll see the current time instead. // This is typical aggregator behavior. - if(item.date) + if (item.date) { item.date = item.date.trim(); - if(!isValidRFC822Date(item.date)) + if (!this.isValidRFC822Date(item.date)) { - // XXX Use this on the other formats as well - item.date = dateRescue(item.date); + // XXX Use this on the other formats as well. + item.date = this.dateRescue(item.date); } } - var content = getNodeValue(this.childrenByTagNameNS(itemNode, RSS_CONTENT_NS, "encoded")[0]); - if(content) - item.content = content; + tags = this.childrenByTagNameNS(itemNode, FeedUtils.RSS_CONTENT_NS, "encoded"); + item.content = this.getNodeValue(tags ? tags[0] : null); - // Handle an enclosure (if present) - var enclosureNode = this.childrenByTagNameNS(itemNode, nsURI, "enclosure")[0]; + // Handle an enclosure (if present). + tags = this.childrenByTagNameNS(itemNode, nsURI, "enclosure"); + let enclosureNode = tags ? tags[0] : null; if (enclosureNode) - item.enclosure = new FeedEnclosure(enclosureNode.getAttribute("url"), - enclosureNode.getAttribute("type"), - enclosureNode.getAttribute("length")); + item.enclosure = new FeedEnclosure(enclosureNode.getAttribute("url"), + enclosureNode.getAttribute("type"), + enclosureNode.getAttribute("length")); parsedItems[i] = item; } + return parsedItems; }, - parseAsRSS1 : function(aFeed, aSource, aBaseURI) + parseAsRSS1 : function(aFeed, aSource, aBaseURI) { - var parsedItems = new Array(); + let parsedItems = new Array(); // RSS 1.0 is valid RDF, so use the RDF parser/service to extract data. // Create a new RDF data source and parse the feed into it. - var ds = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"] - .createInstance(Components.interfaces.nsIRDFDataSource); + let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]. + createInstance(Ci.nsIRDFDataSource); + let rdfparser = Cc["@mozilla.org/rdf/xml-parser;1"]. + createInstance(Ci.nsIRDFXMLParser); rdfparser.parseString(ds, aBaseURI, aSource); - + // Get information about the feed as a whole. - var channel = ds.GetSource(RDF_TYPE, RSS_CHANNEL, true); - - aFeed.title = aFeed.title || getRDFTargetValue(ds, channel, RSS_TITLE) || aFeed.url; - aFeed.description = getRDFTargetValue(ds, channel, RSS_DESCRIPTION) || ""; - aFeed.link = getRDFTargetValue(ds, channel, RSS_LINK) || aFeed.url; + let channel = ds.GetSource(FeedUtils.RDF_TYPE, FeedUtils.RSS_CHANNEL, true); + + aFeed.title = aFeed.title || + this.getRDFTargetValue(ds, channel, FeedUtils.RSS_TITLE) || + aFeed.url; + aFeed.description = this.getRDFTargetValue(ds, channel, FeedUtils.RSS_DESCRIPTION) || + ""; + aFeed.link = this.getRDFTargetValue(ds, channel, FeedUtils.RSS_LINK) || + aFeed.url; if (!aFeed.parseItems) return parsedItems; aFeed.invalidateItems(); - var items = ds.GetTarget(channel, RSS_ITEMS, true); + let items = ds.GetTarget(channel, FeedUtils.RSS_ITEMS, true); if (items) - items = rdfcontainer.MakeSeq(ds, items).GetElements(); - + items = FeedUtils.rdfContainerUtils.MakeSeq(ds, items).GetElements(); + // If the channel doesn't list any items, look for resources of type "item" // (a hacky workaround for some buggy feeds). if (!items || !items.hasMoreElements()) - items = ds.GetSources(RDF_TYPE, RSS_ITEM, true); + items = ds.GetSources(FeedUtils.RDF_TYPE, FeedUtils.RSS_ITEM, true); - var index = 0; - while (items.hasMoreElements()) + let index = 0; + while (items.hasMoreElements()) { - var itemResource = items.getNext().QueryInterface(Components.interfaces.nsIRDFResource); - var item = new FeedItem(); + let itemResource = items.getNext().QueryInterface(Ci.nsIRDFResource); + let item = new FeedItem(); item.feed = aFeed; item.characterSet = "UTF-8"; // Prefer the value of the link tag to the item URI since the URI could be // a relative URN. - var uri = itemResource.Value; - var link = getRDFTargetValue(ds, itemResource, RSS_LINK); + let uri = itemResource.Value; + let link = this.getRDFTargetValue(ds, itemResource, FeedUtils.RSS_LINK); - // XXX - // check for bug258465 -- entities appear escaped - // in the value returned by getRDFTargetValue when they shouldn't + // XXX Check for bug258465 - entities appear escaped in the value + // returned by getRDFTargetValue when they shouldn't. //debug("link comparison\n" + " uri: " + uri + "\nlink: " + link); item.url = link || uri; item.id = item.url; - item.description = getRDFTargetValue(ds, itemResource, RSS_DESCRIPTION); - item.title = getRDFTargetValue(ds, itemResource, RSS_TITLE) - || getRDFTargetValue(ds, itemResource, DC_SUBJECT) - || (item.description ? (this.stripTags(item.description).substr(0, 150)) : null) - || item.title; - item.author = getRDFTargetValue(ds, itemResource, DC_CREATOR) - || getRDFTargetValue(ds, channel, DC_CREATOR) - || aFeed.title - || item.author; - - item.date = getRDFTargetValue(ds, itemResource, DC_DATE) || item.date; - item.content = getRDFTargetValue(ds, itemResource, RSS_CONTENT_ENCODED); + item.description = this.getRDFTargetValue(ds, itemResource, + FeedUtils.RSS_DESCRIPTION); + item.title = this.getRDFTargetValue(ds, itemResource, FeedUtils.RSS_TITLE) || + this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_SUBJECT) || + (item.description ? + (this.stripTags(item.description).substr(0, 150)) : null) || + item.title; + item.author = this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_CREATOR) || + this.getRDFTargetValue(ds, channel, FeedUtils.DC_CREATOR) || + aFeed.title || + item.author; + item.date = this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_DATE) || + item.date; + item.content = this.getRDFTargetValue(ds, itemResource, + FeedUtils.RSS_CONTENT_ENCODED); parsedItems[index++] = item; } - + FeedUtils.log.debug("FeedParser.parseAsRSS1: items parsed - " + index); + return parsedItems; }, - parseAsAtom: function(aFeed, aDOM) + parseAsAtom: function(aFeed, aDOM) { - var parsedItems = new Array(); + let parsedItems = new Array(); // Get the first channel (assuming there is only one per Atom File). - var channel = aDOM.getElementsByTagName("feed")[0]; + let channel = aDOM.getElementsByTagName("feed")[0]; if (!channel) { aFeed.onParseError(aFeed); return parsedItems; } - aFeed.title = aFeed.title || this.stripTags(getNodeValue(this.childrenByTagNameNS(channel, ATOM_03_NS, "title")[0])); - aFeed.description = getNodeValue(this.childrenByTagNameNS(channel, ATOM_03_NS, "tagline")[0]); - aFeed.link = this.findAtomLink("alternate",this.childrenByTagNameNS(channel, ATOM_03_NS, "link")); + let tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "title"); + aFeed.title = aFeed.title || + this.stripTags(this.getNodeValue(tags ? tags[0] : null)); + tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "tagline"); + aFeed.description = this.getNodeValue(tags ? tags[0] : null); + tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "link"); + aFeed.link = this.findAtomLink("alternate", tags); if (!aFeed.parseItems) return parsedItems; aFeed.invalidateItems(); - var items = this.childrenByTagNameNS(channel, ATOM_03_NS, "entry"); - debug("Items to parse: " + items.length); - - for (var i=0; i < items.length; i++) + let items = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "entry"); + FeedUtils.log.debug("FeedParser.parseAsAtom: items to parse - " + + items.length); + + for (let i = 0; i < items.length; i++) { - var itemNode = items[i]; - var item = new FeedItem(); + let itemNode = items[i]; + let item = new FeedItem(); item.feed = aFeed; item.characterSet = "UTF-8"; - var url; - url = this.findAtomLink("alternate",this.childrenByTagNameNS(itemNode, ATOM_03_NS, "link")); + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "link"); + item.url = this.findAtomLink("alternate", tags); - item.url = url; - item.id = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "id")[0]); - item.description = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "summary")[0]); - item.title = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "title")[0]) - || (item.description ? item.description.substr(0, 150) : null) - || item.title; + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "id"); + item.id = this.getNodeValue(tags ? tags[0] : null); + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "summary"); + item.description = this.getNodeValue(tags ? tags[0] : null); + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "title"); + item.title = this.getNodeValue(tags ? tags[0] : null) || + (item.description ? item.description.substr(0, 150) : null) || + item.title; - var authorEl = this.childrenByTagNameNS(itemNode, ATOM_03_NS, "author")[0] - || this.childrenByTagNameNS(itemNode, ATOM_03_NS, "contributor")[0] - || this.childrenByTagNameNS(channel, ATOM_03_NS, "author")[0]; - var author = ""; + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "author"); + if (!tags) + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "contributor"); + if (!tags) + tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "author"); - if (authorEl) + let authorEl = tags ? tags[0] : null; + + let author = ""; + if (authorEl) { - var name = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_03_NS, "name")[0]); - var email = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_03_NS, "email")[0]); + tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_03_NS, "name"); + let name = this.getNodeValue(tags ? tags[0] : null); + tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_03_NS, "email"); + let email = this.getNodeValue(tags ? tags[0] : null); if (name) author = name + (email ? " <" + email + ">" : ""); else if (email) author = email; } - + item.author = author || item.author || aFeed.title; - item.date = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "modified")[0] - || this.childrenByTagNameNS(itemNode, ATOM_03_NS, "issued")[0] - || this.childrenByTagNameNS(itemNode, ATOM_03_NS, "created")[0]) - || item.date; + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "modified"); + if (!tags) + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "issued"); + if (!tags) + tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "created"); + + item.date = this.getNodeValue(tags ? tags[0] : null) || item.date; // XXX We should get the xml:base attribute from the content tag as well // and use it as the base HREF of the message. @@ -348,104 +397,132 @@ FeedParser.prototype = // a namespace to identify the tags as HTML; and a few are buggy and put // HTML tags in without declaring their namespace so they look like Atom. // We deal with the first two but not the third. - - var content; - var contentNode = this.childrenByTagNameNS(itemNode, ATOM_03_NS, "content")[0]; - if (contentNode) + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "content"); + let contentNode = tags ? tags[0] : null; + + let content; + if (contentNode) { content = ""; - for (var j=0; j < contentNode.childNodes.length; j++) + for (let j = 0; j < contentNode.childNodes.length; j++) { - var node = contentNode.childNodes.item(j); + let node = contentNode.childNodes.item(j); if (node.nodeType == node.CDATA_SECTION_NODE) content += node.data; else - content += serializer.serializeToString(node); + content += this.mSerializer.serializeToString(node); } - if (contentNode.getAttribute('mode') == "escaped") + if (contentNode.getAttribute("mode") == "escaped") { content = content.replace(/</g, "<"); content = content.replace(/>/g, ">"); content = content.replace(/&/g, "&"); } - + if (content == "") content = null; } - + item.content = content; parsedItems[i] = item; } + return parsedItems; }, parseAsAtomIETF: function(aFeed, aDOM) { - - var parsedItems = new Array(); + let parsedItems = new Array(); // Get the first channel (assuming there is only one per Atom File). - var channel = this.childrenByTagNameNS(aDOM,ATOM_IETF_NS,"feed")[0]; + let channel = this.childrenByTagNameNS(aDOM, FeedUtils.ATOM_IETF_NS, "feed")[0]; if (!channel) { aFeed.onParseError(aFeed); return parsedItems; } - aFeed.title = aFeed.title || this.stripTags(this.serializeTextConstruct(this.childrenByTagNameNS(channel,ATOM_IETF_NS,"title")[0])); - aFeed.description = this.serializeTextConstruct(this.childrenByTagNameNS(channel,ATOM_IETF_NS,"subtitle")[0]); - aFeed.link = this.findAtomLink("alternate", this.childrenByTagNameNS(channel,ATOM_IETF_NS,"link")); - + let tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "title"); + aFeed.title = aFeed.title || + this.stripTags(this.serializeTextConstruct(tags ? tags[0] : null)); + + tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "subtitle"); + aFeed.description = this.serializeTextConstruct(tags ? tags[0] : null); + + tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "link"); + aFeed.link = this.findAtomLink("alternate", tags); + if (!aFeed.parseItems) return parsedItems; aFeed.invalidateItems(); - var items = this.childrenByTagNameNS(channel,ATOM_IETF_NS,"entry"); - debug("Items to parse: " + items.length); + let items = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "entry"); + FeedUtils.log.debug("FeedParser.parseAsAtomIETF: items to parse - " + + items.length); - for (var i=0; i < items.length; i++) + for (let i = 0; i < items.length; i++) { - var itemNode = items[i]; - var item = new FeedItem(); + let itemNode = items[i]; + let item = new FeedItem(); item.feed = aFeed; item.characterSet = "UTF-8"; item.isStoredWithId = true; - item.url = this.findAtomLink("alternate", this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "link")) || aFeed.link; - item.id = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "id")[0]); - item.description = this.serializeTextConstruct(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "summary")[0]); - item.title = this.stripTags(this.serializeTextConstruct(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "title")[0]) - || (item.description ? item.description.substr(0, 150) : null) - || item.title); - - // XXX Support multiple authors - var source = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "source")[0]; - var authorEl = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "author")[0] - || (source ? this.childrenByTagNameNS(source, ATOM_IETF_NS, "author")[0] : null) - || this.childrenByTagNameNS(channel, ATOM_IETF_NS, "author")[0]; - var author = ""; - if (authorEl) + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "link"); + item.url = this.findAtomLink("alternate", tags) || aFeed.link; + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "id"); + item.id = this.getNodeValue(tags ? tags[0] : null); + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "summary"); + item.description = this.serializeTextConstruct(tags ? tags[0] : null); + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "title"); + item.title = this.stripTags(this.serializeTextConstruct(tags ? tags[0] : null) || + (item.description ? + item.description.substr(0, 150) : null) || + item.title); + + // XXX Support multiple authors. + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "source"); + let source = tags ? tags[0] : null; + + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "author"); + if (!tags && source) + tags = this.childrenByTagNameNS(source, FeedUtils.ATOM_IETF_NS, "author"); + if (!tags) + tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "author"); + + let authorEl = tags ? tags[0] : null; + + let author = ""; + if (authorEl) { - var name = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_IETF_NS, "name")[0]); - var email = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_IETF_NS, "email")[0]); + tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_IETF_NS, "name"); + let name = this.getNodeValue(tags ? tags[0] : null); + tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_IETF_NS, "email"); + let email = this.getNodeValue(tags ? tags[0] : null); if (name) author = name + (email ? " <" + email + ">" : ""); else if (email) author = email; } - + item.author = author || item.author || aFeed.title; - item.date = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "updated")[0] - || this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "published")[0]) - || item.date; - item.content = this.serializeTextConstruct(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "content")[0]); + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "updated"); + if (!tags) + tags = this.childrenByTagNameNS(source, FeedUtils.ATOM_IETF_NS, "published"); + item.date = this.getNodeValue(tags ? tags[0] : null) || item.date; - if(item.content) - item.xmlContentBase = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "content")[0].baseURI; - else if(item.description) - item.xmlContentBase = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "summary")[0].baseURI; + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "content"); + item.content = this.serializeTextConstruct(tags ? tags[0] : null); + + if (item.content) + item.xmlContentBase = tags ? tags[0].baseURI : null; + else if (item.description) + { + tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "summary"); + item.xmlContentBase = tags ? tags[0].baseURI : null; + } else item.xmlContentBase = itemNode.baseURI; @@ -453,79 +530,125 @@ FeedParser.prototype = } return parsedItems; - }, serializeTextConstruct: function(textElement) { - var content = ""; - - if (textElement) + let content = ""; + if (textElement) { - var textType = textElement.getAttribute('type'); + let textType = textElement.getAttribute("type"); - // Atom spec says consider it "text" if not present - if(!textType) + // Atom spec says consider it "text" if not present. + if (!textType) textType = "text"; - // There could be some strange content type we don't handle - if((textType != "text") && (textType != "html") && (textType != "xhtml")) + // There could be some strange content type we don't handle. + if (textType != "text" && textType != "html" && textType != "xhtml") return null; - for (var j=0; j < textElement.childNodes.length; j++) + for (let j = 0; j < textElement.childNodes.length; j++) { - var node = textElement.childNodes.item(j); + let node = textElement.childNodes.item(j); if (node.nodeType == node.CDATA_SECTION_NODE) content += this.xmlEscape(node.data); else - content += serializer.serializeToString(node); + content += this.mSerializer.serializeToString(node); } - if (textType == "html") + + if (textType == "html") content = this.xmlUnescape(content); } - // other parts of the code depend on this being null - // if there's no content + // Other parts of the code depend on this being null if there's no content. return content ? content : null; }, - // finds elements that are direct children of the first arg + getRDFTargetValue: function(ds, source, property) + { + let node = ds.GetTarget(source, property, true); + if (node) + { + try + { + node = node.QueryInterface(Ci.nsIRDFLiteral); + if (node) + return node.Value; + } + catch (e) + { + // If the RDF was bogus, do nothing. Rethrow if it's some other problem. + if (!((e instanceof Ci.nsIXPCException) && + e.result == Cr.NS_ERROR_NO_INTERFACE)) + throw new Error("FeedParser.getRDFTargetValue: " + e); + } + } + + return null; + }, + + getNodeValue: function(node) + { + if (node && node.textContent) + return node.textContent.trim(); + else if (node && node.firstChild) + { + let ret = ""; + for (let child = node.firstChild; child; child = child.nextSibling) + { + let value = this.getNodeValue(child); + if (value) + ret += value; + } + + if (ret) + return ret; + } + + return null; + }, + + // Finds elements that are direct children of the first arg. childrenByTagNameNS: function(aElement, aNamespace, aTagName) { - var matches = aElement.getElementsByTagNameNS(aNamespace, aTagName); - var matchingChildren = new Array(); - for (var i = 0; i < matches.length; i++) + let matches = aElement.getElementsByTagNameNS(aNamespace, aTagName); + let matchingChildren = new Array(); + for (let i = 0; i < matches.length; i++) { - if(matches[i].parentNode == aElement) + if (matches[i].parentNode == aElement) matchingChildren.push(matches[i]) } - return matchingChildren; + + return matchingChildren.length ? matchingChildren : null; }, findAtomLink: function(linkRel, linkElements) { - // XXX Need to check for MIME type and hreflang - for ( var j=0 ; j]+>/g,"") : someHTML; + return someHTML ? someHTML.replace(/<[^>]+>/g, "") : someHTML; }, xmlUnescape: function(s) @@ -542,5 +665,41 @@ FeedParser.prototype = s = s.replace(/>/g, ">"); s = s.replace(/= 0 && yeardiff < 3) + // It's quite likely the correct date. + return d.toString(); + } + + if (dateString.search(/^\d\d\d\d/) != -1) + //Could be an ISO8601/W3C date. + return new Date(dateString).toUTCString(); + + // Can't help. Set to current time. + return (new Date()).toString(); + } }; diff --git a/mailnews/extensions/newsblog/content/feed-subscriptions.js b/mailnews/extensions/newsblog/content/feed-subscriptions.js index a7c63002ae..410b5a9fcb 100644 --- a/mailnews/extensions/newsblog/content/feed-subscriptions.js +++ b/mailnews/extensions/newsblog/content/feed-subscriptions.js @@ -137,9 +137,9 @@ var gFeedSubscriptionsWindow = { { // If selecting a prior selected feed, get its folder from the db // in case an ancestor folder was renamed/moved. - let itemResource = rdf.GetResource(item.url); - let ds = getSubscriptionsDS(item.parentFolder.server); - let itemFolder = ds.GetTarget(itemResource, FZ_DESTFOLDER, true); + let itemResource = FeedUtils.rdf.GetResource(item.url); + let ds = FeedUtils.getSubscriptionsDS(item.parentFolder.server); + let itemFolder = ds.GetTarget(itemResource, FeedUtils.FZ_DESTFOLDER, true); if (itemFolder) { itemFolder = itemFolder.QueryInterface(Ci.nsIMsgFolder); @@ -582,7 +582,7 @@ var gFeedSubscriptionsWindow = { // Finally, set the folder's quickMode based on the its first feed's // quickMode, since that is how the view determines summary mode, and now // quickMode is updated to be the same for all feeds in a folder. - if (feeds) + if (feeds && feeds[0]) folderObject.quickMode = feeds[0].quickMode; return folderObject; @@ -591,16 +591,16 @@ var gFeedSubscriptionsWindow = { getFeedsInFolder: function (aFolder) { let feeds = new Array(); - let feedUrlArray = getFeedUrlsInFolder(aFolder); + let feedUrlArray = FeedUtils.getFeedUrlsInFolder(aFolder); if (!feedUrlArray) // No feedUrls in this folder. - return; + return feeds; for (let url in feedUrlArray) { if (!feedUrlArray[url]) continue; - let feedResource = rdf.GetResource(feedUrlArray[url]); + let feedResource = FeedUtils.rdf.GetResource(feedUrlArray[url]); let feed = new Feed(feedResource, aFolder.server); feeds.push(feed); } @@ -887,7 +887,7 @@ var gFeedSubscriptionsWindow = { item.folder.server.setBoolValue("quickMode", aChecked); this.refreshSubscriptionView(); } - else if (!getFeedUrlsInFolder(item.folder)) + else if (!FeedUtils.getFeedUrlsInFolder(item.folder)) // Not a folder with feeds. return; else @@ -945,7 +945,7 @@ var gFeedSubscriptionsWindow = { if (item && item.folder && (locationValue.hasAttribute("focused") || locationValue.value || - item.folder.isServer || getFeedUrlsInFolder(item.folder))) + item.folder.isServer || FeedUtils.getFeedUrlsInFolder(item.folder))) { // Enable summary for account folder or folder with feeds or focus/value // in the feed url field of empty folders prior to add. @@ -981,9 +981,9 @@ var gFeedSubscriptionsWindow = { return; } - deleteFeed(rdf.GetResource(itemToRemove.url), - itemToRemove.parentFolder.server, - itemToRemove.parentFolder); + FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(itemToRemove.url), + itemToRemove.parentFolder.server, + itemToRemove.parentFolder); // Now that we have removed the feed from the datasource, it is time to // update our view layer. Update parent folder's quickMode if necessary @@ -1061,7 +1061,7 @@ var gFeedSubscriptionsWindow = { // Before we go any further, make sure the user is not already subscribed // to this feed. - if (feedAlreadyExists(feedLocation, addFolder.server)) + if (FeedUtils.feedAlreadyExists(feedLocation, addFolder.server)) { message = FeedUtils.strings.GetStringFromName( "subscribe-feedAlreadySubscribed"); @@ -1094,13 +1094,13 @@ var gFeedSubscriptionsWindow = { // Helper routine used by addFeed and importOPMLFile. storeFeed: function(feedProperties) { - let itemResource = rdf.GetResource(feedProperties.feedLocation); + let itemResource = FeedUtils.rdf.GetResource(feedProperties.feedLocation); let feed = new Feed(itemResource, feedProperties.server); // If the user specified a folder to add the feed to, then set it here. if (feedProperties.folderURI) { - let folderResource = rdf.GetResource(feedProperties.folderURI); + let folderResource = FeedUtils.rdf.GetResource(feedProperties.folderURI); if (folderResource) { let folder = folderResource.QueryInterface(Ci.nsIMsgFolder); @@ -1124,10 +1124,10 @@ var gFeedSubscriptionsWindow = { if (!itemToEdit || itemToEdit.container || !itemToEdit.parentFolder) return; - let resource = rdf.GetResource(itemToEdit.url); + let resource = FeedUtils.rdf.GetResource(itemToEdit.url); let currentFolderServer = itemToEdit.parentFolder.server; - let ds = getSubscriptionsDS(currentFolderServer); - let currentFolder = ds.GetTarget(resource, FZ_DESTFOLDER, true); + let ds = FeedUtils.getSubscriptionsDS(currentFolderServer); + let currentFolder = ds.GetTarget(resource, FeedUtils.FZ_DESTFOLDER, true); let currentFolderURI = currentFolder.QueryInterface(Ci.nsIRDFResource).Value; let feed = new Feed(resource, currentFolderServer); feed.folder = itemToEdit.parentFolder; @@ -1213,15 +1213,15 @@ var gFeedSubscriptionsWindow = { let currentParentIndex = this.mView.getParentIndex(aOldFeedIndex); let currentParentItem = this.mView.getItemAtIndex(currentParentIndex); - let currentParentResource = rdf.GetResource(currentParentItem.url); + let currentParentResource = FeedUtils.rdf.GetResource(currentParentItem.url); let currentFolder = currentParentResource.QueryInterface(Ci.nsIMsgFolder); let newParentItem = this.mView.getItemAtIndex(aNewParentIndex); - let newParentResource = rdf.GetResource(newParentItem.url); + let newParentResource = FeedUtils.rdf.GetResource(newParentItem.url); let newFolder = newParentResource.QueryInterface(Ci.nsIMsgFolder); - let ds = getSubscriptionsDS(currentItem.parentFolder.server); - let resource = rdf.GetResource(currentItem.url); + let ds = FeedUtils.getSubscriptionsDS(currentItem.parentFolder.server); + let resource = FeedUtils.rdf.GetResource(currentItem.url); let accountMoveCopy = false; if (currentFolder.rootFolder.URI == newFolder.rootFolder.URI) @@ -1233,14 +1233,14 @@ var gFeedSubscriptionsWindow = { return; // Unassert the older URI, add an assertion for the new parent URI. - ds.Change(resource, FZ_DESTFOLDER, + ds.Change(resource, FeedUtils.FZ_DESTFOLDER, currentParentResource, newParentResource); ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); // Update the feed url attributes on the databases for each folder: // Remove our feed url property from the current folder. - updateFolderFeedUrl(currentFolder, currentItem.url, true); + FeedUtils.updateFolderFeedUrl(currentFolder, currentItem.url, true); // Add our feed url property to the new folder. - updateFolderFeedUrl(newFolder, currentItem.url, false); + FeedUtils.updateFolderFeedUrl(newFolder, currentItem.url, false); } else { @@ -1256,9 +1256,9 @@ var gFeedSubscriptionsWindow = { // Unsubscribe the feed from the old folder, if add to the new folder // is successfull, and doing a move. if (moveFeed) - deleteFeed(rdf.GetResource(currentItem.url), - currentItem.parentFolder.server, - currentItem.parentFolder); + FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(currentItem.url), + currentItem.parentFolder.server, + currentItem.parentFolder); } // Finally, update our view layer. Update old parent folder's quickMode @@ -1300,7 +1300,7 @@ var gFeedSubscriptionsWindow = { { let feedItem = aFeedItem; let parentItem = aParentItem; - let feedUrlArray = getFeedUrlsInFolder(feedItem.parentFolder); + let feedUrlArray = FeedUtils.getFeedUrlsInFolder(feedItem.parentFolder); let feedsInFolder = feedUrlArray ? feedUrlArray.length : 0; if (aRemove && feedsInFolder < 1) @@ -1314,7 +1314,7 @@ var gFeedSubscriptionsWindow = { // only feed, update the parent folder to the feed's quickMode. if (feedsInFolder > 1) { - let feedResource = rdf.GetResource(feedItem.url); + let feedResource = FeedUtils.rdf.GetResource(feedItem.url); let feed = new Feed(feedResource, feedItem.parentFolder.server); feed.quickMode = parentItem.quickMode; feedItem.quickMode = parentItem.quickMode; @@ -1358,11 +1358,11 @@ var gFeedSubscriptionsWindow = { // If we get here we should always have a folder by now, either in // feed.folder or FeedItems created the folder for us. - updateFolderFeedUrl(feed.folder, feed.url, false); + FeedUtils.updateFolderFeedUrl(feed.folder, feed.url, false); // Add feed adds the feed to the subscriptions db and flushes the // datasource. - addFeed(feed.url, feed.name, feed.folder); + FeedUtils.addFeed(feed.url, feed.name, feed.folder); // Now add the feed to our view. If adding, the current selection will // be a folder; if updating it will be a feed. No need to rebuild the @@ -1432,9 +1432,9 @@ var gFeedSubscriptionsWindow = { { // Non success. Remove intermediate traces from the feeds database. if (feed && feed.url && feed.server) - deleteFeed(rdf.GetResource(feed.url), - feed.server, - feed.server.rootFolder); + FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(feed.url), + feed.server, + feed.server.rootFolder); if (aErrorCode == FeedUtils.kNewsBlogInvalidFeed) message = FeedUtils.strings.GetStringFromName( @@ -1466,7 +1466,7 @@ var gFeedSubscriptionsWindow = { this.onProgress(feed, aCurrentFeedItems, aMaxFeedItems); }, - onProgress: function(feed, aProgress, aProgressMax) + onProgress: function(feed, aProgress, aProgressMax, aLengthComputable) { gFeedSubscriptionsWindow.updateStatusItem("progressMeter", (aProgress * 100) / aProgressMax); @@ -1588,7 +1588,10 @@ var gFeedSubscriptionsWindow = { let curSelItem = this.currentSelectedItem; let feedWindow = this.feedWindow; if (aMove && aDestFolder.getFlag(Ci.nsMsgFolderFlags.Trash)) - return this.folderDeleted(aSrcFolder); + { + this.folderDeleted(aSrcFolder); + return + } setTimeout(function() { feedWindow.refreshSubscriptionView(); @@ -1819,7 +1822,7 @@ var gFeedSubscriptionsWindow = { return -1; // Silently skip feeds that are already subscribed. - if (feedAlreadyExists(newFeedUrl, this.mRSSServer)) + if (FeedUtils.feedAlreadyExists(newFeedUrl, this.mRSSServer)) { FeedUtils.log.debug("importOutline: already subscribed in account "+ this.mRSSServer.prettyName+", url - "+ newFeedUrl); @@ -1846,11 +1849,11 @@ var gFeedSubscriptionsWindow = { feed.link = aOutline.getAttribute("htmlUrl"); feed.createFolder(); - updateFolderFeedUrl(feed.folder, feed.url, false); + FeedUtils.updateFolderFeedUrl(feed.folder, feed.url, false); // addFeed adds the feed we have validated and downloaded to // our datasource, it also flushes the subscription datasource. - addFeed(feed.url, feed.name, feed.folder); + FeedUtils.addFeed(feed.url, feed.name, feed.folder); // Feed correctly added. return 1; } diff --git a/mailnews/extensions/newsblog/content/feed-subscriptions.xul b/mailnews/extensions/newsblog/content/feed-subscriptions.xul index 07b43a25c9..359f65d714 100644 --- a/mailnews/extensions/newsblog/content/feed-subscriptions.xul +++ b/mailnews/extensions/newsblog/content/feed-subscriptions.xul @@ -62,8 +62,6 @@ src="chrome://messenger-newsblog/content/utils.js"/>