Bug 721517 - Convert mailnews/extensions/newsblog/content to Services.jsm and mailServices.js. Also fix global scope. r=dbienvenu, neil

This commit is contained in:
alta88 2012-04-18 09:00:46 -06:00
Родитель a659d92c52
Коммит 502cebd462
11 изменённых файлов: 1323 добавлений и 1258 удалений

Просмотреть файл

@ -3249,16 +3249,14 @@ function FeedSetContentView(val)
if (wintype == "mail:3pane") { if (wintype == "mail:3pane") {
// Get quickmode per feed pref from feeds.rdf // Get quickmode per feed pref from feeds.rdf
var quickMode, targetRes; var quickMode, targetRes;
var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] if (!("FeedUtils" in window))
.getService(Components.interfaces.mozIJSSubScriptLoader); Services.scriptloader.loadSubScript("chrome://messenger-newsblog/content/utils.js");
if (scriptLoader && typeof FZ_NS == 'undefined')
scriptLoader.loadSubScript("chrome://messenger-newsblog/content/utils.js");
try try
{ {
var targetRes = getParentTargetForChildResource( var targetRes = FeedUtils.getParentTargetForChildResource(
gFolderDisplay.displayedFolder.URI, gFolderDisplay.displayedFolder.URI,
FZ_QUICKMODE, FeedUtils.FZ_QUICKMODE,
gFolderDisplay.displayedFolder.server); gFolderDisplay.displayedFolder.server);
} }
catch (ex) {}; catch (ex) {};

Просмотреть файл

@ -35,17 +35,10 @@
# #
# ***** END LICENSE BLOCK ***** */ # ***** END LICENSE BLOCK ***** */
// Cache for all of the feeds currently being downloaded, indexed by URL,
// error codes used to inform the consumer about attempts to download a feed // so the load event listener can access the Feed objects after it finishes
const kNewsBlogSuccess = 0; // downloading the feed.
const kNewsBlogInvalidFeed = 1; // usually means there was an error trying to parse the feed... var FeedCache =
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 =
{ {
mFeeds: {}, mFeeds: {},
@ -56,7 +49,7 @@ var FeedCache =
getFeed: function (aUrl) getFeed: function (aUrl)
{ {
var index = this.normalizeHost(aUrl); let index = this.normalizeHost(aUrl);
if (index in this.mFeeds) if (index in this.mFeeds)
return this.mFeeds[index]; return this.mFeeds[index];
return null; return null;
@ -64,7 +57,7 @@ var FeedCache =
removeFeed: function (aUrl) removeFeed: function (aUrl)
{ {
var index = this.normalizeHost(aUrl); let index = this.normalizeHost(aUrl);
if (index in this.mFeeds) if (index in this.mFeeds)
delete this.mFeeds[index]; 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; this.server = aRSSServer;
} }
@ -101,30 +94,33 @@ Feed.prototype =
items: new Array(), items: new Array(),
mFolder: null, mFolder: null,
mInvalidFeed: false, mInvalidFeed: false,
mFeedType: null,
get folder() get folder()
{ {
if (!this.mFolder) if (!this.mFolder)
{ {
try try
{ {
this.mFolder = this.server.rootMsgFolder.getChildNamed(this.name); this.mFolder = this.server.rootMsgFolder.getChildNamed(this.name);
} catch (ex) {} }
catch (ex) {}
} }
return this.mFolder; return this.mFolder;
}, },
set folder (aFolder) set folder (aFolder)
{ {
this.mFolder = aFolder; this.mFolder = aFolder;
}, },
get name() get name()
{ {
var name = this.title || this.description || this.url; let name = this.title || this.description || this.url;
if (!name) 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 // 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 // to use it as the name of the folder in the filesystem. This may not
@ -139,44 +135,52 @@ Feed.prototype =
return name; return name;
}, },
download: function(aParseItems, aCallback) download: function(aParseItems, aCallback)
{ {
this.downloadCallback = aCallback; // may be null // May be null.
this.downloadCallback = aCallback;
// Whether or not to parse items when downloading and parsing the feed. // Whether or not to parse items when downloading and parsing the feed.
// Defaults to true, but setting to false is useful for obtaining // Defaults to true, but setting to false is useful for obtaining
// just the title of the feed when the user subscribes to it. // just the title of the feed when the user subscribes to it.
this.parseItems = aParseItems == null ? true : aParseItems ? true : false; 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 // Before we do anything, make sure the url is an http url. This is just
// so we don't try opening mailto urls, imap urls, etc. that the user may have tried to subscribe to // a sanity check so we don't try opening mailto urls, imap urls, etc. that
// as an rss feed.. // the user may have tried to subscribe to as an rss feed.
var uri = Components.classes["@mozilla.org/network/standard-url;1"]. let uri = Cc["@mozilla.org/network/standard-url;1"].
createInstance(Components.interfaces.nsIURI); createInstance(Ci.nsIURI);
uri.spec = this.url; 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; return;
} }
// Before we try to download the feed, make sure we aren't already processing the feed // Before we try to download the feed, make sure we aren't already
// by looking up the url in our feed cache // processing the feed by looking up the url in our feed cache.
if (FeedCache.getFeed(this.url)) if (FeedCache.getFeed(this.url))
{ {
if (this.downloadCallback) if (this.downloadCallback)
this.downloadCallback.downloaded(this, kNewsBlogFeedIsBusy); this.downloadCallback.downloaded(this, FeedUtils.kNewsBlogFeedIsBusy);
return ; // don't do anything, the feed is already in use // Return, the feed is already in use.
return;
} }
this.request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
.createInstance(Components.interfaces.nsIXMLHttpRequest); createInstance(Ci.nsIXMLHttpRequest);
this.request.onprogress = this.onProgress; // must be set before calling .open // Must set onProgress before calling open.
this.request.onprogress = this.onProgress;
this.request.open("GET", this.url, true); this.request.open("GET", this.url, true);
var lastModified = this.lastModified; let lastModified = this.lastModified;
if (lastModified) // Some servers, if sent If-Modified-Since, will send 304 if subsequently
this.request.setRequestHeader("If-Modified-Since", lastModified); // 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... // Only order what you're going to eat...
this.request.responseType = "document"; this.request.responseType = "document";
@ -185,95 +189,103 @@ Feed.prototype =
this.request.onerror = this.onDownloadError; this.request.onerror = this.onDownloadError;
FeedCache.putFeed(this); FeedCache.putFeed(this);
this.request.send(null); this.request.send(null);
}, },
onDownloaded: function(aEvent) onDownloaded: function(aEvent)
{ {
var request = aEvent.target; let request = aEvent.target;
var url = request.channel.originalURI.spec; let isHttp = /^http(s?)/.test(request.channel.originalURI.scheme);
debug(url + " downloaded"); let url = request.channel.originalURI.spec;
if (request.status < 200 || request.status >= 300) if (isHttp && (request.status < 200 || request.status >= 300))
{ {
Feed.prototype.onDownloadError(aEvent); Feed.prototype.onDownloadError(aEvent);
return; return;
} }
var feed = FeedCache.getFeed(url);
FeedUtils.log.debug("Feed.onDownloaded: got a download - " + url);
let feed = FeedCache.getFeed(url);
if (!feed) 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 // 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 // feed so we can use it when making future requests, to avoid downloading
// and parsing feeds that have not changed. // and parsing feeds that have not changed.
var lastModifiedHeader = request.getResponseHeader('Last-Modified'); let lastModifiedHeader = request.getResponseHeader("Last-Modified");
if (lastModifiedHeader) if (lastModifiedHeader)
feed.lastModified = lastModifiedHeader; feed.lastModified = lastModifiedHeader;
// The download callback is called asynchronously when parse() is done. // The download callback is called asynchronously when parse() is done.
feed.parse(); feed.parse();
}, },
onProgress: function(aEvent) onProgress: function(aEvent)
{ {
var request = aEvent.target; let request = aEvent.target;
var url = request.channel.originalURI.spec; let url = request.channel.originalURI.spec;
var feed = FeedCache.getFeed(url); let feed = FeedCache.getFeed(url);
if (feed.downloadCallback) 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; let request = aEvent.target;
var url = request.channel.originalURI.spec; let url = request.channel.originalURI.spec;
var feed = FeedCache.getFeed(url); let feed = FeedCache.getFeed(url);
if (feed.downloadCallback) if (feed.downloadCallback)
{ {
var error = kNewsBlogRequestFailure; let error = FeedUtils.kNewsBlogRequestFailure;
try try
{ {
if (request.status == 304) if (request.status == 304)
// If the http status code is 304, the feed has not been modified // 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. // since we last downloaded it and does not need to be parsed.
error = kNewsBlogNoNewItems; error = FeedUtils.kNewsBlogNoNewItems;
} catch (ex) {} }
catch (ex) {}
feed.downloadCallback.downloaded(feed, error); feed.downloadCallback.downloaded(feed, error);
} }
FeedCache.removeFeed(url); FeedCache.removeFeed(url);
}, },
onParseError: function(aFeed) onParseError: function(aFeed)
{ {
if (aFeed) if (!aFeed)
{ return;
aFeed.mInvalidFeed = true;
aFeed.lastModified = "";
if (aFeed.downloadCallback) aFeed.mInvalidFeed = true;
aFeed.downloadCallback.downloaded(aFeed, kNewsBlogInvalidFeed); aFeed.lastModified = "";
FeedCache.removeFeed(aFeed.url); if (aFeed.downloadCallback)
} aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogInvalidFeed);
FeedCache.removeFeed(aFeed.url);
}, },
get url() get url()
{ {
var ds = getSubscriptionsDS(this.server); let ds = FeedUtils.getSubscriptionsDS(this.server);
var url = ds.GetTarget(this.resource, DC_IDENTIFIER, true); let url = ds.GetTarget(this.resource, FeedUtils.DC_IDENTIFIER, true);
if (url) if (url)
url = url.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; url = url.QueryInterface(Ci.nsIRDFLiteral).Value;
else else
url = this.resource.Value; url = this.resource.Value;
return url; return url;
}, },
get title() get title()
{ {
var ds = getSubscriptionsDS(this.server); let ds = FeedUtils.getSubscriptionsDS(this.server);
var title = ds.GetTarget(this.resource, DC_TITLE, true); let title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
if (title) if (title)
title = title.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; title = title.QueryInterface(Ci.nsIRDFLiteral).Value;
return title; return title;
}, },
@ -282,69 +294,78 @@ Feed.prototype =
if (!aNewTitle) if (!aNewTitle)
return; return;
var ds = getSubscriptionsDS(this.server); let ds = FeedUtils.getSubscriptionsDS(this.server);
aNewTitle = rdf.GetLiteral(aNewTitle); aNewTitle = FeedUtils.rdf.GetLiteral(aNewTitle);
var old_title = ds.GetTarget(this.resource, DC_TITLE, true); let old_title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
if (old_title) if (old_title)
ds.Change(this.resource, DC_TITLE, old_title, aNewTitle); ds.Change(this.resource, FeedUtils.DC_TITLE, old_title, aNewTitle);
else else
ds.Assert(this.resource, DC_TITLE, aNewTitle, true); ds.Assert(this.resource, FeedUtils.DC_TITLE, aNewTitle, true);
}, },
get lastModified() get lastModified()
{ {
var ds = getSubscriptionsDS(this.server); let ds = FeedUtils.getSubscriptionsDS(this.server);
var lastModified = ds.GetTarget(this.resource, DC_LASTMODIFIED, true); let lastModified = ds.GetTarget(this.resource,
FeedUtils.DC_LASTMODIFIED,
true);
if (lastModified) if (lastModified)
lastModified = lastModified.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; lastModified = lastModified.QueryInterface(Ci.nsIRDFLiteral).Value;
return lastModified; return lastModified;
}, },
set lastModified(aLastModified) set lastModified(aLastModified)
{ {
var ds = getSubscriptionsDS(this.server); let ds = FeedUtils.getSubscriptionsDS(this.server);
aLastModified = rdf.GetLiteral(aLastModified); aLastModified = FeedUtils.rdf.GetLiteral(aLastModified);
var old_lastmodified = ds.GetTarget(this.resource, DC_LASTMODIFIED, true); let old_lastmodified = ds.GetTarget(this.resource,
FeedUtils.DC_LASTMODIFIED,
true);
if (old_lastmodified) if (old_lastmodified)
ds.Change(this.resource, DC_LASTMODIFIED, old_lastmodified, aLastModified); ds.Change(this.resource, FeedUtils.DC_LASTMODIFIED,
old_lastmodified, aLastModified);
else 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? // Do we need to flush every time this property changes?
ds = ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource); ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
ds.Flush();
}, },
get quickMode () get quickMode ()
{ {
var ds = getSubscriptionsDS(this.server); let ds = FeedUtils.getSubscriptionsDS(this.server);
var quickMode = ds.GetTarget(this.resource, FZ_QUICKMODE, true); let quickMode = ds.GetTarget(this.resource, FeedUtils.FZ_QUICKMODE, true);
if (quickMode) if (quickMode)
{ {
quickMode = quickMode.QueryInterface(Components.interfaces.nsIRDFLiteral); quickMode = quickMode.QueryInterface(Ci.nsIRDFLiteral);
quickMode = quickMode.Value; quickMode = quickMode.Value == "true" ? true : false;
quickMode = eval(quickMode); }
}
return quickMode; return quickMode;
}, },
set quickMode (aNewQuickMode) set quickMode (aNewQuickMode)
{ {
var ds = getSubscriptionsDS(this.server); let ds = FeedUtils.getSubscriptionsDS(this.server);
aNewQuickMode = rdf.GetLiteral(aNewQuickMode); aNewQuickMode = FeedUtils.rdf.GetLiteral(aNewQuickMode);
var old_quickMode = ds.GetTarget(this.resource, FZ_QUICKMODE, true); let old_quickMode = ds.GetTarget(this.resource,
FeedUtils.FZ_QUICKMODE,
true);
if (old_quickMode) if (old_quickMode)
ds.Change(this.resource, FZ_QUICKMODE, old_quickMode, aNewQuickMode); ds.Change(this.resource, FeedUtils.FZ_QUICKMODE,
old_quickMode, aNewQuickMode);
else else
ds.Assert(this.resource, FZ_QUICKMODE, aNewQuickMode, true); ds.Assert(this.resource, FeedUtils.FZ_QUICKMODE,
aNewQuickMode, true);
}, },
get link () get link ()
{ {
var ds = getSubscriptionsDS(this.server); let ds = FeedUtils.getSubscriptionsDS(this.server);
var link = ds.GetTarget(this.resource, RSS_LINK, true); let link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
if(link) if (link)
link = link.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; link = link.QueryInterface(Ci.nsIRDFLiteral).Value;
return link; return link;
}, },
@ -353,19 +374,19 @@ Feed.prototype =
if (!aNewLink) if (!aNewLink)
return; return;
var ds = getSubscriptionsDS(this.server); let ds = FeedUtils.getSubscriptionsDS(this.server);
aNewLink = rdf.GetLiteral(aNewLink); aNewLink = FeedUtils.rdf.GetLiteral(aNewLink);
var old_link = ds.GetTarget(this.resource, RSS_LINK, true); let old_link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
if (old_link) if (old_link)
ds.Change(this.resource, RSS_LINK, old_link, aNewLink); ds.Change(this.resource, FeedUtils.RSS_LINK, old_link, aNewLink);
else 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. // Create a feed parser which will parse the feed.
var parser = new FeedParser(); let parser = new FeedParser();
this.itemsToStore = parser.parseFeed(this, this.itemsToStore = parser.parseFeed(this,
this.request.responseXML, this.request.responseXML,
this.request.channel.URI); this.request.channel.URI);
@ -376,144 +397,148 @@ Feed.prototype =
return; 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.itemsToStoreIndex = 0;
this.storeNextItem(); this.storeNextItem();
}, },
invalidateItems: function () invalidateItems: function ()
{ {
var ds = getItemsDS(this.server); let ds = FeedUtils.getItemsDS(this.server);
debug("invalidating items for " + this.url); FeedUtils.log.debug("Feed.invalidateItems: for url - " + this.url);
var items = ds.GetSources(FZ_FEED, this.resource, true); let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
var item; let item;
while (items.hasMoreElements()) while (items.hasMoreElements())
{ {
item = items.getNext(); item = items.getNext();
item = item.QueryInterface(Components.interfaces.nsIRDFResource); item = item.QueryInterface(Ci.nsIRDFResource);
debug("invalidating " + item.Value); FeedUtils.log.trace("Feed.invalidateItems: item - " + item.Value);
var valid = ds.GetTarget(item, FZ_VALID, true); let valid = ds.GetTarget(item, FeedUtils.FZ_VALID, true);
if (valid) 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); let ds = FeedUtils.getItemsDS(this.server);
debug("removing invalid items for " + this.url); FeedUtils.log.debug("Feed.removeInvalidItems: for url - " + this.url);
var items = ds.GetSources(FZ_FEED, this.resource, true); let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
var item; let item;
var currentTime = new Date().getTime(); let currentTime = new Date().getTime();
while (items.hasMoreElements()) while (items.hasMoreElements())
{ {
item = items.getNext(); 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; continue;
var lastSeenTime = ds.GetTarget(item, FZ_LAST_SEEN_TIMESTAMP, true); let lastSeenTime = ds.GetTarget(item, FeedUtils.FZ_LAST_SEEN_TIMESTAMP, true);
lastSeenTime = lastSeenTime ? if (lastSeenTime)
parseInt(lastSeenTime lastSeenTime = parseInt(lastSeenTime.QueryInterface(Ci.nsIRDFLiteral).Value)
.QueryInterface(Components.interfaces.nsIRDFLiteral) else
.Value) : 0; lastSeenTime = 0;
if ((currentTime - lastSeenTime) < INVALID_ITEM_PURGE_DELAY && !aDeleteFeed)
if ((currentTime - lastSeenTime) < FeedUtils.INVALID_ITEM_PURGE_DELAY &&
!aDeleteFeed)
// Don't immediately purge items in active feeds; do so for deleted feeds. // Don't immediately purge items in active feeds; do so for deleted feeds.
continue; continue;
debug("removing " + item.Value); FeedUtils.log.trace("Feed.removeInvalidItems: item - " + item.Value);
ds.Unassert(item, FZ_FEED, this.resource, true); ds.Unassert(item, FeedUtils.FZ_FEED, this.resource, true);
if (ds.hasArcOut(item, FZ_FEED)) if (ds.hasArcOut(item, FeedUtils.FZ_FEED))
debug(item.Value + " is from more than one feed; only the reference to this feed removed"); FeedUtils.log.debug("Feed.removeInvalidItems: " + item.Value +
" is from more than one feed; only the reference to" +
" this feed removed");
else else
removeAssertions(ds, item); FeedUtils.removeAssertions(ds, item);
} }
}, },
createFolder: function() createFolder: function()
{ {
if (!this.folder) if (!this.folder)
this.server.rootMsgFolder.createSubfolder(this.name, null /* supposed to be a msg window */); this.server.rootMsgFolder.createSubfolder(this.name, null);
}, },
// gets the next item from gItemsToStore and forces that item to be stored // 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. // to the folder. If more items are left to be stored, fires a timer for
// otherwise it triggers a download done notification to the UI // the next one, otherwise triggers a download done notification to the UI.
storeNextItem: function() storeNextItem: function()
{ {
if (!this.itemsToStore || !this.itemsToStore.length) if (!this.itemsToStore || !this.itemsToStore.length)
{ {
this.createFolder(); this.createFolder();
this.cleanupParsingState(this); this.cleanupParsingState(this);
return; return;
} }
var item = this.itemsToStore[this.itemsToStoreIndex]; let item = this.itemsToStore[this.itemsToStoreIndex];
item.store(); item.store();
this.itemsToStoreIndex++; 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) if (item.feed.downloadCallback && item.feed.downloadCallback.onFeedItemStored)
item.feed.downloadCallback.onFeedItemStored(item.feed, this.itemsToStoreIndex, this.itemsToStore.length); item.feed.downloadCallback.onFeedItemStored(item.feed,
this.itemsToStoreIndex,
// eventually we'll report individual progress here.... this.itemsToStore.length);
// Eventually we'll report individual progress here.
if (this.itemsToStoreIndex < this.itemsToStore.length) if (this.itemsToStoreIndex < this.itemsToStore.length)
{ {
if (!this.storeItemsTimer) if (!this.storeItemsTimer)
this.storeItemsTimer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); this.storeItemsTimer = Cc["@mozilla.org/timer;1"].
this.storeItemsTimer.initWithCallback(this, 50, Components.interfaces.nsITimer.TYPE_ONE_SHOT); createInstance(Ci.nsITimer);
this.storeItemsTimer.initWithCallback(this, 50, Ci.nsITimer.TYPE_ONE_SHOT);
} }
else else
{ {
// we have just finished downloading one or more feed items into the destination folder, // We have just finished downloading one or more feed items into the
// if the folder is still listed as having new messages in it, then we should set the biff state on the folder // destination folder; if the folder is still listed as having new
// so the right RDF UI changes happen in the folder pane to indicate new mail. // 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) if (item.feed.folder.hasNewMessages)
{ {
item.feed.folder.biffState = Components.interfaces.nsIMsgFolder.nsMsgBiffState_NewMail; item.feed.folder.biffState = Ci.nsIMsgFolder.nsMsgBiffState_NewMail;
// run the bayesian spam filter, if enabled // Run the bayesian spam filter, if enabled.
item.feed.folder.callFilterPlugins(null); item.feed.folder.callFilterPlugins(null);
} }
this.cleanupParsingState(item.feed); 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); FeedCache.removeFeed(aFeed.url);
aFeed.removeInvalidItems(false); aFeed.removeInvalidItems(false);
// let's be sure to flush any feed item changes back to disk // Flush any feed item changes to disk.
var ds = getItemsDS(aFeed.server); let ds = FeedUtils.getItemsDS(aFeed.server);
ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush(); // flush any changes ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
if (aFeed.downloadCallback) 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.itemsToStore = "";
this.itemsToStoreIndex = 0; this.itemsToStoreIndex = 0;
this.storeItemsTimer = null; this.storeItemsTimer = null;
}, },
notify: function(aTimer) // nsITimerCallback
notify: function(aTimer)
{ {
this.storeNextItem(); 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;
} }
}; };

Просмотреть файл

@ -35,52 +35,46 @@
# #
# ***** END LICENSE BLOCK ***** */ # ***** 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\
<html>\n\
<head>\n\
<title>%TITLE%</title>\n\
<base href=\"%BASE%\">\n\
</head>\n\
<body id=\"msgFeedSummaryBody\" selected=\"false\">\n\
%CONTENT%\n\
</body>\n\
</html>\n\
";
function FeedItem() function FeedItem()
{ {
this.mDate = new Date().toString(); this.mDate = new Date().toString();
this.mUnicodeConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] this.mUnicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter); createInstance(Ci.nsIScriptableUnicodeConverter);
this.mUnescapeHTML = Cc["@mozilla.org/feed-unescapehtml;1"].
getService(Ci.nsIScriptableUnescapeHTML);
} }
FeedItem.prototype = 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, isStoredWithId: false,
// Only for IETF Atom // Only for IETF Atom.
xmlContentBase: null, xmlContentBase: null,
id: null, id: null,
feed: null, feed: null,
description: null, description: null,
content: null, content: null,
// Currently only support one enclosure per feed item... // Currently only support one enclosure per feed item.
enclosure: null, enclosure: null,
// TO DO: this needs to be localized // TO DO: this needs to be localized.
title: "(no subject)", title: "(no subject)",
author: "anonymous", author: "anonymous",
mURL: null, mURL: null,
characterSet: "", characterSet: "",
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
ENCLOSURE_HEADER_BOUNDARY_PREFIX: "------------", // 12 dashes
MESSAGE_TEMPLATE: '\n' +
'<html>\n' +
' <head>\n' +
' <title>%TITLE%</title>\n' +
' <base href="%BASE%">\n' +
' </head>\n' +
' <body id="msgFeedSummaryBody" selected="false">\n' +
' %CONTENT%\n' +
' </body>\n' +
'</html>\n',
get url() get url()
{ {
return this.mURL; return this.mURL;
@ -90,9 +84,7 @@ FeedItem.prototype =
{ {
try try
{ {
var ioService = Components.classes["@mozilla.org/network/io-service;1"] this.mURL = Services.io.newURI(aVal, null, null).spec;
.getService(Components.interfaces.nsIIOService);
this.mURL = ioService.newURI(aVal, null, null).spec;
} }
catch(ex) catch(ex)
{ {
@ -120,25 +112,26 @@ FeedItem.prototype =
get messageID() get messageID()
{ {
var messageID = this.id || this.mURL || this.title; let messageID = this.id || this.mURL || this.title;
debug('messageID - id = ' + this.id); FeedUtils.log.trace("FeedItem.messageID: id - " + this.id);
debug('messageID - mURL = ' + this.mURL); FeedUtils.log.trace("FeedItem.messageID: mURL - " + this.mURL);
debug('messageID - title = ' + this.title); FeedUtils.log.trace("FeedItem.messageID: title - " + this.title);
debug('messageID - messageID = ' + messageID);
// Escape occurrences of message ID meta characters <, >, and @. // Escape occurrences of message ID meta characters <, >, and @.
messageID.replace(/</g, "%3C"); messageID.replace(/</g, "%3C");
messageID.replace(/>/g, "%3E"); messageID.replace(/>/g, "%3E");
messageID.replace(/@/g, "%40"); messageID.replace(/@/g, "%40");
messageID = messageID + "@" + "localhost.localdomain"; messageID = messageID + "@" + "localhost.localdomain";
FeedUtils.log.trace("FeedItem.messageID: messageID - " + messageID);
return messageID; return messageID;
}, },
get itemUniqueURI() get itemUniqueURI()
{ {
return (this.isStoredWithId && this.id) ? createURN(this.id) : return this.isStoredWithId && this.id ? this.createURN(this.id) :
createURN(this.mURL || this.id); this.createURN(this.mURL || this.id);
}, },
get contentBase() get contentBase()
@ -153,25 +146,27 @@ FeedItem.prototype =
{ {
this.mUnicodeConverter.charset = this.characterSet; this.mUnicodeConverter.charset = this.characterSet;
// this.title and this.content contain HTML // this.title and this.content contain HTML.
// this.mUrl and this.contentBase contain plain text // this.mUrl and this.contentBase contain plain text.
let resource = this.findStoredResource(); let resource = this.findStoredResource();
if (resource == null) if (resource == null)
{ {
resource = rdf.GetResource(this.itemUniqueURI); resource = FeedUtils.rdf.GetResource(this.itemUniqueURI);
if (!this.content) 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; this.content = this.description || this.title;
} }
debug(this.identity + " store both remote/no content and content items"); FeedUtils.log.trace("FeedItem.store: " + this.identity +
var content = MESSAGE_TEMPLATE; " store both remote/no content and content items");
let content = this.MESSAGE_TEMPLATE;
content = content.replace(/%TITLE%/, this.title); 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); 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.content = content;
this.writeToFolder(); this.writeToFolder();
this.markStored(resource); this.markStored(resource);
@ -181,37 +176,39 @@ FeedItem.prototype =
findStoredResource: function() 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"); let server = this.feed.server;
let folder = this.feed.folder;
var server = this.feed.server;
var folder = this.feed.folder;
if (!folder) if (!folder)
{ {
debug(this.feed.name + " folder doesn't exist; creating"); FeedUtils.log.debug("FeedItem.findStoredResource: " + this.feed.name +
debug("creating " + this.feed.name + "as child of " + " folder doesn't exist; creating as child of " +
server.rootMsgFolder + "\n"); server.rootMsgFolder.prettyName + "\n");
server.rootMsgFolder.createSubfolder(this.feed.name, null /* supposed to be a msg window */); server.rootMsgFolder.createSubfolder(this.feed.name, null);
folder = server.rootMsgFolder.findSubFolder(this.feed.name); 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; return null;
} }
var ds = getItemsDS(server); let ds = FeedUtils.getItemsDS(server);
var itemURI = this.itemUniqueURI; let itemURI = this.itemUniqueURI;
var itemResource = rdf.GetResource(itemURI); 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 // Backward compatibility: we might have stored this item before
// isStoredWithId has been turned on for RSS 2.0 (bug 354345). // isStoredWithId has been turned on for RSS 2.0 (bug 354345).
// Check whether this item has been stored with its URL. // Check whether this item has been stored with its URL.
if (!downloaded && this.mURL && itemURI != this.mURL) if (!downloaded && this.mURL && itemURI != this.mURL)
{ {
itemResource = rdf.GetResource(this.mURL); itemResource = FeedUtils.rdf.GetResource(this.mURL);
downloaded = ds.GetTarget(itemResource, FZ_STORED, true); downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
} }
// Backward compatibility: the item may have been stored // Backward compatibility: the item may have been stored
@ -219,97 +216,108 @@ FeedItem.prototype =
// (bug 410842 & bug 461109) // (bug 410842 & bug 461109)
if (!downloaded) if (!downloaded)
{ {
itemResource = rdf.GetResource((this.isStoredWithId && this.id) ? itemResource = FeedUtils.rdf.GetResource((this.isStoredWithId && this.id) ?
("urn:" + this.id) : ("urn:" + this.id) :
(this.mURL || ("urn:" + this.id))); (this.mURL || ("urn:" + this.id)));
downloaded = ds.GetTarget(itemResource, FZ_STORED, true); downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
} }
if (!downloaded || if (!downloaded ||
downloaded.QueryInterface(Components.interfaces.nsIRDFLiteral) downloaded.QueryInterface(Ci.nsIRDFLiteral).Value == "false")
.Value == "false")
{ {
// HACK ALERT: before we give up, try to work around an entity // 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(/&lt;/g, '<'); itemURI = itemURI.replace(/&lt;/g, '<');
itemURI = itemURI.replace(/&gt;/g, '>'); itemURI = itemURI.replace(/&gt;/g, '>');
itemURI = itemURI.replace(/&quot;/g, '"'); itemURI = itemURI.replace(/&quot;/g, '"');
itemURI = itemURI.replace(/&amp;/g, '&'); itemURI = itemURI.replace(/&amp;/g, '&');
debug('Failed to find item, trying entity replacement version: ' + itemURI); FeedUtils.log.trace("FeedItem.findStoredResource: failed to find item," +
itemResource = rdf.GetResource(itemURI); " trying entity replacement version - " + itemURI);
downloaded = ds.GetTarget(itemResource, FZ_STORED, true); itemResource = FeedUtils.rdf.GetResource(itemURI);
downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
if (downloaded) if (downloaded)
{ {
debug(this.identity + " stored"); FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity +
" stored");
return itemResource; return itemResource;
} }
debug(this.identity + " not stored"); FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity +
" not stored");
return null; return null;
} }
else else
{ {
debug(this.identity + " stored"); FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity +
" stored");
return itemResource; return itemResource;
} }
}, },
markValid: function(resource) markValid: function(resource)
{ {
var ds = getItemsDS(this.feed.server); let ds = FeedUtils.getItemsDS(this.feed.server);
var newTimeStamp = rdf.GetLiteral(new Date().getTime()); let newTimeStamp = FeedUtils.rdf.GetLiteral(new Date().getTime());
var currentTimeStamp = ds.GetTarget(resource, FZ_LAST_SEEN_TIMESTAMP, true); let currentTimeStamp = ds.GetTarget(resource,
FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
true);
if (currentTimeStamp) if (currentTimeStamp)
ds.Change(resource, FZ_LAST_SEEN_TIMESTAMP, currentTimeStamp, newTimeStamp); ds.Change(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
currentTimeStamp, newTimeStamp);
else 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)) if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
ds.Assert(resource, FZ_FEED, rdf.GetResource(this.feed.url), true); 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); let currentValue = ds.GetTarget(resource, FeedUtils.FZ_VALID, true);
ds.Change(resource, FZ_VALID, currentValue, RDF_LITERAL_TRUE); ds.Change(resource, FeedUtils.FZ_VALID,
currentValue, FeedUtils.RDF_LITERAL_TRUE);
} }
else else
ds.Assert(resource, FZ_VALID, RDF_LITERAL_TRUE, true); ds.Assert(resource, FeedUtils.FZ_VALID, FeedUtils.RDF_LITERAL_TRUE, true);
}, },
markStored: function(resource) 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)) if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
ds.Assert(resource, FZ_FEED, rdf.GetResource(this.feed.url), true); FeedUtils.rdf.GetResource(this.feed.url), true))
ds.Assert(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true);
var currentValue; let currentValue;
if (ds.hasArcOut(resource, FZ_STORED)) if (ds.hasArcOut(resource, FeedUtils.FZ_STORED))
{ {
currentValue = ds.GetTarget(resource, FZ_STORED, true); currentValue = ds.GetTarget(resource, FeedUtils.FZ_STORED, true);
ds.Change(resource, FZ_STORED, currentValue, RDF_LITERAL_TRUE); ds.Change(resource, FeedUtils.FZ_STORED,
currentValue, FeedUtils.RDF_LITERAL_TRUE);
} }
else 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) 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 // This routine sometimes throws exceptions for mis-encoded data so
// wrap it with a try catch for now.. // wrap it with a try catch for now.
var newSubject; let newSubject;
try try
{ {
newSubject = mimeEncoder.encodeMimePartIIStr( newSubject = mailServices.mimeConverter.encodeMimePartIIStr(
this.mUnicodeConverter.ConvertFromUnicode(aSubject), this.mUnicodeConverter.ConvertFromUnicode(aSubject),
false, aCharset, 9, 72); false,
aCharset, 9, 72);
} }
catch (ex) catch (ex)
{ {
@ -321,24 +329,21 @@ FeedItem.prototype =
writeToFolder: function() writeToFolder: function()
{ {
debug(this.identity + " writing to message folder" + this.feed.name + "\n"); FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity +
" writing to message folder " + this.feed.name);
var server = this.feed.server;
this.mUnicodeConverter.charset = this.characterSet; this.mUnicodeConverter.charset = this.characterSet;
// If the sender isn't a valid email address, quote it so it looks nicer. // If the sender isn't a valid email address, quote it so it looks nicer.
if (this.author && this.author.indexOf('@') == -1) if (this.author && this.author.indexOf("@") == -1)
this.author = '<' + this.author + '>'; this.author = "<" + this.author + ">";
// Convert the title to UTF-16 before performing our HTML entity // Convert the title to UTF-16 before performing our HTML entity
// replacement reg expressions. // replacement reg expressions.
var title = this.title; let title = this.title;
// the subject may contain HTML entities. // The subject may contain HTML entities. Convert these to their unencoded
// Convert these to their unencoded state. i.e. &amp; becomes '&' // state. i.e. &amp; becomes '&'.
title = Components.classes["@mozilla.org/feed-unescapehtml;1"] title = this.mUnescapeHTML.unescape(title);
.getService(Components.interfaces.nsIScriptableUnescapeHTML)
.unescape(title);
// Compress white space in the subject to make it look better. Trim // Compress white space in the subject to make it look better. Trim
// leading/trailing spaces to prevent mbox header folding issue at just // 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 // use it to calculate the offset of the X-Mozilla-Status lines from
// the front of the message for the statusOffset property of the // the front of the message for the statusOffset property of the
// DB header object. // DB header object.
var openingLine = 'From - ' + this.mDate + '\n'; let openingLine = 'From - ' + this.mDate + '\n';
var source = let source =
openingLine + openingLine +
'X-Mozilla-Status: 0000\n' + 'X-Mozilla-Status: 0000\n' +
'X-Mozilla-Status2: 00000000\n' + 'X-Mozilla-Status2: 00000000\n' +
@ -381,9 +386,11 @@ FeedItem.prototype =
if (this.enclosure && this.enclosure.mFileName) if (this.enclosure && this.enclosure.mFileName)
{ {
var boundaryID = source.length + this.enclosure.mLength; let boundaryID = source.length + this.enclosure.mLength;
source += 'Content-Type: multipart/mixed;\n boundary="' + ENCLOSURE_HEADER_BOUNDARY_PREFIX + boundaryID + '"' + '\n\n' + source += 'Content-Type: multipart/mixed;\n boundary="' +
'This is a multi-part message in MIME format.\n' + ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '\n' + 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-Type: text/html; charset=' + this.characterSet + '\n' +
'Content-Transfer-Encoding: 8bit\n' + 'Content-Transfer-Encoding: 8bit\n' +
this.content; 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. // Get the folder and database storing the feed's messages and headers.
var folder = this.feed.folder.QueryInterface(Components.interfaces.nsIMsgLocalMailFolder); let folder = this.feed.folder.QueryInterface(Ci.nsIMsgLocalMailFolder);
var msgFolder = folder.QueryInterface(Components.interfaces.nsIMsgFolder); let msgFolder = folder.QueryInterface(Ci.nsIMsgFolder);
msgFolder.gettingNewMessages = true; msgFolder.gettingNewMessages = true;
// Source is a unicode string, we want to save a char * string in // 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)); folder.addMessage(this.mUnicodeConverter.ConvertFromUnicode(source));
msgFolder.gettingNewMessages = false; msgFolder.gettingNewMessages = false;
},
htmlEscape: function(s)
{
s = s.replace(/&/g, "&amp;");
s = s.replace(/>/g, "&gt;");
s = s.replace(/</g, "&lt;");
s = s.replace(/'/g, "&#39;");
s = s.replace(/"/g, "&quot;");
return s;
},
createURN: function(aName)
{
// Returns name as a URN in the 'feeditem' namespace. The returned URN is
// (or is intended to be) RFC2141 compliant.
// The builtin encodeURI provides nearly the exact encoding functionality
// required by the RFC. The exceptions are that NULL characters should not
// appear, and that #, /, ?, &, and ~ should be escaped.
// NULL characters are removed before encoding.
let name = aName.replace(/\0/g, "");
let encoded = encodeURI(name);
encoded = encoded.replace(/\#/g, "%23");
encoded = encoded.replace(/\//g, "%2f");
encoded = encoded.replace(/\?/g, "%3f");
encoded = encoded.replace(/\&/g, "%26");
encoded = encoded.replace(/\~/g, "%7e");
return FeedUtils.FZ_ITEM_NS + encoded;
} }
}; };
// A feed enclosure is to RSS what an attachment is for e-mail. We make enclosures look // A feed enclosure is to RSS what an attachment is for e-mail. We make
// like attachments in the UI. // enclosures look like attachments in the UI.
function FeedEnclosure(aURL, aContentType, aLength) function FeedEnclosure(aURL, aContentType, aLength)
{ {
this.mURL = aURL; this.mURL = aURL;
this.mContentType = aContentType; this.mContentType = aContentType;
this.mLength = aLength; this.mLength = aLength;
// generate a fileName from the URL // Generate a fileName from the URL.
if (this.mURL) if (this.mURL)
{ {
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var enclosureURL, fileName;
try try
{ {
enclosureURL = ioService.newURI(this.mURL, null, null) this.mFileName = Services.io.newURI(this.mURL, null, null).
.QueryInterface(Components.interfaces.nsIURL); QueryInterface(Ci.nsIURL).
fileName = enclosureURL.fileName; fileName;
} }
catch(ex) catch(ex)
{ {
fileName = this.mURL; this.mFileName = this.mURL;
} }
if (fileName)
this.mFileName = fileName;
} }
} }
@ -446,20 +478,20 @@ FeedEnclosure.prototype =
mContentType: "", mContentType: "",
mLength: 0, mLength: 0,
mFileName: "", mFileName: "",
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
// Returns a string that looks like an e-mail attachment which // Returns a string that looks like an e-mail attachment which represents
// represents the enclosure. // the enclosure.
convertToAttachment: function(aBoundaryID) convertToAttachment: function(aBoundaryID)
{ {
return '\n' + return '\n' +
ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '\n' + this.ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '\n' +
'Content-Type: ' + this.mContentType + 'Content-Type: ' + this.mContentType +
'; name="' + this.mFileName + '; name="' + this.mFileName +
(this.mLength ? '"; size=' + this.mLength : '"') + '\n' + (this.mLength ? '"; size=' + this.mLength : '"') + '\n' +
'X-Mozilla-External-Attachment-URL: ' + this.mURL + '\n' + 'X-Mozilla-External-Attachment-URL: ' + this.mURL + '\n' +
'Content-Disposition: attachment; filename="' + this.mFileName + '"\n\n' + 'Content-Disposition: attachment; filename="' + this.mFileName + '"\n\n' +
'This MIME attachment is stored separately from the message.\n' + 'This MIME attachment is stored separately from the message.\n' +
ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '--' + '\n'; this.ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '--' + '\n';
} }
}; };

Просмотреть файл

@ -1,48 +0,0 @@
function enumerateInterfaces(obj)
{
var interfaces = new Array();
for (i in Components.interfaces)
{
try
{
obj.QueryInterface(Components.interfaces[i]);
interfaces.push(i);
}
catch(e) {}
}
return interfaces;
}
function enumerateProperties(obj, excludeComplexTypes)
{
var properties = "";
for (p in obj)
{
try
{
if (excludeComplexTypes
&& (typeof obj[p] == 'object' || typeof obj[p] == 'function')) next;
properties += p + " = " + obj[p] + "\n";
}
catch(e) {
properties += p + " = " + e + "\n";
}
}
return properties;
}
// minimal implementation of nsIOutputStream for use by dumpRDF, adapted from
// http://groups.google.com/search?as_umsgid=20011203111618.C1302%40erde.jan.netgaroo.de
var DumpOutputStream = {
write: function(buf, count) { dump(buf); return count; }
};
function dumpRDF( aDS ) {
var serializer = Components.classes["@mozilla.org/rdf/xml-serializer;1"]
.createInstance( Components.interfaces.nsIRDFXMLSerializer );
serializer.init( aDS );
serializer.QueryInterface( Components.interfaces.nsIRDFXMLSource )
.Serialize( DumpOutputStream );
}

Просмотреть файл

@ -34,122 +34,138 @@
* the terms of any one of the MPL, the GPL or the * the terms of any one of the MPL, the GPL or the
* ***** END LICENSE BLOCK ***** */ * ***** END LICENSE BLOCK ***** */
// The feed parser depends on FeedItems.js, Feed.js. // The feed parser depends on FeedItem.js, Feed.js.
function FeedParser() {
this.mSerializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].
createInstance(Ci.nsIDOMSerializer);
}
var rdfcontainer = Components.classes["@mozilla.org/rdf/container-utils;1"].getService(Components.interfaces.nsIRDFContainerUtils); FeedParser.prototype =
var rdfparser = Components.classes["@mozilla.org/rdf/xml-parser;1"].createInstance(Components.interfaces.nsIRDFXMLParser);
var serializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"].createInstance(Components.interfaces.nsIDOMSerializer);
var gIOService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
function FeedParser()
{}
FeedParser.prototype =
{ {
// parseFeed returns an array of parsed items ready for processing // parseFeed() returns an array of parsed items ready for processing. It is
// it is currently a synchronous operation. If there was an error parsing the feed, // currently a synchronous operation. If there is an error parsing the feed,
// parseFeed returns an empty feed in addition to calling aFeed.onParseError // parseFeed returns an empty feed in addition to calling aFeed.onParseError.
parseFeed: function (aFeed, aDOM, aBaseURI) parseFeed: function (aFeed, aDOM, aBaseURI)
{ {
if (!(aDOM instanceof Components.interfaces.nsIDOMXMLDocument) || let doc = aDOM.documentElement;
aDOM.documentElement.getElementsByTagNameNS("http://www.mozilla.org/newlayout/xml/parsererror.xml", "parsererror")[0]) if (!(aDOM instanceof Ci.nsIDOMXMLDocument) ||
doc.getElementsByTagNameNS(FeedUtils.MOZ_PARSERERROR_NS, "parsererror")[0])
{ {
// No xml doc or gecko caught a basic parsing error. // No xml doc or gecko caught a basic parsing error.
aFeed.onParseError(aFeed); aFeed.onParseError(aFeed);
return new Array(); return new Array();
} }
else if((aDOM.documentElement.namespaceURI == "http://www.w3.org/1999/02/22-rdf-syntax-ns#") else if(doc.namespaceURI == FeedUtils.RDF_SYNTAX_NS &&
&& (aDOM.documentElement.getElementsByTagNameNS("http://purl.org/rss/1.0/", "channel")[0])) doc.getElementsByTagNameNS(FeedUtils.RSS_NS, "channel")[0])
{ {
debug(aFeed.url + " is an RSS 1.x (RDF-based) feed"); aFeed.mFeedType = "RSS_1.xRDF"
// aSource can be misencoded (XMLHttpRequest converts to UTF-8 by default), FeedUtils.log.debug("FeedParser.parseFeed: type:url - " +
// but the DOM is almost always right because it uses the hints in the XML file. aFeed.mFeedType +" : " +aFeed.url);
// This is slower, but not noticably so. Mozilla doesn't have the // aSource can be misencoded (XMLHttpRequest converts to UTF-8 by default),
// XMLHttpRequest.responseBody property that IE has, which provides access // but the DOM is almost always right because it uses the hints in the
// to the unencoded response. // XML file. This is slower, but not noticably so. Mozilla doesn't have
var xmlString=serializer.serializeToString(aDOM.documentElement); // the XMLHttpRequest.responseBody property that IE has, which provides
// access to the unencoded response.
let xmlString = this.mSerializer.serializeToString(doc);
return this.parseAsRSS1(aFeed, xmlString, aBaseURI); return this.parseAsRSS1(aFeed, xmlString, aBaseURI);
} }
else if (aDOM.documentElement.namespaceURI == ATOM_03_NS) else if (doc.namespaceURI == FeedUtils.ATOM_03_NS)
{ {
debug(aFeed.url + " is an Atom 0.3 feed"); aFeed.mFeedType = "ATOM_0.3"
FeedUtils.log.debug("FeedParser.parseFeed: type:url - " +
aFeed.mFeedType +" : " +aFeed.url);
return this.parseAsAtom(aFeed, aDOM); return this.parseAsAtom(aFeed, aDOM);
} }
else if (aDOM.documentElement.namespaceURI == ATOM_IETF_NS) else if (doc.namespaceURI == FeedUtils.ATOM_IETF_NS)
{ {
debug(aFeed.url + " is an IETF Atom feed"); aFeed.mFeedType = "ATOM_IETF"
FeedUtils.log.debug("FeedParser.parseFeed: type:url - " +
aFeed.mFeedType +" : " +aFeed.url);
return this.parseAsAtomIETF(aFeed, aDOM); return this.parseAsAtomIETF(aFeed, aDOM);
} }
else if (aDOM.documentElement.getElementsByTagNameNS("http://my.netscape.com/rdf/simple/0.9/", "channel")[0]) else if (doc.getElementsByTagNameNS(FeedUtils.RSS_090_NS, "channel")[0])
{ {
debug(aFeed.url + " is an 0.90 feed"); aFeed.mFeedType = "RSS_0.90"
FeedUtils.log.debug("FeedParser.parseFeed: type:url - " +
aFeed.mFeedType +" : " +aFeed.url);
return this.parseAsRSS2(aFeed, aDOM); return this.parseAsRSS2(aFeed, aDOM);
} }
// XXX Explicitly check for RSS 2.0 instead of letting it be handled by the else
// default behavior (who knows, we may change the default at some point).
else
{ {
// We don't know what kind of feed this is; let's pretend it's RSS 0.9x // Parse as RSS 0.9x. In theory even RSS 1.0 feeds could be parsed by
// and hope things work out for the best. In theory even RSS 1.0 feeds // the 0.9x parser if the RSS namespace were the default.
// could be parsed by the 0.9x parser if the RSS namespace was the default. let rssVer = doc.localName == "rss" ? doc.getAttribute("version") : null;
debug(aFeed.url + " is of unknown format; assuming an RSS 0.9x feed"); if (rssVer)
aFeed.mFeedType = "RSS_" + rssVer;
else
aFeed.mFeedType = "RSS_0.9x?";
FeedUtils.log.debug("FeedParser.parseFeed: type:url - " +
aFeed.mFeedType +" : " +aFeed.url);
return this.parseAsRSS2(aFeed, aDOM); return this.parseAsRSS2(aFeed, aDOM);
} }
}, },
parseAsRSS2: function (aFeed, aDOM) parseAsRSS2: function (aFeed, aDOM)
{ {
// Get the first channel (assuming there is only one per RSS File). // Get the first channel (assuming there is only one per RSS File).
var parsedItems = new Array(); let parsedItems = new Array();
var channel = aDOM.getElementsByTagName("channel")[0]; let channel = aDOM.getElementsByTagName("channel")[0];
if (!channel) if (!channel)
return aFeed.onParseError(aFeed); return aFeed.onParseError(aFeed);
//usually the empty string, unless this is RSS .90 // Usually the empty string, unless this is RSS .90.
var nsURI = channel.namespaceURI || ""; let nsURI = channel.namespaceURI || "";
debug("channel NS: '" + nsURI +"'"); FeedUtils.log.debug("FeedParser.parseAsRSS2: channel nsURI - " + nsURI);
aFeed.title = aFeed.title || getNodeValue(this.childrenByTagNameNS(channel, nsURI, "title")[0]); let tags = this.childrenByTagNameNS(channel, nsURI, "title");
aFeed.description = getNodeValue(this.childrenByTagNameNS(channel, nsURI, "description")[0]); aFeed.title = aFeed.title || this.getNodeValue(tags ? tags[0] : null);
aFeed.link = getNodeValue(this.childrenByTagNameNS(channel, nsURI, "link")[0]); tags = this.childrenByTagNameNS(channel, nsURI, "description");
aFeed.description = this.getNodeValue(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(channel, nsURI, "link");
aFeed.link = this.getNodeValue(tags ? tags[0] : null);
if (!aFeed.parseItems) if (!aFeed.parseItems)
return parsedItems; return parsedItems;
aFeed.invalidateItems(); aFeed.invalidateItems();
// XXX use getElementsByTagNameNS for now // XXX use getElementsByTagNameNS for now; childrenByTagNameNS would be
// childrenByTagNameNS would be better, but RSS .90 is still with us // better, but RSS .90 is still with us.
var itemNodes = aDOM.getElementsByTagNameNS(nsURI,"item"); let itemNodes = aDOM.getElementsByTagNameNS(nsURI, "item");
FeedUtils.log.debug("FeedParser.parseAsRSS2: items to parse - " +
itemNodes.length);
for (var i=0; i < itemNodes.length; i++) for (let i = 0; i < itemNodes.length; i++)
{ {
var itemNode = itemNodes[i]; let itemNode = itemNodes[i];
var item = new FeedItem(); let item = new FeedItem();
item.feed = aFeed; item.feed = aFeed;
item.characterSet = "UTF-8"; item.characterSet = "UTF-8";
var link = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "link")[0]); tags = this.childrenByTagNameNS(itemNode, nsURI, "link");
var guidNode = this.childrenByTagNameNS(itemNode, nsURI, "guid")[0]; let link = this.getNodeValue(tags ? tags[0] : null);
var guid; tags = this.childrenByTagNameNS(itemNode, nsURI, "guid");
var isPermaLink = false; let guidNode = tags ? tags[0] : null;
if (guidNode)
let guid;
let isPermaLink = false;
if (guidNode)
{ {
guid = getNodeValue(guidNode); guid = this.getNodeValue(guidNode);
// isPermaLink is true if the value is "true" or if the attribute is // isPermaLink is true if the value is "true" or if the attribute is
// not present; all other values, including "false" and "False" and // not present; all other values, including "false" and "False" and
// for that matter "TRuE" and "meatcake" are false. // for that matter "TRuE" and "meatcake" are false.
if (!guidNode.hasAttribute("isPermaLink") || if (!guidNode.hasAttribute("isPermaLink") ||
guidNode.getAttribute("isPermaLink") == "true") guidNode.getAttribute("isPermaLink") == "true")
isPermaLink = true; isPermaLink = true;
// if attribute isPermaLink is missing, it is good to check the validity // If attribute isPermaLink is missing, it is good to check the validity
// of <guid> value as an URL to avoid linking to non-URL strings // of <guid> value as an URL to avoid linking to non-URL strings.
if (!guidNode.hasAttribute("isPermaLink")) if (!guidNode.hasAttribute("isPermaLink"))
{ {
try try
{ {
gIOService.newURI(guid, null, null); Services.io.newURI(guid, null, null);
if (gIOService.extractScheme(guid) == "tag") if (Services.io.extractScheme(guid) == "tag")
isPermaLink = false; isPermaLink = false;
} }
catch (ex) catch (ex)
@ -163,182 +179,215 @@ FeedParser.prototype =
} }
item.url = (guid && isPermaLink) ? guid : link ? link : null; item.url = (guid && isPermaLink) ? guid : link ? link : null;
item.description = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "description")[0]); tags = this.childrenByTagNameNS(itemNode, nsURI, "description");
item.title = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "title")[0]) item.description = this.getNodeValue(tags ? tags[0] : null);
|| (item.description ? (this.stripTags(item.description).substr(0, 150)) : null) tags = this.childrenByTagNameNS(itemNode, nsURI, "title");
|| item.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] tags = this.childrenByTagNameNS(itemNode, nsURI, "author");
|| this.childrenByTagNameNS(itemNode, DC_NS, "creator")[0]) if (!tags)
|| aFeed.title tags = this.childrenByTagNameNS(itemNode, FeedUtils.DC_NS, "creator");
|| item.author; item.author = this.getNodeValue(tags ? tags[0] : null) ||
item.date = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "pubDate")[0] aFeed.title ||
|| this.childrenByTagNameNS(itemNode, DC_NS, "date")[0]) item.author;
|| item.date;
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) 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 // 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. // unless we reset it here, so they'll see the current time instead.
// This is typical aggregator behavior. // This is typical aggregator behavior.
if(item.date) if (item.date)
{ {
item.date = item.date.trim(); item.date = item.date.trim();
if(!isValidRFC822Date(item.date)) if (!this.isValidRFC822Date(item.date))
{ {
// XXX Use this on the other formats as well // XXX Use this on the other formats as well.
item.date = dateRescue(item.date); item.date = this.dateRescue(item.date);
} }
} }
var content = getNodeValue(this.childrenByTagNameNS(itemNode, RSS_CONTENT_NS, "encoded")[0]); tags = this.childrenByTagNameNS(itemNode, FeedUtils.RSS_CONTENT_NS, "encoded");
if(content) item.content = this.getNodeValue(tags ? tags[0] : null);
item.content = content;
// Handle an enclosure (if present) // Handle an enclosure (if present).
var enclosureNode = this.childrenByTagNameNS(itemNode, nsURI, "enclosure")[0]; tags = this.childrenByTagNameNS(itemNode, nsURI, "enclosure");
let enclosureNode = tags ? tags[0] : null;
if (enclosureNode) if (enclosureNode)
item.enclosure = new FeedEnclosure(enclosureNode.getAttribute("url"), item.enclosure = new FeedEnclosure(enclosureNode.getAttribute("url"),
enclosureNode.getAttribute("type"), enclosureNode.getAttribute("type"),
enclosureNode.getAttribute("length")); enclosureNode.getAttribute("length"));
parsedItems[i] = item; parsedItems[i] = item;
} }
return parsedItems; 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. // 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. // 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"] let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
.createInstance(Components.interfaces.nsIRDFDataSource); createInstance(Ci.nsIRDFDataSource);
let rdfparser = Cc["@mozilla.org/rdf/xml-parser;1"].
createInstance(Ci.nsIRDFXMLParser);
rdfparser.parseString(ds, aBaseURI, aSource); rdfparser.parseString(ds, aBaseURI, aSource);
// Get information about the feed as a whole. // Get information about the feed as a whole.
var channel = ds.GetSource(RDF_TYPE, RSS_CHANNEL, true); let channel = ds.GetSource(FeedUtils.RDF_TYPE, FeedUtils.RSS_CHANNEL, true);
aFeed.title = aFeed.title || getRDFTargetValue(ds, channel, RSS_TITLE) || aFeed.url; aFeed.title = aFeed.title ||
aFeed.description = getRDFTargetValue(ds, channel, RSS_DESCRIPTION) || ""; this.getRDFTargetValue(ds, channel, FeedUtils.RSS_TITLE) ||
aFeed.link = getRDFTargetValue(ds, channel, RSS_LINK) || aFeed.url; 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) if (!aFeed.parseItems)
return parsedItems; return parsedItems;
aFeed.invalidateItems(); aFeed.invalidateItems();
var items = ds.GetTarget(channel, RSS_ITEMS, true); let items = ds.GetTarget(channel, FeedUtils.RSS_ITEMS, true);
if (items) 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" // If the channel doesn't list any items, look for resources of type "item"
// (a hacky workaround for some buggy feeds). // (a hacky workaround for some buggy feeds).
if (!items || !items.hasMoreElements()) 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; let index = 0;
while (items.hasMoreElements()) while (items.hasMoreElements())
{ {
var itemResource = items.getNext().QueryInterface(Components.interfaces.nsIRDFResource); let itemResource = items.getNext().QueryInterface(Ci.nsIRDFResource);
var item = new FeedItem(); let item = new FeedItem();
item.feed = aFeed; item.feed = aFeed;
item.characterSet = "UTF-8"; item.characterSet = "UTF-8";
// Prefer the value of the link tag to the item URI since the URI could be // Prefer the value of the link tag to the item URI since the URI could be
// a relative URN. // a relative URN.
var uri = itemResource.Value; let uri = itemResource.Value;
var link = getRDFTargetValue(ds, itemResource, RSS_LINK); let link = this.getRDFTargetValue(ds, itemResource, FeedUtils.RSS_LINK);
// XXX // XXX Check for bug258465 - entities appear escaped in the value
// check for bug258465 -- entities appear escaped // returned by getRDFTargetValue when they shouldn't.
// in the value returned by getRDFTargetValue when they shouldn't
//debug("link comparison\n" + " uri: " + uri + "\nlink: " + link); //debug("link comparison\n" + " uri: " + uri + "\nlink: " + link);
item.url = link || uri; item.url = link || uri;
item.id = item.url; item.id = item.url;
item.description = getRDFTargetValue(ds, itemResource, RSS_DESCRIPTION); item.description = this.getRDFTargetValue(ds, itemResource,
item.title = getRDFTargetValue(ds, itemResource, RSS_TITLE) FeedUtils.RSS_DESCRIPTION);
|| getRDFTargetValue(ds, itemResource, DC_SUBJECT) item.title = this.getRDFTargetValue(ds, itemResource, FeedUtils.RSS_TITLE) ||
|| (item.description ? (this.stripTags(item.description).substr(0, 150)) : null) this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_SUBJECT) ||
|| item.title; (item.description ?
item.author = getRDFTargetValue(ds, itemResource, DC_CREATOR) (this.stripTags(item.description).substr(0, 150)) : null) ||
|| getRDFTargetValue(ds, channel, DC_CREATOR) item.title;
|| aFeed.title item.author = this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_CREATOR) ||
|| item.author; this.getRDFTargetValue(ds, channel, FeedUtils.DC_CREATOR) ||
aFeed.title ||
item.date = getRDFTargetValue(ds, itemResource, DC_DATE) || item.date; item.author;
item.content = getRDFTargetValue(ds, itemResource, RSS_CONTENT_ENCODED); item.date = this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_DATE) ||
item.date;
item.content = this.getRDFTargetValue(ds, itemResource,
FeedUtils.RSS_CONTENT_ENCODED);
parsedItems[index++] = item; parsedItems[index++] = item;
} }
FeedUtils.log.debug("FeedParser.parseAsRSS1: items parsed - " + index);
return parsedItems; 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). // 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) if (!channel)
{ {
aFeed.onParseError(aFeed); aFeed.onParseError(aFeed);
return parsedItems; return parsedItems;
} }
aFeed.title = aFeed.title || this.stripTags(getNodeValue(this.childrenByTagNameNS(channel, ATOM_03_NS, "title")[0])); let tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "title");
aFeed.description = getNodeValue(this.childrenByTagNameNS(channel, ATOM_03_NS, "tagline")[0]); aFeed.title = aFeed.title ||
aFeed.link = this.findAtomLink("alternate",this.childrenByTagNameNS(channel, ATOM_03_NS, "link")); 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) if (!aFeed.parseItems)
return parsedItems; return parsedItems;
aFeed.invalidateItems(); aFeed.invalidateItems();
var items = this.childrenByTagNameNS(channel, ATOM_03_NS, "entry"); let items = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "entry");
debug("Items to parse: " + items.length); FeedUtils.log.debug("FeedParser.parseAsAtom: 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]; let itemNode = items[i];
var item = new FeedItem(); let item = new FeedItem();
item.feed = aFeed; item.feed = aFeed;
item.characterSet = "UTF-8"; item.characterSet = "UTF-8";
var url; tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "link");
url = this.findAtomLink("alternate",this.childrenByTagNameNS(itemNode, ATOM_03_NS, "link")); item.url = this.findAtomLink("alternate", tags);
item.url = url; tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "id");
item.id = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "id")[0]); item.id = this.getNodeValue(tags ? tags[0] : null);
item.description = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "summary")[0]); tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "summary");
item.title = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "title")[0]) item.description = this.getNodeValue(tags ? tags[0] : null);
|| (item.description ? item.description.substr(0, 150) : null) tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "title");
|| item.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] tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "author");
|| this.childrenByTagNameNS(itemNode, ATOM_03_NS, "contributor")[0] if (!tags)
|| this.childrenByTagNameNS(channel, ATOM_03_NS, "author")[0]; tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "contributor");
var author = ""; 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]); tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_03_NS, "name");
var email = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_03_NS, "email")[0]); 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) if (name)
author = name + (email ? " <" + email + ">" : ""); author = name + (email ? " <" + email + ">" : "");
else if (email) else if (email)
author = email; author = email;
} }
item.author = author || item.author || aFeed.title; item.author = author || item.author || aFeed.title;
item.date = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "modified")[0] tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "modified");
|| this.childrenByTagNameNS(itemNode, ATOM_03_NS, "issued")[0] if (!tags)
|| this.childrenByTagNameNS(itemNode, ATOM_03_NS, "created")[0]) tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "issued");
|| item.date; 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 // XXX We should get the xml:base attribute from the content tag as well
// and use it as the base HREF of the message. // 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 // 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. // HTML tags in without declaring their namespace so they look like Atom.
// We deal with the first two but not the third. // We deal with the first two but not the third.
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "content");
var content; let contentNode = tags ? tags[0] : null;
var contentNode = this.childrenByTagNameNS(itemNode, ATOM_03_NS, "content")[0];
if (contentNode) let content;
if (contentNode)
{ {
content = ""; 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) if (node.nodeType == node.CDATA_SECTION_NODE)
content += node.data; content += node.data;
else else
content += serializer.serializeToString(node); content += this.mSerializer.serializeToString(node);
} }
if (contentNode.getAttribute('mode') == "escaped") if (contentNode.getAttribute("mode") == "escaped")
{ {
content = content.replace(/&lt;/g, "<"); content = content.replace(/&lt;/g, "<");
content = content.replace(/&gt;/g, ">"); content = content.replace(/&gt;/g, ">");
content = content.replace(/&amp;/g, "&"); content = content.replace(/&amp;/g, "&");
} }
if (content == "") if (content == "")
content = null; content = null;
} }
item.content = content; item.content = content;
parsedItems[i] = item; parsedItems[i] = item;
} }
return parsedItems; return parsedItems;
}, },
parseAsAtomIETF: function(aFeed, aDOM) parseAsAtomIETF: function(aFeed, aDOM)
{ {
let parsedItems = new Array();
var parsedItems = new Array();
// Get the first channel (assuming there is only one per Atom File). // 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) if (!channel)
{ {
aFeed.onParseError(aFeed); aFeed.onParseError(aFeed);
return parsedItems; return parsedItems;
} }
aFeed.title = aFeed.title || this.stripTags(this.serializeTextConstruct(this.childrenByTagNameNS(channel,ATOM_IETF_NS,"title")[0])); let tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "title");
aFeed.description = this.serializeTextConstruct(this.childrenByTagNameNS(channel,ATOM_IETF_NS,"subtitle")[0]); aFeed.title = aFeed.title ||
aFeed.link = this.findAtomLink("alternate", this.childrenByTagNameNS(channel,ATOM_IETF_NS,"link")); 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) if (!aFeed.parseItems)
return parsedItems; return parsedItems;
aFeed.invalidateItems(); aFeed.invalidateItems();
var items = this.childrenByTagNameNS(channel,ATOM_IETF_NS,"entry"); let items = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "entry");
debug("Items to parse: " + items.length); 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]; let itemNode = items[i];
var item = new FeedItem(); let item = new FeedItem();
item.feed = aFeed; item.feed = aFeed;
item.characterSet = "UTF-8"; item.characterSet = "UTF-8";
item.isStoredWithId = true; 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]); tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_IETF_NS, "name");
var email = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_IETF_NS, "email")[0]); 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) if (name)
author = name + (email ? " <" + email + ">" : ""); author = name + (email ? " <" + email + ">" : "");
else if (email) else if (email)
author = email; author = email;
} }
item.author = author || item.author || aFeed.title; 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) tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "content");
item.xmlContentBase = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "content")[0].baseURI; item.content = this.serializeTextConstruct(tags ? tags[0] : null);
else if(item.description)
item.xmlContentBase = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "summary")[0].baseURI; 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 else
item.xmlContentBase = itemNode.baseURI; item.xmlContentBase = itemNode.baseURI;
@ -453,79 +530,125 @@ FeedParser.prototype =
} }
return parsedItems; return parsedItems;
}, },
serializeTextConstruct: function(textElement) serializeTextConstruct: function(textElement)
{ {
var content = ""; let content = "";
if (textElement)
if (textElement)
{ {
var textType = textElement.getAttribute('type'); let textType = textElement.getAttribute("type");
// Atom spec says consider it "text" if not present // Atom spec says consider it "text" if not present.
if(!textType) if (!textType)
textType = "text"; textType = "text";
// There could be some strange content type we don't handle // There could be some strange content type we don't handle.
if((textType != "text") && (textType != "html") && (textType != "xhtml")) if (textType != "text" && textType != "html" && textType != "xhtml")
return null; 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) if (node.nodeType == node.CDATA_SECTION_NODE)
content += this.xmlEscape(node.data); content += this.xmlEscape(node.data);
else else
content += serializer.serializeToString(node); content += this.mSerializer.serializeToString(node);
} }
if (textType == "html")
if (textType == "html")
content = this.xmlUnescape(content); content = this.xmlUnescape(content);
} }
// other parts of the code depend on this being null // Other parts of the code depend on this being null if there's no content.
// if there's no content
return content ? content : null; 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) childrenByTagNameNS: function(aElement, aNamespace, aTagName)
{ {
var matches = aElement.getElementsByTagNameNS(aNamespace, aTagName); let matches = aElement.getElementsByTagNameNS(aNamespace, aTagName);
var matchingChildren = new Array(); let matchingChildren = new Array();
for (var i = 0; i < matches.length; i++) for (let i = 0; i < matches.length; i++)
{ {
if(matches[i].parentNode == aElement) if (matches[i].parentNode == aElement)
matchingChildren.push(matches[i]) matchingChildren.push(matches[i])
} }
return matchingChildren;
return matchingChildren.length ? matchingChildren : null;
}, },
findAtomLink: function(linkRel, linkElements) findAtomLink: function(linkRel, linkElements)
{ {
// XXX Need to check for MIME type and hreflang // XXX Need to check for MIME type and hreflang.
for ( var j=0 ; j<linkElements.length ; j++ ) { for (let j = 0; j < linkElements.length; j++) {
var alink = linkElements[j]; let alink = linkElements[j];
if (alink && if (alink &&
//if there's a link rel // If there's a link rel.
((alink.getAttribute('rel') && alink.getAttribute('rel') == linkRel) || ((alink.getAttribute("rel") && alink.getAttribute("rel") == linkRel) ||
//if there isn't, assume 'alternate' // If there isn't, assume 'alternate'.
(!alink.getAttribute('rel') && (linkRel=="alternate"))) (!alink.getAttribute("rel") && (linkRel == "alternate"))) &&
&& alink.getAttribute('href')) alink.getAttribute("href"))
{ {
// Atom links are interpreted relative to xml:base // Atom links are interpreted relative to xml:base.
var ioService = Components.classes["@mozilla.org/network/io-service;1"] try {
.getService(Components.interfaces.nsIIOService); return Services.io.newURI(alink.baseURI, null, null).
return ioService.newURI(alink.baseURI, null, null).resolve(alink.getAttribute('href')); resolve(alink.getAttribute("href"));
}
catch (ex) {}
} }
} }
return null; return null;
}, },
stripTags: function(someHTML) stripTags: function(someHTML)
{ {
return someHTML ? someHTML.replace(/<[^>]+>/g,"") : someHTML; return someHTML ? someHTML.replace(/<[^>]+>/g, "") : someHTML;
}, },
xmlUnescape: function(s) xmlUnescape: function(s)
@ -542,5 +665,41 @@ FeedParser.prototype =
s = s.replace(/>/g, "&gt;"); s = s.replace(/>/g, "&gt;");
s = s.replace(/</g, "&lt;"); s = s.replace(/</g, "&lt;");
return s; return s;
} },
// Date validator for RSS feeds
FZ_RFC822_RE: "^(((Mon)|(Tue)|(Wed)|(Thu)|(Fri)|(Sat)|(Sun)), *)?\\d\\d?" +
" +((Jan)|(Feb)|(Mar)|(Apr)|(May)|(Jun)|(Jul)|(Aug)|(Sep)|(Oct)|(Nov)|(Dec))" +
" +\\d\\d(\\d\\d)? +\\d\\d:\\d\\d(:\\d\\d)? +(([+-]?\\d\\d\\d\\d)|(UT)|(GMT)" +
"|(EST)|(EDT)|(CST)|(CDT)|(MST)|(MDT)|(PST)|(PDT)|\\w)$",
isValidRFC822Date: function(pubDate)
{
let regex = new RegExp(this.FZ_RFC822_RE);
return regex.test(pubDate);
},
dateRescue: function(dateString)
{
// Deal with various kinds of invalid dates.
if (!isNaN(parseInt(dateString)))
{
// It's an integer, so maybe it's a timestamp.
let d = new Date(parseInt(dateString) * 1000);
let now = new Date();
let yeardiff = now.getFullYear() - d.getFullYear();
FeedUtils.log.trace("FeedParser.dateRescue: Rescue Timestamp date - " +
d.toString() + " ,year diff - " + yeardiff);
if (yeardiff >= 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();
}
}; };

Просмотреть файл

@ -137,9 +137,9 @@ var gFeedSubscriptionsWindow = {
{ {
// If selecting a prior selected feed, get its folder from the db // If selecting a prior selected feed, get its folder from the db
// in case an ancestor folder was renamed/moved. // in case an ancestor folder was renamed/moved.
let itemResource = rdf.GetResource(item.url); let itemResource = FeedUtils.rdf.GetResource(item.url);
let ds = getSubscriptionsDS(item.parentFolder.server); let ds = FeedUtils.getSubscriptionsDS(item.parentFolder.server);
let itemFolder = ds.GetTarget(itemResource, FZ_DESTFOLDER, true); let itemFolder = ds.GetTarget(itemResource, FeedUtils.FZ_DESTFOLDER, true);
if (itemFolder) if (itemFolder)
{ {
itemFolder = itemFolder.QueryInterface(Ci.nsIMsgFolder); itemFolder = itemFolder.QueryInterface(Ci.nsIMsgFolder);
@ -582,7 +582,7 @@ var gFeedSubscriptionsWindow = {
// Finally, set the folder's quickMode based on the its first feed's // 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, since that is how the view determines summary mode, and now
// quickMode is updated to be the same for all feeds in a folder. // quickMode is updated to be the same for all feeds in a folder.
if (feeds) if (feeds && feeds[0])
folderObject.quickMode = feeds[0].quickMode; folderObject.quickMode = feeds[0].quickMode;
return folderObject; return folderObject;
@ -591,16 +591,16 @@ var gFeedSubscriptionsWindow = {
getFeedsInFolder: function (aFolder) getFeedsInFolder: function (aFolder)
{ {
let feeds = new Array(); let feeds = new Array();
let feedUrlArray = getFeedUrlsInFolder(aFolder); let feedUrlArray = FeedUtils.getFeedUrlsInFolder(aFolder);
if (!feedUrlArray) if (!feedUrlArray)
// No feedUrls in this folder. // No feedUrls in this folder.
return; return feeds;
for (let url in feedUrlArray) for (let url in feedUrlArray)
{ {
if (!feedUrlArray[url]) if (!feedUrlArray[url])
continue; continue;
let feedResource = rdf.GetResource(feedUrlArray[url]); let feedResource = FeedUtils.rdf.GetResource(feedUrlArray[url]);
let feed = new Feed(feedResource, aFolder.server); let feed = new Feed(feedResource, aFolder.server);
feeds.push(feed); feeds.push(feed);
} }
@ -887,7 +887,7 @@ var gFeedSubscriptionsWindow = {
item.folder.server.setBoolValue("quickMode", aChecked); item.folder.server.setBoolValue("quickMode", aChecked);
this.refreshSubscriptionView(); this.refreshSubscriptionView();
} }
else if (!getFeedUrlsInFolder(item.folder)) else if (!FeedUtils.getFeedUrlsInFolder(item.folder))
// Not a folder with feeds. // Not a folder with feeds.
return; return;
else else
@ -945,7 +945,7 @@ var gFeedSubscriptionsWindow = {
if (item && item.folder && if (item && item.folder &&
(locationValue.hasAttribute("focused") || locationValue.value || (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 // Enable summary for account folder or folder with feeds or focus/value
// in the feed url field of empty folders prior to add. // in the feed url field of empty folders prior to add.
@ -981,9 +981,9 @@ var gFeedSubscriptionsWindow = {
return; return;
} }
deleteFeed(rdf.GetResource(itemToRemove.url), FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(itemToRemove.url),
itemToRemove.parentFolder.server, itemToRemove.parentFolder.server,
itemToRemove.parentFolder); itemToRemove.parentFolder);
// Now that we have removed the feed from the datasource, it is time to // 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 // 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 // Before we go any further, make sure the user is not already subscribed
// to this feed. // to this feed.
if (feedAlreadyExists(feedLocation, addFolder.server)) if (FeedUtils.feedAlreadyExists(feedLocation, addFolder.server))
{ {
message = FeedUtils.strings.GetStringFromName( message = FeedUtils.strings.GetStringFromName(
"subscribe-feedAlreadySubscribed"); "subscribe-feedAlreadySubscribed");
@ -1094,13 +1094,13 @@ var gFeedSubscriptionsWindow = {
// Helper routine used by addFeed and importOPMLFile. // Helper routine used by addFeed and importOPMLFile.
storeFeed: function(feedProperties) storeFeed: function(feedProperties)
{ {
let itemResource = rdf.GetResource(feedProperties.feedLocation); let itemResource = FeedUtils.rdf.GetResource(feedProperties.feedLocation);
let feed = new Feed(itemResource, feedProperties.server); let feed = new Feed(itemResource, feedProperties.server);
// If the user specified a folder to add the feed to, then set it here. // If the user specified a folder to add the feed to, then set it here.
if (feedProperties.folderURI) if (feedProperties.folderURI)
{ {
let folderResource = rdf.GetResource(feedProperties.folderURI); let folderResource = FeedUtils.rdf.GetResource(feedProperties.folderURI);
if (folderResource) if (folderResource)
{ {
let folder = folderResource.QueryInterface(Ci.nsIMsgFolder); let folder = folderResource.QueryInterface(Ci.nsIMsgFolder);
@ -1124,10 +1124,10 @@ var gFeedSubscriptionsWindow = {
if (!itemToEdit || itemToEdit.container || !itemToEdit.parentFolder) if (!itemToEdit || itemToEdit.container || !itemToEdit.parentFolder)
return; return;
let resource = rdf.GetResource(itemToEdit.url); let resource = FeedUtils.rdf.GetResource(itemToEdit.url);
let currentFolderServer = itemToEdit.parentFolder.server; let currentFolderServer = itemToEdit.parentFolder.server;
let ds = getSubscriptionsDS(currentFolderServer); let ds = FeedUtils.getSubscriptionsDS(currentFolderServer);
let currentFolder = ds.GetTarget(resource, FZ_DESTFOLDER, true); let currentFolder = ds.GetTarget(resource, FeedUtils.FZ_DESTFOLDER, true);
let currentFolderURI = currentFolder.QueryInterface(Ci.nsIRDFResource).Value; let currentFolderURI = currentFolder.QueryInterface(Ci.nsIRDFResource).Value;
let feed = new Feed(resource, currentFolderServer); let feed = new Feed(resource, currentFolderServer);
feed.folder = itemToEdit.parentFolder; feed.folder = itemToEdit.parentFolder;
@ -1213,15 +1213,15 @@ var gFeedSubscriptionsWindow = {
let currentParentIndex = this.mView.getParentIndex(aOldFeedIndex); let currentParentIndex = this.mView.getParentIndex(aOldFeedIndex);
let currentParentItem = this.mView.getItemAtIndex(currentParentIndex); 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 currentFolder = currentParentResource.QueryInterface(Ci.nsIMsgFolder);
let newParentItem = this.mView.getItemAtIndex(aNewParentIndex); 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 newFolder = newParentResource.QueryInterface(Ci.nsIMsgFolder);
let ds = getSubscriptionsDS(currentItem.parentFolder.server); let ds = FeedUtils.getSubscriptionsDS(currentItem.parentFolder.server);
let resource = rdf.GetResource(currentItem.url); let resource = FeedUtils.rdf.GetResource(currentItem.url);
let accountMoveCopy = false; let accountMoveCopy = false;
if (currentFolder.rootFolder.URI == newFolder.rootFolder.URI) if (currentFolder.rootFolder.URI == newFolder.rootFolder.URI)
@ -1233,14 +1233,14 @@ var gFeedSubscriptionsWindow = {
return; return;
// Unassert the older URI, add an assertion for the new parent URI. // 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); currentParentResource, newParentResource);
ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
// Update the feed url attributes on the databases for each folder: // Update the feed url attributes on the databases for each folder:
// Remove our feed url property from the current 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. // Add our feed url property to the new folder.
updateFolderFeedUrl(newFolder, currentItem.url, false); FeedUtils.updateFolderFeedUrl(newFolder, currentItem.url, false);
} }
else else
{ {
@ -1256,9 +1256,9 @@ var gFeedSubscriptionsWindow = {
// Unsubscribe the feed from the old folder, if add to the new folder // Unsubscribe the feed from the old folder, if add to the new folder
// is successfull, and doing a move. // is successfull, and doing a move.
if (moveFeed) if (moveFeed)
deleteFeed(rdf.GetResource(currentItem.url), FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(currentItem.url),
currentItem.parentFolder.server, currentItem.parentFolder.server,
currentItem.parentFolder); currentItem.parentFolder);
} }
// Finally, update our view layer. Update old parent folder's quickMode // Finally, update our view layer. Update old parent folder's quickMode
@ -1300,7 +1300,7 @@ var gFeedSubscriptionsWindow = {
{ {
let feedItem = aFeedItem; let feedItem = aFeedItem;
let parentItem = aParentItem; let parentItem = aParentItem;
let feedUrlArray = getFeedUrlsInFolder(feedItem.parentFolder); let feedUrlArray = FeedUtils.getFeedUrlsInFolder(feedItem.parentFolder);
let feedsInFolder = feedUrlArray ? feedUrlArray.length : 0; let feedsInFolder = feedUrlArray ? feedUrlArray.length : 0;
if (aRemove && feedsInFolder < 1) if (aRemove && feedsInFolder < 1)
@ -1314,7 +1314,7 @@ var gFeedSubscriptionsWindow = {
// only feed, update the parent folder to the feed's quickMode. // only feed, update the parent folder to the feed's quickMode.
if (feedsInFolder > 1) if (feedsInFolder > 1)
{ {
let feedResource = rdf.GetResource(feedItem.url); let feedResource = FeedUtils.rdf.GetResource(feedItem.url);
let feed = new Feed(feedResource, feedItem.parentFolder.server); let feed = new Feed(feedResource, feedItem.parentFolder.server);
feed.quickMode = parentItem.quickMode; feed.quickMode = parentItem.quickMode;
feedItem.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 // If we get here we should always have a folder by now, either in
// feed.folder or FeedItems created the folder for us. // 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 // Add feed adds the feed to the subscriptions db and flushes the
// datasource. // 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 // 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 // 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. // Non success. Remove intermediate traces from the feeds database.
if (feed && feed.url && feed.server) if (feed && feed.url && feed.server)
deleteFeed(rdf.GetResource(feed.url), FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(feed.url),
feed.server, feed.server,
feed.server.rootFolder); feed.server.rootFolder);
if (aErrorCode == FeedUtils.kNewsBlogInvalidFeed) if (aErrorCode == FeedUtils.kNewsBlogInvalidFeed)
message = FeedUtils.strings.GetStringFromName( message = FeedUtils.strings.GetStringFromName(
@ -1466,7 +1466,7 @@ var gFeedSubscriptionsWindow = {
this.onProgress(feed, aCurrentFeedItems, aMaxFeedItems); this.onProgress(feed, aCurrentFeedItems, aMaxFeedItems);
}, },
onProgress: function(feed, aProgress, aProgressMax) onProgress: function(feed, aProgress, aProgressMax, aLengthComputable)
{ {
gFeedSubscriptionsWindow.updateStatusItem("progressMeter", gFeedSubscriptionsWindow.updateStatusItem("progressMeter",
(aProgress * 100) / aProgressMax); (aProgress * 100) / aProgressMax);
@ -1588,7 +1588,10 @@ var gFeedSubscriptionsWindow = {
let curSelItem = this.currentSelectedItem; let curSelItem = this.currentSelectedItem;
let feedWindow = this.feedWindow; let feedWindow = this.feedWindow;
if (aMove && aDestFolder.getFlag(Ci.nsMsgFolderFlags.Trash)) if (aMove && aDestFolder.getFlag(Ci.nsMsgFolderFlags.Trash))
return this.folderDeleted(aSrcFolder); {
this.folderDeleted(aSrcFolder);
return
}
setTimeout(function() { setTimeout(function() {
feedWindow.refreshSubscriptionView(); feedWindow.refreshSubscriptionView();
@ -1819,7 +1822,7 @@ var gFeedSubscriptionsWindow = {
return -1; return -1;
// Silently skip feeds that are already subscribed. // 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 "+ FeedUtils.log.debug("importOutline: already subscribed in account "+
this.mRSSServer.prettyName+", url - "+ newFeedUrl); this.mRSSServer.prettyName+", url - "+ newFeedUrl);
@ -1846,11 +1849,11 @@ var gFeedSubscriptionsWindow = {
feed.link = aOutline.getAttribute("htmlUrl"); feed.link = aOutline.getAttribute("htmlUrl");
feed.createFolder(); 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 // addFeed adds the feed we have validated and downloaded to
// our datasource, it also flushes the subscription datasource. // 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. // Feed correctly added.
return 1; return 1;
} }

Просмотреть файл

@ -62,8 +62,6 @@
src="chrome://messenger-newsblog/content/utils.js"/> src="chrome://messenger-newsblog/content/utils.js"/>
<script type="application/javascript" <script type="application/javascript"
src="chrome://messenger-newsblog/content/file-utils.js"/> src="chrome://messenger-newsblog/content/file-utils.js"/>
<script type="application/javascript"
src="chrome://messenger-newsblog/content/debug-utils.js"/>
<script type="application/javascript" <script type="application/javascript"
src="chrome://messenger-newsblog/content/feed-subscriptions.js"/> src="chrome://messenger-newsblog/content/feed-subscriptions.js"/>
<script type="application/javascript" <script type="application/javascript"

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,7 +1,6 @@
newsblog.jar: newsblog.jar:
% content messenger-newsblog %content/messenger-newsblog/ % content messenger-newsblog %content/messenger-newsblog/
* content/messenger-newsblog/newsblogOverlay.js (content/newsblogOverlay.js) * content/messenger-newsblog/newsblogOverlay.js (content/newsblogOverlay.js)
* content/messenger-newsblog/debug-utils.js (content/debug-utils.js)
* content/messenger-newsblog/Feed.js (content/Feed.js) * content/messenger-newsblog/Feed.js (content/Feed.js)
* content/messenger-newsblog/FeedItem.js (content/FeedItem.js) * content/messenger-newsblog/FeedItem.js (content/FeedItem.js)
* content/messenger-newsblog/feed-parser.js (content/feed-parser.js) * content/messenger-newsblog/feed-parser.js (content/feed-parser.js)

Просмотреть файл

@ -23,6 +23,7 @@
* Myk Melez <myk@mozilla.org) (Original Author) * Myk Melez <myk@mozilla.org) (Original Author)
* David Bienvenu <bienvenu@nventure.com> * David Bienvenu <bienvenu@nventure.com>
* Ian Neal <iann_bugzilla@blueyonder.co.uk> * Ian Neal <iann_bugzilla@blueyonder.co.uk>
* alta88 <alta88@gmail.com>
* *
* Alternatively, the contents of this file may be used under the terms of * 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 * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -59,14 +60,6 @@ var nsNewsBlogFeedDownloader =
return; return;
} }
let feedUrlArray = getFeedUrlsInFolder(aFolder);
// Return if there are no feedUrls for the base folder in the feeds
// database, the base folder has no subfolders, or the folder is in Trash.
if ((!feedUrlArray && !aFolder.hasSubFolders) ||
aFolder.isSpecialFolder(Ci.nsMsgFolderFlags.Trash, true))
return;
let allFolders = Cc["@mozilla.org/supports-array;1"]. let allFolders = Cc["@mozilla.org/supports-array;1"].
createInstance(Ci.nsISupportsArray); createInstance(Ci.nsISupportsArray);
// Add the base folder; it does not get added by ListDescendents. // Add the base folder; it does not get added by ListDescendents.
@ -107,7 +100,7 @@ var nsNewsBlogFeedDownloader =
continue; continue;
} }
let feedUrlArray = getFeedUrlsInFolder(folder); let feedUrlArray = FeedUtils.getFeedUrlsInFolder(folder);
// Continue if there are no feedUrls for the folder in the feeds // Continue if there are no feedUrls for the folder in the feeds
// database. All folders in Trash are now unsubscribed, so perhaps // database. All folders in Trash are now unsubscribed, so perhaps
// we may not want to check that here each biff each folder. // we may not want to check that here each biff each folder.
@ -126,7 +119,7 @@ var nsNewsBlogFeedDownloader =
{ {
if (feedUrlArray[url]) if (feedUrlArray[url])
{ {
id = rdf.GetResource(feedUrlArray[url]); id = FeedUtils.rdf.GetResource(feedUrlArray[url]);
feed = new Feed(id, folder.server); feed = new Feed(id, folder.server);
feed.folder = folder; feed.folder = folder;
// Bump our pending feed download count. // Bump our pending feed download count.
@ -197,7 +190,7 @@ var nsNewsBlogFeedDownloader =
for (let i = 0; i < allServers.Count() && !aFolder; i++) for (let i = 0; i < allServers.Count() && !aFolder; i++)
{ {
let currentServer = allServers.QueryElementAt(i, Ci.nsIMsgIncomingServer); let currentServer = allServers.QueryElementAt(i, Ci.nsIMsgIncomingServer);
if (currentServer && currentServer.type == 'rss') if (currentServer && currentServer.type == "rss")
aFolder = currentServer.rootFolder; aFolder = currentServer.rootFolder;
} }
} }
@ -265,16 +258,16 @@ var nsNewsBlogFeedDownloader =
// Make sure we aren't already subscribed to this feed before we attempt // Make sure we aren't already subscribed to this feed before we attempt
// to subscribe to it. // to subscribe to it.
if (feedAlreadyExists(aUrl, aFolder.server)) if (FeedUtils.feedAlreadyExists(aUrl, aFolder.server))
{ {
aMsgWindow.statusFeedback.showStatusString( aMsgWindow.statusFeedback.showStatusString(
FeedUtils.strings.GetStringFromName('subscribe-feedAlreadySubscribed')); FeedUtils.strings.GetStringFromName("subscribe-feedAlreadySubscribed"));
return; return;
} }
let itemResource = rdf.GetResource(aUrl); let itemResource = FeedUtils.rdf.GetResource(aUrl);
let feed = new Feed(itemResource, aFolder.server); let feed = new Feed(itemResource, aFolder.server);
feed.quickMode = feed.server.getBoolValue('quickMode'); feed.quickMode = feed.server.getBoolValue("quickMode");
// If the root server, create a new folder for the feed. The user must // If the root server, create a new folder for the feed. The user must
// want us to add this subscription url to an existing RSS folder. // want us to add this subscription url to an existing RSS folder.
@ -293,13 +286,13 @@ var nsNewsBlogFeedDownloader =
// An rss folder was just changed, get the folder's feedUrls and update // An rss folder was just changed, get the folder's feedUrls and update
// our feed data source. // our feed data source.
let feedUrlArray = getFeedUrlsInFolder(aFolder); let feedUrlArray = FeedUtils.getFeedUrlsInFolder(aFolder);
if (!feedUrlArray) if (!feedUrlArray)
// No feedUrls in this folder. // No feedUrls in this folder.
return; return;
let newFeedUrl, id, resource, node; let newFeedUrl, id, resource, node;
let ds = getSubscriptionsDS(aFolder.server); let ds = FeedUtils.getSubscriptionsDS(aFolder.server);
let trashFolder = let trashFolder =
aFolder.rootFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Trash); aFolder.rootFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Trash);
for (let url in feedUrlArray) for (let url in feedUrlArray)
@ -307,22 +300,22 @@ var nsNewsBlogFeedDownloader =
newFeedUrl = feedUrlArray[url]; newFeedUrl = feedUrlArray[url];
if (newFeedUrl) if (newFeedUrl)
{ {
id = rdf.GetResource(newFeedUrl); id = FeedUtils.rdf.GetResource(newFeedUrl);
// If explicit delete or move to trash, unsubscribe. // If explicit delete or move to trash, unsubscribe.
if (aUnsubscribe || if (aUnsubscribe ||
(trashFolder && trashFolder.isAncestorOf(aFolder))) (trashFolder && trashFolder.isAncestorOf(aFolder)))
{ {
deleteFeed(id, aFolder.server, aFolder); FeedUtils.deleteFeed(id, aFolder.server, aFolder);
} }
else else
{ {
resource = rdf.GetResource(aFolder.URI); resource = FeedUtils.rdf.GetResource(aFolder.URI);
// Get the node for the current folder URI. // Get the node for the current folder URI.
node = ds.GetTarget(id, FZ_DESTFOLDER, true); node = ds.GetTarget(id, FeedUtils.FZ_DESTFOLDER, true);
if (node) if (node)
ds.Change(id, FZ_DESTFOLDER, node, resource); ds.Change(id, FeedUtils.FZ_DESTFOLDER, node, resource);
else else
addFeed(newFeedUrl, resource.name, resource); FeedUtils.addFeed(newFeedUrl, resource.name, resource);
} }
} }
} // for each feed url in the folder property } // for each feed url in the folder property

Просмотреть файл

@ -3102,14 +3102,14 @@ function FeedSetContentView(val)
if (wintype == "mail:3pane") { if (wintype == "mail:3pane") {
// Get quickmode per feed pref from feeds.rdf // Get quickmode per feed pref from feeds.rdf
var quickMode, targetRes; var quickMode, targetRes;
if (typeof FZ_NS == 'undefined') if (!("FeedUtils" in window))
Services.scriptloader.loadSubScript("chrome://messenger-newsblog/content/utils.js"); Services.scriptloader.loadSubScript("chrome://messenger-newsblog/content/utils.js");
try try
{ {
var targetRes = getParentTargetForChildResource( var targetRes = FeedUtils.getParentTargetForChildResource(
gMsgFolderSelected.URI, gFolderDisplay.displayedFolder.URI,
FZ_QUICKMODE, FeedUtils.FZ_QUICKMODE,
gMsgFolderSelected.server); gFolderDisplay.displayedFolder.server);
} }
catch (ex) {}; catch (ex) {};