Mercurial > addons > firefox-addons > feed-preview
changeset 14:376a0e415bba
Properly handle non-text content in Atom feed elements
The title, subtitle, summary and content elements of Atom feeds can all have
non-text content. When parsing title and subtitle elements HTML and XHTML
content will be stripped of any markup in order to keep it simple. In summary
and content elements markup will be preserved. Element content of any other
type as well as remote content in content elements will be ignored.
author | Guido Berhoerster <guido+feed-preview@berhoerster.name> |
---|---|
date | Mon, 10 Dec 2018 16:38:11 +0100 |
parents | 799d633ccd4d |
children | 150f07c7595f |
files | js/background.js js/feed-parser.js |
diffstat | 2 files changed, 56 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/js/background.js Sat Dec 08 12:12:18 2018 +0100 +++ b/js/background.js Mon Dec 10 16:38:11 2018 +0100 @@ -48,7 +48,10 @@ const FEED_MAGIC = [ '<rss', '<feed', - ...Object.values(feedParser.XMLNS) + feedParser.XMLNS.ATOM03, + feedParser.XMLNS.ATOM10, + feedParser.XMLNS.RSS09, + feedParser.XMLNS.RSS10 ]; var tabsFeeds = new Map(); var tabsFeedPreviews = new Map();
--- a/js/feed-parser.js Sat Dec 08 12:12:18 2018 +0100 +++ b/js/feed-parser.js Mon Dec 10 16:38:11 2018 +0100 @@ -10,7 +10,8 @@ export const XMLNS = { ATOM10: 'http://www.w3.org/2005/Atom', - RSS09: 'http://my.netscape.com/rdf/simple/0.9/' + RSS09: 'http://my.netscape.com/rdf/simple/0.9/', + XHTML: 'http://www.w3.org/1999/xhtml' } const ALLOWED_LINK_PROTOCOLS = new Set(['http:', 'https:', 'ftp:']); @@ -216,6 +217,46 @@ return new FeedLogo(url); } + parseAtomTextConstruct(containerElement, textOnly = true) { + let contentType = containerElement.getAttribute('type'); + if (contentType === null) { + contentType = 'text'; + } + + if (contentType === 'xhtml') { + let xhtmlRootElement = containerElement.firstElementChild; + if (xhtmlRootElement !== null && + xhtmlRootElement.localName === 'div' && + xhtmlRootElement.namespaceURI === XMLNS.XHTML) { + return textOnly ? xhtmlRootElement.textContent.trim() : + xhtmlRootElement.innerHTML; + } + } else if (contentType === 'html') { + let htmlText = containerElement.textContent; + if (textOnly) { + let htmlDocument = new DOMParser().parseFromString(htmlText, + 'text/html'); + return htmlDocument.body.textContent.trim(); + } + return htmlText + } else if (contentType === 'text') { + let text = containerElement.textContent.trim(); + return textOnly ? text : `<pre>${encodeXML(text)}</pre>`; + } + + // unsupported content type + return; + } + + parseAtomContent(contentElement) { + let contentSrc = contentElement.getAttribute('src'); + if (contentSrc !== null) { + // externally referenced content is not supported + return; + } + return this.parseAtomTextConstruct(contentElement, false); + } + parseAtomEntry(entryElement) { let title; let link; @@ -224,7 +265,7 @@ let titleElement = feedQueryXPath(this.document, entryElement, './atom:title'); if (titleElement !== null) { - title = titleElement.textContent.trim(); + title = this.parseAtomTextConstruct(titleElement); } let linkElement = feedQueryXPath(this.document, entryElement, @@ -241,24 +282,14 @@ let contentElement = feedQueryXPath(this.document, entryElement, './atom:content'); - if (contentElement === null) { - contentElement = feedQueryXPath(this.document, entryElement, - './atom:summary'); + if (contentElement !== null) { + content = this.parseAtomContent(contentElement); } - if (contentElement !== null) { - let contentType = contentElement.getAttribute('type'); - if (contentType === null) { - contentType = 'text'; - } - contentType = contentType.toLowerCase(); - if (contentType === 'xhtml') { - content = contentElement.innerHTML; - } else if (contentType === 'html') { - content = contentElement.textContent; - } else { - let encodedContent = - encodeXML(contentElement.textContent.trim()); - content = `<pre>${encodedContent}</pre>`; + if (typeof content === 'undefined') { + let summaryElement = feedQueryXPath(this.document, entryElement, + './atom:summary'); + if (summaryElement !== null) { + content = this.parseAtomTextConstruct(summaryElement, false); } } @@ -275,13 +306,13 @@ let titleElement = feedQueryXPath(this.document, documentElement, './atom:title'); if (titleElement !== null) { - title = titleElement.textContent.trim(); + title = this.parseAtomTextConstruct(titleElement); } let subtitleElement = feedQueryXPath(this.document, documentElement, './atom:subtitle'); if (subtitleElement !== null) { - subtitle = subtitleElement.textContent.trim(); + subtitle = this.parseAtomTextConstruct(subtitleElement); } let logoElement = feedQueryXPath(this.document, documentElement,