Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/parse5/lib/common/foreign-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,17 @@ export function causesExit(startTagToken: TagToken): boolean {
}

//Token adjustments
function adjustTokenAttrLocation(token: TagToken, oldName: string, newName: string): void {
if (token.location?.attrs) {
token.location.attrs[newName] = token.location.attrs[oldName];
delete token.location.attrs[oldName];
}
}

export function adjustTokenMathMLAttrs(token: TagToken): void {
for (let i = 0; i < token.attrs.length; i++) {
if (token.attrs[i].name === DEFINITION_URL_ATTR) {
adjustTokenAttrLocation(token, DEFINITION_URL_ATTR, ADJUSTED_DEFINITION_URL_ATTR);
token.attrs[i].name = ADJUSTED_DEFINITION_URL_ATTR;
break;
}
Expand All @@ -202,6 +210,7 @@ export function adjustTokenSVGAttrs(token: TagToken): void {
const adjustedAttrName = SVG_ATTRS_ADJUSTMENT_MAP.get(token.attrs[i].name);

if (adjustedAttrName != null) {
adjustTokenAttrLocation(token, token.attrs[i].name, adjustedAttrName);
token.attrs[i].name = adjustedAttrName;
}
}
Expand All @@ -212,6 +221,7 @@ export function adjustTokenXMLAttrs(token: TagToken): void {
const adjustedAttrEntry = XML_ATTRS_ADJUSTMENT_MAP.get(token.attrs[i].name);

if (adjustedAttrEntry) {
adjustTokenAttrLocation(token, token.attrs[i].name, adjustedAttrEntry.name);
token.attrs[i].prefix = adjustedAttrEntry.prefix;
token.attrs[i].name = adjustedAttrEntry.name;
token.attrs[i].namespace = adjustedAttrEntry.namespace;
Expand Down
47 changes: 47 additions & 0 deletions packages/parse5/lib/parser/parser-location-info.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,53 @@ generateTestsForEachTreeAdapter('location-info-parser', (treeAdapter) => {
);
});

it('Regression - SVG attribute location keys should match adjusted camelCase names (GH-318)', () => {
const html = '<svg viewBox="0 0 100 100"></svg>';

const opts = {
treeAdapter,
sourceCodeLocationInfo: true,
};

const fragment = parseFragment(html, opts);
const svg = treeAdapter.getChildNodes(fragment)[0];
const location = treeAdapter.getNodeSourceCodeLocation(svg);
const attrs = treeAdapter.getAttrList(svg);

assert.ok(location?.attrs);

// The attrs array should have the camelCase name
const viewBoxAttr = attrs.find((a) => a.name === 'viewBox');
assert.ok(viewBoxAttr, 'attrs should contain viewBox with camelCase name');

// The location attrs should use the same camelCase key
assert.ok(location.attrs['viewBox'], 'location.attrs should have camelCase key "viewBox"');
assert.ok(!location.attrs['viewbox'], 'location.attrs should not have lowercase key "viewbox"');
});

it('Regression - MathML definitionURL attribute location key should match adjusted name (GH-318)', () => {
const html = '<math><mprescripts definitionURL="http://example.com"></mprescripts></math>';

const opts = {
treeAdapter,
sourceCodeLocationInfo: true,
};

const fragment = parseFragment(html, opts);
const math = treeAdapter.getChildNodes(fragment)[0];
const mprescripts = treeAdapter.getChildNodes(math)[0];
const location = treeAdapter.getNodeSourceCodeLocation(mprescripts);
const attrs = treeAdapter.getAttrList(mprescripts);

assert.ok(location?.attrs);

const attr = attrs.find((a) => a.name === 'definitionURL');
assert.ok(attr, 'attrs should contain definitionURL');

assert.ok(location.attrs['definitionURL'], 'location.attrs should have "definitionURL"');
assert.ok(!location.attrs['definitionurl'], 'location.attrs should not have "definitionurl"');
});

it('Regression - Escaped script content has incorrect location info (GH-265)', () => {
const html = '<script>"<!--";</script>';

Expand Down