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:
Родитель
a659d92c52
Коммит
502cebd462
|
@ -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(/</g, '<');
|
itemURI = itemURI.replace(/</g, '<');
|
||||||
itemURI = itemURI.replace(/>/g, '>');
|
itemURI = itemURI.replace(/>/g, '>');
|
||||||
itemURI = itemURI.replace(/"/g, '"');
|
itemURI = itemURI.replace(/"/g, '"');
|
||||||
itemURI = itemURI.replace(/&/g, '&');
|
itemURI = itemURI.replace(/&/g, '&');
|
||||||
|
|
||||||
debug('Failed to find item, trying entity replacement version: ' + itemURI);
|
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. & becomes '&'
|
// state. i.e. & 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, "&");
|
||||||
|
s = s.replace(/>/g, ">");
|
||||||
|
s = s.replace(/</g, "<");
|
||||||
|
s = s.replace(/'/g, "'");
|
||||||
|
s = s.replace(/"/g, """);
|
||||||
|
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(/</g, "<");
|
content = content.replace(/</g, "<");
|
||||||
content = content.replace(/>/g, ">");
|
content = content.replace(/>/g, ">");
|
||||||
content = content.replace(/&/g, "&");
|
content = content.replace(/&/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, ">");
|
s = s.replace(/>/g, ">");
|
||||||
s = s.replace(/</g, "<");
|
s = s.replace(/</g, "<");
|
||||||
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) {};
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче