xml5ever: compare prefix and local name when looking for duplicate attributes - #780
xml5ever: compare prefix and local name when looking for duplicate attributes#780FadeHack wants to merge 2 commits into
Conversation
414e9b6 to
95ad992
Compare
|
Think the issue here is that you don't have an expanded name. To quote the spec:
So in your case you have a Because XML namespace with name <root
lang = "a"
lang = "b"
/>So the error is the intended behavior. What your PR allows is for someone to write following element When What can change the situation in your case EDIT: Look at following
This is identical to Because XML namespace Do not care about Prefixes. Prefixes are links to XML namespace and XML namespace determines what expanded name is. |
|
The way I see it you can solve this option in several ways. But do we care about XML Namespaces or not? If we do, then proper XMLNS-aware tokenizer sounds like the right solution. If we want to unblock a special use case or de-facto use case, then your changes are okish, but the tree builder will need to catch these errors instead. If we don't need mixed XMLNS attributes in a node, then PR is not needed. |
The duplicate check compared the raw attribute name we had just read against the local names of the attributes already on the tag. The stored ones have been through process_qname by then, so xml:lang is sitting there as prefix "xml" with local name "lang", and reading a plain lang right after it matched and got rejected as a duplicate. That is why it was order dependent. Writing lang first and xml:lang second was fine, because the raw name "xml:lang" never matches the local name "lang". Namespaces in XML section 6.3 says attributes are the same only when their expanded names are the same, and xml:lang and lang have different expanded names, so both orderings should parse cleanly. This also shows up on the WPT test table-align-float.xhtml, whose root element carries both. So process the name first and compare the prefix along with the local name. Real duplicates, including two attributes with the same prefix, are still reported. The tokenizer has no namespace bindings yet, they are resolved later in XmlTreeBuilder::bind_qname, so two different prefixes bound to the same namespace still slip through here. That is a separate thing and it needs to be handled in the tree builder. Fixes servo#775
36c960f to
ecd0908
Compare
|
Thanks for digging into this, the section 6.3 point is the right thing to be checking. I went and read it again along with section 3, and I think the two cases you raised land differently. On
So <good a="1" n1:a="2" />That is the same pattern as Two distinct expanded names on the element, which is what the reporter was after. The old behaviour rejected that in one order and accepted it in the other, so it was not enforcing 6.3, it was just comparing a raw name against an already split local name. On Only one Both fail to resolve, both land in no namespace, and the second is dropped as a duplicate. So to answer your three options directly, I think we are already on the middle one and have been for a while. The split is that the tokenizer catches names that are literally identical, and the tree builder catches everything that needs bindings. This PR only fixes the tokenizer half, which was comparing the wrong two things and rejecting names it had no business rejecting. You did make me realise one of my tests was overclaiming. I had One thing I noticed while checking this that is worth a separate issue if you agree: the tree builder drops the duplicate attribute silently, no |
Fixes #775
<root xml:lang="en" lang="en"/>was reported asDuplicate attribute, but<root lang="en" xml:lang="en"/>parsed fine.Why
finish_attributecompared the raw attribute name it had just read against the local names of the attributes already on the tag:The stored attributes have been through
process_qnameby then, soxml:langis sitting there as prefixxmlwith local namelang. Reading a plainlangright after it matches that local name and gets rejected.That is also where the order dependence comes from. With
langfirst, the raw namexml:langis compared against the local namelang, they do not match, and both attributes survive.Namespaces in XML section 6.3 says two attributes are the same only when their expanded names are the same. The
xmlprefix is bound tohttp://www.w3.org/XML/1998/namespaceby definition per section 3, whether or not it is declared, soxml:langandlanghave different expanded names and both orderings should parse cleanly. Section 6.3 lists that exact shape as legal, in its<good a="1" n1:a="2" />example. It also affects the WPT testhtml/rendering/non-replaced-elements/tables/table-align-float.xhtml, whose root element carries both.The fix
Run
process_qnamefirst, then compare the prefix along with the local name. Real duplicates still get reported, including two attributes that share a prefix.Worth being clear about where this check sits. The tokenizer has no namespace bindings, they are resolved later, so it can only catch names that are identical. The case where two different prefixes are bound to the same namespace URI is a real duplicate under section 6.3, and that one is already handled in the tree builder by
bind_attr_qnameandcheck_duplicate_attr, which resolve the prefix and compare(Namespace, LocalName). So the split is that the tokenizer catches identical names and the tree builder catches everything that needs bindings. This PR only corrects the tokenizer half.Testing
Added unit tests in
xml5ever/src/tokenizer/mod.rsfor both orderings from the issue, for real duplicates with and without a prefix, and one recording that the tokenizer deliberately stays quiet about two different prefixes since it cannot resolve them. The first two fail without the change.cargo test --all,cargo fmt --all -- --checkandcargo clippy --all-features --all-targetsare clean.