aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/xmlname.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/bindings/xmlname.rs')
-rw-r--r--components/script/dom/bindings/xmlname.rs176
1 files changed, 176 insertions, 0 deletions
diff --git a/components/script/dom/bindings/xmlname.rs b/components/script/dom/bindings/xmlname.rs
new file mode 100644
index 00000000000..e8a53707420
--- /dev/null
+++ b/components/script/dom/bindings/xmlname.rs
@@ -0,0 +1,176 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Functions for validating and extracting qualified XML names.
+
+use dom::bindings::error::{Error, ErrorResult, Fallible};
+use string_cache::{Atom, Namespace};
+use util::str::DOMString;
+
+/// Validate a qualified name. See https://dom.spec.whatwg.org/#validate for details.
+pub fn validate_qualified_name(qualified_name: &str) -> ErrorResult {
+ match xml_name_type(qualified_name) {
+ XMLName::InvalidXMLName => {
+ // Step 1.
+ Err(Error::InvalidCharacter)
+ },
+ XMLName::Name => {
+ // Step 2.
+ Err(Error::Namespace)
+ },
+ XMLName::QName => Ok(())
+ }
+}
+
+/// Validate a namespace and qualified name and extract their parts.
+/// See https://dom.spec.whatwg.org/#validate-and-extract for details.
+pub fn validate_and_extract(namespace: Option<DOMString>, qualified_name: &str)
+ -> Fallible<(Namespace, Option<Atom>, Atom)> {
+ // Step 1.
+ let namespace = namespace_from_domstring(namespace);
+
+ // Step 2.
+ try!(validate_qualified_name(qualified_name));
+
+ let colon = ':';
+
+ // Step 5.
+ let mut parts = qualified_name.splitn(2, colon);
+
+ let (maybe_prefix, local_name) = {
+ let maybe_prefix = parts.next();
+ let maybe_local_name = parts.next();
+
+ debug_assert!(parts.next().is_none());
+
+ if let Some(local_name) = maybe_local_name {
+ debug_assert!(!maybe_prefix.unwrap().is_empty());
+
+ (maybe_prefix, local_name)
+ } else {
+ (None, maybe_prefix.unwrap())
+ }
+ };
+
+ debug_assert!(!local_name.contains(colon));
+
+ match (namespace, maybe_prefix) {
+ (ns!(""), Some(_)) => {
+ // Step 6.
+ Err(Error::Namespace)
+ },
+ (ref ns, Some("xml")) if ns != &ns!(XML) => {
+ // Step 7.
+ Err(Error::Namespace)
+ },
+ (ref ns, p) if ns != &ns!(XMLNS) &&
+ (qualified_name == "xmlns" || p == Some("xmlns")) => {
+ // Step 8.
+ Err(Error::Namespace)
+ },
+ (ns!(XMLNS), p) if qualified_name != "xmlns" && p != Some("xmlns") => {
+ // Step 9.
+ Err(Error::Namespace)
+ },
+ (ns, p) => {
+ // Step 10.
+ Ok((ns, p.map(Atom::from_slice), Atom::from_slice(local_name)))
+ }
+ }
+}
+
+/// Results of `xml_name_type`.
+#[derive(PartialEq)]
+#[allow(missing_docs)]
+pub enum XMLName {
+ QName,
+ Name,
+ InvalidXMLName
+}
+
+/// Check if an element name is valid. See http://www.w3.org/TR/xml/#NT-Name
+/// for details.
+pub fn xml_name_type(name: &str) -> XMLName {
+ fn is_valid_start(c: char) -> bool {
+ match c {
+ ':' |
+ 'A' ... 'Z' |
+ '_' |
+ 'a' ... 'z' |
+ '\u{C0}' ... '\u{D6}' |
+ '\u{D8}' ... '\u{F6}' |
+ '\u{F8}' ... '\u{2FF}' |
+ '\u{370}' ... '\u{37D}' |
+ '\u{37F}' ... '\u{1FFF}' |
+ '\u{200C}' ... '\u{200D}' |
+ '\u{2070}' ... '\u{218F}' |
+ '\u{2C00}' ... '\u{2FEF}' |
+ '\u{3001}' ... '\u{D7FF}' |
+ '\u{F900}' ... '\u{FDCF}' |
+ '\u{FDF0}' ... '\u{FFFD}' |
+ '\u{10000}' ... '\u{EFFFF}' => true,
+ _ => false,
+ }
+ }
+
+ fn is_valid_continuation(c: char) -> bool {
+ is_valid_start(c) || match c {
+ '-' |
+ '.' |
+ '0' ... '9' |
+ '\u{B7}' |
+ '\u{300}' ... '\u{36F}' |
+ '\u{203F}' ... '\u{2040}' => true,
+ _ => false,
+ }
+ }
+
+ let mut iter = name.chars();
+ let mut non_qname_colons = false;
+ let mut seen_colon = false;
+ let mut last = match iter.next() {
+ None => return XMLName::InvalidXMLName,
+ Some(c) => {
+ if !is_valid_start(c) {
+ return XMLName::InvalidXMLName;
+ }
+ if c == ':' {
+ non_qname_colons = true;
+ }
+ c
+ }
+ };
+
+ for c in iter {
+ if !is_valid_continuation(c) {
+ return XMLName::InvalidXMLName;
+ }
+ if c == ':' {
+ match seen_colon {
+ true => non_qname_colons = true,
+ false => seen_colon = true
+ }
+ }
+ last = c
+ }
+
+ if last == ':' {
+ non_qname_colons = true
+ }
+
+ match non_qname_colons {
+ false => XMLName::QName,
+ true => XMLName::Name
+ }
+}
+
+/// Convert a possibly-null URL to a namespace.
+///
+/// If the URL is None, returns the empty namespace.
+pub fn namespace_from_domstring(url: Option<DOMString>) -> Namespace {
+ match url {
+ None => ns!(""),
+ Some(ref s) => Namespace(Atom::from_slice(s)),
+ }
+}