* Introduction

;; This section is [[non-normative]].

[342] This specification defines a set of steps that can be used to
parse feeds in the wild.

[346] This specification supports feed using following formats and modules:
[FIG(list short)[
- [[RSS 0.9[VAR[x]]]]
- [[RSS 1.0]]
- [[RSS 2.0]]
- [[Atom 0.3]]
- [[Atom 1.0]]
- [[Dublin Core]]
- [[GData]]
- [[Hatena module]]
- [[Podcast]] ([[iTunes namespace]])
- [[Media RSS]]
- [[RSS 1.0 Content module]]
]FIG]

* Data model

[15] A [DFN[feed]] has
[DFN[[F[entries]]][feed's entries]], which is a list of [[entries][entry]], and
[DFN[[F[authors]]][feed's authors]], which is a list of [[persons][person]].
They are initially empty.

[39] A [[feed]] has 
[DFN[[F[page URL]]][feed's page URL]],
[DFN[[F[feed URL]]][feed's feed URL]],
[DFN[[F[previous feed URL]]][feed's previous feed URL]],
[DFN[[F[next feed URL]]][feed's next feed URL]],
[DFN[[F[icon]]][feed's icon]],
[DFN[[F[logo]]][feed's logo]], and
[DFN[[F[updated]]][feed's updated]].
They are initially [[null]].

[226] A [[feed]] has
[DFN[[F[title]]][feed's title]],
[DFN[[F[subtitle]]][feed's subtitle]], and
[DFN[[F[description]]][feed's description]].
They are initially [[null]].  They can be [[null]], a [[string]], or a [CODE(DOMi)@en[Node]].

[28] An [DFN[entry]] has
[DFN[[F[authors]]][entry's authors]], which is a list of [[persons][person]],
[DFN[[F[categories]]][feed's categories]], which is a set of strings, and
[DFN[[F[enclosures]]][feed's enclosures]], which is a list of [[enclosures][enclosure]].
They are initially empty.

[88] An [[entry]] has
[DFN[[F[feed]]][entry's feed]],
[DFN[[F[page URL]]][entry's page URL]],
[DFN[[F[thumbnail]]][entry's thumbnail]],
[DFN[[F[duration][entry's duration]]]],
[DFN[[F[published]]][entry's published]], and
[DFN[[F[updated]]][entry's updated]].
They are initially [[null]].

[227] An [[entry]] has
[DFN[[F[title]]][entry's title]],
[DFN[[F[summary]]][entry's summary]], and
[DFN[[F[content]]][entry's content]].
They are initially [[null]].  They can be [[null]], a [[string]], or a [CODE(DOMi)@en[Node]].

[81] To get the [DFN[[F[computed authors]]]] of an [[entry]] [VAR[entry]], run these steps:
[FIG(steps)[
= [82] If [VAR[entry]]'s [F[authors][entry's authors]] is not empty,
return [VAR[entry]]'s [F[authors][entry's authors]].
= [83] Otherwise, if [VAR[entry]]'s [F[feed][entry's feed]] is not null,
return [VAR[entry]]'s [F[feed][entry's feed]]'s [F[authors][feed's authors]].
= [84] Otherwise, return an empty list.
]FIG]

[93] To get the [DFN[[F[computed updated]]]] of an [[entry]] [VAR[entry]], run these steps:
[FIG(steps)[
= [94] If [VAR[entry]]'s [F[updated][entry's updated]] is not [[null]],
return [VAR[entry]]'s [F[updated][entry's updated]].
= [97] Otherwise, if [VAR[entry]]'s [F[published][entry's published]] is not [[null]],
return [VAR[entry]]'s [F[published][entry's published]].
= [95] Otherwise, if [VAR[entry]]'s [F[feed][feed's entry]] is not null,
return [VAR[entry]]'s [F[feed][entry's feed]]'s [F[updated][feed's updated]].
= [96] Otherwise, return
@@ the current timestamp.
]FIG]

[29] A [DFN[person]] is a tuple of 
[DFN[[F[name]]][person's name]],
[DFN[[F[email]]][person's email]],
[DFN[[F[page URL]]][person's page URL]], and
[DFN[[F[icon]]][person's icon]].
They are initially [[null]].

[160] An [DFN[image]] has
[DFN[[F[URL]]][image's URL]],
[DFN[[F[width]]][image's width]], and
[DFN[[F[height]]][image's height]].
They are initially [[null]].

[188] An [DFN[enclosure]] has
[DFN[[F[URL]]][enclosure's URL]],
[DFN[[F[MIME type]]][enclosure's MIME type]], and
[DFN[[F[length]]][enclosure's length]].
They are initially [[null]].

;; [190] Note that a [[MIME type]] might or might not be a [[parsible MIME type]].

* Parsing

[344] An implementation that supports this specification [MUST[MUST]] use the steps to
[[process a feed response]] to parse a [[response]] as a feed.

[345] An implementation that supports this specification [MUST[MUST]] use the steps to
[[process a feed document]] to parse a [CODE(DOMi)@en[Document]] as a feed.

** Namespaces

[365] The following namespaces are used in this specification:
[FIG(table)[
:name: Name
:url: [[Namespace URL][namespace URL]]

:name: The [DFN@en[null namespace]]
:url: [[null]]

:name: The [DFN@en[Atom namespace]]
:url: [CODE(URI)@en[http://www.w3.org/2005/Atom]]

:name: The [DFN@en[Atom 0.3 namespace]]
:url: [CODE(URI)@en[http://purl.org/atom/ns#]]

:name: The [DFN@en[Dublin Core namespace]]
:url: [CODE(URI)@en[http://purl.org/dc/elements/1.1/]]

:name: The [DFN@en[GData namespace]]
:url: [CODE(URI)@en[http://schemas.google.com/g/2005]]

:name: The [DFN@en[Hatena namespace]]
:url: [CODE(URI)@en[http://www.hatena.ne.jp/info/xmlns#]]

:name: The [DFN@en[HTML namespace]]
:url: [CODE(URI)@en[http://www.w3.org/1999/xhtml]]

:name: The [DFN@en[iTunes namespace]]
:url: [CODE(URI)@en[http://www.itunes.com/dtds/podcast-1.0.dtd]]
or [CODE(URI)@en[http://www.itunes.com/DTDs/Podcast-1.0.dtd]]

:name: The [DFN@en[Media RSS namespace]]
:url: [CODE(URI)@en[http://search.yahoo.com/mrss/]] or
[CODE(URI)@en[http://search.yahoo.com/mrss]]

:name: The [DFN@en[RDF namespace]]
:url: [CODE(URI)@en[http://www.w3.org/1999/02/22-rdf-syntax-ns#]]

:name: The [DFN@en[RSS namespace]]
:url: [CODE(URI)@en[http://purl.org/rss/1.0/]]

:name: The [DFN@en[RSS content namespace]]
:url: [CODE(URI)@en[http://purl.org/rss/1.0/modules/content/]]

:name: The [DFN@en[SVG namespace]]
:url: [CODE(URI)@en[http://www.w3.org/2000/svg]]

]FIG]

** Timestamps

[51] To [DFN[process an Atom date]] [VAR[element]], run these steps:
[FIG(steps)[
= [75] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [76] Return the result of [[parsing an RFC 3339 [CODE[xs:dateTime]] string][parse an RFC 3339 [CODE[xs:dateTime]] string]] [VAR[text]].
]FIG]

@@ [77] Need to define [DFN[parse an RFC 3339 [CODE[xs:dateTime]] string]].
It returns a number (JS timestamp) or [[null]].

[166] To [DFN[process a W3C-DTF date]] [VAR[element]], run these steps:
[FIG(steps)[
= [167] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [168] Return the result of [[parsing a W3C-DTF string][parse a W3C-DTF string]] [VAR[text]].
]FIG]

@@ [169] Need to define [DFN[parse a W3C-DTF string]].
It returns a number (JS timestamp) or [[null]].

[219] To [DFN[process an RSS 2.0 date]] [VAR[element]], run these steps:
[FIG(steps)[
= [220] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [221] Return the result of [[parsing an RFC 822 date-time string][parse an RFC 822 date-time string]] [VAR[text]].
]FIG]

@@ [222] Need to define [DFN[parse an RFC 822 date-time string]].
It returns a number (JS timestamp) or [[null]].

** Links

[372] To [DFN[process a URL element]] [VAR[element]], run these steps:
[FIG(steps)[
= [35] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [196] If [VAR[text]] is the [[empty string]], return [[null]] and abort these steps.
= [36] [[Parse][parse a URL]] [VAR[text]] relative to [VAR[element]]'s [F[node document]].
= [38] If not failed, return the [[resulting URL string]].
= [373] Otherwise, return [[null]].
]FIG]

[130] The [DFN[[F[link relation]]]] of a [CODE(XMLe)@en[link][atom:link]] element 
in the [[Atom namespace]] or in the [[Atom 0.3 namespace]]
[VAR[element]] is the value returned by the following steps:
[FIG(steps)[
= [131] If [VAR[element]] does not have a [CODE(XMLa)@en[rel]] attribute:
== [132] Return [CODE[http://www.iana.org/assignments/relation/alternate]].
= [134] Otherwise:
== [135] Let [VAR[rel]] be [VAR[element]]'s [CODE(XMLa)@en[rel]] attribute value.
== [136] If [VAR[rel]] contains a [CODE[:]] character:
=== [137] Return [VAR[rel]].
== [138] Otherwise:
=== [133] Return [CODE[http://www.iana.org/assignments/relation/]]
followed by [VAR[rel]].
]FIG]

[140] The [DFN[[F[href URL]]]] of a [CODE(XMLe)@en[link][atom:link]] element
in the [[Atom namespace]] or in the [[Atom 0.3 namespace]]
[VAR[element]] is the value returned by the following steps:
[FIG(steps)[
= [141] Let [VAR[text]] be [VAR[element]]'s [CODE(XMLa)@en[href]] attribute value,
if any, or the empty string.
= [142] [[Parse][parse a URL]] [VAR[text]] relative to [VAR[element]]'s [F[node document]].
= [143] If failed, return [[null]].
= [144] Otherwise, return the [[resulting URL record]].
]FIG]

[234] To [DFN[process an Atom link]] [VAR[element]] for [VAR[object]]
with [VAR[type]], run these steps:

[FIG(steps)[
= [139] If [VAR[element]]'s [F[link relation]] is 
[CODE[http://www.iana.org/assignments/relation/alternate]]:
== [145] If [VAR[object]]'s [F[page URL]] is [[null]]:
=== [146] Set [VAR[object]]'s [F[page URL]] to
[VAR[child]]'s [F[href URL]].
= [235] If [VAR[mode]] is [CODE[feed]]:
== [150] If [VAR[element]]'s [F[link relation]] is 
[CODE[http://www.iana.org/assignments/relation/self]]:
=== [151] If [VAR[object]]'s [F[feed URL][feed's feed URL]] is [[null]]:
==== [152] Set [VAR[object]]'s [F[feed URL][feed's feed URL]] to
[VAR[child]]'s [F[href URL]].
== [199] If [VAR[element]]'s [F[link relation]] is 
[CODE[http://www.iana.org/assignments/relation/prev]] or
[CODE[http://www.iana.org/assignments/relation/previous]]:
=== [201] If [VAR[object]]'s [F[previous feed URL][feed's previous feed URL]] is [[null]]:
==== [202] Set [VAR[object]]'s [F[previous feed URL][feed's previous feed URL]] to
[VAR[child]]'s [F[href URL]].
== [203] If [VAR[element]]'s [F[link relation]] is 
[CODE[http://www.iana.org/assignments/relation/next]]:
=== [204] If [VAR[object]]'s [F[next feed URL][feed's next feed URL]] is [[null]]:
==== [205] Set [VAR[object]]'s [F[next feed URL][feed's next feed URL]] to
[VAR[child]]'s [F[href URL]].
= [269] If [VAR[mode]] is [CODE[entry]]:
== [270] If [VAR[element]]'s [F[link relation]] is 
[CODE[http://www.iana.org/assignments/relation/enclosure]]:
=== [336] Let [VAR[url]] be [VAR[child]]'s [F[href URL]].
=== [337] If [VAR[url]] is not [[null]]:
==== [271] Let [VAR[enclosure]] be an [[enclosure]],
whose [F[URL][enclosure's URL]] is [VAR[url]].
==== [273] Set [VAR[enclosure]]'s [F[type][enclosure's type]] to [VAR[child]]'s
[CODE(XMLa)@en[type]] attribute value.
==== [275] Let [VAR[length]] be [VAR[child]]'s [CODE(XMLa)@en[length]] attribute value.
==== [276] If [VAR[length]] is not null:
===== [277] Let [VAR[n]] be the result of applying the
[[rules for parsing non-negative integers]] to [VAR[length]].
===== [278] If [VAR[n]] is not an error and [VAR[n]] is greater than zero:
====== [274] Set [VAR[enclosure]]'s [F[length][enclosure's length]] to [VAR[n]].
=== [272] Append [VAR[enclosure]] to [VAR[object]]'s [F[enclosures][entry's enclosures]].
]FIG]

[176] To [DFN[process an image element]] [VAR[element]] with string
[VAR[attribute name]], run these steps:

[FIG(steps)[
= [161] Let [VAR[text]] be [VAR[element]]'s [VAR[attribute name]] attribute value.
= [170] If [VAR[text]] is not [[null]] or the [[empty string]]:
== [179] [[Parse][parse a URL]] [VAR[text]] relative to [VAR[element]]'s [F[node document]].
== [187] If not failed:
=== [228] Let [VAR[image]] be an [[image]].
=== [229] Set [VAR[image]]'s [F[URL][image's URL]] to the [[resulting URL string]].
=== [297] Let [VAR[w]] be the result of applying the
[[rules for parsing non-negative integers]] to [VAR[element]]'s [CODE(XMLa)@en[width]]
attribute value, if any, or the empty string.
=== [298] If [VAR[w]] is not an error, set [VAR[image]]'s [F[width][image's width]]
to [VAR[w]].
=== [299] Let [VAR[h]] be the result of applying the
[[rules for parsing non-negative integers]] to [VAR[element]]'s [CODE(XMLa)@en[height]]
attribute value, if any, or the empty string.
=== [300] If [VAR[h]] is not an error, set [VAR[image]]'s [F[height][image's height]]
to [VAR[h]].
=== [296] Return [VAR[image]] and abort these steps.
= [177] Return [[null]].
]FIG]

** Persons

[24] To [DFN[process an Atom person]] [VAR[element]], run these steps:
[FIG(steps)[
= [54] Let [VAR[person]] be a [[person]].
= [55] For each [[element]] [VAR[child]] in [VAR[element]]'s [F[children]], in order,
run these substeps:
== [56] Switch by [VAR[child]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[name]] element in the [[Atom namespace]] or in the [[Atom 0.3 namespace]]:
If [VAR[person]]'s [F[name][person's name]] is [[null]],
set [VAR[person]]'s [F[name][person's name]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[email]] element in the [[Atom namespace]] or in the [[Atom 0.3 namespace]]:
If [VAR[person]]'s [F[email][person's email]] is [[null]],
set [VAR[person]]'s [F[email][person's email]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[uri]] element in the [[Atom namespace]] or in the [[Atom 0.3 namespace]] :
If [VAR[person]]'s [F[page URL][person's page URL]] is [[null]],
set [VAR[person]]'s [F[page URL][person's page URL]] to the result of
[[processing a URL element][process a URL element]] [VAR[child]].
: [CODE(XMLe)@en[image]] element in the [[GData namespace]] :
If [VAR[person]]'s [F[icon][person's icon]] is [[null]],
set [VAR[person]]'s [F[icon][person's icon]] to the result of
[[processing an image element][process an image element]] [VAR[child]]
with [VAR[attribute name]] [CODE[src]].
]FIG]
= [53] Return [VAR[person]].
]FIG]

[247] To [DFN[process an RSS 2.0 person]] [VAR[element]], run these steps:
[FIG(steps)[
= [254] Let [VAR[person]] be a [[person]].
= [253] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [318] If [VAR[text]] is the [[empty string]]:
== [319] Return [[null]].
= [157] Otherwise, if [VAR[text]] is
one or more [[Unicode code points][Unicode code point]] that are not [[space characters]],
followed by one or more [[space characters]],
followed by a [CODE[(]] character,
followed by one or more [[Unicode code points][Unicode code point]],
followed by a [CODE[)]] character:
== [158] Set [VAR[person]]'s [F[name][person's name]] to the substring
between [CODE[(]] and [CODE[)]] characters in [VAR[text]], not inclusive.
== [159] Set [VAR[person]]'s [F[email][person's email]] to the substring
before the first [[space character][space characters]] in [VAR[text]].
= [320] Otherwise:
== [255] Set [VAR[person]]'s [F[name][person's name]] to [VAR[text]].
= [256] Return [VAR[person]].
]FIG]

** Texts

[60] To [DFN[process a string element]] [VAR[element]], run these steps:
[FIG(steps)[
= [98] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [99] If [VAR[text]] contains a [[Unicode code point]] that is not a [[space character]]:
== [100] Return [VAR[text]].
= [164] Otherwise:
== [174] Return [[null]].
]FIG]

[50] To [DFN[process an Atom text]] [VAR[element]], run these steps:
[FIG(steps)[
= [61] Let [VAR[type]] be the [CODE(XMLa)@en[type]] attribute value
of [VAR[element]], if any, or [[null]].
= [62] If [VAR[type]] is [CODE[html]]:
== [267] Return the result of [[parsing escaped HTML content][parse escaped HTML content]] of [VAR[element]].
= [63] Otherwise, if [VAR[type]] is [CODE[xhtml]] and
[VAR[element]]'s [F[children]] contains a [CODE(HTMLe)@en[div]] element:
== [66] Let [VAR[div]] be a [[clone]] of [VAR[element]]'s
first [CODE(HTMLe)@en[div]] element child, with [VAR[clone children flag]] set.
== [67] Let [VAR[fragment]] be a [CODE(DOMi)@en[DocumentFragment]]
whose [F[node document]] is [VAR[element]]'s [F[node document]].
== [68] For each child [VAR[node]] in [VAR[div]]'s [F[children]], in order,
[[insert]] [VAR[node]] into [VAR[fragment]].
== [104] [[Sanitize][sanitize]] [VAR[fragment]].
== [105] If [VAR[fragment]] [[has significant content]]:
=== [69] Return [VAR[fragment]].
== [106] Otherwise:
=== [107] Return [[null]].
= [64] Otherwise:
== [65] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
== [45] If [VAR[text]] contains a [[Unicode code point]] that is not a [[space character]]:
=== [46] Return [VAR[text]].
== [47] Otherwise:
=== [48] Return [[null]].
]FIG]

;; [102] [[MIME type]] [CODE(XMLa)@en[type]] attribute values
and [CODE(XMLa)@en[src]] attribute of the [CODE(XMLe)@en[atom:content]]
element is not supported by these steps as they are not widely used.

[153] To [DFN[process an Atom 0.3 content]] [VAR[element]], run these steps:
[FIG(steps)[
= [155] Let [VAR[mode]] be [VAR[element]]'s [CODE(XMLa)@en[mode]] attribute value,
if any, or [CODE(MIME)@en[xml]].
= [154] Let [VAR[type]] be [VAR[element]]'s [CODE(XMLa)@en[type]] attribute value,
if any, or [CODE(MIME)@en[text/plain]].
= [175] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [162] If [VAR[mode]] is [CODE[escaped]]:
== [180] If [VAR[type]] is equal to [CODE(MIME)@en[text/html]] ([[ASCII case-insensitive]]):
=== [268] Return the result of [[parsing escaped HTML content][parse escaped HTML content]] of [VAR[element]] and abort these steps.
= [49] If [VAR[text]] contains a [[Unicode code point]] that is not a [[space character]]:
== [57] Return [VAR[text]].
= [58] Otherwise:
== [59] Return [[null]].
]FIG]

;; [103] Combinations of [VAR[mode]] and [VAR[type]] not widely used 
are not supported by these steps.

[178] To [DFN[parse escaped HTML content]] of element [VAR[element]], run these steps:
[FIG(steps)[
= [101] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [73] Let [VAR[div]] be a [CODE(HTMLe)@en[div]] element
whose [F[node document]] is [VAR[element]]'s [F[node document]].
= [74] Let [VAR[fragment]] be a [CODE(DOMi)@en[DocumentFragment]]
whose [F[node document]] is [VAR[element]]'s [F[node document]].
= [70] Let [VAR[nodes]] be the result of running the [[HTML fragment parsing algorithm]]
with [VAR[context]] set to [VAR[div]] and [VAR[input]] set to [VAR[text]].
= [71] For each item [VAR[node]] in [VAR[nodes]], in order,
[[insert]] [VAR[node]] into [VAR[fragment]].
= [108] [[Sanitize][sanitize]] [VAR[fragment]].
= [109] If [VAR[fragment]] [[has significant content]]:
== [339] If [VAR[fragment]]'s [F[children]] contains exactly one [CODE(DOMi)@en[Text]]:
=== [340] Return [VAR[fragment]]'s [F[children]]'s first item's [F[data]].
== [341] Otherwise:
=== [72] Return [VAR[fragment]].
= [110] Otherwise:
== [111] Return [[null]].
]FIG]

[112] To [DFN[sanitize]] [CODE(DOMi)@en[Node]] [VAR[node]], run these steps:
[FIG(steps)[
= [113] If there is
an [CODE(HTMLe)@en[img]] element whose
[CODE(HTMLa)@en[width]] attribute value is [CODE[1]] and
[CODE(HTMLa)@en[height]] attribute value is [CODE[1]],
[[remove]] it from its [F[parent]].
]FIG]

;; [115] These steps do not remove possibly dangerous content, such as 
[CODE(HTMLe)@en[script]] elements.  When [VAR[node]] is used
(e.g. inserted into a document), they have to be removed or
sandboxed (by, e.g., [CODE(HTMLa)@en[sandbox]] attribute and [[CSP]])
such that unreliable script will not run and
any fetch to a third party origin will be blocked.

[114] A [CODE(DOMi)@en[Node]] [VAR[node]] [DFN[has significant content]] if
there is a [[feed significant content]] [[inclusive descendant]] of [VAR[node]],
which is not an [[inclusive descendant]] of
an element matching one of the following conditions
which is an [[inclusive descendant]] of [VAR[node]]:
[FIG(list)[
- a [CODE(HTMLe)@en[script]] element in the [[HTML namespace]]
- a [CODE(HTMLe)@en[style]] element in the [[HTML namespace]]
- a [CODE(XMLe)@en[script]] element in the [[SVG namespace]]
- a [CODE(XMLe)@en[style]] element in the [[SVG namespace]]
- an element with [CODE(HTMLa)@en[hidden]] attribute
]FIG]

[129] A [CODE(DOMi)@en[Node]] is a [[feed significant content]] if
it is a [[palpable content]] and is an [[embedded content]].

;; [173] There is a [[non-normative]] [[JSON]] data file including the
list of [[feed significant content]]:
[REFS[
- [156] [CITE@en[data-web-defs/elements.json at master · manakai/data-web-defs]] ([TIME[2016-03-19 22:43:10 +09:00]] 版) <https://github.com/manakai/data-web-defs/blob/master/data/elements.json>
-- [172] Documentation: [CITE@en[data-web-defs/elements.txt at master · manakai/data-web-defs]] ([TIME[2016-03-19 22:44:01 +09:00]] 版) <https://github.com/manakai/data-web-defs/blob/master/doc/elements.txt>
]REFS]

;; [128] As the [CODE(DOMi)@en[Node]] objects returned by algorithms in this specification
share the same [F[node document]] as the input document, their [[base URL]]
is the [[URL]] of the input response.

[362] Two inputs [VAR[A]] and [VAR[B]] are [DFN[same text content]]
[[iff]]:
[FIG(list)[
- both [VAR[A]] and [VAR[B]] are [[string]]s and they are equal, or
- both [VAR[A]] and [VAR[B]] are [CODE(DOMi)@en[Node]]s and they are [[equal]].
]FIG]

;; [363] [[Equality][equals]] of nodes can be tested by the [[DOM]]
[CODE(DOMm)@en[isEqualNode]] method.

** Entries

[262] To [DFN[cleanup entry]] [VAR[entry]], run these steps:
[FIG(steps)[
= [263] If [VAR[entry]]'s [F[page URL][entry's page URL]] is not [[null]] and
there is an [[enclosure]] whose [F[URL][enclosure's URL]] is equal to
[VAR[entry]]'s [F[page URL][entry's page URL]] in
[VAR[entry]]'s [F[enclosures][entry's enclosures]]:
== [264] Set [VAR[entry]]'s [F[page URL][entry's page URL]] to [[null]].
= [332] If [VAR[entry]]'s [F[page URL][entry's page URL]] is not [[null]],
[VAR[entry]]'s [F[thumbnail][entry's thumbnail]] is [[null]],
and there is an [[enclosure]] whose
[F[type][enclosure's type]] starts with [CODE[image/]] ([[ASCII case-insensitive]]) or
whose [F[type][enclosure's type]] is [[null]] and
[F[URL][enclosure's URL]] ends by [CODE[.jpeg]], [CODE[.jpg]], or [CODE[.png]]:
== [334] Let [VAR[enclosure]] be the first such [[enclosure]].
== [333] Let [VAR[image]] be an [[image]] whose [F[URL][image's URL]]
is [VAR[enclosure]]'s [F[URL][enclosure's URL]].
== [335] Remove [VAR[enclosure]] from [VAR[entry]]'s [F[enclosures][entry's enclosures]].
= [265] If 
[VAR[entry]]'s [F[title][entry's title]] and
[VAR[entry]]'s [F[subtitle][entry's subtitle]]
have [[same text content]],
set [VAR[entry]]'s [F[subtitle][entry's subtitle]] to [[null]].
= [266] If 
[VAR[entry]]'s [F[title][entry's title]] and
[VAR[entry]]'s [F[summary][entry's summary]]
have [[same text content]],
set [VAR[entry]]'s [F[summary][entry's summary]] to [[null]].
= [291] If 
[VAR[entry]]'s [F[summary][entry's summary]] and
[VAR[entry]]'s [F[content][entry's content]]
have [[same text content]],
set [VAR[entry]]'s [F[content][entry's content]] to [[null]].
]FIG]

*** Atom entries

[52] To [DFN[process an Atom entry]] [VAR[element]], run these steps:
[FIG(steps)[
= [78] Let [VAR[entry]] be an [[entry]].
= [79] For each [[element]] [VAR[child]] in [VAR[element]]'s [F[children]], in order,
run these substeps:
== [80] Switch by [VAR[child]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[author]] element in the [[Atom namespace]] or in the [[Atom 0.3 namespace]] :
Append the result of [[processing an Atom person][process an Atom person]] [VAR[child]]
to [VAR[entry]]'s [F[authors][entry's authors]].
: [CODE(XMLe)@en[category]] element in the [[Atom namespace]] :
[FIG(steps)[
= [30] If [VAR[child]] has a [CODE(XMLa)@en[term]] attribute:
== [44] Let [VAR[term]] be [VAR[child]]'s [CODE(XMLa)@en[term]] attribute value.
== [89] If [VAR[term]] is not an empty string,
add [VAR[term]] to [VAR[entry]]'s [F[categories][entry's categories]].
]FIG]
: [CODE(XMLe)@en[subject]] element in the [[Dublin Core namespace]] :
[FIG(list)[
= [90] Let [VAR[term]] be [VAR[child]]'s [F[child text content]].
= [91] If [VAR[term]] is not an empty string,
add [VAR[term]] to [VAR[entry]]'s [F[categories][entry's categories]].
]FIG]
: [CODE(XMLe)@en[published]] element in the [[Atom namespace]] :
If [VAR[entry]]'s [F[published][entry's published]] is [[null]],
set [VAR[entry]]'s [F[published][entry's published]] to the result of
[[processing an Atom date][process an Atom date]] [VAR[child]].
: [CODE(XMLe)@en[created]] element in the [[Atom 0.3 namespace]] or in the [[Atom namespace]] :
If [VAR[entry]]'s [F[published][entry's published]] is [[null]],
set [VAR[entry]]'s [F[published][entry's published]] to the result of
[[processing a W3C-DTF date][process a W3C-DTF date]] [VAR[child]].
: [CODE(XMLe)@en[updated]] element in the [[Atom namespace]] :
If [VAR[entry]]'s [F[updated][entry's updated]] is [[null]],
set [VAR[entry]]'s [F[updated][entry's updated]] to the result of
[[processing an Atom date][process an Atom date]] [VAR[child]].
: [CODE(XMLe)@en[modified]] element in the [[Atom 0.3 namespace]] or in the [[Atom namespace]]:
If [VAR[entry]]'s [F[updated][entry's updated]] is [[null]],
set [VAR[entry]]'s [F[updated][entry's updated]] to the result of
[[processing a W3C-DTF date][process a W3C-DTF date]] [VAR[child]].
: [CODE(XMLe)@en[title]] element in the [[Atom namespace]] :
If [VAR[entry]]'s [F[title][entry's title]] is [[null]],
set [VAR[entry]]'s [F[title][entry's title]] to the result of
[[processing an Atom text][process an Atom text]] [VAR[child]].
: [CODE(XMLe)@en[title]] element in the [[Atom 0.3 namespace]] :
If [VAR[entry]]'s [F[title][entry's title]] is [[null]],
set [VAR[entry]]'s [F[title][entry's title]] to the result of
[[processing an Atom 0.3 content][process an Atom 0.3 content]] [VAR[child]].
: [CODE(XMLe)@en[summary]] element in the [[Atom namespace]] :
If [VAR[entry]]'s [F[summary][entry's summary]] is [[null]],
set [VAR[entry]]'s [F[summary][entry's summary]] to the result of
[[processing an Atom text][process an Atom text]] [VAR[child]].
: [CODE(XMLe)@en[summary]] element in the [[Atom 0.3 namespace]] :
If [VAR[entry]]'s [F[summary][entry's summary]] is [[null]],
set [VAR[entry]]'s [F[summary][entry's summary]] to the result of
[[processing an Atom 0.3 content][process an Atom 0.3 content]] [VAR[child]].
: [CODE(XMLe)@en[content]] element in the [[Atom namespace]] :
If [VAR[entry]]'s [F[content][entry's content]] is [[null]],
set [VAR[entry]]'s [F[content][entry's content]] to the result of
[[processing an Atom text][process an Atom text]] [VAR[child]].
: [CODE(XMLe)@en[content]] element in the [[Atom 0.3 namespace]] :
If [VAR[entry]]'s [F[content][entry's content]] is [[null]],
set [VAR[entry]]'s [F[content][entry's content]] to the result of
[[processing an Atom 0.3 content][process an Atom 0.3 content]] [VAR[child]].
: [CODE(XMLe)@en[link]] element in the [[Atom namespace]] or in the [[Atom 0.3 namespace]] :
[[Process an Atom link]] [VAR[child]] for [VAR[entry]], with [VAR[type]] [CODE[entry]].
: [CODE(XMLe)@en[thumbnail]] element in the [[Media RSS namespace]] :
If [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] is [[null]],
set [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] to the result of
[[processing an image element][process an image element]] [VAR[child]]
with [VAR[attribute name]] [CODE[url]].
: [CODE(XMLe)@en[group]] element in the [[Media RSS namespace]] :
[FIG(steps)[
= [116] For each [[element]] [VAR[gc]] in [VAR[child]]'s [F[children]], in order,
run these substeps:
== [117] Switch by [VAR[gc]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[title]] element in the [[Media RSS namespace]] :
If [VAR[entry]]'s [F[title][entry's title]] is [[null]],
set [VAR[entry]]'s [F[title][entry's title]] to the result of
[[processing a string element][process a string element]] [VAR[gc]].
: [CODE(XMLe)@en[description]] element in the [[Media RSS namespace]] :
If [VAR[entry]]'s [F[summary][entry's summary]] is [[null]],
set [VAR[entry]]'s [F[summary][entry's summary]] to the result of
[[processing a string element][process a string element]] [VAR[gc]].
: [CODE(XMLe)@en[thumbnail]] element in the [[Media RSS namespace]] :
If [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] is [[null]],
set [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] to the result of
[[processing an image element][process an image element]] [VAR[child]]
with [VAR[attribute name]] [CODE[url]].
: [CODE(XMLe)@en[content]] element in the [[Media RSS namespace]] :
[FIG(steps)[
= [123] Let [VAR[enclosure]] be an [[enclosure]].
= [124] Let [VAR[text]] be [VAR[gc]]'s [CODE(XMLa)@en[url]] attribute value.
= [125] If [VAR[text]] is not [[null]] or the [[empty string]]:
== [126] [[Parse][parse a URL]] [VAR[text]] relative to [VAR[gc]]'s [F[node document]].
== [127] If not failed:
=== [163] Set [VAR[enclosure]]'s [F[URL][enclosure's URL]] to the [[resulting URL string]].
=== [165] Set [VAR[enclosure]]'s [F[type][enclosure's type]] to [VAR[gc]]'s
[CODE(XMLa)@en[type]] attribute value.
=== [171] Append [VAR[enclosure]] to [VAR[object]]'s [F[enclosures][entry's enclosures]].
]FIG]
]FIG]
]FIG]
]FIG]
= [246] Return [VAR[entry]].
]FIG]

*** RSS 2.0 and RSS 0.9[VAR[x]] items

[249] To [DFN[process an RSS 2.0 item]] [VAR[element]], run these steps:
[FIG(steps)[
= [248] Let [VAR[entry]] be an [[entry]].
= [250] For each [[element]] [VAR[child]] in [VAR[element]]'s [F[children]], in order,
run these substeps:
== [251] Switch by [VAR[child]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[category]] element in the [[null namespace]] :
[FIG(list)[
= [92] Let [VAR[term]] be [VAR[child]]'s [F[child text content]].
= [293] If [VAR[term]] is not the empty string,
add [VAR[term]] to [VAR[entry]]'s [F[categories][entry's categories]].
]FIG]
: [CODE(XMLe)@en[author]] element in the [[null namespace]] :
[FIG(steps)[
= [355] Let [VAR[person]] be the result of [[processing an RSS 2.0 person][process an RSS 2.0 person]] [VAR[child]].
= [361] If [VAR[person]] is not [[null]]:
== [356] If [VAR[person]]'s [F[email][person's email]] is not [[null]],
or if there is no [[person]] whose [F[name][person's name]] is [VAR[person]]'s [F[name][person's name]]
in [VAR[entry]]'s [F[authors][entry's authors]]:
=== [354] Append [VAR[person]] to [VAR[entry]]'s [F[authors][entry's authors]].
]FIG]
: [CODE(XMLe)@en[creator]] element in the [[Dublin Core namespace]] or [CODE(XMLe)@en[author]] element in the [[iTunes namespace]] :
[FIG(steps)[
= [118] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [119] If [VAR[text]] is not the [[empty string]] and
there is no [[person]] whose [F[name][person's name]] is [VAR[text]]
in [VAR[entry]]'s [F[authors][entry's authors]]:
== [120] Append a [[person]] whose [F[name]] is [VAR[text]]
to [VAR[entry]]'s [F[authors][entry's authors]].
]FIG]
: [CODE(XMLe)@en[pubDate]] element in the [[null namespace]]:
If [VAR[entry]]'s [F[published][entry's published]] is [[null]],
set [VAR[entry]]'s [F[published][entry's published]] to the result of
[[processing an RSS 2.0 date][process an RSS 2.0 date]] [VAR[child]].
: [CODE(XMLe)@en[updated]] element in the [[Atom namespace]]:
If [VAR[entry]]'s [F[updated][entry's updated]] is [[null]],
set [VAR[entry]]'s [F[updated][entry's updated]] to the result of
[[processing an Atom date][process an Atom date]] [VAR[child]].
: [CODE(XMLe)@en[link]] element in the [[null namespace]] :
If [VAR[entry]]'s [F[page URL][entry's page URL]] is [[null]],
set [VAR[entry]]'s [F[page URL][entry's page URL]] to the result of
[[processing a URL element][process a URL element]] [VAR[child]].
: [CODE(XMLe)@en[thumbnail]] element in the [[Media RSS namespace]] :
If [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] is [[null]],
set [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] to the result of
[[processing an image element][process an image element]] [VAR[child]]
with [VAR[attribute name]] [CODE[url]].
: [CODE(XMLe)@en[image]] element in the [[iTunes namespace]] :
If [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] is [[null]],
set [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] to the result of
[[processing an image element][process an image element]] [VAR[child]]
with [VAR[attribute name]] [CODE[href]].
: [CODE(XMLe)@en[content]] element in the [[Media RSS namespace]] :
[FIG(steps)[
= [292] Let [VAR[enclosure]] be an [[enclosure]].
= [364] Let [VAR[text]] be [VAR[child]]'s [CODE(XMLa)@en[url]] attribute value.
= [366] If [VAR[text]] is not [[null]] or the [[empty string]]:
== [367] [[Parse][parse a URL]] [VAR[text]] relative to [VAR[child]]'s [F[node document]].
== [368] If not failed:
=== [369] Set [VAR[enclosure]]'s [F[URL][enclosure's URL]] to the [[resulting URL string]].
=== [370] Set [VAR[enclosure]]'s [F[type][enclosure's type]] to [VAR[child]]'s
[CODE(XMLa)@en[type]] attribute value.
=== [371] Append [VAR[enclosure]] to [VAR[object]]'s [F[enclosures][entry's enclosures]].
]FIG]
: [CODE(XMLe)@en[enclosure]] element in the [[null namespace]] :
[FIG(steps)[
= [279] Let [VAR[enclosure]] be an [[enclosure]].
= [280] Let [VAR[text]] be [VAR[child]]'s [CODE(XMLa)@en[url]] attribute value.
= [281] If [VAR[text]] is not [[null]] or the [[empty string]]:
== [282] [[Parse][parse a URL]] [VAR[text]] relative to [VAR[child]]'s [F[node document]].
== [283] If not failed:
=== [195] Set [VAR[enclosure]]'s [F[URL][enclosure's URL]] to the [[resulting URL string]].
=== [284] Set [VAR[enclosure]]'s [F[type][enclosure's type]] to [VAR[child]]'s
[CODE(XMLa)@en[type]] attribute value.
=== [285] Let [VAR[length]] be [VAR[child]]'s [CODE(XMLa)@en[length]] attribute value.
=== [286] If [VAR[length]] is not null:
==== [287] Let [VAR[n]] be the result of applying the
[[rules for parsing non-negative integers]] to [VAR[length]].
==== [288] If [VAR[n]] is not an error and [VAR[n]] is greater than zero:
===== [289] Set [VAR[enclosure]]'s [F[length][enclosure's length]] to [VAR[n]].
=== [290] Append [VAR[enclosure]] to [VAR[object]]'s [F[enclosures][entry's enclosures]].
]FIG]
: [CODE(XMLe)@en[title]] element in the [[null namespace]] :
If [VAR[entry]]'s [F[title][entry's title]] is [[null]],
set [VAR[entry]]'s [F[title][entry's title]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[subtitle]] element in the [[iTunes namespace]] :
If [VAR[entry]]'s [F[subtitle][entry's subtitle]] is [[null]],
set [VAR[entry]]'s [F[subtitle][entry's subtitle]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[description]] element in the [[null namespace]] :
If [VAR[entry]]'s [F[summary][entry's summary]] is [[null]],
set [VAR[entry]]'s [F[summary][entry's summary]] to the result of
[[parsing escaped HTML content][parse escaped HTML content]] of [VAR[child]].
: [CODE(XMLe)@en[summary]] element in the [[iTunes namespace]] :
If [VAR[entry]]'s [F[subtitle][entry's subtitle]] is [[null]],
set [VAR[entry]]'s [F[subtitle][entry's subtitle]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[encoded]] element in the [[RSS content namespace]] :
If [VAR[entry]]'s [F[content][entry's content]] is [[null]],
set [VAR[entry]]'s [F[content][entry's content]] to the result of
[[parsing escaped HTML content][parse escaped HTML content]] of [VAR[child]].
: [CODE(XMLe)@en[duration]] element in the [[iTunes namespace]] :
[FIG(steps)[
= [218] If [VAR[entry]]'s [F[duration][entry's duration]] is not [[null]]:
== [198] Let [VAR[text]] be [VAR[child]]'s [F[child text content]].
== [212] If [VAR[text]] is one or more [[ASCII digits]]:
=== [217] Set [VAR[entry]]'s [F[duration][entry's duration]] to
the [[ASCII digits]] in [VAR[text]], interpreted as a decimal number.
== [215] Otherwise, if [VAR[text]] is one or more [[ASCII digits]],
followed by a [CODE(char)[:]] character,
followed by one or more [[ASCII digits]]:
=== [223] Set [VAR[m]] to the first sequence of [[ASCII digits]] in [VAR[text]],
interpreted as a decimal number.
=== [224] Set [VAR[s]] to the second sequence of [[ASCII digits]] in [VAR[text]],
interpreted as a decimal number.
=== [230] Set [VAR[entry]]'s [F[duration][entry's duration]] to
[CODE(math)[[VAR[m]] × 60 + [VAR[s]]]].
== [216] Otherwise, if [VAR[text]] is one or more [[ASCII digits]],
followed by a [CODE(char)[:]] character,
followed by one or more [[ASCII digits]],
followed by a [CODE(char)[:]] character,
followed by one or more [[ASCII digits]]:
=== [231] Set [VAR[h]] to the first sequence of [[ASCII digits]] in [VAR[text]],
interpreted as a decimal number.
=== [232] Set [VAR[m]] to the second sequence of [[ASCII digits]] in [VAR[text]],
interpreted as a decimal number.
=== [233] Set [VAR[s]] to the third sequence of [[ASCII digits]] in [VAR[text]],
interpreted as a decimal number.
=== [257] Set [VAR[entry]]'s [F[duration][entry's duration]] to
[CODE(math)[[VAR[h]] × 3600 + [VAR[m]] × 60 + [VAR[s]]]].
]FIG]
]FIG]
= [252] Return [VAR[entry]].
]FIG]

*** RSS 1.0 items

[322] To [DFN[process an RSS 1.0 item]] [VAR[element]], run these steps:
[FIG(steps)[
= [323] Let [VAR[entry]] be an [[entry]].
= [324] For each [[element]] [VAR[child]] in [VAR[element]]'s [F[children]], in order,
run these substeps:
== [325] Switch by [VAR[child]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[link]] element in the [[RSS namespace]] :
If [VAR[entry]]'s [F[page URL][entry's page URL]] is [[null]],
set [VAR[entry]]'s [F[page URL][entry's page URL]] to the result of
[[processing a URL element][process a URL element]] [VAR[child]].
: [CODE(XMLe)@en[imageurl]] element in the [[Hatena namespace]] :
[FIG(steps)[
= [189] If [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] is [[null]]:
== [191] Set [VAR[url]] to the result of
[[processing a URL element][process a URL element]] [VAR[child]].
== [192] If [VAR[url]] is not [[null]]:
=== [193] Let [VAR[image]] be an [[image]] whose [F[URL][image's URL]] is [VAR[url]].
=== [194] Set [VAR[entry]]'s [F[thumbnail][entry's thumbnail]] to [VAR[image]].
]FIG]
: [CODE(XMLe)@en[creator]] element in the [[Dublin Core namespace]] :
[FIG(steps)[
= [121] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [122] If [VAR[text]] is not the [[empty string]]:
= [356] If [VAR[person]]'s [F[email][person's email]] is not [[null]] and
there is no [[person]] whose [F[name][person's name]] is [VAR[text]]
in [VAR[entry]]'s [F[authors][entry's authors]]:
== [338] Append a [[person]] whose [F[name]] is [VAR[text]]
to [VAR[entry]]'s [F[authors][entry's authors]].
]FIG]
: [CODE(XMLe)@en[date]] element in the [[Dublin Core namespace]]:
If [VAR[entry]]'s [F[updated][entry's updated]] is [[null]],
set [VAR[entry]]'s [F[updated][entry's updated]] to the result of
[[processing a W3C-DTF date][process a W3C-DTF date]] [VAR[child]].
: [CODE(XMLe)@en[subject]] element in the [[Dublin Core namespace]] :
[FIG(list)[
= [294] Let [VAR[term]] be [VAR[child]]'s [F[child text content]].
= [295] If [VAR[term]] is not the empty string,
add [VAR[term]] to [VAR[entry]]'s [F[categories][entry's categories]].
]FIG]
: [CODE(XMLe)@en[title]] element in the [[RSS namespace]] :
If [VAR[entry]]'s [F[title][entry's title]] is [[null]],
set [VAR[entry]]'s [F[title][entry's title]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[description]] element in the [[RSS namespace]] :
If [VAR[entry]]'s [F[summary][entry's summary]] is [[null]],
set [VAR[entry]]'s [F[summary][entry's summary]] to the result of
[[parsing escaped HTML content][parse escaped HTML content]] of [VAR[element]].
: [CODE(XMLe)@en[encoded]] element in the [[RSS content namespace]] :
If [VAR[entry]]'s [F[content][entry's content]] is [[null]],
set [VAR[entry]]'s [F[content][entry's content]] to the result of
[[parsing escaped HTML content][parse escaped HTML content]] of [VAR[element]].
]FIG]
= [343] Return [VAR[entry]].
]FIG]

** Feeds

[1] To [DFN[process a feed response]] [VAR[res]], run these steps:
[FIG(steps)[
= [9] If [VAR[res]] is a [[network error]] or
[VAR[res]]'s [F[status][status code]] is not [CODE(HTTP)[200]],
return [[null]] and abort these steps.
= [2] Let [VAR[type]] be [VAR[res]]'s [F[computed MIME type]].
= [3] If [VAR[type]] is an [[XML MIME type]]:
== [7] Let [VAR[doc]] be a [CODE(DOMi)@en[Document]].
== [6] Let [VAR[parser]] be an [[XML parser]] associated with [VAR[doc]].
The [[XML parser]] [MUST[MUST]] implement [[XML5]].
It [MUST[MUST NOT]] [[fetch]] and process [[external entities][external entity]].
== [8] Run [VAR[parser]], using [VAR[res]]'s [F[body]] as its [F[input byte stream]].
The [CODE(MIME)@en[charset]] parameter value in the 
[CODE(HTTP)@en[Content-Type]] [[header value]] of [VAR[res]], if any, is used as
the [[encoding label]] provided by the underlying transport.
== [11] Set [VAR[doc]]'s [F[address][document's address]] to
[VAR[res]]'s [F[url][response's URL]].
== [27] Set [VAR[doc]]'s [F[character encoding][document's character encoding]]
to the [[character encoding]] used by [VAR[parser]].
== [5] Return the result of [[processing a feed document][process a feed document]] [VAR[doc]].
= [4] Otherwise, return [[null]].
]FIG]

;; [33] Scripting is disabled as the document is not in any [[browsing context]].

[10] To [DFN[process a feed document]] [VAR[doc]], run these steps:
[FIG(steps)[
= [12] Let [VAR[root]] be [VAR[doc]]'s [F[root element]].
= [17] Switch by [VAR[root]]:
[FIG(switch)[
: [16] If it is a [CODE(XMLe)@en[feed]] element in the [[Atom namespace]] or in the [[Atom 0.3 namespace]] :
Let [VAR[feed]] be the result of [[processing an Atom feed element][process an Atom feed element]] [VAR[root]].
: [19] If it is an [CODE(XMLe)@en[rss]] element in the [[null namespace]] :
Let [VAR[feed]] be the result of [[processing an rss element][process an rss element]] [VAR[root]].
: [18] If it is an [CODE(XMLe)@en[RDF]] element in the [[RDF namespace]] :
Let [VAR[feed]] be the result of [[processing an RDF element][process an RDF element]] [VAR[root]].
]FIG]
= [350] If [VAR[feed]] is not [[null]], [[cleanup feed]] [VAR[feed]].
= [13] Return [VAR[feed]].
]FIG]

[351] To [DFN[cleanup feed]] [VAR[feed]], run these steps:
[FIG(steps)[
= [352] If [VAR[feed]]'s [F[logo][feed's logo]] is not [[null]],
[VAR[feed]]'s [F[icon][feed's icon]] is not [[null]], and
[VAR[feed]]'s [F[logo][feed's logo]]'s [F[URL][image's URL]] is
[VAR[feed]]'s [F[icon][feed's icon]]'s [F[URL][image's URL]]:
== [353] Set [VAR[feed]]'s [F[icon][feed's icon]] to [[null]].
]FIG]

*** Atom feeds

[20] To [DFN[process an Atom feed element]] [VAR[element]], run these steps:
[FIG(steps)[
= [14] Let [VAR[feed]] be a [[feed]].
= [21] For each [[element]] [VAR[child]] in [VAR[element]]'s [F[children]], in order,
run these substeps:
== [22] Switch by [VAR[child]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[title]] element in the [[Atom namespace]] :
If [VAR[feed]]'s [F[title][feed's title]] is [[null]],
set [VAR[feed]]'s [F[title][feed's title]] to the result of
[[processing an Atom text][process an Atom text]] [VAR[child]].
: [CODE(XMLe)@en[title]] element in the [[Atom 0.3 namespace]] :
If [VAR[feed]]'s [F[title][feed's title]] is [[null]],
set [VAR[feed]]'s [F[title][feed's title]] to the result of
[[processing an Atom 0.3 content][process an Atom 0.3 content]] [VAR[child]].
: [CODE(XMLe)@en[subtitle]] element in the [[Atom namespace]] :
If [VAR[feed]]'s [F[subtitle][feed's subtitle]] is [[null]],
set [VAR[feed]]'s [F[subtitle][feed's subtitle]] to the result of
[[processing an Atom text][process an Atom text]] [VAR[child]].
: [CODE(XMLe)@en[tagline]] element in the [[Atom 0.3 namespace]] :
If [VAR[feed]]'s [F[subtitle][feed's subtitle]] is [[null]],
set [VAR[feed]]'s [F[subtitle][feed's subtitle]] to the result of
[[processing an Atom 0.3 content][process an Atom 0.3 content]] [VAR[child]].
: [CODE(XMLe)@en[updated]] element in the [[Atom namespace]] :
If [VAR[feed]]'s [F[updated][feed's updated]] is [[null]],
set [VAR[feed]]'s [F[updated][feed's updated]] to the result of
[[processing an Atom date][process an Atom date]] [VAR[child]].
: [CODE(XMLe)@en[modified]] element in the [[Atom 0.3 namespace]] or in the [[Atom namespace]]:
If [VAR[feed]]'s [F[updated][feed's updated]] is [[null]],
set [VAR[feed]]'s [F[updated][feed's updated]] to the result of
[[processing a W3C-DTF date][process a W3C-DTF date]] [VAR[child]].
: [CODE(XMLe)@en[link]] element in the [[Atom namespace]] or in the [[Atom 0.3 namespace]] :
[[Process an Atom link]] [VAR[child]] for [VAR[feed]], with [VAR[type]] [CODE[feed]].
: [CODE(XMLe)@en[icon]] element in the [[Atom namespace]] :
[FIG(steps)[
= [181] If [VAR[feed]]'s [F[icon][feed's icon]] is [[null]]:
== [184] Let [VAR[image]] be an [[image]].
== [183] Set [VAR[image]]'s [F[URL][image's URL]] to the result of
[[processing a URL element][process a URL element]] [VAR[child]].
== [182] If [VAR[image]]'s [F[URL][image's URL]] is not [[null]],
set [VAR[feed]]'s [F[icon][feed's icon]] to [VAR[image]].
]FIG]
: [CODE(XMLe)@en[logo]] element in the [[Atom namespace]] :
[FIG(steps)[
= [185] If [VAR[feed]]'s [F[logo URL][feed's logo]] is [[null]]:
== [186] Let [VAR[image]] be an [[image]].
== [197] Set [VAR[image]]'s [F[URL][image's URL]] to the result of
[[processing a URL element][process a URL element]] [VAR[child]].
== [225] If [VAR[image]]'s [F[URL][image's URL]] is not [[null]],
set [VAR[feed]]'s [F[logo][feed's logo]] to [VAR[image]].
]FIG]
: [CODE(XMLe)@en[author]] element in the [[Atom namespace]] or in the [[Atom 0.3 namespace]] :
Append the result of [[processing an Atom person][process an Atom person]] [VAR[child]]
to [VAR[feed]]'s [F[authors][feed's authors]].
: [CODE(XMLe)@en[entry]] element in the [[Atom namespace]] or in the [[Atom 0.3 namespace]] :
[FIG(steps)[
= [86] Let [VAR[entry]] be the result of [[processing an Atom entry][process an Atom entry]]
[VAR[child]].
= [85] Set [VAR[entry]]'s [F[feed][entry's feed]] to [VAR[feed]].
= [258] [[Cleanup entry][cleanup entry]] [VAR[entry]].
= [87] Append [VAR[entry]] to [VAR[feed]]'s [F[entries][feed's entry]].
]FIG]
]FIG]
= [25] Return [VAR[feed]].
]FIG]

*** RSS 2.0 and RSS 0.9[VAR[x]] feeds

[301] To [DFN[process an rss element]] [VAR[element]], run these steps:
[FIG(steps)[
= [239] Let [VAR[feed]] be a [[feed]].
= [240] For each [[element]] [VAR[child]] in [VAR[element]]'s [F[children]], in order,
run these substeps:
== [302] Switch by [VAR[child]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[channel]] element in the [[null namespace]] :
[[Process an RSS 2.0 channel][process an RSS 2.0 channel]] [VAR[element]] with [VAR[feed]].
: [CODE(XMLe)@en[item]] element in the [[null namespace]] :
[FIG(steps)[
= [242] Let [VAR[entry]] be the result of [[processing an RSS 2.0 item][process an RSS 2.0 item]]
[VAR[child]].
= [243] Set [VAR[entry]]'s [F[feed][entry's feed]] to [VAR[feed]].
= [259] [[Cleanup entry][cleanup entry]] [VAR[entry]].
= [244] Append [VAR[entry]] to [VAR[feed]]'s [F[entries][feed's entry]].
]FIG]
]FIG]
= [245] Return [VAR[feed]].
]FIG]

[206] To [DFN[process an RSS 2.0 channel]] [VAR[element]] with [[feed]] [VAR[feed]], 
run these steps:
[FIG(steps)[
= [208] For each [[element]] [VAR[child]] in [VAR[element]]'s [F[children]], in order,
run these substeps:
== [209] Switch by [VAR[child]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[image]] element in the [[null namespace]] :
[FIG(steps)[
= [211] If [VAR[feed]]'s [F[logo][feed's logo]] is [[null]]:
== [213] Let [VAR[element]] be [VAR[child]]'s first [CODE(XMLe)@en[url]] child element
in the [[null namespace]].
== [214] If [VAR[element]] is not [[null]]:
=== [308] Let [VAR[url]] be the result of
[[processing a URL element][process a URL element]] [VAR[element]].
=== [37] If [VAR[url]] is not [[null]]:
==== [310] Let [VAR[image]] be an [[image]].
==== [314] Set [VAR[image]]'s [F[URL][image's URL]] to [VAR[url]].
==== [309] Set [VAR[feed]]'s [F[logo][feed's logo]] to [VAR[image]].
]FIG]
: [CODE(XMLe)@en[image]] element in the [[iTunes namespace]] :
If [VAR[feed]]'s [F[icon][feed's icon]] is [[null]],
set [VAR[feed]]'s [F[icon][feed's icon]] to the result of
[[processing an image element][process an image element]] [VAR[child]]
with [VAR[attribute name]] [CODE[href]].
: [CODE(XMLe)@en[creator]] element in the [[Dublin Core namespace]] or [CODE(XMLe)@en[author]] element in the [[iTunes namespace]] :
[FIG(steps)[
= [316] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [317] If [VAR[text]] is not the [[empty string]] and
there is no [[person]] whose [F[name][person's name]] is [VAR[text]]
in [VAR[feed]]'s [F[authors][feed's authors]]:
== [315] Append a [[person]] whose [F[name]] is [VAR[text]]
to [VAR[feed]]'s [F[authors][feed's authors]].
]FIG]
: [CODE(XMLe)@en[managingEditor]] element in the [[null namespace]] :
[FIG(steps)[
= [357] Let [VAR[person]] be the result of [[processing an RSS 2.0 person][process an RSS 2.0 person]] [VAR[child]].
= [360] If [VAR[person]] is not [[null]]:
== [358] If [VAR[person]]'s [F[email][person's email]] is not [[null]],
or if there is no [[person]] whose [F[name][person's name]] is [VAR[person]]'s [F[name][person's name]]
in [VAR[feed]]'s [F[authors][feed's authors]]:
=== [359] Append [VAR[person]] to [VAR[feed]]'s [F[authors][feed's authors]].
]FIG]
: [CODE(XMLe)@en[pubDate]] or [CODE(XMLe)@en[lastBuildDate]] element in the [[null namespace]]:
If [VAR[feed]]'s [F[updated][feed's updated]] is [[null]],
set [VAR[feed]]'s [F[updated][feed's updated]] to the result of
[[processing an RSS 2.0 date][process an RSS 2.0 date]] [VAR[child]].
: [CODE(XMLe)@en[title]] element in the [[null namespace]] :
If [VAR[feed]]'s [F[title][feed's title]] is [[null]],
set [VAR[feed]]'s [F[title][feed's title]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[subtitle]] element in the [[iTunes namespace]] :
If [VAR[feed]]'s [F[subtitle][feed's subtitle]] is [[null]],
set [VAR[feed]]'s [F[subtitle][feed's subtitle]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[description]] element in the [[null namespace]] or [CODE(XMLe)@en[summary]] element in the [[iTunes namespace]] :
If [VAR[feed]]'s [F[description][feed's description]] is [[null]],
set [VAR[feed]]'s [F[description][feed's description]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[link]] element in the [[null namespace]] :
If [VAR[feed]]'s [F[page URL][feed's page URL]] is [[null]],
set [VAR[feed]]'s [F[page URL][feed's page URL]] to the result of
[[processing a URL element][process a URL element]] [VAR[child]].
: [CODE(XMLe)@en[link]] element in the [[Atom namespace]] :
[[Process an Atom link]] [VAR[child]] for [VAR[feed]], with [VAR[type]] [CODE[feed]].
: [CODE(XMLe)@en[item]] element in the [[null namespace]] :
[FIG(steps)[
= [147] Let [VAR[entry]] be the result of [[processing an RSS 2.0 item][process an RSS 2.0 item]]
[VAR[child]].
= [148] Set [VAR[entry]]'s [F[feed][entry's feed]] to [VAR[feed]].
= [261] [[Cleanup entry][cleanup entry]] [VAR[entry]].
= [149] Append [VAR[entry]] to [VAR[feed]]'s [F[entries][feed's entry]].
]FIG]
]FIG]
]FIG]

*** RSS 1.0 feeds

[207] To [DFN[process an RDF element]] [VAR[element]], run these steps:
[FIG(steps)[
= [238] Let [VAR[feed]] be a [[feed]].
= [210] For each [[element]] [VAR[child]] in [VAR[element]]'s [F[children]], in order,
run these substeps:
== [236] Switch by [VAR[child]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[channel]] element in the [[RSS namespace]] :
[[Process an RSS 1.0 channel][process an RSS 1.0 channel]] [VAR[element]] with [VAR[feed]].
: [CODE(XMLe)@en[item]] element in the [[RSS namespace]] :
[FIG(steps)[
= [237] Let [VAR[entry]] be the result of [[processing an RSS 1.0 item][process an RSS 1.0 item]]
[VAR[child]].
= [241] Set [VAR[entry]]'s [F[feed][entry's feed]] to [VAR[feed]].
= [260] [[Cleanup entry][cleanup entry]] [VAR[entry]].
= [303] Append [VAR[entry]] to [VAR[feed]]'s [F[entries][feed's entry]].
]FIG]
: [CODE(XMLe)@en[image]] element in the [[RSS namespace]] :
[FIG(steps)[
= [305] If [VAR[feed]]'s [F[logo][feed's logo]] is [[null]]:
== [40] Let [VAR[element]] be [VAR[child]]'s first [CODE(XMLe)@en[url]] child element
in the [[RSS namespace]].
== [306] If [VAR[element]] is not [[null]]:
=== [307] Let [VAR[url]] be the result of
[[processing a URL element][process a URL element]] [VAR[element]].
=== [321] If [VAR[url]] is not [[null]]:
==== [326] Let [VAR[image]] be an [[image]].
==== [327] Set [VAR[image]]'s [F[URL][image's URL]] to [VAR[url]].
==== [328] Set [VAR[feed]]'s [F[logo][feed's logo]] to [VAR[image]].
]FIG]
]FIG]
= [304] Return [VAR[feed]].
]FIG]

[311] To [DFN[process an RSS 1.0 channel]] [VAR[element]] with [[feed]] [VAR[feed]], 
run these steps:
[FIG(steps)[
= [312] For each [[element]] [VAR[child]] in [VAR[element]]'s [F[children]], in order,
run these substeps:
== [313] Switch by [VAR[child]]'s [F[namespace]] and [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[date]] element in the [[Dublin Core namespace]]:
If [VAR[feed]]'s [F[updated][feed's updated]] is [[null]],
set [VAR[feed]]'s [F[updated][feed's updated]] to the result of
[[processing a W3C-DTF date][process a W3C-DTF date]] [VAR[child]].
: [CODE(XMLe)@en[creator]] element in the [[Dublin Core namespace]] :
[FIG(steps)[
= [329] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [330] If [VAR[text]] is not the [[empty string]] and
there is no [[person]] whose [F[name][person's name]] is [VAR[text]]
in [VAR[feed]]'s [F[authors][feed's authors]]:
== [331] Append a [[person]] whose [F[name]] is [VAR[text]]
to [VAR[feed]]'s [F[authors][feed's authors]].
]FIG]
: [CODE(XMLe)@en[title]] element in the [[RSS namespace]] :
If [VAR[feed]]'s [F[title][feed's title]] is [[null]],
set [VAR[feed]]'s [F[title][feed's title]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[description]] element in the [[RSS namespace]] :
If [VAR[feed]]'s [F[description][feed's description]] is [[null]],
set [VAR[feed]]'s [F[description][feed's description]] to the result of
[[processing a string element][process a string element]] [VAR[child]].
: [CODE(XMLe)@en[link]] element in the [[RSS namespace]] :
If [VAR[feed]]'s [F[page URL][feed's page URL]] is [[null]],
set [VAR[feed]]'s [F[page URL][feed's page URL]] to the result of
[[processing a URL element][process a URL element]] [VAR[child]].
: [CODE(XMLe)@en[link]] element in the [[Atom namespace]] :
[[Process an Atom link]] [VAR[child]] for [VAR[feed]], with [VAR[type]] [CODE[feed]].
]FIG]
]FIG]

* References

[43] The key word [DFN[[MUST[MUST]]]] is defined by [[RFC 2119]].

[34] The terms [DFN[[[ASCII digits]]]] and [DFN[[[URL record]]]] 
are defined by the [[URL Standard]].

[42] The terms [DFN[[[MIME type]]]], [DFN[[[computed MIME type]]]],
[DFN[[F[type]]]], and
[DFN[[[parse a MIME type]]]] are defined by the [[MIME Sniffing Standard]].

[41] The terms [DFN[[[response]]]], [DFN[[[status][status code]]]],
[DFN[[[url][response's url]]]], and [DFN[[[network error]]]]
are defined by the [[Fetch Standard]].

[26] The interfaces [DFN[[CODE(DOMi)@en[Node]]]] and
[DFN[[CODE(DOMi)@en[DocumentFragment]]]] are defined by the [[DOM Standard]].

[31] The terms [DFN[[F[parent]]]], [DFN[[F[children]]]], [DFN[[[inclusive descendant]]]],
[DFN[[[insert]]]], [DFN[[[remove]]]], [DFN[[[clone]]]],
[DFN[[[equals]]]] (of [CODE(DOMi)@en[Node]]s),
[DFN[[F[local name]]]], [DFN[[F[namespace]]]], and
[DFN[[F[node document]]]] are defined by the [[DOM Standard]].

[23] The terms
[DFN[[[XML MIME type]]]],
[DFN[[[Unicode code point]]]], [DFN[[[space characters]]]], [DFN[[[ASCII case-insensitive]]]],
[DFN[[[parse a URL]]]], [DFN[[[resulting URL string]]]], [DFN[[[resulting URL string]]]],
[DFN[[[rules for parsing non-negative integers]]]],
[DFN[[F[document's address]]]], [DFN[[F[document's character encoding]]]],
[DFN[[[palpable content]]]], [DFN[[[embedded content]]]],
[DFN[[F[child text content]]]],
[DFN[[F[input byte stream]]]],
[DFN[[[HTML fragment parsing algorithm]]]], [DFN[[[HTML parser]]]], and [DFN[[[XML parser]]]]
are defined by the [[HTML Standard]].

[32] The [DFN[[CODE(HTMLe)@en[div]]]] and [DFN[[CODE(HTMLe)@en[img]]]]
elements are defined by the [[HTML Standard]].

* Tests

[348] Test data are available at:
<https://github.com/wakaba/tests-web/tree/master/feed/parsing>.

* License

[347] Per [[CC0]] <https://creativecommons.org/publicdomain/zero/1.0/>, to the
extent possible under law, the author of this specification
has waived all copyright and related or neighboring rights to
this specification.

* Notes

[349] There is an implementation:
<https://manakai.github.io/pod/Web/Feed/Parser>.