diff --git a/lib/NodeUtils.js b/lib/NodeUtils.js index 2c5c183..8af3e72 100644 --- a/lib/NodeUtils.js +++ b/lib/NodeUtils.js @@ -217,6 +217,33 @@ function escapeFallbackRawText(rawText, parentTag, ancestorTags) { return result; } +/** Whether `node` is serialized verbatim, without markup of its own. */ +function isCharacterDataNode(node) { + return !!node && + (node.nodeType === 3 /*TEXT_NODE*/ || node.nodeType === 4 /*CDATA_SECTION_NODE*/); +} + +/** + * Concatenates the data of `node` with the data of every character data + * sibling that immediately follows it. + * + * The parser sees one byte stream, so escaping fallback raw text one Text node + * at a time is not sound: `#text('<script>alert("comment_end_split")//</script>', + ); + + const reparsedDocument = domino.createDocument('
' + html + ''); + reparsedDocument.getElementsByTagName('script').length.should.equal(0); + reparsedDocument.body.firstChild.childNodes.length.should.equal(2); + reparsedDocument.body.firstChild.childNodes[0].nodeType.should.equal(8 /* COMMENT_NODE */); +}; + +exports.fallbackRawTextPreservesCommentSyntaxSplitAcrossTextNodes = function () { + // Guard against over-escaping: a legitimate comment split across + // interpolated text nodes still round trips as one comment. + const document = domino.createDocument(''); + const noscript = document.createElement('noscript'); + noscript.appendChild(document.createTextNode('')); + document.body.appendChild(noscript); + + document.body.serialize().should.equal(''); +}; + +exports.fallbackRawTextEmitsEveryAdjacentTextNodeExactlyOnce = function () { + // The run is escaped in one go, so check that nothing is dropped or emitted + // twice, including around a sibling that interrupts it. + const document = domino.createDocument(''); + const noscript = document.createElement('noscript'); + noscript.appendChild(document.createTextNode('a')); + noscript.appendChild(document.createTextNode('b')); + noscript.appendChild(document.createTextNode('c')); + noscript.appendChild(document.createComment('d')); + noscript.appendChild(document.createTextNode('e')); + noscript.appendChild(document.createTextNode('f')); + document.body.appendChild(noscript); + + document.body.serialize().should.equal(''); +};