Skip to content

Commit

Permalink
fix: [#1750] Fixes issue where an error was thrown for xmlns or unkno…
Browse files Browse the repository at this point in the history
…wn prefixes in Element.setAttribute (#1767)

* fix: [#1750] Fixes issue where an error was thrown for attributes xlink or an unknown prefix during parsing of HTML

* fix: [#1750] Fixes issue where an error was thrown for xmlns or unknown prefixes in Element.setAttribute

* fix: [#1750] Fixes issue where an error was thrown for xmlns or unknown prefixes in Element.setAttribute
  • Loading branch information
capricorn86 authored Mar 8, 2025
1 parent 150a590 commit bc3583b
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 8 deletions.
44 changes: 36 additions & 8 deletions packages/happy-dom/src/nodes/element/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -704,15 +704,43 @@ export default class Element
{ method: 'setAttribute', instance: 'Element' }
);
name = String(name);

const namespaceURI = this[PropertySymbol.namespaceURI];
// TODO: Is it correct to check for namespaceURI === NamespaceURI.svg?
const attribute =
namespaceURI === NamespaceURI.html &&
this[PropertySymbol.ownerDocument][PropertySymbol.contentType] === 'text/html'
? this[PropertySymbol.ownerDocument].createAttribute(name)
: this[PropertySymbol.ownerDocument].createAttributeNS(null, name);
attribute[PropertySymbol.value] = String(value);
this[PropertySymbol.attributes].setNamedItem(attribute);

if (namespaceURI === NamespaceURI.html) {
const attribute = this[PropertySymbol.ownerDocument].createAttribute(name);
attribute[PropertySymbol.value] = String(value);
this[PropertySymbol.attributes][PropertySymbol.setNamedItem](attribute);
} else {
const nameParts = name.split(':');
let attributeNamespaceURI = null;

// In the XML namespace, the attribute "xmlns" should be set to the "http://www.w3.org/2000/xmlns/" namespace and "xlink" to the "http://www.w3.org/1999/xlink" namespace.
switch (nameParts[0]) {
case 'xmlns':
attributeNamespaceURI =
!nameParts[1] || nameParts[1] === 'xlink' ? NamespaceURI.xmlns : null;
break;
case 'xlink':
attributeNamespaceURI = NamespaceURI.xlink;
break;
}

const attribute = NodeFactory.createNode(
this[PropertySymbol.ownerDocument],
this[PropertySymbol.window].Attr
);

attribute[PropertySymbol.namespaceURI] = attributeNamespaceURI;
attribute[PropertySymbol.name] = name;
attribute[PropertySymbol.localName] =
attributeNamespaceURI && nameParts[1] ? nameParts[1] : name;
attribute[PropertySymbol.prefix] =
attributeNamespaceURI && nameParts[1] ? nameParts[0] : null;
attribute[PropertySymbol.value] = String(value);

this[PropertySymbol.attributes][PropertySymbol.setNamedItem](attribute);
}
}

/**
Expand Down
20 changes: 20 additions & 0 deletions packages/happy-dom/test/nodes/element/Element.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,6 +1706,26 @@ describe('Element', () => {
expect(element.getAttribute('data-custom')).toBe('1'); // common custom attribute pattern
});

it('Sets SVG attribute "xmlns:xlink" on an element.', () => {
const div = document.createElement('div');

div.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><use/></svg>';

div.children[0].setAttribute('xmlns:xlink', 'test');
div.children[0].children[0].setAttribute('xlink:href', '#a');

expect(div.children[0].getAttributeNode('xmlns:xlink')?.namespaceURI).toBe(
NamespaceURI.xmlns
);
expect(div.children[0].getAttribute('xmlns:xlink')).toBe('test');

expect(div.children[0].children[0].getAttributeNode('xlink:href')?.namespaceURI).toBe(
NamespaceURI.xlink
);
expect(div.children[0].children[0].getAttribute('xlink:href')).toBe('#a');
});

it('Throws an error when given an invalid character in the attribute name', () => {
try {
element.setAttribute('☺', '1');
Expand Down

0 comments on commit bc3583b

Please # to comment.