Skip to content

Commit

Permalink
feat(parser): warn on all attribute parse issues
Browse files Browse the repository at this point in the history
NOTE: After un-recoverable warnings, attribute parsing may
still be aborted (and no attributes are returned). However
the remainder of the XML document will be parsed, unless the
<warn> handler throws an error.
  • Loading branch information
nikku committed Nov 9, 2017
1 parent 2318faf commit a5014b2
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 26 deletions.
19 changes: 13 additions & 6 deletions parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ function error(msg) {
return new Error(msg);
}

function unmappedPrefix(prefix) {
return 'unmapped prefix <' + prefix + '>';
function missingNamespaceForPrefix(prefix) {
return 'missing namespace for prefix <' + prefix + '>';
}

function getter(getFn) {
Expand Down Expand Up @@ -364,7 +364,8 @@ function Saxen(options) {
// wait for non whitespace character
if (w < 65 || w > 122 || (w > 90 && w < 97) ) {
if (w !== 95 && w !== 58) { // char 95"_" 58":"
return cachedAttrs = false; // error. invalid first char
handleWarning('illegal first char attribute name');
return cachedAttrs = false;
}
}

Expand All @@ -377,7 +378,9 @@ function Saxen(options) {
}

if (w !== 61) { // "=" == 61
return cachedAttrs = false; // error. invalid char "="
// expected "="
handleWarning('missing attribute value');
return cachedAttrs = false;
}

break;
Expand All @@ -387,6 +390,7 @@ function Saxen(options) {
ok = true;

if (name === 'xmlns:xmlns') {
handleWarning('illegal declaration of xmlns');
return cachedAttrs = false; // error. invalid name
}

Expand All @@ -397,13 +401,15 @@ function Saxen(options) {

} else {
if (w !== 39) { // "'"
handleWarning('missing attribute value quotes');
return cachedAttrs = false; // error. invalid char
}

j = s.indexOf('\'', i = j + 2 );
}

if (j === -1) {
handleWarning('attribute value quote missmatch');
return cachedAttrs = false; // error. invalid char
}

Expand All @@ -412,6 +418,7 @@ function Saxen(options) {

if (w > 32 || w < 9 || (w < 32 && w > 13)) {
// error. invalid char
handleWarning('illegal character after attribute end');
return cachedAttrs = false;
}
}
Expand Down Expand Up @@ -498,7 +505,7 @@ function Saxen(options) {

// normalize ns attribute name
if (!(nsName = nsMatrix[name.substring(0, w)])) {
handleWarning(unmappedPrefix(name.substring(0, w)));
handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
continue;
}

Expand Down Expand Up @@ -545,7 +552,7 @@ function Saxen(options) {
if (w !== -1) {
// normalize ns attribute name
if (!(nsName = nsMatrix[name.substring(0, w)])) {
handleWarning(unmappedPrefix(name.substring(0, w)));
handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
continue;
}

Expand Down
197 changes: 190 additions & 7 deletions test/elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,46 @@ test({
],
});

test({
xml: '<div />',
expect: [
['openTag', 'div', true, true],
['closeTag', 'div', true],
],
});

test({
xml: '<div></div>',
expect: [
['openTag', 'div', true, false ],
['closeTag', 'div', false ],
]
});

test({
xml: '<DIV/>',
expect: [
['openTag', 'DIV', true, true],
['closeTag', 'DIV', true],
],
});

test({
xml: '<DIV />',
expect: [
['openTag', 'DIV', true, true],
['closeTag', 'DIV', true],
],
});

test({
xml: '<DIV></DIV>',
expect: [
['openTag', 'DIV', true, false ],
['closeTag', 'DIV', false ],
]
});

test({
xml: '<DIV a="B"></DIV>',
expect: [
Expand Down Expand Up @@ -63,7 +103,11 @@ test({
test({
xml: '<!- HELLO',
expect: [
['error', 'unclosed tag'],
['error', 'unclosed tag', {
line: 0,
column: 0,
data: '<!- HELLO'
}]
],
});

Expand Down Expand Up @@ -190,6 +234,7 @@ test({
],
});

// attributes / ""
test({
xml: '<root LENGTH="abc=ABC"></root>',
expect: [
Expand All @@ -198,6 +243,17 @@ test({
],
});

// attributes / no xmlns assignment
test({
xml: '<root xmlns:xmlns="http://foo"></root>',
expect: [
['warn', 'illegal declaration of xmlns'],
['openTag', 'root'],
['closeTag', 'root'],
],
});

// attributes / ''
test({
xml: '<root length=\'abc=abc\'></root>',
expect: [
Expand All @@ -206,6 +262,7 @@ test({
],
});

// attributes / = inside attribute
test({
xml: '<root _abc="abc=abc" :abc="abc"></root>',
expect: [
Expand All @@ -214,7 +271,7 @@ test({
],
});


// attributes / space between attributes
test({
xml: '<root attr1="first"\t attr2="second"/>',
expect: [
Expand All @@ -223,6 +280,66 @@ test({
],
});

// attributes / warnings / no space between attributes
test({
xml: '<root attr1="first"attr2="second"/>',
expect: [
['warn', 'illegal character after attribute end' ],
['openTag', 'root', false, true],
['closeTag', 'root', true],
],
});

// attributes / warnings / illegal first char
test({
xml: '<root =attr1="first"/>',
expect: [
['warn', 'illegal first char attribute name'],
['openTag', 'root', false, true],
['closeTag', 'root', true],
],
});

// attributes / warnings / open - close missmatch
test({
xml: '<root attr1="first\'/>',
expect: [
['warn', 'attribute value quote missmatch'],
['openTag', 'root', false, true],
['closeTag', 'root', true],
],
});

// attributes / warnings / open - close missmatch
test({
xml: '<root attr1=\'first"/>',
expect: [
['warn', 'attribute value quote missmatch'],
['openTag', 'root', false, true],
['closeTag', 'root', true],
],
});

// attributes / warnings / attribute without value
test({
xml: '<root attr1 attr2="second"/>',
expect: [
['warn', 'missing attribute value' ],
['openTag', 'root', false, true],
['closeTag', 'root', true],
],
});

// attributes / warnings / missing quoting
test({
xml: '<root attr1=value />',
expect: [
['warn', 'missing attribute value quotes' ],
['openTag', 'root', false, true],
['closeTag', 'root', true],
],
});

test({
xml: '<root length=\'12345\'><item/></root>',
expect: [
Expand All @@ -234,11 +351,11 @@ test({
});

test({
xml: '<r><![CDATA[ this is ]]><![CDATA[ this is ]]></r>',
xml: '<r><![CDATA[ this is ]]><![CDATA[ this is [] ]]></r>',
expect: [
['openTag', 'r'],
['cdata', ' this is '],
['cdata', ' this is '],
['cdata', ' this is [] '],
['closeTag', 'r'],
],
});
Expand All @@ -260,6 +377,20 @@ test({
],
});

test({
xml: '<html><head><script>\'<div>foo</div></\'</script></head></html>',
expect: [
['openTag', 'html'],
['openTag', 'head'],
['openTag', 'script'],
['text', '\''],
['openTag', 'div'],
['text', 'foo'],
['closeTag', 'div'],
['error', 'closing tag mismatch'],
]
});

test({
xml: '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" id="aa" media:title="bb"/>',
ns: true,
Expand Down Expand Up @@ -553,7 +684,7 @@ test({
],
});

// test missing namespace
// test missing namespace / element
test({
xml: (
'<foo xmlns="http://xxx">' +
Expand Down Expand Up @@ -690,19 +821,71 @@ test({
],
});

// unmapped prefix
// attributes / missing namespace for prefixed
test({
xml: (
'<foo:foo xmlns:foo="http://foo" bar:no-ns="BAR" />'
),
ns: true,
expect: [
['warn', 'unmapped prefix <bar>', {
['warn', 'missing namespace for prefix <bar>', {
column: 0,
line: 0,
data: '<foo:foo xmlns:foo="http://foo" bar:no-ns="BAR" />'
}],
['openTag', 'foo:foo'],
['closeTag', 'foo:foo']
],
});

// attributes / missing namespace for prefixed / default ns
test({
xml: (
'<foo xmlns="http://xxx" bar:no-ns="BAR" />'
),
ns: true,
expect: [
['warn', 'missing namespace for prefix <bar>'],
['openTag', 'ns0:foo' ],
['closeTag', 'ns0:foo' ]
],
});

// whitespace / BOM at start
test({
xml: '\uFEFF<div />',
expect: [
['text', '\uFEFF'],
['openTag', 'div'],
['closeTag', 'div'],
],
});

// whitespace / _ at start
test({
xml: ' \uFEFF<div />',
expect: [
['text', ' \uFEFF'],
['openTag', 'div'],
['closeTag', 'div'],
],
});

// cyrillic in text
test({
xml: '<P>тест</P>',
expect: [
['openTag', 'P'],
['text', 'тест'],
['closeTag', 'P'],
],
});

// kanji in attribute value
test({
xml: '<P foo="误" />',
expect: [
['openTag', 'P', { foo: '误' }, true],
['closeTag', 'P'],
],
});

0 comments on commit a5014b2

Please sign in to comment.