mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 03:47:31 +09:00
620 lines
20 KiB
JavaScript
620 lines
20 KiB
JavaScript
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// 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: {},
|
|
|
|
putFeed: function (aFeed)
|
|
{
|
|
this.mFeeds[this.normalizeHost(aFeed.url)] = aFeed;
|
|
},
|
|
|
|
getFeed: function (aUrl)
|
|
{
|
|
let index = this.normalizeHost(aUrl);
|
|
if (index in this.mFeeds)
|
|
return this.mFeeds[index];
|
|
|
|
return null;
|
|
},
|
|
|
|
removeFeed: function (aUrl)
|
|
{
|
|
let index = this.normalizeHost(aUrl);
|
|
if (index in this.mFeeds)
|
|
delete this.mFeeds[index];
|
|
},
|
|
|
|
normalizeHost: function (aUrl)
|
|
{
|
|
try
|
|
{
|
|
let normalizedUrl = Services.io.newURI(aUrl, null, null);
|
|
normalizedUrl.host = normalizedUrl.host.toLowerCase();
|
|
return normalizedUrl.spec
|
|
}
|
|
catch (ex)
|
|
{
|
|
return aUrl;
|
|
}
|
|
}
|
|
};
|
|
|
|
function Feed(aResource, aRSSServer)
|
|
{
|
|
this.resource = aResource.QueryInterface(Ci.nsIRDFResource);
|
|
this.server = aRSSServer;
|
|
}
|
|
|
|
Feed.prototype =
|
|
{
|
|
description: null,
|
|
author: null,
|
|
request: null,
|
|
server: null,
|
|
downloadCallback: null,
|
|
resource: null,
|
|
items: new Array(),
|
|
itemsStored: 0,
|
|
mFolder: null,
|
|
mInvalidFeed: false,
|
|
mFeedType: null,
|
|
mLastModified: null,
|
|
|
|
get folder()
|
|
{
|
|
return this.mFolder;
|
|
},
|
|
|
|
set folder (aFolder)
|
|
{
|
|
this.mFolder = aFolder;
|
|
},
|
|
|
|
get name()
|
|
{
|
|
// Used for the feed's title in Subcribe dialog and opml export.
|
|
let name = this.title || this.description || this.url;
|
|
return name.replace(/[\n\r\t]+/g, " ").replace(/[\x00-\x1F]+/g, "");
|
|
},
|
|
|
|
get folderName()
|
|
{
|
|
if (this.mFolderName)
|
|
return this.mFolderName;
|
|
|
|
// Get a unique sanitized name. Use title or description as a base;
|
|
// these are mandatory by spec. Length of 80 is plenty.
|
|
let folderName = (this.title || this.description || "").substr(0,80);
|
|
let defaultName = FeedUtils.strings.GetStringFromName("ImportFeedsNew");
|
|
return this.mFolderName = FeedUtils.getSanitizedFolderName(this.server.rootMsgFolder,
|
|
folderName,
|
|
defaultName,
|
|
true);
|
|
},
|
|
|
|
download: function(aParseItems, aCallback)
|
|
{
|
|
// May be null.
|
|
this.downloadCallback = aCallback;
|
|
|
|
// Whether or not to parse items when downloading and parsing the feed.
|
|
// Defaults to true, but setting to false is useful for obtaining
|
|
// just the title of the feed when the user subscribes to it.
|
|
this.parseItems = aParseItems == null ? true : aParseItems ? true : false;
|
|
|
|
// Before we do anything, make sure the url is an http url. This is just
|
|
// a sanity check so we don't try opening mailto urls, imap urls, etc. that
|
|
// the user may have tried to subscribe to as an rss feed.
|
|
if (!FeedUtils.isValidScheme(this.url))
|
|
{
|
|
// Simulate an invalid feed error.
|
|
FeedUtils.log.info("Feed.download: invalid protocol for - " + this.url);
|
|
this.onParseError(this);
|
|
return;
|
|
}
|
|
|
|
// Before we try to download the feed, make sure we aren't already
|
|
// processing the feed by looking up the url in our feed cache.
|
|
if (FeedCache.getFeed(this.url))
|
|
{
|
|
if (this.downloadCallback)
|
|
this.downloadCallback.downloaded(this, FeedUtils.kNewsBlogFeedIsBusy);
|
|
// Return, the feed is already in use.
|
|
return;
|
|
}
|
|
|
|
if (Services.io.offline) {
|
|
// If offline and don't want to go online, just add the feed subscription;
|
|
// it can be verified later (the folder name will be the url if not adding
|
|
// to an existing folder). Only for subscribe actions; passive biff and
|
|
// active get new messages are handled prior to getting here.
|
|
let win = Services.wm.getMostRecentWindow("mail:3pane");
|
|
if (!win.MailOfflineMgr.getNewMail()) {
|
|
this.storeNextItem();
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
|
createInstance(Ci.nsIXMLHttpRequest);
|
|
// Must set onProgress before calling open.
|
|
this.request.onprogress = this.onProgress;
|
|
this.request.open("GET", this.url, true);
|
|
this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE |
|
|
Ci.nsIRequest.INHIBIT_CACHING;
|
|
|
|
// Some servers, if sent If-Modified-Since, will send 304 if subsequently
|
|
// not sent If-Modified-Since, as in the case of an unsubscribe and new
|
|
// subscribe. Send start of century date to force a download; some servers
|
|
// will 304 on older dates (such as epoch 1970).
|
|
let lastModified = this.lastModified || "Sat, 01 Jan 2000 00:00:00 GMT";
|
|
this.request.setRequestHeader("If-Modified-Since", lastModified);
|
|
|
|
// Only order what you're going to eat...
|
|
this.request.responseType = "document";
|
|
this.request.overrideMimeType("text/xml");
|
|
this.request.setRequestHeader("Accept", FeedUtils.REQUEST_ACCEPT);
|
|
this.request.timeout = FeedUtils.REQUEST_TIMEOUT;
|
|
this.request.onload = this.onDownloaded;
|
|
this.request.onerror = this.onDownloadError;
|
|
this.request.ontimeout = this.onDownloadError;
|
|
FeedCache.putFeed(this);
|
|
this.request.send(null);
|
|
},
|
|
|
|
onDownloaded: function(aEvent)
|
|
{
|
|
let request = aEvent.target;
|
|
let isHttp = request.channel.originalURI.scheme.startsWith("http");
|
|
let url = request.channel.originalURI.spec;
|
|
if (isHttp && (request.status < 200 || request.status >= 300))
|
|
{
|
|
Feed.prototype.onDownloadError(aEvent);
|
|
return;
|
|
}
|
|
|
|
FeedUtils.log.debug("Feed.onDownloaded: got a download - " + url);
|
|
let feed = FeedCache.getFeed(url);
|
|
if (!feed)
|
|
throw new Error("Feed.onDownloaded: error - couldn't retrieve feed " +
|
|
"from cache");
|
|
|
|
// If the server sends a Last-Modified header, store the property on the
|
|
// feed so we can use it when making future requests, to avoid downloading
|
|
// and parsing feeds that have not changed. Don't update if merely checking
|
|
// the url, as for subscribe move/copy, as a subsequent refresh may get a 304.
|
|
// Save the response and persist it only upon successful completion of the
|
|
// refresh cycle (i.e. not if the request is cancelled).
|
|
let lastModifiedHeader = request.getResponseHeader("Last-Modified");
|
|
feed.mLastModified = (lastModifiedHeader && feed.parseItems) ?
|
|
lastModifiedHeader : null;
|
|
|
|
// The download callback is called asynchronously when parse() is done.
|
|
feed.parse();
|
|
},
|
|
|
|
onProgress: function(aEvent)
|
|
{
|
|
let request = aEvent.target;
|
|
let url = request.channel.originalURI.spec;
|
|
let feed = FeedCache.getFeed(url);
|
|
|
|
if (feed.downloadCallback)
|
|
feed.downloadCallback.onProgress(feed, aEvent.loaded, aEvent.total,
|
|
aEvent.lengthComputable);
|
|
},
|
|
|
|
onDownloadError: function(aEvent)
|
|
{
|
|
let request = aEvent.target;
|
|
let url = request.channel.originalURI.spec;
|
|
let feed = FeedCache.getFeed(url);
|
|
if (feed.downloadCallback)
|
|
{
|
|
// Generic network or 'not found' error initially.
|
|
let error = FeedUtils.kNewsBlogRequestFailure;
|
|
|
|
if (request.status == 304) {
|
|
// If the http status code is 304, the feed has not been modified
|
|
// since we last downloaded it and does not need to be parsed.
|
|
error = FeedUtils.kNewsBlogNoNewItems;
|
|
}
|
|
else {
|
|
let [errType, errName] = FeedUtils.createTCPErrorFromFailedXHR(request);
|
|
FeedUtils.log.info("Feed.onDownloaded: request errType:errName:statusCode - " +
|
|
errType + ":" + errName + ":" + request.status);
|
|
if (errType == "SecurityCertificate")
|
|
// This is the code for nsINSSErrorsService.ERROR_CLASS_BAD_CERT
|
|
// overrideable security certificate errors.
|
|
error = FeedUtils.kNewsBlogBadCertError;
|
|
|
|
if (request.status == 401 || request.status == 403)
|
|
// Unauthorized or Forbidden.
|
|
error = FeedUtils.kNewsBlogNoAuthError;
|
|
}
|
|
|
|
feed.downloadCallback.downloaded(feed, error);
|
|
}
|
|
|
|
FeedCache.removeFeed(url);
|
|
},
|
|
|
|
onParseError: function(aFeed)
|
|
{
|
|
if (!aFeed)
|
|
return;
|
|
|
|
aFeed.mInvalidFeed = true;
|
|
if (aFeed.downloadCallback)
|
|
aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogInvalidFeed);
|
|
|
|
FeedCache.removeFeed(aFeed.url);
|
|
},
|
|
|
|
onUrlChange: function(aFeed, aOldUrl)
|
|
{
|
|
if (!aFeed)
|
|
return;
|
|
|
|
// Simulate a cancel after a url update; next cycle will check the new url.
|
|
aFeed.mInvalidFeed = true;
|
|
if (aFeed.downloadCallback)
|
|
aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogCancel);
|
|
|
|
FeedCache.removeFeed(aOldUrl);
|
|
},
|
|
|
|
get url()
|
|
{
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
let url = ds.GetTarget(this.resource, FeedUtils.DC_IDENTIFIER, true);
|
|
if (url)
|
|
url = url.QueryInterface(Ci.nsIRDFLiteral).Value;
|
|
else
|
|
url = this.resource.ValueUTF8;
|
|
|
|
return url;
|
|
},
|
|
|
|
get title()
|
|
{
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
let title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
|
|
if (title)
|
|
title = title.QueryInterface(Ci.nsIRDFLiteral).Value;
|
|
|
|
return title;
|
|
},
|
|
|
|
set title (aNewTitle)
|
|
{
|
|
if (!aNewTitle)
|
|
return;
|
|
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
aNewTitle = FeedUtils.rdf.GetLiteral(aNewTitle);
|
|
let old_title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
|
|
if (old_title)
|
|
ds.Change(this.resource, FeedUtils.DC_TITLE, old_title, aNewTitle);
|
|
else
|
|
ds.Assert(this.resource, FeedUtils.DC_TITLE, aNewTitle, true);
|
|
},
|
|
|
|
get lastModified()
|
|
{
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
let lastModified = ds.GetTarget(this.resource,
|
|
FeedUtils.DC_LASTMODIFIED,
|
|
true);
|
|
if (lastModified)
|
|
lastModified = lastModified.QueryInterface(Ci.nsIRDFLiteral).Value;
|
|
|
|
return lastModified;
|
|
},
|
|
|
|
set lastModified(aLastModified)
|
|
{
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
aLastModified = FeedUtils.rdf.GetLiteral(aLastModified);
|
|
let old_lastmodified = ds.GetTarget(this.resource,
|
|
FeedUtils.DC_LASTMODIFIED,
|
|
true);
|
|
if (old_lastmodified)
|
|
ds.Change(this.resource, FeedUtils.DC_LASTMODIFIED,
|
|
old_lastmodified, aLastModified);
|
|
else
|
|
ds.Assert(this.resource, FeedUtils.DC_LASTMODIFIED, aLastModified, true);
|
|
},
|
|
|
|
get quickMode ()
|
|
{
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
let quickMode = ds.GetTarget(this.resource, FeedUtils.FZ_QUICKMODE, true);
|
|
if (quickMode)
|
|
{
|
|
quickMode = quickMode.QueryInterface(Ci.nsIRDFLiteral);
|
|
quickMode = quickMode.Value == "true";
|
|
}
|
|
|
|
return quickMode;
|
|
},
|
|
|
|
set quickMode (aNewQuickMode)
|
|
{
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
aNewQuickMode = FeedUtils.rdf.GetLiteral(aNewQuickMode);
|
|
let old_quickMode = ds.GetTarget(this.resource,
|
|
FeedUtils.FZ_QUICKMODE,
|
|
true);
|
|
if (old_quickMode)
|
|
ds.Change(this.resource, FeedUtils.FZ_QUICKMODE,
|
|
old_quickMode, aNewQuickMode);
|
|
else
|
|
ds.Assert(this.resource, FeedUtils.FZ_QUICKMODE,
|
|
aNewQuickMode, true);
|
|
},
|
|
|
|
get options ()
|
|
{
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
let options = ds.GetTarget(this.resource, FeedUtils.FZ_OPTIONS, true);
|
|
if (options)
|
|
return JSON.parse(options.QueryInterface(Ci.nsIRDFLiteral).Value);
|
|
|
|
return null;
|
|
},
|
|
|
|
set options (aOptions)
|
|
{
|
|
let newOptions = aOptions ? FeedUtils.newOptions(aOptions) :
|
|
FeedUtils._optionsDefault;
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
newOptions = FeedUtils.rdf.GetLiteral(JSON.stringify(newOptions));
|
|
let oldOptions = ds.GetTarget(this.resource, FeedUtils.FZ_OPTIONS, true);
|
|
if (oldOptions)
|
|
ds.Change(this.resource, FeedUtils.FZ_OPTIONS, oldOptions, newOptions);
|
|
else
|
|
ds.Assert(this.resource, FeedUtils.FZ_OPTIONS, newOptions, true);
|
|
},
|
|
|
|
categoryPrefs: function ()
|
|
{
|
|
let categoryPrefsAcct = FeedUtils.getOptionsAcct(this.server).category;
|
|
if (!this.options)
|
|
return categoryPrefsAcct;
|
|
|
|
return this.options.category;
|
|
},
|
|
|
|
get link ()
|
|
{
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
let link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
|
|
if (link)
|
|
link = link.QueryInterface(Ci.nsIRDFLiteral).Value;
|
|
|
|
return link;
|
|
},
|
|
|
|
set link (aNewLink)
|
|
{
|
|
if (!aNewLink)
|
|
return;
|
|
|
|
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
|
aNewLink = FeedUtils.rdf.GetLiteral(aNewLink);
|
|
let old_link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
|
|
if (old_link)
|
|
ds.Change(this.resource, FeedUtils.RSS_LINK, old_link, aNewLink);
|
|
else
|
|
ds.Assert(this.resource, FeedUtils.RSS_LINK, aNewLink, true);
|
|
},
|
|
|
|
parse: function()
|
|
{
|
|
// Create a feed parser which will parse the feed.
|
|
let parser = new FeedParser();
|
|
this.itemsToStore = parser.parseFeed(this, this.request.responseXML);
|
|
parser = null;
|
|
|
|
if (this.mInvalidFeed)
|
|
{
|
|
this.request = null;
|
|
this.mInvalidFeed = false;
|
|
return;
|
|
}
|
|
|
|
// storeNextItem() will iterate through the parsed items, storing each one.
|
|
this.itemsToStoreIndex = 0;
|
|
this.itemsStored = 0;
|
|
this.storeNextItem();
|
|
},
|
|
|
|
invalidateItems: function ()
|
|
{
|
|
let ds = FeedUtils.getItemsDS(this.server);
|
|
FeedUtils.log.debug("Feed.invalidateItems: for url - " + this.url);
|
|
let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
|
|
let item;
|
|
|
|
while (items.hasMoreElements())
|
|
{
|
|
item = items.getNext();
|
|
item = item.QueryInterface(Ci.nsIRDFResource);
|
|
FeedUtils.log.trace("Feed.invalidateItems: item - " + item.Value);
|
|
let valid = ds.GetTarget(item, FeedUtils.FZ_VALID, true);
|
|
if (valid)
|
|
ds.Unassert(item, FeedUtils.FZ_VALID, valid, true);
|
|
}
|
|
},
|
|
|
|
removeInvalidItems: function(aDeleteFeed)
|
|
{
|
|
let ds = FeedUtils.getItemsDS(this.server);
|
|
FeedUtils.log.debug("Feed.removeInvalidItems: for url - " + this.url);
|
|
let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
|
|
let item;
|
|
let currentTime = new Date().getTime();
|
|
while (items.hasMoreElements())
|
|
{
|
|
item = items.getNext();
|
|
item = item.QueryInterface(Ci.nsIRDFResource);
|
|
|
|
if (ds.HasAssertion(item, FeedUtils.FZ_VALID,
|
|
FeedUtils.RDF_LITERAL_TRUE, true))
|
|
continue;
|
|
|
|
let lastSeenTime = ds.GetTarget(item, FeedUtils.FZ_LAST_SEEN_TIMESTAMP, true);
|
|
if (lastSeenTime)
|
|
lastSeenTime = parseInt(lastSeenTime.QueryInterface(Ci.nsIRDFLiteral).Value)
|
|
else
|
|
lastSeenTime = 0;
|
|
|
|
if ((currentTime - lastSeenTime) < FeedUtils.INVALID_ITEM_PURGE_DELAY &&
|
|
!aDeleteFeed)
|
|
// Don't immediately purge items in active feeds; do so for deleted feeds.
|
|
continue;
|
|
|
|
FeedUtils.log.trace("Feed.removeInvalidItems: item - " + item.Value);
|
|
ds.Unassert(item, FeedUtils.FZ_FEED, this.resource, true);
|
|
if (ds.hasArcOut(item, FeedUtils.FZ_FEED))
|
|
FeedUtils.log.debug("Feed.removeInvalidItems: " + item.Value +
|
|
" is from more than one feed; only the reference to" +
|
|
" this feed removed");
|
|
else
|
|
FeedUtils.removeAssertions(ds, item);
|
|
}
|
|
},
|
|
|
|
createFolder: function()
|
|
{
|
|
if (this.folder)
|
|
return;
|
|
|
|
try {
|
|
this.folder = this.server.rootMsgFolder
|
|
.QueryInterface(Ci.nsIMsgLocalMailFolder)
|
|
.createLocalSubfolder(this.folderName);
|
|
}
|
|
catch (ex) {
|
|
// An error creating.
|
|
FeedUtils.log.info("Feed.createFolder: error creating folder - '"+
|
|
this.folderName+"' in parent folder "+
|
|
this.server.rootMsgFolder.filePath.path + " -- "+ex);
|
|
// But its remnants are still there, clean up.
|
|
let xfolder = this.server.rootMsgFolder.getChildNamed(this.folderName);
|
|
this.server.rootMsgFolder.propagateDelete(xfolder, true, null);
|
|
}
|
|
},
|
|
|
|
// Gets the next item from itemsToStore and forces that item to be stored
|
|
// to the folder. If more items are left to be stored, fires a timer for
|
|
// the next one, otherwise triggers a download done notification to the UI.
|
|
storeNextItem: function()
|
|
{
|
|
if (FeedUtils.CANCEL_REQUESTED)
|
|
{
|
|
FeedUtils.CANCEL_REQUESTED = false;
|
|
this.cleanupParsingState(this, FeedUtils.kNewsBlogCancel);
|
|
return;
|
|
}
|
|
|
|
if (!this.itemsToStore || !this.itemsToStore.length)
|
|
{
|
|
let code = FeedUtils.kNewsBlogSuccess;
|
|
this.createFolder();
|
|
if (!this.folder)
|
|
code = FeedUtils.kNewsBlogFileError;
|
|
this.cleanupParsingState(this, code);
|
|
return;
|
|
}
|
|
|
|
let item = this.itemsToStore[this.itemsToStoreIndex];
|
|
|
|
if (item.store())
|
|
this.itemsStored++;
|
|
|
|
if (!this.folder)
|
|
{
|
|
this.cleanupParsingState(this, FeedUtils.kNewsBlogFileError);
|
|
return;
|
|
}
|
|
|
|
this.itemsToStoreIndex++;
|
|
|
|
// If the listener is tracking progress for each item, report it here.
|
|
if (item.feed.downloadCallback && item.feed.downloadCallback.onFeedItemStored)
|
|
item.feed.downloadCallback.onFeedItemStored(item.feed,
|
|
this.itemsToStoreIndex,
|
|
this.itemsToStore.length);
|
|
|
|
// Eventually we'll report individual progress here.
|
|
|
|
if (this.itemsToStoreIndex < this.itemsToStore.length)
|
|
{
|
|
if (!this.storeItemsTimer)
|
|
this.storeItemsTimer = Cc["@mozilla.org/timer;1"].
|
|
createInstance(Ci.nsITimer);
|
|
this.storeItemsTimer.initWithCallback(this, 50, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
}
|
|
else
|
|
{
|
|
// We have just finished downloading one or more feed items into the
|
|
// destination folder; if the folder is still listed as having new
|
|
// messages in it, then we should set the biff state on the folder so the
|
|
// right RDF UI changes happen in the folder pane to indicate new mail.
|
|
if (item.feed.folder.hasNewMessages)
|
|
{
|
|
item.feed.folder.biffState = Ci.nsIMsgFolder.nsMsgBiffState_NewMail;
|
|
// Run the bayesian spam filter, if enabled.
|
|
item.feed.folder.callFilterPlugins(null);
|
|
}
|
|
|
|
this.cleanupParsingState(this, FeedUtils.kNewsBlogSuccess);
|
|
}
|
|
},
|
|
|
|
cleanupParsingState: function(aFeed, aCode)
|
|
{
|
|
// Now that we are done parsing the feed, remove the feed from the cache.
|
|
FeedCache.removeFeed(aFeed.url);
|
|
|
|
if (aFeed.parseItems)
|
|
{
|
|
// Do this only if we're in parse/store mode.
|
|
aFeed.removeInvalidItems(false);
|
|
|
|
if (aCode == FeedUtils.kNewsBlogSuccess && aFeed.mLastModified)
|
|
aFeed.lastModified = aFeed.mLastModified;
|
|
|
|
// Flush any feed item changes to disk.
|
|
let ds = FeedUtils.getItemsDS(aFeed.server);
|
|
ds.Flush();
|
|
FeedUtils.log.debug("Feed.cleanupParsingState: items stored - " + this.itemsStored);
|
|
}
|
|
|
|
// Force the xml http request to go away. This helps reduce some nasty
|
|
// assertions on shut down.
|
|
this.request = null;
|
|
this.itemsToStore = "";
|
|
this.itemsToStoreIndex = 0;
|
|
this.itemsStored = 0;
|
|
this.storeItemsTimer = null;
|
|
|
|
if (aFeed.downloadCallback)
|
|
aFeed.downloadCallback.downloaded(aFeed, aCode);
|
|
},
|
|
|
|
// nsITimerCallback
|
|
notify: function(aTimer)
|
|
{
|
|
this.storeNextItem();
|
|
}
|
|
};
|