fix(dom): match setAttribute by qualified name, not local name only
Node::set_attribute looked up the existing attribute by local name alone,
while get_attribute and the remove_attribute op both match by qualified
name. A parsed namespaced attribute is stored with a separate prefix
(xlink:href -> prefix="xlink", local="href"), so setAttribute("xlink:href")
never matched it: the update was pushed as a second attribute, getAttribute
kept returning the stale original, and the element serialized xlink:href
twice.
Match set_attribute by qualified_name_eq, consistent with the read/remove
paths, so the update lands in place and no duplicate is created.
Fixes #547
This commit is contained in:
1 parent
f0541f40a4
commit
ae33543850
2 files changed
+21
-1
No files matched your search
@@ -136,7 +136,11 @@ impl Node {
|
|||||||
|
|
||||||
pub fn set_attribute(&mut self, name: &str, value: String) {
|
pub fn set_attribute(&mut self, name: &str, value: String) {
|
||||||
if let NodeData::Element { attrs, .. } = &mut self.data {
|
if let NodeData::Element { attrs, .. } = &mut self.data {
|
||||||
if let Some(attr) = attrs.iter_mut().find(|a| a.name.local.as_ref() == name) {
|
// Match by qualified name, consistent with get_attribute and the
|
||||||
|
// remove_attribute op. A parsed namespaced attribute is stored with
|
||||||
|
// a separate prefix (e.g. xlink:href -> prefix="xlink", local="href");
|
||||||
|
// matching on local name alone would miss it and push a duplicate.
|
||||||
|
if let Some(attr) = attrs.iter_mut().find(|a| a.qualified_name_eq(name)) {
|
||||||
attr.value = value;
|
attr.value = value;
|
||||||
} else {
|
} else {
|
||||||
attrs.push(Attribute {
|
attrs.push(Attribute {
|
||||||
|
|||||||
@@ -1662,6 +1662,22 @@ mod tests {
|
|||||||
assert_eq!(v, serde_json::json!("#icon|#icon|id,xlink:href"));
|
assert_eq!(v, serde_json::json!("#icon|#icon|id,xlink:href"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_attribute_updates_a_parsed_namespaced_attribute_in_place() {
|
||||||
|
// setAttribute matched the stored attribute by local name only, so a
|
||||||
|
// parsed `xlink:href` (prefix=xlink, local=href) was never found by the
|
||||||
|
// qualified name "xlink:href": the update was pushed as a *second*
|
||||||
|
// attribute, getAttribute kept returning the stale original, and the
|
||||||
|
// element serialized `xlink:href` twice.
|
||||||
|
let mut rt = setup_runtime(
|
||||||
|
r##"<html><body><svg><use id="u" xlink:href="#a"></use></svg></body></html>"##,
|
||||||
|
);
|
||||||
|
let v = rt
|
||||||
|
.evaluate("(function(){var u=document.getElementById('u');u.setAttribute('xlink:href','#b');var dup=(u.outerHTML.match(/xlink:href/g)||[]).length;return u.getAttribute('xlink:href')+'|'+u.getAttributeNS('http://www.w3.org/1999/xlink','href')+'|'+u.getAttributeNames().join(',')+'|'+dup;})()")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(v, serde_json::json!("#b|#b|id,xlink:href|1"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_attribute_ns_validates_namespace_constraints() {
|
fn set_attribute_ns_validates_namespace_constraints() {
|
||||||
let mut rt = setup_runtime("<html><body></body></html>");
|
let mut rt = setup_runtime("<html><body></body></html>");
|
||||||
|
|||||||
Reference in new issue
Block a user