mirror of
https://github.com/servo/servo.git
synced 2025-06-11 01:50:10 +00:00
176 lines
4.9 KiB
Rust
176 lines
4.9 KiB
Rust
/* 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)),
|
|
}
|
|
}
|