diff --git a/Changelog.md b/Changelog.md index 052697c2..f3d2bd70 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,8 @@ ### New Features +- [#545]: resolve well-known namespaces to their approrpriate URIs + ### Bug Fixes ### Misc Changes diff --git a/src/name.rs b/src/name.rs index 07d261ab..fb8fa558 100644 --- a/src/name.rs +++ b/src/name.rs @@ -399,6 +399,24 @@ pub(crate) struct NamespaceResolver { nesting_level: i32, } +/// These constants define the reserved namespaces for the xml standard. +/// +/// The prefix `xml` is by definition bound to the namespace name +/// `http://www.w3.org/XML/1998/namespace`. It may, but need not, be declared, and must not be +/// undeclared or bound to any other namespace name. Other prefixes must not be bound to this +/// namespace name, and it must not be declared as the default namespace. +/// +/// The prefix `xmlns` is used only to declare namespace bindings and is by definition bound +/// to the namespace name http://www.w3.org/2000/xmlns/. It must not be declared or +/// undeclared. Other prefixes must not be bound to this namespace name, and it must not be +/// declared as the default namespace. Element names must not have the prefix xmlns. +/// +/// [reserved namespaces]: https://www.w3.org/TR/xml-names11/#xmlReserved +static WELL_KNOWN_NAMESPACES: &'static [[&str; 2]] = &[ + ["xml", "http://www.w3.org/XML/1998/namespace"], + ["xmlns", "http://www.w3.org/2000/xmlns/"] +]; + impl NamespaceResolver { /// Begins a new scope and add to it all [namespace bindings] that found in /// the specified start element. @@ -542,7 +560,12 @@ impl NamespaceResolver { #[inline] fn maybe_unknown(prefix: Option) -> ResolveResult<'static> { match prefix { - Some(p) => ResolveResult::Unknown(p.into_inner().to_vec()), + Some(p) => { + WELL_KNOWN_NAMESPACES.iter().find(|x| {p == Prefix(x[0].as_bytes())}).map_or_else( + || ResolveResult::Unknown(p.into_inner().to_vec()), + |[_, uri]| ResolveResult::Bound(Namespace(uri.as_bytes())), + ) + }, None => ResolveResult::Unbound, } } @@ -787,6 +810,63 @@ mod namespaces { } } + mod builtin_prefixes { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn undeclared_reserved_prefix_xml() { + let prefix_name = "xml"; + let namespace_uri = "http://www.w3.org/XML/1998/namespace"; + + let prefix = Prefix(prefix_name.as_bytes()); + let namespace = Namespace(namespace_uri.as_bytes()); + + let resolver = NamespaceResolver::default(); + let tag = b"random"; + + let name_buf = [prefix.into_inner(), tag].join(&b":"[..]); + let name = QName(&name_buf); + + assert_eq!( + resolver.resolve(name, b"", true), + (Bound(namespace), LocalName(tag)) + ); + + assert_eq!( + resolver.resolve(name.clone(), b"", false), + (Bound(namespace), LocalName(tag)) + ); + assert_eq!(resolver.find(name.clone(), b""), Bound(namespace)); + } + + #[test] + fn undeclared_reserved_prefix_xmlns() { + let prefix_name = "xmlns"; + let namespace_uri = "http://www.w3.org/2000/xmlns/"; + + let prefix = Prefix(prefix_name.as_bytes()); + let namespace = Namespace(namespace_uri.as_bytes()); + + let resolver = NamespaceResolver::default(); + let tag = b"random"; + + let name_buf = [prefix.into_inner(), tag].join(&b":"[..]); + let name = QName(&name_buf); + + assert_eq!( + resolver.resolve(name, b"", true), + (Bound(namespace), LocalName(tag)) + ); + + assert_eq!( + resolver.resolve(name.clone(), b"", false), + (Bound(namespace), LocalName(tag)) + ); + assert_eq!(resolver.find(name.clone(), b""), Bound(namespace)); + } + } + #[test] fn undeclared_prefix() { let name = QName(b"unknown:prefix");