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") {
// Get quickmode per feed pref from feeds.rdf
var quickMode, targetRes;
var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
if (scriptLoader && typeof FZ_NS == 'undefined')
scriptLoader.loadSubScript("chrome://messenger-newsblog/content/utils.js");
if (!("FeedUtils" in window))
Services.scriptloader.loadSubScript("chrome://messenger-newsblog/content/utils.js");
try
{
var targetRes = getParentTargetForChildResource(
gFolderDisplay.displayedFolder.URI,
FZ_QUICKMODE,
gFolderDisplay.displayedFolder.server);
var targetRes = FeedUtils.getParentTargetForChildResource(
gFolderDisplay.displayedFolder.URI,
FeedUtils.FZ_QUICKMODE,
gFolderDisplay.displayedFolder.server);
}
catch (ex) {};

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

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

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

@ -35,52 +35,46 @@
#
# ***** END LICENSE BLOCK ***** */
// Handy conversion values.
const HOURS_TO_MINUTES = 60;
const MINUTES_TO_SECONDS = 60;
const SECONDS_TO_MILLISECONDS = 1000;
const MINUTES_TO_MILLISECONDS = MINUTES_TO_SECONDS * SECONDS_TO_MILLISECONDS;
const HOURS_TO_MILLISECONDS = HOURS_TO_MINUTES * MINUTES_TO_MILLISECONDS;
const ENCLOSURE_BOUNDARY_PREFIX = "--------------"; // 14 dashes
const ENCLOSURE_HEADER_BOUNDARY_PREFIX = "------------"; // 12 dashes
const MESSAGE_TEMPLATE = "\n\
<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()
{
this.mDate = new Date().toString();
this.mUnicodeConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
this.mUnicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
this.mUnescapeHTML = Cc["@mozilla.org/feed-unescapehtml;1"].
getService(Ci.nsIScriptableUnescapeHTML);
}
FeedItem.prototype =
{
// Currently only for IETF Atom. RSS2 with GUIDs should do this as too.
// Currently only for IETF Atom. RSS2 with GUIDs should do this too.
isStoredWithId: false,
// Only for IETF Atom
// Only for IETF Atom.
xmlContentBase: null,
id: null,
feed: null,
description: null,
content: null,
// Currently only support one enclosure per feed item...
// Currently only support one enclosure per feed item.
enclosure: null,
// TO DO: this needs to be localized
// TO DO: this needs to be localized.
title: "(no subject)",
author: "anonymous",
mURL: null,
characterSet: "",
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
ENCLOSURE_HEADER_BOUNDARY_PREFIX: "------------", // 12 dashes
MESSAGE_TEMPLATE: '\n' +
'<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()
{
return this.mURL;
@ -90,9 +84,7 @@ FeedItem.prototype =
{
try
{
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
this.mURL = ioService.newURI(aVal, null, null).spec;
this.mURL = Services.io.newURI(aVal, null, null).spec;
}
catch(ex)
{
@ -120,25 +112,26 @@ FeedItem.prototype =
get messageID()
{
var messageID = this.id || this.mURL || this.title;
let messageID = this.id || this.mURL || this.title;
debug('messageID - id = ' + this.id);
debug('messageID - mURL = ' + this.mURL);
debug('messageID - title = ' + this.title);
debug('messageID - messageID = ' + messageID);
FeedUtils.log.trace("FeedItem.messageID: id - " + this.id);
FeedUtils.log.trace("FeedItem.messageID: mURL - " + this.mURL);
FeedUtils.log.trace("FeedItem.messageID: title - " + this.title);
// Escape occurrences of message ID meta characters <, >, and @.
messageID.replace(/</g, "%3C");
messageID.replace(/>/g, "%3E");
messageID.replace(/@/g, "%40");
messageID = messageID + "@" + "localhost.localdomain";
FeedUtils.log.trace("FeedItem.messageID: messageID - " + messageID);
return messageID;
},
get itemUniqueURI()
{
return (this.isStoredWithId && this.id) ? createURN(this.id) :
createURN(this.mURL || this.id);
return this.isStoredWithId && this.id ? this.createURN(this.id) :
this.createURN(this.mURL || this.id);
},
get contentBase()
@ -153,25 +146,27 @@ FeedItem.prototype =
{
this.mUnicodeConverter.charset = this.characterSet;
// this.title and this.content contain HTML
// this.mUrl and this.contentBase contain plain text
// this.title and this.content contain HTML.
// this.mUrl and this.contentBase contain plain text.
let resource = this.findStoredResource();
if (resource == null)
{
resource = rdf.GetResource(this.itemUniqueURI);
resource = FeedUtils.rdf.GetResource(this.itemUniqueURI);
if (!this.content)
{
debug(this.identity + " no content; storing");
FeedUtils.log.trace("FeedItem.store: " + this.identity +
" no content; storing");
this.content = this.description || this.title;
}
debug(this.identity + " store both remote/no content and content items");
var content = MESSAGE_TEMPLATE;
FeedUtils.log.trace("FeedItem.store: " + this.identity +
" store both remote/no content and content items");
let content = this.MESSAGE_TEMPLATE;
content = content.replace(/%TITLE%/, this.title);
content = content.replace(/%BASE%/, htmlEscape(this.contentBase));
content = content.replace(/%BASE%/, this.htmlEscape(this.contentBase));
content = content.replace(/%CONTENT%/, this.content);
// XXX store it elsewhere, f.e. this.page
// XXX store it elsewhere, f.e. this.page.
this.content = content;
this.writeToFolder();
this.markStored(resource);
@ -181,37 +176,39 @@ FeedItem.prototype =
findStoredResource: function()
{
// Checks to see if the item has already been stored in its feed's message folder.
// Checks to see if the item has already been stored in its feed's
// message folder.
FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity +
" checking to see if stored");
debug(this.identity + " checking to see if stored");
var server = this.feed.server;
var folder = this.feed.folder;
let server = this.feed.server;
let folder = this.feed.folder;
if (!folder)
{
debug(this.feed.name + " folder doesn't exist; creating");
debug("creating " + this.feed.name + "as child of " +
server.rootMsgFolder + "\n");
server.rootMsgFolder.createSubfolder(this.feed.name, null /* supposed to be a msg window */);
FeedUtils.log.debug("FeedItem.findStoredResource: " + this.feed.name +
" folder doesn't exist; creating as child of " +
server.rootMsgFolder.prettyName + "\n");
server.rootMsgFolder.createSubfolder(this.feed.name, null);
folder = server.rootMsgFolder.findSubFolder(this.feed.name);
debug(this.identity + " not stored (folder didn't exist)");
FeedUtils.log.debug("FeedItem.findStoredResource: " + this.identity +
" not stored (folder didn't exist)");
return null;
}
var ds = getItemsDS(server);
var itemURI = this.itemUniqueURI;
var itemResource = rdf.GetResource(itemURI);
let ds = FeedUtils.getItemsDS(server);
let itemURI = this.itemUniqueURI;
let itemResource = FeedUtils.rdf.GetResource(itemURI);
var downloaded = ds.GetTarget(itemResource, FZ_STORED, true);
let downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
// Backward compatibility: we might have stored this item before
// isStoredWithId has been turned on for RSS 2.0 (bug 354345).
// Check whether this item has been stored with its URL.
if (!downloaded && this.mURL && itemURI != this.mURL)
{
itemResource = rdf.GetResource(this.mURL);
downloaded = ds.GetTarget(itemResource, FZ_STORED, true);
itemResource = FeedUtils.rdf.GetResource(this.mURL);
downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
}
// Backward compatibility: the item may have been stored
@ -219,97 +216,108 @@ FeedItem.prototype =
// (bug 410842 & bug 461109)
if (!downloaded)
{
itemResource = rdf.GetResource((this.isStoredWithId && this.id) ?
("urn:" + this.id) :
(this.mURL || ("urn:" + this.id)));
downloaded = ds.GetTarget(itemResource, FZ_STORED, true);
itemResource = FeedUtils.rdf.GetResource((this.isStoredWithId && this.id) ?
("urn:" + this.id) :
(this.mURL || ("urn:" + this.id)));
downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
}
if (!downloaded ||
downloaded.QueryInterface(Components.interfaces.nsIRDFLiteral)
.Value == "false")
downloaded.QueryInterface(Ci.nsIRDFLiteral).Value == "false")
{
// HACK ALERT: before we give up, try to work around an entity
// escaping bug in RDF. See Bug #258465 for more details
// escaping bug in RDF. See Bug #258465 for more details.
itemURI = itemURI.replace(/&lt;/g, '<');
itemURI = itemURI.replace(/&gt;/g, '>');
itemURI = itemURI.replace(/&quot;/g, '"');
itemURI = itemURI.replace(/&amp;/g, '&');
debug('Failed to find item, trying entity replacement version: ' + itemURI);
itemResource = rdf.GetResource(itemURI);
downloaded = ds.GetTarget(itemResource, FZ_STORED, true);
FeedUtils.log.trace("FeedItem.findStoredResource: failed to find item," +
" trying entity replacement version - " + itemURI);
itemResource = FeedUtils.rdf.GetResource(itemURI);
downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
if (downloaded)
{
debug(this.identity + " stored");
FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity +
" stored");
return itemResource;
}
debug(this.identity + " not stored");
FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity +
" not stored");
return null;
}
else
{
debug(this.identity + " stored");
FeedUtils.log.trace("FeedItem.findStoredResource: " + this.identity +
" stored");
return itemResource;
}
},
markValid: function(resource)
{
var ds = getItemsDS(this.feed.server);
let ds = FeedUtils.getItemsDS(this.feed.server);
var newTimeStamp = rdf.GetLiteral(new Date().getTime());
var currentTimeStamp = ds.GetTarget(resource, FZ_LAST_SEEN_TIMESTAMP, true);
let newTimeStamp = FeedUtils.rdf.GetLiteral(new Date().getTime());
let currentTimeStamp = ds.GetTarget(resource,
FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
true);
if (currentTimeStamp)
ds.Change(resource, FZ_LAST_SEEN_TIMESTAMP, currentTimeStamp, newTimeStamp);
ds.Change(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
currentTimeStamp, newTimeStamp);
else
ds.Assert(resource, FZ_LAST_SEEN_TIMESTAMP, newTimeStamp, true);
ds.Assert(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
newTimeStamp, true);
if (!ds.HasAssertion(resource, FZ_FEED, rdf.GetResource(this.feed.url), true))
ds.Assert(resource, FZ_FEED, rdf.GetResource(this.feed.url), true);
if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true))
ds.Assert(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true);
if (ds.hasArcOut(resource, FZ_VALID))
if (ds.hasArcOut(resource, FeedUtils.FZ_VALID))
{
var currentValue = ds.GetTarget(resource, FZ_VALID, true);
ds.Change(resource, FZ_VALID, currentValue, RDF_LITERAL_TRUE);
let currentValue = ds.GetTarget(resource, FeedUtils.FZ_VALID, true);
ds.Change(resource, FeedUtils.FZ_VALID,
currentValue, FeedUtils.RDF_LITERAL_TRUE);
}
else
ds.Assert(resource, FZ_VALID, RDF_LITERAL_TRUE, true);
ds.Assert(resource, FeedUtils.FZ_VALID, FeedUtils.RDF_LITERAL_TRUE, true);
},
markStored: function(resource)
{
var ds = getItemsDS(this.feed.server);
let ds = FeedUtils.getItemsDS(this.feed.server);
if (!ds.HasAssertion(resource, FZ_FEED, rdf.GetResource(this.feed.url), true))
ds.Assert(resource, FZ_FEED, rdf.GetResource(this.feed.url), true);
if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true))
ds.Assert(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true);
var currentValue;
if (ds.hasArcOut(resource, FZ_STORED))
let currentValue;
if (ds.hasArcOut(resource, FeedUtils.FZ_STORED))
{
currentValue = ds.GetTarget(resource, FZ_STORED, true);
ds.Change(resource, FZ_STORED, currentValue, RDF_LITERAL_TRUE);
currentValue = ds.GetTarget(resource, FeedUtils.FZ_STORED, true);
ds.Change(resource, FeedUtils.FZ_STORED,
currentValue, FeedUtils.RDF_LITERAL_TRUE);
}
else
ds.Assert(resource, FZ_STORED, RDF_LITERAL_TRUE, true);
ds.Assert(resource, FeedUtils.FZ_STORED,
FeedUtils.RDF_LITERAL_TRUE, true);
},
mimeEncodeSubject: function(aSubject, aCharset)
{
// Get the mime header encoder service
var mimeEncoder = Components.classes["@mozilla.org/messenger/mimeconverter;1"]
.getService(Components.interfaces.nsIMimeConverter);
// This routine sometimes throws exceptions for mis-encoded data so
// wrap it with a try catch for now..
var newSubject;
// wrap it with a try catch for now.
let newSubject;
try
{
newSubject = mimeEncoder.encodeMimePartIIStr(
this.mUnicodeConverter.ConvertFromUnicode(aSubject),
false, aCharset, 9, 72);
newSubject = mailServices.mimeConverter.encodeMimePartIIStr(
this.mUnicodeConverter.ConvertFromUnicode(aSubject),
false,
aCharset, 9, 72);
}
catch (ex)
{
@ -321,24 +329,21 @@ FeedItem.prototype =
writeToFolder: function()
{
debug(this.identity + " writing to message folder" + this.feed.name + "\n");
var server = this.feed.server;
FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity +
" writing to message folder " + this.feed.name);
this.mUnicodeConverter.charset = this.characterSet;
// If the sender isn't a valid email address, quote it so it looks nicer.
if (this.author && this.author.indexOf('@') == -1)
this.author = '<' + this.author + '>';
if (this.author && this.author.indexOf("@") == -1)
this.author = "<" + this.author + ">";
// Convert the title to UTF-16 before performing our HTML entity
// replacement reg expressions.
var title = this.title;
let title = this.title;
// the subject may contain HTML entities.
// Convert these to their unencoded state. i.e. &amp; becomes '&'
title = Components.classes["@mozilla.org/feed-unescapehtml;1"]
.getService(Components.interfaces.nsIScriptableUnescapeHTML)
.unescape(title);
// The subject may contain HTML entities. Convert these to their unencoded
// state. i.e. &amp; becomes '&'.
title = this.mUnescapeHTML.unescape(title);
// Compress white space in the subject to make it look better. Trim
// leading/trailing spaces to prevent mbox header folding issue at just
@ -364,9 +369,9 @@ FeedItem.prototype =
// use it to calculate the offset of the X-Mozilla-Status lines from
// the front of the message for the statusOffset property of the
// DB header object.
var openingLine = 'From - ' + this.mDate + '\n';
let openingLine = 'From - ' + this.mDate + '\n';
var source =
let source =
openingLine +
'X-Mozilla-Status: 0000\n' +
'X-Mozilla-Status2: 00000000\n' +
@ -381,9 +386,11 @@ FeedItem.prototype =
if (this.enclosure && this.enclosure.mFileName)
{
var boundaryID = source.length + this.enclosure.mLength;
source += 'Content-Type: multipart/mixed;\n boundary="' + ENCLOSURE_HEADER_BOUNDARY_PREFIX + boundaryID + '"' + '\n\n' +
'This is a multi-part message in MIME format.\n' + ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '\n' +
let boundaryID = source.length + this.enclosure.mLength;
source += 'Content-Type: multipart/mixed;\n boundary="' +
this.ENCLOSURE_HEADER_BOUNDARY_PREFIX + boundaryID + '"' + '\n\n' +
'This is a multi-part message in MIME format.\n' +
this.ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '\n' +
'Content-Type: text/html; charset=' + this.characterSet + '\n' +
'Content-Transfer-Encoding: 8bit\n' +
this.content;
@ -396,47 +403,72 @@ FeedItem.prototype =
}
debug(this.identity + " is " + source.length + " characters long");
FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity +
" is " + source.length + " characters long");
// Get the folder and database storing the feed's messages and headers.
var folder = this.feed.folder.QueryInterface(Components.interfaces.nsIMsgLocalMailFolder);
var msgFolder = folder.QueryInterface(Components.interfaces.nsIMsgFolder);
let folder = this.feed.folder.QueryInterface(Ci.nsIMsgLocalMailFolder);
let msgFolder = folder.QueryInterface(Ci.nsIMsgFolder);
msgFolder.gettingNewMessages = true;
// Source is a unicode string, we want to save a char * string in
// the original charset. So convert back
// the original charset. So convert back.
folder.addMessage(this.mUnicodeConverter.ConvertFromUnicode(source));
msgFolder.gettingNewMessages = false;
},
htmlEscape: function(s)
{
s = s.replace(/&/g, "&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
// like attachments in the UI.
// A feed enclosure is to RSS what an attachment is for e-mail. We make
// enclosures look like attachments in the UI.
function FeedEnclosure(aURL, aContentType, aLength)
{
this.mURL = aURL;
this.mContentType = aContentType;
this.mLength = aLength;
// generate a fileName from the URL
// Generate a fileName from the URL.
if (this.mURL)
{
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var enclosureURL, fileName;
try
{
enclosureURL = ioService.newURI(this.mURL, null, null)
.QueryInterface(Components.interfaces.nsIURL);
fileName = enclosureURL.fileName;
this.mFileName = Services.io.newURI(this.mURL, null, null).
QueryInterface(Ci.nsIURL).
fileName;
}
catch(ex)
{
fileName = this.mURL;
this.mFileName = this.mURL;
}
if (fileName)
this.mFileName = fileName;
}
}
@ -446,20 +478,20 @@ FeedEnclosure.prototype =
mContentType: "",
mLength: 0,
mFileName: "",
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
// Returns a string that looks like an e-mail attachment which
// represents the enclosure.
// Returns a string that looks like an e-mail attachment which represents
// the enclosure.
convertToAttachment: function(aBoundaryID)
{
return '\n' +
ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '\n' +
'Content-Type: ' + this.mContentType +
'; name="' + this.mFileName +
(this.mLength ? '"; size=' + this.mLength : '"') + '\n' +
'X-Mozilla-External-Attachment-URL: ' + this.mURL + '\n' +
'Content-Disposition: attachment; filename="' + this.mFileName + '"\n\n' +
'This MIME attachment is stored separately from the message.\n' +
ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '--' + '\n';
this.ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '\n' +
'Content-Type: ' + this.mContentType +
'; name="' + this.mFileName +
(this.mLength ? '"; size=' + this.mLength : '"') + '\n' +
'X-Mozilla-External-Attachment-URL: ' + this.mURL + '\n' +
'Content-Disposition: attachment; filename="' + this.mFileName + '"\n\n' +
'This MIME attachment is stored separately from the message.\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
* ***** 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);
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 =
FeedParser.prototype =
{
// parseFeed returns an array of parsed items ready for processing
// it is currently a synchronous operation. If there was an error parsing the feed,
// parseFeed returns an empty feed in addition to calling aFeed.onParseError
// parseFeed() returns an array of parsed items ready for processing. It is
// currently a synchronous operation. If there is an error parsing the feed,
// parseFeed returns an empty feed in addition to calling aFeed.onParseError.
parseFeed: function (aFeed, aDOM, aBaseURI)
{
if (!(aDOM instanceof Components.interfaces.nsIDOMXMLDocument) ||
aDOM.documentElement.getElementsByTagNameNS("http://www.mozilla.org/newlayout/xml/parsererror.xml", "parsererror")[0])
let doc = aDOM.documentElement;
if (!(aDOM instanceof Ci.nsIDOMXMLDocument) ||
doc.getElementsByTagNameNS(FeedUtils.MOZ_PARSERERROR_NS, "parsererror")[0])
{
// No xml doc or gecko caught a basic parsing error.
aFeed.onParseError(aFeed);
return new Array();
}
else if((aDOM.documentElement.namespaceURI == "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
&& (aDOM.documentElement.getElementsByTagNameNS("http://purl.org/rss/1.0/", "channel")[0]))
else if(doc.namespaceURI == FeedUtils.RDF_SYNTAX_NS &&
doc.getElementsByTagNameNS(FeedUtils.RSS_NS, "channel")[0])
{
debug(aFeed.url + " is an RSS 1.x (RDF-based) feed");
// aSource can be misencoded (XMLHttpRequest converts to UTF-8 by default),
// but the DOM is almost always right because it uses the hints in the XML file.
// This is slower, but not noticably so. Mozilla doesn't have the
// XMLHttpRequest.responseBody property that IE has, which provides access
// to the unencoded response.
var xmlString=serializer.serializeToString(aDOM.documentElement);
aFeed.mFeedType = "RSS_1.xRDF"
FeedUtils.log.debug("FeedParser.parseFeed: type:url - " +
aFeed.mFeedType +" : " +aFeed.url);
// aSource can be misencoded (XMLHttpRequest converts to UTF-8 by default),
// but the DOM is almost always right because it uses the hints in the
// XML file. This is slower, but not noticably so. Mozilla doesn't have
// 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);
}
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);
}
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);
}
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);
}
// XXX Explicitly check for RSS 2.0 instead of letting it be handled by the
// default behavior (who knows, we may change the default at some point).
else
else
{
// We don't know what kind of feed this is; let's pretend it's RSS 0.9x
// and hope things work out for the best. In theory even RSS 1.0 feeds
// could be parsed by the 0.9x parser if the RSS namespace was the default.
debug(aFeed.url + " is of unknown format; assuming an RSS 0.9x feed");
// Parse as RSS 0.9x. In theory even RSS 1.0 feeds could be parsed by
// the 0.9x parser if the RSS namespace were the default.
let rssVer = doc.localName == "rss" ? doc.getAttribute("version") : null;
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);
}
},
parseAsRSS2: function (aFeed, aDOM)
parseAsRSS2: function (aFeed, aDOM)
{
// 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)
return aFeed.onParseError(aFeed);
//usually the empty string, unless this is RSS .90
var nsURI = channel.namespaceURI || "";
debug("channel NS: '" + nsURI +"'");
// Usually the empty string, unless this is RSS .90.
let nsURI = channel.namespaceURI || "";
FeedUtils.log.debug("FeedParser.parseAsRSS2: channel nsURI - " + nsURI);
aFeed.title = aFeed.title || getNodeValue(this.childrenByTagNameNS(channel, nsURI, "title")[0]);
aFeed.description = getNodeValue(this.childrenByTagNameNS(channel, nsURI, "description")[0]);
aFeed.link = getNodeValue(this.childrenByTagNameNS(channel, nsURI, "link")[0]);
let tags = this.childrenByTagNameNS(channel, nsURI, "title");
aFeed.title = aFeed.title || this.getNodeValue(tags ? tags[0] : null);
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)
return parsedItems;
aFeed.invalidateItems();
// XXX use getElementsByTagNameNS for now
// childrenByTagNameNS would be better, but RSS .90 is still with us
var itemNodes = aDOM.getElementsByTagNameNS(nsURI,"item");
// XXX use getElementsByTagNameNS for now; childrenByTagNameNS would be
// better, but RSS .90 is still with us.
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];
var item = new FeedItem();
let itemNode = itemNodes[i];
let item = new FeedItem();
item.feed = aFeed;
item.characterSet = "UTF-8";
var link = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "link")[0]);
var guidNode = this.childrenByTagNameNS(itemNode, nsURI, "guid")[0];
var guid;
var isPermaLink = false;
if (guidNode)
tags = this.childrenByTagNameNS(itemNode, nsURI, "link");
let link = this.getNodeValue(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(itemNode, nsURI, "guid");
let guidNode = tags ? tags[0] : null;
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
// not present; all other values, including "false" and "False" and
// for that matter "TRuE" and "meatcake" are false.
if (!guidNode.hasAttribute("isPermaLink") ||
guidNode.getAttribute("isPermaLink") == "true")
isPermaLink = true;
// 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
// 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.
if (!guidNode.hasAttribute("isPermaLink"))
{
try
{
gIOService.newURI(guid, null, null);
if (gIOService.extractScheme(guid) == "tag")
Services.io.newURI(guid, null, null);
if (Services.io.extractScheme(guid) == "tag")
isPermaLink = false;
}
catch (ex)
@ -163,182 +179,215 @@ FeedParser.prototype =
}
item.url = (guid && isPermaLink) ? guid : link ? link : null;
item.description = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "description")[0]);
item.title = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "title")[0])
|| (item.description ? (this.stripTags(item.description).substr(0, 150)) : null)
|| item.title;
tags = this.childrenByTagNameNS(itemNode, nsURI, "description");
item.description = this.getNodeValue(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(itemNode, nsURI, "title");
item.title = this.getNodeValue(tags ? tags[0] : null) ||
(item.description ?
this.stripTags(item.description).substr(0, 150) : null) ||
item.title;
item.author = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "author")[0]
|| this.childrenByTagNameNS(itemNode, DC_NS, "creator")[0])
|| aFeed.title
|| item.author;
item.date = getNodeValue(this.childrenByTagNameNS(itemNode, nsURI, "pubDate")[0]
|| this.childrenByTagNameNS(itemNode, DC_NS, "date")[0])
|| item.date;
tags = this.childrenByTagNameNS(itemNode, nsURI, "author");
if (!tags)
tags = this.childrenByTagNameNS(itemNode, FeedUtils.DC_NS, "creator");
item.author = this.getNodeValue(tags ? tags[0] : null) ||
aFeed.title ||
item.author;
tags = this.childrenByTagNameNS(itemNode, nsURI, "pubDate");
if (!tags)
tags = this.childrenByTagNameNS(itemNode, FeedUtils.DC_NS, "date");
item.date = this.getNodeValue(tags ? tags[0] : null) || item.date;
if (!item.id)
item.id = item.feed.url + '#' + (item.date || item.title);
item.id = item.feed.url + "#" + (item.date || item.title);
// If the date is invalid, users will see the beginning of the epoch
// unless we reset it here, so they'll see the current time instead.
// This is typical aggregator behavior.
if(item.date)
if (item.date)
{
item.date = item.date.trim();
if(!isValidRFC822Date(item.date))
if (!this.isValidRFC822Date(item.date))
{
// XXX Use this on the other formats as well
item.date = dateRescue(item.date);
// XXX Use this on the other formats as well.
item.date = this.dateRescue(item.date);
}
}
var content = getNodeValue(this.childrenByTagNameNS(itemNode, RSS_CONTENT_NS, "encoded")[0]);
if(content)
item.content = content;
tags = this.childrenByTagNameNS(itemNode, FeedUtils.RSS_CONTENT_NS, "encoded");
item.content = this.getNodeValue(tags ? tags[0] : null);
// Handle an enclosure (if present)
var enclosureNode = this.childrenByTagNameNS(itemNode, nsURI, "enclosure")[0];
// Handle an enclosure (if present).
tags = this.childrenByTagNameNS(itemNode, nsURI, "enclosure");
let enclosureNode = tags ? tags[0] : null;
if (enclosureNode)
item.enclosure = new FeedEnclosure(enclosureNode.getAttribute("url"),
enclosureNode.getAttribute("type"),
enclosureNode.getAttribute("length"));
item.enclosure = new FeedEnclosure(enclosureNode.getAttribute("url"),
enclosureNode.getAttribute("type"),
enclosureNode.getAttribute("length"));
parsedItems[i] = item;
}
return parsedItems;
},
parseAsRSS1 : function(aFeed, aSource, aBaseURI)
parseAsRSS1 : function(aFeed, aSource, aBaseURI)
{
var parsedItems = new Array();
let parsedItems = new Array();
// RSS 1.0 is valid RDF, so use the RDF parser/service to extract data.
// Create a new RDF data source and parse the feed into it.
var ds = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
.createInstance(Components.interfaces.nsIRDFDataSource);
let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
createInstance(Ci.nsIRDFDataSource);
let rdfparser = Cc["@mozilla.org/rdf/xml-parser;1"].
createInstance(Ci.nsIRDFXMLParser);
rdfparser.parseString(ds, aBaseURI, aSource);
// Get information about the feed as a whole.
var channel = ds.GetSource(RDF_TYPE, RSS_CHANNEL, true);
aFeed.title = aFeed.title || getRDFTargetValue(ds, channel, RSS_TITLE) || aFeed.url;
aFeed.description = getRDFTargetValue(ds, channel, RSS_DESCRIPTION) || "";
aFeed.link = getRDFTargetValue(ds, channel, RSS_LINK) || aFeed.url;
let channel = ds.GetSource(FeedUtils.RDF_TYPE, FeedUtils.RSS_CHANNEL, true);
aFeed.title = aFeed.title ||
this.getRDFTargetValue(ds, channel, FeedUtils.RSS_TITLE) ||
aFeed.url;
aFeed.description = this.getRDFTargetValue(ds, channel, FeedUtils.RSS_DESCRIPTION) ||
"";
aFeed.link = this.getRDFTargetValue(ds, channel, FeedUtils.RSS_LINK) ||
aFeed.url;
if (!aFeed.parseItems)
return parsedItems;
aFeed.invalidateItems();
var items = ds.GetTarget(channel, RSS_ITEMS, true);
let items = ds.GetTarget(channel, FeedUtils.RSS_ITEMS, true);
if (items)
items = rdfcontainer.MakeSeq(ds, items).GetElements();
items = FeedUtils.rdfContainerUtils.MakeSeq(ds, items).GetElements();
// If the channel doesn't list any items, look for resources of type "item"
// (a hacky workaround for some buggy feeds).
if (!items || !items.hasMoreElements())
items = ds.GetSources(RDF_TYPE, RSS_ITEM, true);
items = ds.GetSources(FeedUtils.RDF_TYPE, FeedUtils.RSS_ITEM, true);
var index = 0;
while (items.hasMoreElements())
let index = 0;
while (items.hasMoreElements())
{
var itemResource = items.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
var item = new FeedItem();
let itemResource = items.getNext().QueryInterface(Ci.nsIRDFResource);
let item = new FeedItem();
item.feed = aFeed;
item.characterSet = "UTF-8";
// Prefer the value of the link tag to the item URI since the URI could be
// a relative URN.
var uri = itemResource.Value;
var link = getRDFTargetValue(ds, itemResource, RSS_LINK);
let uri = itemResource.Value;
let link = this.getRDFTargetValue(ds, itemResource, FeedUtils.RSS_LINK);
// XXX
// check for bug258465 -- entities appear escaped
// in the value returned by getRDFTargetValue when they shouldn't
// XXX Check for bug258465 - entities appear escaped in the value
// returned by getRDFTargetValue when they shouldn't.
//debug("link comparison\n" + " uri: " + uri + "\nlink: " + link);
item.url = link || uri;
item.id = item.url;
item.description = getRDFTargetValue(ds, itemResource, RSS_DESCRIPTION);
item.title = getRDFTargetValue(ds, itemResource, RSS_TITLE)
|| getRDFTargetValue(ds, itemResource, DC_SUBJECT)
|| (item.description ? (this.stripTags(item.description).substr(0, 150)) : null)
|| item.title;
item.author = getRDFTargetValue(ds, itemResource, DC_CREATOR)
|| getRDFTargetValue(ds, channel, DC_CREATOR)
|| aFeed.title
|| item.author;
item.date = getRDFTargetValue(ds, itemResource, DC_DATE) || item.date;
item.content = getRDFTargetValue(ds, itemResource, RSS_CONTENT_ENCODED);
item.description = this.getRDFTargetValue(ds, itemResource,
FeedUtils.RSS_DESCRIPTION);
item.title = this.getRDFTargetValue(ds, itemResource, FeedUtils.RSS_TITLE) ||
this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_SUBJECT) ||
(item.description ?
(this.stripTags(item.description).substr(0, 150)) : null) ||
item.title;
item.author = this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_CREATOR) ||
this.getRDFTargetValue(ds, channel, FeedUtils.DC_CREATOR) ||
aFeed.title ||
item.author;
item.date = this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_DATE) ||
item.date;
item.content = this.getRDFTargetValue(ds, itemResource,
FeedUtils.RSS_CONTENT_ENCODED);
parsedItems[index++] = item;
}
FeedUtils.log.debug("FeedParser.parseAsRSS1: items parsed - " + index);
return parsedItems;
},
parseAsAtom: function(aFeed, aDOM)
parseAsAtom: function(aFeed, aDOM)
{
var parsedItems = new Array();
let parsedItems = new Array();
// Get the first channel (assuming there is only one per Atom File).
var channel = aDOM.getElementsByTagName("feed")[0];
let channel = aDOM.getElementsByTagName("feed")[0];
if (!channel)
{
aFeed.onParseError(aFeed);
return parsedItems;
}
aFeed.title = aFeed.title || this.stripTags(getNodeValue(this.childrenByTagNameNS(channel, ATOM_03_NS, "title")[0]));
aFeed.description = getNodeValue(this.childrenByTagNameNS(channel, ATOM_03_NS, "tagline")[0]);
aFeed.link = this.findAtomLink("alternate",this.childrenByTagNameNS(channel, ATOM_03_NS, "link"));
let tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "title");
aFeed.title = aFeed.title ||
this.stripTags(this.getNodeValue(tags ? tags[0] : null));
tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "tagline");
aFeed.description = this.getNodeValue(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "link");
aFeed.link = this.findAtomLink("alternate", tags);
if (!aFeed.parseItems)
return parsedItems;
aFeed.invalidateItems();
var items = this.childrenByTagNameNS(channel, ATOM_03_NS, "entry");
debug("Items to parse: " + items.length);
for (var i=0; i < items.length; i++)
let items = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "entry");
FeedUtils.log.debug("FeedParser.parseAsAtom: items to parse - " +
items.length);
for (let i = 0; i < items.length; i++)
{
var itemNode = items[i];
var item = new FeedItem();
let itemNode = items[i];
let item = new FeedItem();
item.feed = aFeed;
item.characterSet = "UTF-8";
var url;
url = this.findAtomLink("alternate",this.childrenByTagNameNS(itemNode, ATOM_03_NS, "link"));
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "link");
item.url = this.findAtomLink("alternate", tags);
item.url = url;
item.id = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "id")[0]);
item.description = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "summary")[0]);
item.title = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "title")[0])
|| (item.description ? item.description.substr(0, 150) : null)
|| item.title;
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "id");
item.id = this.getNodeValue(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "summary");
item.description = this.getNodeValue(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "title");
item.title = this.getNodeValue(tags ? tags[0] : null) ||
(item.description ? item.description.substr(0, 150) : null) ||
item.title;
var authorEl = this.childrenByTagNameNS(itemNode, ATOM_03_NS, "author")[0]
|| this.childrenByTagNameNS(itemNode, ATOM_03_NS, "contributor")[0]
|| this.childrenByTagNameNS(channel, ATOM_03_NS, "author")[0];
var author = "";
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "author");
if (!tags)
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "contributor");
if (!tags)
tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "author");
if (authorEl)
let authorEl = tags ? tags[0] : null;
let author = "";
if (authorEl)
{
var name = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_03_NS, "name")[0]);
var email = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_03_NS, "email")[0]);
tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_03_NS, "name");
let name = this.getNodeValue(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_03_NS, "email");
let email = this.getNodeValue(tags ? tags[0] : null);
if (name)
author = name + (email ? " <" + email + ">" : "");
else if (email)
author = email;
}
item.author = author || item.author || aFeed.title;
item.date = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_03_NS, "modified")[0]
|| this.childrenByTagNameNS(itemNode, ATOM_03_NS, "issued")[0]
|| this.childrenByTagNameNS(itemNode, ATOM_03_NS, "created")[0])
|| item.date;
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "modified");
if (!tags)
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "issued");
if (!tags)
tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_03_NS, "created");
item.date = this.getNodeValue(tags ? tags[0] : null) || item.date;
// XXX We should get the xml:base attribute from the content tag as well
// and use it as the base HREF of the message.
@ -348,104 +397,132 @@ FeedParser.prototype =
// a namespace to identify the tags as HTML; and a few are buggy and put
// HTML tags in without declaring their namespace so they look like Atom.
// We deal with the first two but not the third.
var content;
var contentNode = this.childrenByTagNameNS(itemNode, ATOM_03_NS, "content")[0];
if (contentNode)
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_03_NS, "content");
let contentNode = tags ? tags[0] : null;
let content;
if (contentNode)
{
content = "";
for (var j=0; j < contentNode.childNodes.length; j++)
for (let j = 0; j < contentNode.childNodes.length; j++)
{
var node = contentNode.childNodes.item(j);
let node = contentNode.childNodes.item(j);
if (node.nodeType == node.CDATA_SECTION_NODE)
content += node.data;
else
content += serializer.serializeToString(node);
content += this.mSerializer.serializeToString(node);
}
if (contentNode.getAttribute('mode') == "escaped")
if (contentNode.getAttribute("mode") == "escaped")
{
content = content.replace(/&lt;/g, "<");
content = content.replace(/&gt;/g, ">");
content = content.replace(/&amp;/g, "&");
}
if (content == "")
content = null;
}
item.content = content;
parsedItems[i] = item;
}
return parsedItems;
},
parseAsAtomIETF: function(aFeed, aDOM)
{
var parsedItems = new Array();
let parsedItems = new Array();
// Get the first channel (assuming there is only one per Atom File).
var channel = this.childrenByTagNameNS(aDOM,ATOM_IETF_NS,"feed")[0];
let channel = this.childrenByTagNameNS(aDOM, FeedUtils.ATOM_IETF_NS, "feed")[0];
if (!channel)
{
aFeed.onParseError(aFeed);
return parsedItems;
}
aFeed.title = aFeed.title || this.stripTags(this.serializeTextConstruct(this.childrenByTagNameNS(channel,ATOM_IETF_NS,"title")[0]));
aFeed.description = this.serializeTextConstruct(this.childrenByTagNameNS(channel,ATOM_IETF_NS,"subtitle")[0]);
aFeed.link = this.findAtomLink("alternate", this.childrenByTagNameNS(channel,ATOM_IETF_NS,"link"));
let tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "title");
aFeed.title = aFeed.title ||
this.stripTags(this.serializeTextConstruct(tags ? tags[0] : null));
tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "subtitle");
aFeed.description = this.serializeTextConstruct(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "link");
aFeed.link = this.findAtomLink("alternate", tags);
if (!aFeed.parseItems)
return parsedItems;
aFeed.invalidateItems();
var items = this.childrenByTagNameNS(channel,ATOM_IETF_NS,"entry");
debug("Items to parse: " + items.length);
let items = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "entry");
FeedUtils.log.debug("FeedParser.parseAsAtomIETF: items to parse - " +
items.length);
for (var i=0; i < items.length; i++)
for (let i = 0; i < items.length; i++)
{
var itemNode = items[i];
var item = new FeedItem();
let itemNode = items[i];
let item = new FeedItem();
item.feed = aFeed;
item.characterSet = "UTF-8";
item.isStoredWithId = true;
item.url = this.findAtomLink("alternate", this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "link")) || aFeed.link;
item.id = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "id")[0]);
item.description = this.serializeTextConstruct(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "summary")[0]);
item.title = this.stripTags(this.serializeTextConstruct(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "title")[0])
|| (item.description ? item.description.substr(0, 150) : null)
|| item.title);
// XXX Support multiple authors
var source = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "source")[0];
var authorEl = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "author")[0]
|| (source ? this.childrenByTagNameNS(source, ATOM_IETF_NS, "author")[0] : null)
|| this.childrenByTagNameNS(channel, ATOM_IETF_NS, "author")[0];
var author = "";
if (authorEl)
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "link");
item.url = this.findAtomLink("alternate", tags) || aFeed.link;
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "id");
item.id = this.getNodeValue(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "summary");
item.description = this.serializeTextConstruct(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "title");
item.title = this.stripTags(this.serializeTextConstruct(tags ? tags[0] : null) ||
(item.description ?
item.description.substr(0, 150) : null) ||
item.title);
// XXX Support multiple authors.
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "source");
let source = tags ? tags[0] : null;
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "author");
if (!tags && source)
tags = this.childrenByTagNameNS(source, FeedUtils.ATOM_IETF_NS, "author");
if (!tags)
tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "author");
let authorEl = tags ? tags[0] : null;
let author = "";
if (authorEl)
{
var name = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_IETF_NS, "name")[0]);
var email = getNodeValue(this.childrenByTagNameNS(authorEl, ATOM_IETF_NS, "email")[0]);
tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_IETF_NS, "name");
let name = this.getNodeValue(tags ? tags[0] : null);
tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_IETF_NS, "email");
let email = this.getNodeValue(tags ? tags[0] : null);
if (name)
author = name + (email ? " <" + email + ">" : "");
else if (email)
author = email;
}
item.author = author || item.author || aFeed.title;
item.date = getNodeValue(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "updated")[0]
|| this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "published")[0])
|| item.date;
item.content = this.serializeTextConstruct(this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "content")[0]);
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "updated");
if (!tags)
tags = this.childrenByTagNameNS(source, FeedUtils.ATOM_IETF_NS, "published");
item.date = this.getNodeValue(tags ? tags[0] : null) || item.date;
if(item.content)
item.xmlContentBase = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "content")[0].baseURI;
else if(item.description)
item.xmlContentBase = this.childrenByTagNameNS(itemNode, ATOM_IETF_NS, "summary")[0].baseURI;
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "content");
item.content = this.serializeTextConstruct(tags ? tags[0] : null);
if (item.content)
item.xmlContentBase = tags ? tags[0].baseURI : null;
else if (item.description)
{
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "summary");
item.xmlContentBase = tags ? tags[0].baseURI : null;
}
else
item.xmlContentBase = itemNode.baseURI;
@ -453,79 +530,125 @@ FeedParser.prototype =
}
return parsedItems;
},
serializeTextConstruct: function(textElement)
{
var content = "";
if (textElement)
let content = "";
if (textElement)
{
var textType = textElement.getAttribute('type');
let textType = textElement.getAttribute("type");
// Atom spec says consider it "text" if not present
if(!textType)
// Atom spec says consider it "text" if not present.
if (!textType)
textType = "text";
// There could be some strange content type we don't handle
if((textType != "text") && (textType != "html") && (textType != "xhtml"))
// There could be some strange content type we don't handle.
if (textType != "text" && textType != "html" && textType != "xhtml")
return null;
for (var j=0; j < textElement.childNodes.length; j++)
for (let j = 0; j < textElement.childNodes.length; j++)
{
var node = textElement.childNodes.item(j);
let node = textElement.childNodes.item(j);
if (node.nodeType == node.CDATA_SECTION_NODE)
content += this.xmlEscape(node.data);
else
content += serializer.serializeToString(node);
content += this.mSerializer.serializeToString(node);
}
if (textType == "html")
if (textType == "html")
content = this.xmlUnescape(content);
}
// other parts of the code depend on this being null
// if there's no content
// Other parts of the code depend on this being null if there's no content.
return content ? content : null;
},
// finds elements that are direct children of the first arg
getRDFTargetValue: function(ds, source, property)
{
let node = ds.GetTarget(source, property, true);
if (node)
{
try
{
node = node.QueryInterface(Ci.nsIRDFLiteral);
if (node)
return node.Value;
}
catch (e)
{
// If the RDF was bogus, do nothing. Rethrow if it's some other problem.
if (!((e instanceof Ci.nsIXPCException) &&
e.result == Cr.NS_ERROR_NO_INTERFACE))
throw new Error("FeedParser.getRDFTargetValue: " + e);
}
}
return null;
},
getNodeValue: function(node)
{
if (node && node.textContent)
return node.textContent.trim();
else if (node && node.firstChild)
{
let ret = "";
for (let child = node.firstChild; child; child = child.nextSibling)
{
let value = this.getNodeValue(child);
if (value)
ret += value;
}
if (ret)
return ret;
}
return null;
},
// Finds elements that are direct children of the first arg.
childrenByTagNameNS: function(aElement, aNamespace, aTagName)
{
var matches = aElement.getElementsByTagNameNS(aNamespace, aTagName);
var matchingChildren = new Array();
for (var i = 0; i < matches.length; i++)
let matches = aElement.getElementsByTagNameNS(aNamespace, aTagName);
let matchingChildren = new Array();
for (let i = 0; i < matches.length; i++)
{
if(matches[i].parentNode == aElement)
if (matches[i].parentNode == aElement)
matchingChildren.push(matches[i])
}
return matchingChildren;
return matchingChildren.length ? matchingChildren : null;
},
findAtomLink: function(linkRel, linkElements)
{
// XXX Need to check for MIME type and hreflang
for ( var j=0 ; j<linkElements.length ; j++ ) {
var alink = linkElements[j];
if (alink &&
//if there's a link rel
((alink.getAttribute('rel') && alink.getAttribute('rel') == linkRel) ||
//if there isn't, assume 'alternate'
(!alink.getAttribute('rel') && (linkRel=="alternate")))
&& alink.getAttribute('href'))
// XXX Need to check for MIME type and hreflang.
for (let j = 0; j < linkElements.length; j++) {
let alink = linkElements[j];
if (alink &&
// If there's a link rel.
((alink.getAttribute("rel") && alink.getAttribute("rel") == linkRel) ||
// If there isn't, assume 'alternate'.
(!alink.getAttribute("rel") && (linkRel == "alternate"))) &&
alink.getAttribute("href"))
{
// Atom links are interpreted relative to xml:base
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
return ioService.newURI(alink.baseURI, null, null).resolve(alink.getAttribute('href'));
// Atom links are interpreted relative to xml:base.
try {
return Services.io.newURI(alink.baseURI, null, null).
resolve(alink.getAttribute("href"));
}
catch (ex) {}
}
}
return null;
},
stripTags: function(someHTML)
{
return someHTML ? someHTML.replace(/<[^>]+>/g,"") : someHTML;
return someHTML ? someHTML.replace(/<[^>]+>/g, "") : someHTML;
},
xmlUnescape: function(s)
@ -542,5 +665,41 @@ FeedParser.prototype =
s = s.replace(/>/g, "&gt;");
s = s.replace(/</g, "&lt;");
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
// in case an ancestor folder was renamed/moved.
let itemResource = rdf.GetResource(item.url);
let ds = getSubscriptionsDS(item.parentFolder.server);
let itemFolder = ds.GetTarget(itemResource, FZ_DESTFOLDER, true);
let itemResource = FeedUtils.rdf.GetResource(item.url);
let ds = FeedUtils.getSubscriptionsDS(item.parentFolder.server);
let itemFolder = ds.GetTarget(itemResource, FeedUtils.FZ_DESTFOLDER, true);
if (itemFolder)
{
itemFolder = itemFolder.QueryInterface(Ci.nsIMsgFolder);
@ -582,7 +582,7 @@ var gFeedSubscriptionsWindow = {
// Finally, set the folder's quickMode based on the its first feed's
// quickMode, since that is how the view determines summary mode, and now
// quickMode is updated to be the same for all feeds in a folder.
if (feeds)
if (feeds && feeds[0])
folderObject.quickMode = feeds[0].quickMode;
return folderObject;
@ -591,16 +591,16 @@ var gFeedSubscriptionsWindow = {
getFeedsInFolder: function (aFolder)
{
let feeds = new Array();
let feedUrlArray = getFeedUrlsInFolder(aFolder);
let feedUrlArray = FeedUtils.getFeedUrlsInFolder(aFolder);
if (!feedUrlArray)
// No feedUrls in this folder.
return;
return feeds;
for (let url in feedUrlArray)
{
if (!feedUrlArray[url])
continue;
let feedResource = rdf.GetResource(feedUrlArray[url]);
let feedResource = FeedUtils.rdf.GetResource(feedUrlArray[url]);
let feed = new Feed(feedResource, aFolder.server);
feeds.push(feed);
}
@ -887,7 +887,7 @@ var gFeedSubscriptionsWindow = {
item.folder.server.setBoolValue("quickMode", aChecked);
this.refreshSubscriptionView();
}
else if (!getFeedUrlsInFolder(item.folder))
else if (!FeedUtils.getFeedUrlsInFolder(item.folder))
// Not a folder with feeds.
return;
else
@ -945,7 +945,7 @@ var gFeedSubscriptionsWindow = {
if (item && item.folder &&
(locationValue.hasAttribute("focused") || locationValue.value ||
item.folder.isServer || getFeedUrlsInFolder(item.folder)))
item.folder.isServer || FeedUtils.getFeedUrlsInFolder(item.folder)))
{
// Enable summary for account folder or folder with feeds or focus/value
// in the feed url field of empty folders prior to add.
@ -981,9 +981,9 @@ var gFeedSubscriptionsWindow = {
return;
}
deleteFeed(rdf.GetResource(itemToRemove.url),
itemToRemove.parentFolder.server,
itemToRemove.parentFolder);
FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(itemToRemove.url),
itemToRemove.parentFolder.server,
itemToRemove.parentFolder);
// Now that we have removed the feed from the datasource, it is time to
// update our view layer. Update parent folder's quickMode if necessary
@ -1061,7 +1061,7 @@ var gFeedSubscriptionsWindow = {
// Before we go any further, make sure the user is not already subscribed
// to this feed.
if (feedAlreadyExists(feedLocation, addFolder.server))
if (FeedUtils.feedAlreadyExists(feedLocation, addFolder.server))
{
message = FeedUtils.strings.GetStringFromName(
"subscribe-feedAlreadySubscribed");
@ -1094,13 +1094,13 @@ var gFeedSubscriptionsWindow = {
// Helper routine used by addFeed and importOPMLFile.
storeFeed: function(feedProperties)
{
let itemResource = rdf.GetResource(feedProperties.feedLocation);
let itemResource = FeedUtils.rdf.GetResource(feedProperties.feedLocation);
let feed = new Feed(itemResource, feedProperties.server);
// If the user specified a folder to add the feed to, then set it here.
if (feedProperties.folderURI)
{
let folderResource = rdf.GetResource(feedProperties.folderURI);
let folderResource = FeedUtils.rdf.GetResource(feedProperties.folderURI);
if (folderResource)
{
let folder = folderResource.QueryInterface(Ci.nsIMsgFolder);
@ -1124,10 +1124,10 @@ var gFeedSubscriptionsWindow = {
if (!itemToEdit || itemToEdit.container || !itemToEdit.parentFolder)
return;
let resource = rdf.GetResource(itemToEdit.url);
let resource = FeedUtils.rdf.GetResource(itemToEdit.url);
let currentFolderServer = itemToEdit.parentFolder.server;
let ds = getSubscriptionsDS(currentFolderServer);
let currentFolder = ds.GetTarget(resource, FZ_DESTFOLDER, true);
let ds = FeedUtils.getSubscriptionsDS(currentFolderServer);
let currentFolder = ds.GetTarget(resource, FeedUtils.FZ_DESTFOLDER, true);
let currentFolderURI = currentFolder.QueryInterface(Ci.nsIRDFResource).Value;
let feed = new Feed(resource, currentFolderServer);
feed.folder = itemToEdit.parentFolder;
@ -1213,15 +1213,15 @@ var gFeedSubscriptionsWindow = {
let currentParentIndex = this.mView.getParentIndex(aOldFeedIndex);
let currentParentItem = this.mView.getItemAtIndex(currentParentIndex);
let currentParentResource = rdf.GetResource(currentParentItem.url);
let currentParentResource = FeedUtils.rdf.GetResource(currentParentItem.url);
let currentFolder = currentParentResource.QueryInterface(Ci.nsIMsgFolder);
let newParentItem = this.mView.getItemAtIndex(aNewParentIndex);
let newParentResource = rdf.GetResource(newParentItem.url);
let newParentResource = FeedUtils.rdf.GetResource(newParentItem.url);
let newFolder = newParentResource.QueryInterface(Ci.nsIMsgFolder);
let ds = getSubscriptionsDS(currentItem.parentFolder.server);
let resource = rdf.GetResource(currentItem.url);
let ds = FeedUtils.getSubscriptionsDS(currentItem.parentFolder.server);
let resource = FeedUtils.rdf.GetResource(currentItem.url);
let accountMoveCopy = false;
if (currentFolder.rootFolder.URI == newFolder.rootFolder.URI)
@ -1233,14 +1233,14 @@ var gFeedSubscriptionsWindow = {
return;
// Unassert the older URI, add an assertion for the new parent URI.
ds.Change(resource, FZ_DESTFOLDER,
ds.Change(resource, FeedUtils.FZ_DESTFOLDER,
currentParentResource, newParentResource);
ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
// Update the feed url attributes on the databases for each folder:
// Remove our feed url property from the current folder.
updateFolderFeedUrl(currentFolder, currentItem.url, true);
FeedUtils.updateFolderFeedUrl(currentFolder, currentItem.url, true);
// Add our feed url property to the new folder.
updateFolderFeedUrl(newFolder, currentItem.url, false);
FeedUtils.updateFolderFeedUrl(newFolder, currentItem.url, false);
}
else
{
@ -1256,9 +1256,9 @@ var gFeedSubscriptionsWindow = {
// Unsubscribe the feed from the old folder, if add to the new folder
// is successfull, and doing a move.
if (moveFeed)
deleteFeed(rdf.GetResource(currentItem.url),
currentItem.parentFolder.server,
currentItem.parentFolder);
FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(currentItem.url),
currentItem.parentFolder.server,
currentItem.parentFolder);
}
// Finally, update our view layer. Update old parent folder's quickMode
@ -1300,7 +1300,7 @@ var gFeedSubscriptionsWindow = {
{
let feedItem = aFeedItem;
let parentItem = aParentItem;
let feedUrlArray = getFeedUrlsInFolder(feedItem.parentFolder);
let feedUrlArray = FeedUtils.getFeedUrlsInFolder(feedItem.parentFolder);
let feedsInFolder = feedUrlArray ? feedUrlArray.length : 0;
if (aRemove && feedsInFolder < 1)
@ -1314,7 +1314,7 @@ var gFeedSubscriptionsWindow = {
// only feed, update the parent folder to the feed's quickMode.
if (feedsInFolder > 1)
{
let feedResource = rdf.GetResource(feedItem.url);
let feedResource = FeedUtils.rdf.GetResource(feedItem.url);
let feed = new Feed(feedResource, feedItem.parentFolder.server);
feed.quickMode = parentItem.quickMode;
feedItem.quickMode = parentItem.quickMode;
@ -1358,11 +1358,11 @@ var gFeedSubscriptionsWindow = {
// If we get here we should always have a folder by now, either in
// feed.folder or FeedItems created the folder for us.
updateFolderFeedUrl(feed.folder, feed.url, false);
FeedUtils.updateFolderFeedUrl(feed.folder, feed.url, false);
// Add feed adds the feed to the subscriptions db and flushes the
// datasource.
addFeed(feed.url, feed.name, feed.folder);
FeedUtils.addFeed(feed.url, feed.name, feed.folder);
// Now add the feed to our view. If adding, the current selection will
// be a folder; if updating it will be a feed. No need to rebuild the
@ -1432,9 +1432,9 @@ var gFeedSubscriptionsWindow = {
{
// Non success. Remove intermediate traces from the feeds database.
if (feed && feed.url && feed.server)
deleteFeed(rdf.GetResource(feed.url),
feed.server,
feed.server.rootFolder);
FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(feed.url),
feed.server,
feed.server.rootFolder);
if (aErrorCode == FeedUtils.kNewsBlogInvalidFeed)
message = FeedUtils.strings.GetStringFromName(
@ -1466,7 +1466,7 @@ var gFeedSubscriptionsWindow = {
this.onProgress(feed, aCurrentFeedItems, aMaxFeedItems);
},
onProgress: function(feed, aProgress, aProgressMax)
onProgress: function(feed, aProgress, aProgressMax, aLengthComputable)
{
gFeedSubscriptionsWindow.updateStatusItem("progressMeter",
(aProgress * 100) / aProgressMax);
@ -1588,7 +1588,10 @@ var gFeedSubscriptionsWindow = {
let curSelItem = this.currentSelectedItem;
let feedWindow = this.feedWindow;
if (aMove && aDestFolder.getFlag(Ci.nsMsgFolderFlags.Trash))
return this.folderDeleted(aSrcFolder);
{
this.folderDeleted(aSrcFolder);
return
}
setTimeout(function() {
feedWindow.refreshSubscriptionView();
@ -1819,7 +1822,7 @@ var gFeedSubscriptionsWindow = {
return -1;
// Silently skip feeds that are already subscribed.
if (feedAlreadyExists(newFeedUrl, this.mRSSServer))
if (FeedUtils.feedAlreadyExists(newFeedUrl, this.mRSSServer))
{
FeedUtils.log.debug("importOutline: already subscribed in account "+
this.mRSSServer.prettyName+", url - "+ newFeedUrl);
@ -1846,11 +1849,11 @@ var gFeedSubscriptionsWindow = {
feed.link = aOutline.getAttribute("htmlUrl");
feed.createFolder();
updateFolderFeedUrl(feed.folder, feed.url, false);
FeedUtils.updateFolderFeedUrl(feed.folder, feed.url, false);
// addFeed adds the feed we have validated and downloaded to
// our datasource, it also flushes the subscription datasource.
addFeed(feed.url, feed.name, feed.folder);
FeedUtils.addFeed(feed.url, feed.name, feed.folder);
// Feed correctly added.
return 1;
}

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

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

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

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

@ -1,7 +1,6 @@
newsblog.jar:
% content messenger-newsblog %content/messenger-newsblog/
* 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/FeedItem.js (content/FeedItem.js)
* content/messenger-newsblog/feed-parser.js (content/feed-parser.js)

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

@ -23,6 +23,7 @@
* Myk Melez <myk@mozilla.org) (Original Author)
* David Bienvenu <bienvenu@nventure.com>
* Ian Neal <iann_bugzilla@blueyonder.co.uk>
* alta88 <alta88@gmail.com>
*
* 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
@ -59,14 +60,6 @@ var nsNewsBlogFeedDownloader =
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"].
createInstance(Ci.nsISupportsArray);
// Add the base folder; it does not get added by ListDescendents.
@ -107,7 +100,7 @@ var nsNewsBlogFeedDownloader =
continue;
}
let feedUrlArray = getFeedUrlsInFolder(folder);
let feedUrlArray = FeedUtils.getFeedUrlsInFolder(folder);
// Continue if there are no feedUrls for the folder in the feeds
// database. All folders in Trash are now unsubscribed, so perhaps
// we may not want to check that here each biff each folder.
@ -126,7 +119,7 @@ var nsNewsBlogFeedDownloader =
{
if (feedUrlArray[url])
{
id = rdf.GetResource(feedUrlArray[url]);
id = FeedUtils.rdf.GetResource(feedUrlArray[url]);
feed = new Feed(id, folder.server);
feed.folder = folder;
// Bump our pending feed download count.
@ -197,7 +190,7 @@ var nsNewsBlogFeedDownloader =
for (let i = 0; i < allServers.Count() && !aFolder; i++)
{
let currentServer = allServers.QueryElementAt(i, Ci.nsIMsgIncomingServer);
if (currentServer && currentServer.type == 'rss')
if (currentServer && currentServer.type == "rss")
aFolder = currentServer.rootFolder;
}
}
@ -265,16 +258,16 @@ var nsNewsBlogFeedDownloader =
// Make sure we aren't already subscribed to this feed before we attempt
// to subscribe to it.
if (feedAlreadyExists(aUrl, aFolder.server))
if (FeedUtils.feedAlreadyExists(aUrl, aFolder.server))
{
aMsgWindow.statusFeedback.showStatusString(
FeedUtils.strings.GetStringFromName('subscribe-feedAlreadySubscribed'));
FeedUtils.strings.GetStringFromName("subscribe-feedAlreadySubscribed"));
return;
}
let itemResource = rdf.GetResource(aUrl);
let itemResource = FeedUtils.rdf.GetResource(aUrl);
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
// 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
// our feed data source.
let feedUrlArray = getFeedUrlsInFolder(aFolder);
let feedUrlArray = FeedUtils.getFeedUrlsInFolder(aFolder);
if (!feedUrlArray)
// No feedUrls in this folder.
return;
let newFeedUrl, id, resource, node;
let ds = getSubscriptionsDS(aFolder.server);
let ds = FeedUtils.getSubscriptionsDS(aFolder.server);
let trashFolder =
aFolder.rootFolder.getFolderWithFlags(Ci.nsMsgFolderFlags.Trash);
for (let url in feedUrlArray)
@ -307,22 +300,22 @@ var nsNewsBlogFeedDownloader =
newFeedUrl = feedUrlArray[url];
if (newFeedUrl)
{
id = rdf.GetResource(newFeedUrl);
id = FeedUtils.rdf.GetResource(newFeedUrl);
// If explicit delete or move to trash, unsubscribe.
if (aUnsubscribe ||
(trashFolder && trashFolder.isAncestorOf(aFolder)))
{
deleteFeed(id, aFolder.server, aFolder);
FeedUtils.deleteFeed(id, aFolder.server, aFolder);
}
else
{
resource = rdf.GetResource(aFolder.URI);
resource = FeedUtils.rdf.GetResource(aFolder.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)
ds.Change(id, FZ_DESTFOLDER, node, resource);
ds.Change(id, FeedUtils.FZ_DESTFOLDER, node, resource);
else
addFeed(newFeedUrl, resource.name, resource);
FeedUtils.addFeed(newFeedUrl, resource.name, resource);
}
}
} // for each feed url in the folder property

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

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