diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 97f6eaf9392..c8a4e6a9da7 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -7,14 +7,17 @@ use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; use dom::bindings::conversions::{native_from_reflector_jsmanaged, is_dom_class}; -use dom::bindings::error::{Error, ErrorResult, throw_type_error}; +use dom::bindings::error::{Error, ErrorResult, Fallible, throw_type_error}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{Temporary, Root}; use dom::browsercontext; use dom::window; +use util::namespace; +use util::str::DOMString; use libc; use libc::c_uint; +use std::borrow::ToOwned; use std::boxed; use std::cell::Cell; use std::ffi::CString; @@ -43,6 +46,7 @@ use js::rust::with_compartment; use js::{JSPROP_ENUMERATE, JSPROP_READONLY, JSPROP_PERMANENT}; use js::JSFUN_CONSTRUCTOR; use js; +use string_cache::{Atom, Namespace}; /// Proxy handler for a WindowProxy. pub struct WindowProxyHandler(pub *const libc::c_void); @@ -619,6 +623,53 @@ pub fn validate_qualified_name(qualified_name: &str) -> ErrorResult { } } +/// 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, qualified_name: &str) + -> Fallible<(Namespace, Option, Atom)> { + // Step 1. + let namespace = namespace::from_domstring(namespace); + + // Step 2. + try!(validate_qualified_name(qualified_name)); + + let (prefix, local_name) = if qualified_name.contains(":") { + // Step 5. + let mut parts = qualified_name.splitn(1, ':'); + let prefix = parts.next().unwrap(); + debug_assert!(!prefix.is_empty()); + let local_name = parts.next().unwrap(); + debug_assert!(!local_name.contains(":")); + (Some(prefix), local_name) + } else { + (None, qualified_name) + }; + + match (namespace, 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(|s| s.to_owned()), Atom::from_slice(local_name))) + } + } +} + /// Results of `xml_name_type`. #[derive(PartialEq)] #[allow(missing_docs)] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index a280507d4ff..3e490fe8ba4 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -21,20 +21,20 @@ use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, HTMLImageElem use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived}; use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::Error::{NotSupported, InvalidCharacter, Security}; -use dom::bindings::error::Error::{HierarchyRequest, Namespace}; +use dom::bindings::error::Error::HierarchyRequest; use dom::bindings::global::GlobalRef; use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable}; use dom::bindings::js::{OptionalRootable, RootedReference}; use dom::bindings::refcounted::Trusted; use dom::bindings::utils::reflect_dom_object; -use dom::bindings::utils::{xml_name_type, validate_qualified_name}; +use dom::bindings::utils::{xml_name_type, validate_and_extract}; use dom::bindings::utils::XMLName::InvalidXMLName; use dom::comment::Comment; use dom::customevent::CustomEvent; use dom::documentfragment::DocumentFragment; use dom::documenttype::DocumentType; use dom::domimplementation::DOMImplementation; -use dom::element::{Element, ElementCreator, AttributeHandlers, get_attribute_parts}; +use dom::element::{Element, ElementCreator, AttributeHandlers}; use dom::element::{ElementTypeId, ActivationElementHelpers}; use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers}; use dom::eventtarget::{EventTarget, EventTargetTypeId, EventTargetHelpers}; @@ -67,7 +67,7 @@ use net_traits::CookieSource::NonHTTP; use net_traits::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl}; use script_task::Runnable; use script_traits::{MouseButton, UntrustedNodeAddress}; -use util::{opts, namespace}; +use util::opts; use util::str::{DOMString, split_html_space_chars}; use layout_interface::{ReflowGoal, ReflowQueryType}; @@ -975,35 +975,10 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { fn CreateElementNS(self, namespace: Option, qualified_name: DOMString) -> Fallible> { - let ns = namespace::from_domstring(namespace); - try!(validate_qualified_name(&qualified_name)); - - let (prefix_from_qname, local_name_from_qname) = get_attribute_parts(&qualified_name); - match (&ns, prefix_from_qname, local_name_from_qname) { - // throw if prefix is not null and namespace is null - (&ns!(""), Some(_), _) => { - debug!("Namespace can't be null with a non-null prefix"); - return Err(Namespace); - }, - // throw if prefix is "xml" and namespace is not the XML namespace - (_, Some(ref prefix), _) if "xml" == *prefix && ns != ns!(XML) => { - debug!("Namespace must be the xml namespace if the prefix is 'xml'"); - return Err(Namespace); - }, - // throw if namespace is the XMLNS namespace and neither qualifiedName nor prefix is - // "xmlns" - (&ns!(XMLNS), Some(ref prefix), _) if "xmlns" == *prefix => {}, - (&ns!(XMLNS), _, "xmlns") => {}, - (&ns!(XMLNS), _, _) => { - debug!("The prefix or the qualified name must be 'xmlns' if namespace is the XMLNS namespace "); - return Err(Namespace); - }, - _ => {} - } - - let name = QualName::new(ns, Atom::from_slice(local_name_from_qname)); - Ok(Element::create(name, prefix_from_qname.map(|s| s.to_owned()), self, - ElementCreator::ScriptCreated)) + let (namespace, prefix, local_name) = + try!(validate_and_extract(namespace, &qualified_name)); + let name = QualName::new(namespace, local_name); + Ok(Element::create(name, prefix, self, ElementCreator::ScriptCreated)) } // http://dom.spec.whatwg.org/#dom-document-createattribute diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 0ebf4d02bea..afdd42f6bf6 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -24,13 +24,12 @@ use dom::bindings::codegen::InheritTypes::{HTMLTableRowElementDerived, HTMLTextA use dom::bindings::codegen::InheritTypes::{HTMLTableSectionElementDerived, NodeCast}; use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast; use dom::bindings::error::{ErrorResult, Fallible}; -use dom::bindings::error::Error; use dom::bindings::error::Error::{InvalidCharacter, Syntax}; use dom::bindings::error::Error::NoModificationAllowed; use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable}; use dom::bindings::js::{OptionalRootable, RootedReference}; use dom::bindings::trace::RootedVec; -use dom::bindings::utils::{xml_name_type, validate_qualified_name}; +use dom::bindings::utils::{xml_name_type, validate_and_extract}; use dom::bindings::utils::XMLName::InvalidXMLName; use dom::create::create_element; use dom::domrect::DOMRect; @@ -1031,52 +1030,14 @@ impl<'a> ElementMethods for JSRef<'a, Element> { // http://dom.spec.whatwg.org/#dom-element-setattributens fn SetAttributeNS(self, - namespace_url: Option, - name: DOMString, + namespace: Option, + qualified_name: DOMString, value: DOMString) -> ErrorResult { - // Step 1. - let namespace = namespace::from_domstring(namespace_url); - - // Steps 2-3. - try!(validate_qualified_name(&name)); - - // Step 4. - let (prefix, local_name) = get_attribute_parts(&name); - - if let Some(ref prefix_str) = prefix { - // Step 5. - if namespace == ns!("") { - return Err(Error::Namespace); - } - - // Step 6. - if "xml" == *prefix_str && namespace != ns!(XML) { - return Err(Error::Namespace); - } - - // Step 7b. - if "xmlns" == *prefix_str && namespace != ns!(XMLNS) { - return Err(Error::Namespace); - } - } - - let name = Atom::from_slice(&name); - let local_name = Atom::from_slice(local_name); - let xmlns = atom!("xmlns"); - - // Step 7a. - if xmlns == name && namespace != ns!(XMLNS) { - return Err(Error::Namespace); - } - - // Step 8. - if namespace == ns!(XMLNS) && xmlns != name && Some("xmlns") != prefix { - return Err(Error::Namespace); - } - - // Step 9. + let (namespace, prefix, local_name) = + try!(validate_and_extract(namespace, &qualified_name)); + let qualified_name = Atom::from_slice(&qualified_name); let value = self.parse_attribute(&namespace, &local_name, value); - self.do_set_attribute(local_name.clone(), value, name, + self.do_set_attribute(local_name.clone(), value, qualified_name, namespace.clone(), prefix.map(|s| s.to_owned()), |attr| { *attr.local_name() == local_name && @@ -1266,17 +1227,6 @@ impl<'a> ElementMethods for JSRef<'a, Element> { } } -pub fn get_attribute_parts<'a>(name: &'a str) -> (Option<&'a str>, &'a str) { - //FIXME: Throw for XML-invalid names - //FIXME: Throw for XMLNS-invalid names - if name.contains(":") { - let mut parts = name.splitn(1, ':'); - (Some(parts.next().unwrap()), parts.next().unwrap()) - } else { - (None, name) - } -} - impl<'a> VirtualMethods for JSRef<'a, Element> { fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> { let node: &JSRef = NodeCast::from_borrowed_ref(self); diff --git a/tests/wpt/metadata/dom/nodes/DOMImplementation-createDocument.html.ini b/tests/wpt/metadata/dom/nodes/DOMImplementation-createDocument.html.ini deleted file mode 100644 index 8d02570c391..00000000000 --- a/tests/wpt/metadata/dom/nodes/DOMImplementation-createDocument.html.ini +++ /dev/null @@ -1,35 +0,0 @@ -[DOMImplementation-createDocument.html] - type: testharness - [createDocument test 23: null,"xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 41: undefined,"xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 64: "http://example.com/","xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 69: "http://example.com/","xmlns:foo",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 108: "/","xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 111: "/","xmlns:foo",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 121: "http://www.w3.org/XML/1998/namespace","xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 124: "http://www.w3.org/XML/1998/namespace","xmlns:foo",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 141: "http://www.w3.org/2000/xmlns/","foo:xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 150: "foo:","xmlns",null,"NAMESPACE_ERR"] - expected: FAIL - - [createDocument test 153: "foo:","xmlns:foo",null,"NAMESPACE_ERR"] - expected: FAIL - diff --git a/tests/wpt/metadata/dom/nodes/Document-createElementNS.html.ini b/tests/wpt/metadata/dom/nodes/Document-createElementNS.html.ini deleted file mode 100644 index 69ab187bf8d..00000000000 --- a/tests/wpt/metadata/dom/nodes/Document-createElementNS.html.ini +++ /dev/null @@ -1,35 +0,0 @@ -[Document-createElementNS.html] - type: testharness - [createElementNS test 23: null,"xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 41: undefined,"xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 64: "http://example.com/","xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 69: "http://example.com/","xmlns:foo","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 108: "/","xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 111: "/","xmlns:foo","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 121: "http://www.w3.org/XML/1998/namespace","xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 124: "http://www.w3.org/XML/1998/namespace","xmlns:foo","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 141: "http://www.w3.org/2000/xmlns/","foo:xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 150: "foo:","xmlns","NAMESPACE_ERR"] - expected: FAIL - - [createElementNS test 153: "foo:","xmlns:foo","NAMESPACE_ERR"] - expected: FAIL -