htmlChecks() implements the FHIR-specific FHIRPath function described in
fhirpath.html#fn-htmlChecks:
When invoked on a single xhtml element, the function returns true if the rules around HTML usage are met, and false if they are not. If invoked on items of type 'string', the contents of the string are parsed as the content of a div and the function returns false if the string does not parse as valid HTML or fails the rules around HTML usage. The return value is empty on any other kind of element or if invoked on a collection of elements.
The "rules around HTML usage" are defined in narrative.html#rules.
| Input | Behavior |
|---|---|
A single FHIR.xhtml element (e.g. Narrative.div with a model) |
Checked as a document whose single root element must be a div |
A single FHIR.string or System.String |
Checked as the content of a div |
A single element of a type derived from string (code, id, markdown) |
Checked as the content of a div |
A single element of a uri-derived type (uri, url, canonical, oid, uuid) |
Returns an empty collection |
| Any other element type | Returns an empty collection |
| A collection of more than one item, or an empty collection | Returns an empty collection |
Note that without a model, a Narrative.div value has no FHIR type
information and is therefore treated as a System.String, i.e. as div content.
The spec's "items of type 'string'" is read with the usual FHIRPath subtype
semantics, so the types derived from string are included. In every supported
model (dstu2, stu3, r4, r5) those are exactly code, id and
markdown — Patient.gender is FHIR.string evaluates to true, and
Patient.gender.htmlChecks() therefore checks the value as div content.
The uri-derived types are not derived from string and return an empty
collection. xhtml is not derived from string either (its parent is
Element), so the two overloads stay disjoint.
The spec describes the string overload as parsing "as valid HTML", but the
string is defined as "the content of a div", i.e. narrative content, and
narrative content is XHTML ("the XHTML is contained in general XML"). A single
strict XHTML rule set is therefore applied to both overloads.
- Only the elements and attributes allowed by the narrative rules may be used. The lists follow the chapters of HTML 4.0 identified by the narrative rules.
- Image maps are rejected because they belong to chapter 13, outside the
permitted chapters 7-11 and 15: the elements
mapandarea, and theimgattributesusemapandismapthat refer to them. Images themselves stay allowed, because the narrative rules name them explicitly. - An
aelement may carrynameorhref(or both); these are the only anchor-specific attributes, and neither of them is required. Permitted global formatting attributes may also be present. widthis accepted on the table elements and onimgonly; it is not a global HTML 4.0 attribute. The other table attributes (abbr,align,axis,char,charoff,colspan,headers,rowspan,scope,span,valign) are accepted on any element, as they are in thetxt-1XPath.- Scripts, forms, objects, frames, iframes,
head,body,base,link,xlink:*, deprecated elements and event-related attributes (e.g.onClick) are rejected, because they are not in the allow lists. - The
divSHALL have some non-whitespace content — text, or an<img>that has asrcattribute, which is what thetxt-2XPath requires (descendant::text()[normalize-space(.)!=''] or descendant::h:img[@src]). A character reference counts as content even when it denotes a whitespace character (see the interpretation notes below). This rule is applied for every FHIR version, because it istxt-2, whose FHIRPath expression ishtmlChecks()in STU3, R4 and R5. - There is no support for HTML entities such as
or©; Unicode characters or numeric character references must be used instead ( substitutes for ). - Processing instructions such as
<?xml ...?>are not allowed. - A default namespace declaration is optional, but when one is present it may
only declare the XHTML namespace,
http://www.w3.org/1999/xhtml— on the rootdivas well as on any descendant that redeclares it. Without this rule the element allow list would be applied to elements that are not XHTML at all, e.g. in<div xmlns="http://www.w3.org/1999/xhtml"><p xmlns="urn:x">t</p></div>. The URI is compared literally: neither case nor whitespace is normalized, and a value spelled with character references (http://www.w3.org/1999/xhtml) is not recognized. An empty declaration (xmlns="") undeclares the default namespace and is rejected as well. - For an
xhtmlelement, the single root element must be adiv, and nothing but whitespace may appear outside it — in JSON "the characters between the first>and the last<delimiters is the content of the<div>element".
Narrative "is contained in general XML", so the content must also be well-formed XML:
- Every element must be closed (
<br></br>) or self-closed (<br/>). There is no HTML void-element shorthand, so<br>is rejected. - Every attribute must have a value, and every value must be quoted;
<td nowrap>and<p class=c>are both rejected. - Attributes must be separated by whitespace;
<p title="a"class="c">is rejected. - A raw
<is not allowed in an attribute value. - Only legal XML characters may appear: C0 control characters other than tab, LF and CR, unpaired surrogates and U+FFFE/U+FFFF are rejected, both as raw characters and as numeric character references. This applies inside tags as well: only space, tab, CR and LF separate a name from what follows it, so a raw control character between a tag name and an attribute is rejected rather than treated as a separator.
- Comments may not contain
--, and a comment body may not end with-. - A literal
]]>is not allowed in character data;]]>must be used. - DOCTYPE declarations and CDATA sections are not allowed.
- Namespace prefixes are not supported. A default
xmlnsdeclaration, which is how FHIR narrative declares the XHTML namespace, is accepted on any element as long as it declares the XHTML namespace (see the narrative rules above);xmlns:<prefix>and prefixed attribute names such asxml:lang,xml:spaceandxml:idare rejected. Rejecting the prefixed declarations is also what bounds the duplicate attribute scan incheckHtml(): an unbounded family such asxmlns:a1,xmlns:a2, ... would let a single start tag carry an arbitrary number of distinct accepted attributes and turn that scan quadratic.
Element and attribute names are compared case-sensitively, as XHTML requires.
The STU3 Narrative.div invariants txt-1 and txt-2 spell the function
htmlchecks(), whereas R4 and R5 use htmlChecks(). FHIRPath is
case-sensitive, so the STU3 spelling is registered as an alias in order to keep
those invariants evaluable. This is the only alias of its kind — FHIRPath
function names are not case-insensitive in general.
htmlChecks() is the FHIRPath expression of the Narrative.div invariants
themselves (txt-1 and txt-2 in STU3, R4 and R5), so the only concrete
definitions of the rules are the prose in narrative.html and
the R4 XPath. Where
these leave room, the choices made here are the following.
Beyond the names the narrative rules exclude outright, these are not accepted either:
- The anchor attributes
shapeandcoords, which go with the image maps of chapter 13 that are already rejected (map,area,img.usemap,img.ismap). - The remaining anchor attributes of chapter 12,
charset,type,hreflang,relandrev: the narrative rules name<a>elements "either name or href", and those two are the only anchor attributes accepted here. pre.space: a barespaceattribute exists neither in HTML 4.0 nor in thetxt-1XPath, and the prefixed spellingxml:spaceis rejected as a prefixed name, so neither form is accepted.
- Attribute names the XPath permits that are never accepted here:
bgcolor,cellhalign,cellvalign,charset,compact,coords,hreflang,hspace,rel,rev,shape,start,type,value,vspace. All are deprecated in HTML 4.0 or belong to chapters outside the permitted ones. - The XPath tests attribute names document-wide, so it permits any of them on
any element. These are accepted only on the elements they belong to:
alt,border,cellpadding,cellspacing,cite,frame,height,href,longdesc,name,nowrap,rules,src,summary,width. The remaining table attributes (abbr,align,axis,char,charoff,colspan,headers,rowspan,scope,span,valign) stay global, so<p rowspan="2">is accepted.
Note that the XPath also rejects xml:lang and xml:space (it matches
attributes with name(.), which yields the prefixed name), so it cannot be
read as normative-precise either. It says nothing at all about xmlns:
namespace declarations are not attributes in the XPath data model, so @*
never selects them.
- The elements
address,bdoandkbdare accepted. They belong to the permitted chapters 7, 8 and 9, but the XPath's element list does not enumerate them. - A character reference counts as
divcontent even when it denotes a whitespace character ( ,	, , ), whereasnormalize-space()in thetxt-2XPath would ignore it. Escaping a character is taken as a statement that it is significant.
- An
<a>element is not required to carrynameorhref. Thetxt-1human text, "<a>elements (either name or href)", enumerates the permitted elements and attributes, so it is read as an allow list rather than as a requirement — as it is by thetxt-1XPath. Narratives generated by the FHIR publisher rely on this: the R4 and R5observation-examplenarratives contain<p><b>subject</b>: <a>Patient/example</a></p>(seetest/resources/r4/observation-example.json). - XHTML content models are not enforced.
<p><div>x</div></p>, nested<a>elements and an orphan<td>are all accepted:txt-1andtxt-2constrain the element and attribute names and the content of thediv, not how the elements are nested, and their XPaths test no nesting either. - The XHTML namespace declaration is not required on the root
div.narrative.htmldoes require the narrative to be in the XHTML namespace — which in JSON means anxmlnson thediv— but a narrative that declares no namespace at all is still accepted. A declaration that names another namespace is rejected, see the narrative rules above. styleattribute values are not inspected. The narrative rules forbid external stylesheet references, and aurl(...)inside astyleattribute is arguably one, buttxt-1constrains attribute names, not their values.hrefandsrcvalues are not inspected. Ajavascript:URL, or a link to external content, passes the checks; see the security note below.
htmlChecks() validates a narrative; it is not an HTML sanitizer and must
not be relied upon as a defense against XSS. See the security note in
narrative.html.