diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs index 934625a83f4..e37f1d68153 100644 --- a/src/components/script/dom/bindings/utils.rs +++ b/src/components/script/dom/bindings/utils.rs @@ -770,6 +770,7 @@ pub enum Error { FailureUnknown, NotFound, HierarchyRequest, + InvalidCharacter, } pub type ErrorResult = Result<(), Error>; @@ -832,3 +833,59 @@ pub fn CreateDOMGlobal(cx: *JSContext, class: *JSClass) -> *JSObject { obj } } + +/// Check if an element name is valid. See http://www.w3.org/TR/xml/#NT-Name +/// for details. +pub fn is_valid_element_name(name: &str) -> bool { + fn is_valid_start(c: char) -> bool { + match c { + ':' | + 'A' .. 'Z' | + '_' | + 'a' .. 'z' | + '\xC0' .. '\xD6' | + '\xD8' .. '\xF6' | + '\xF8' .. '\u02FF' | + '\u0370' .. '\u037D' | + '\u037F' .. '\u1FFF' | + '\u200C' .. '\u200D' | + '\u2070' .. '\u218F' | + '\u2C00' .. '\u2FEF' | + '\u3001' .. '\uD7FF' | + '\uF900' .. '\uFDCF' | + '\uFDF0' .. '\uFFFD' | + '\U00010000' .. '\U000EFFFF' => true, + _ => false, + } + } + + fn is_valid_continuation(c: char) -> bool { + is_valid_start(c) || match c { + '-' | + '.' | + '0' .. '9' | + '\xB7' | + '\u0300' .. '\u036F' | + '\u203F' .. '\u2040' => true, + _ => false, + } + } + + let mut iter = name.iter(); + match iter.next() { + None => return false, + Some(c) => { + if !is_valid_start(c) { + return false; + } + } + } + + for c in name.iter() { + if !is_valid_continuation(c) { + return false; + } + } + + true +} diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index 5472c10728b..d84f2427f8d 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -5,7 +5,7 @@ use dom::bindings::codegen::DocumentBinding; use dom::bindings::utils::{DOMString, WrapperCache, ErrorResult, null_string, str}; use dom::bindings::utils::{BindingObject, CacheableWrapper, rust_box, DerivedWrapper}; -use dom::bindings::utils::Traceable; +use dom::bindings::utils::{is_valid_element_name, InvalidCharacter, Traceable}; use dom::element::{Element}; use dom::element::{HTMLHtmlElementTypeId, HTMLHeadElementTypeId, HTMLTitleElementTypeId}; use dom::event::Event; @@ -28,6 +28,7 @@ use std::cast; use std::ptr; use std::str::eq_slice; use std::libc; +use std::ascii::StrAsciiExt; pub trait WrappableDocument { fn init_wrapper(@mut self, cx: *JSContext); @@ -240,9 +241,16 @@ impl Document { None } - pub fn CreateElement(&self, local_name: &DOMString, _rv: &mut ErrorResult) -> AbstractNode { + pub fn CreateElement(&self, local_name: &DOMString, rv: &mut ErrorResult) -> AbstractNode { let cx = self.get_cx(); - build_element_from_tag(cx, local_name.to_str()) + let local_name = local_name.to_str(); + if !is_valid_element_name(local_name) { + *rv = Err(InvalidCharacter); + // FIXME #909: what to return here? + fail!("invalid character"); + } + let local_name = local_name.to_ascii_lower(); + build_element_from_tag(cx, local_name) } pub fn CreateElementNS(&self, _namespace: &DOMString, _qualified_name: &DOMString, _rv: &mut ErrorResult) -> AbstractNode { diff --git a/src/test/html/content/test_create_element.html b/src/test/html/content/test_create_element.html index c59106b6603..eaa3e11377a 100644 --- a/src/test/html/content/test_create_element.html +++ b/src/test/html/content/test_create_element.html @@ -9,6 +9,8 @@ is(elem.tagName, "FOO"); var elem = document.createElement("p"); is(elem instanceof HTMLParagraphElement, true); + var elem = document.createElement("sPAn"); + is(elem instanceof HTMLSpanElement, true); var text = document.createTextNode("hello"); is(text instanceof Text, true); finish();