=> {
// @ts-ignore
return Array.from(parent.children).
filter(
(e) =>
e.tagName.toLowerCase() == 'link' &&
(e.getAttribute('type') || '').toLowerCase().startsWith(type.toLowerCase()) &&
(e.getAttribute('href') || '').length > 0
).
map((e) => toAbsoluteURL(e.getAttribute('href'))).
filter((l) => l != null)
}
// Get the RSS/Atom root element of the current page, if available
const getFeedRoot = (): HTMLElement | null => {
const xmlDoc = document.documentElement
// Check if it's an RSS feed
if (xmlDoc.tagName.toLowerCase() === 'rss')
return xmlDoc
// Check if it's an Atom feed
if (xmlDoc.tagName.toLowerCase() === 'feed')
return xmlDoc
// Chrome-based browsers may wrap the XML into an HTML view
const webkitSource = document.getElementById('webkit-xml-viewer-source-xml')
if (webkitSource)
return webkitSource
// For some ugly reasons, some RSS feeds are rendered inside of a in a normal HTML DOM
const preElements = document.getElementsByTagName('pre')
if (preElements.length !== 1)
return null
return preElements[0]
}
// Convert an XML string to a DOM object if it's a valid feed
const textToDOM = (text: string) => {
const parser = new DOMParser()
let xmlDoc = null
try {
// @ts-ignore
xmlDoc = parser.parseFromString(text, 'text/xml')
} catch (e) { }
if (!xmlDoc)
return
// @ts-ignore
const root = xmlDoc.documentElement
if (
root.tagName.toLowerCase() === 'rss' ||
root.tagName.toLowerCase() === 'feed'
)
return root
}
// Render a feed. It accepts an XML string as an argument.
// If not passed, it will try to render any feeds on the current page.
const renderFeed = (text: string, url: string | null) => {
const xmlDoc = text?.length ? textToDOM(text) : getFeedRoot()
if (!xmlDoc)
// Not a feed
return
// Check if it's an RSS feed
let feed = xmlDoc.getElementsByTagName('channel')[0]
if (!feed) {
// Check if it's an Atom feed
if (xmlDoc.tagName.toLowerCase() !== 'feed')
return
feed = xmlDoc
}
// Save the parsed feed to the storage and redirect to the viewer
browser.storage.local.set(parseFeed(feed, url))
window.location.href = browser.runtime.getURL('viewer/index.html')
}
// Extract any feed URL published on the page
const extractFeedUrl = () => {
const links = Array.from(document.getElementsByTagName('link'))
.filter((link) =>
link.getAttribute('rel') === 'alternate' &&
(
link.getAttribute('type')?.startsWith('application/rss+xml') ||
link.getAttribute('type')?.startsWith('application/atom+xml')
)
)
if (!links.length)
return
return toAbsoluteURL(links[0].getAttribute('href'))
}
// Main message listener
browser.runtime.onMessage.addListener(
async (
message: {
type: string,
url: string | null,
document: string,
}
) => {
if (message.type === 'renderFeed')
return renderFeed(message.document, message.url)
if (message.type === 'extractFeedUrl')
return extractFeedUrl()
console.warn(`Received unknown message type: ${message.type}`)
}
)