diff options
author | James Graham <james@hoppipolla.co.uk> | 2013-09-10 10:24:40 +0100 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2013-11-12 17:39:04 +0900 |
commit | 15b9d4d19967ed96bd459bf15cae5b793567c227 (patch) | |
tree | 5e1237d9bd62386b327e30e52bf8870f5ff9f107 /src/components/script/dom/element.rs | |
parent | 433f19f5a310f71273f03504261568d393e648ea (diff) | |
download | servo-15b9d4d19967ed96bd459bf15cae5b793567c227.tar.gz servo-15b9d4d19967ed96bd459bf15cae5b793567c227.zip |
Initial support for Attr and namespaces.
Diffstat (limited to 'src/components/script/dom/element.rs')
-rw-r--r-- | src/components/script/dom/element.rs | 175 |
1 files changed, 126 insertions, 49 deletions
diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 1901278b40d..d0baf973bc9 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -5,12 +5,17 @@ //! Element nodes. use dom::bindings::utils::{Reflectable, DOMString, ErrorResult, Fallible, Reflector}; -use dom::bindings::utils::{null_str_as_empty, null_str_as_empty_ref}; +use dom::bindings::utils::{null_str_as_empty, null_str_as_empty_ref, NamespaceError}; +use dom::bindings::utils::{InvalidCharacter, QName, Name, InvalidXMLName, xml_name_type}; use dom::htmlcollection::HTMLCollection; use dom::clientrect::ClientRect; use dom::clientrectlist::ClientRectList; use dom::document::AbstractDocument; use dom::node::{ElementNodeTypeId, Node, ScriptView, AbstractNode}; +use dom::attr:: Attr; +use dom::document; +use dom::namespace; +use dom::namespace::Namespace; use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery}; use layout_interface::{ContentBoxesResponse}; use style; @@ -18,12 +23,13 @@ use servo_util::tree::{TreeNodeRef, ElementLike}; use std::comm; use std::hashmap::HashMap; +use std::str::{eq, eq_slice}; use std::ascii::StrAsciiExt; pub struct Element { node: Node<ScriptView>, tag_name: ~str, // TODO: This should be an atom, not a ~str. - attrs: HashMap<~str, ~str>, + attrs: HashMap<~str, ~[@mut Attr]>, attrs_list: ~[~str], // store an order of attributes. style_attribute: Option<style::PropertyDeclarationBlock>, } @@ -120,18 +126,11 @@ impl ElementLike for Element { self.tag_name.as_slice() } - fn get_attr<'a>(&'a self, name: &str) -> Option<&'a str> { - // FIXME: only case-insensitive in the HTML namespace (as opposed to SVG, etc.) - let name = name.to_ascii_lower(); - let value: Option<&str> = self.attrs.find_equiv(&name).map(|value| { - let value: &str = *value; - value - }); - - return value; + fn get_attr(&self, name: &str) -> Option<~str> { + self.get_attribute(&None, name).map(|attr| attr.value.clone()) } - fn get_link<'a>(&'a self) -> Option<&'a str> { + fn get_link(&self) -> Option<~str>{ // FIXME: This is HTML only. match self.node.type_id { // http://www.whatwg.org/specs/web-apps/current-work/multipage/selectors.html#selector-link @@ -155,28 +154,103 @@ impl<'self> Element { } } + pub fn normalize_attr_name(&self, name: &DOMString) -> ~str { + //FIXME: Throw for XML-invalid names + let owner = self.node.owner_doc(); + if owner.document().doctype == document::HTML { // && self.namespace == Namespace::HTML + null_str_as_empty(name).to_ascii_lower() + } else { + null_str_as_empty(name) + } + } + + pub fn get_attribute<'a>(&'a self, + namespace_url: &DOMString, + name: &str) -> Option<@mut Attr> { + let namespace = Namespace::from_str(namespace_url); + // FIXME: only case-insensitive in the HTML namespace (as opposed to SVG, etc.) + let name = name.to_ascii_lower(); + self.attrs.find_equiv(&name).and_then(|attrs| { + do attrs.iter().find |attr| { + eq_slice(attr.local_name(), name) && attr.namespace == namespace + }.map(|x| *x) + }) + } + pub fn set_attr(&mut self, abstract_self: AbstractNode<ScriptView>, raw_name: &DOMString, - raw_value: &DOMString) { + raw_value: &DOMString) -> ErrorResult { + self.set_attribute(abstract_self, namespace::Null, raw_name, raw_value) + } + + pub fn set_attribute(&mut self, + abstract_self: AbstractNode<ScriptView>, + namespace: Namespace, + raw_name: &DOMString, + raw_value: &DOMString) -> ErrorResult { + //FIXME: Throw for XML-invalid names + //FIXME: Throw for XMLNS-invalid names let name = null_str_as_empty(raw_name).to_ascii_lower(); let value = null_str_as_empty(raw_value); + let (prefix, local_name) = if name.contains(":") { + let parts: ~[&str] = name.splitn_iter(':', 1).collect(); + (Some(parts[0].to_owned()), parts[1].to_owned()) + } else { + (None, name.clone()) + }; + match prefix { + Some(ref prefix_str) => { + if (namespace == namespace::Null || + (eq(prefix_str, &~"xml") && namespace != namespace::XML) || + (eq(prefix_str, &~"xmlns") && namespace != namespace::XMLNS)) { + return Err(NamespaceError); + } + }, + None => {} + } // FIXME: reduce the time of `value.clone()`. - self.attrs.mangle(name.clone(), value.clone(), - |new_name: &~str, new_value: ~str| { + let win = self.node.owner_doc().document().window; + let new_attr = Attr::new_ns(win, local_name.clone(), value.clone(), + name.clone(), namespace.clone(), prefix); + self.attrs.mangle(local_name.clone(), new_attr, + |new_name: &~str, new_value: @mut Attr| { // register to the ordered list. self.attrs_list.push(new_name.clone()); - new_value + ~[new_value] }, - |_, old_value: &mut ~str, new_value: ~str| { + |name, old_value: &mut ~[@mut Attr], new_value: @mut Attr| { // update value. - *old_value = new_value; + let mut found = false; + for attr in old_value.mut_iter() { + if eq_slice(attr.local_name(), *name) && + attr.namespace == new_value.namespace { + *attr = new_value; + found = true; + break; + } + + } + if !found { + old_value.push(new_value); + self.attrs_list.push(name.clone()); + } }); - if "style" == name { - self.style_attribute = Some(style::parse_style_attribute( - null_str_as_empty_ref(raw_value))); + self.after_set_attr(abstract_self, &namespace, local_name, raw_value); + Ok(()) + } + + fn after_set_attr(&mut self, + abstract_self: AbstractNode<ScriptView>, + namespace: &Namespace, + local_name: ~str, + value: &DOMString) { + + if "style" == local_name && *namespace == namespace::Null { + self.style_attribute = Some(style::parse_style_attribute( + null_str_as_empty_ref(value))); } // TODO: update owner document's id hashmap for `document.getElementById()` @@ -187,12 +261,12 @@ impl<'self> Element { match abstract_self.type_id() { ElementNodeTypeId(HTMLImageElementTypeId) => { do abstract_self.with_mut_image_element |image| { - image.AfterSetAttr(raw_name, raw_value); + image.AfterSetAttr(&Some(local_name.clone()), value); } } ElementNodeTypeId(HTMLIframeElementTypeId) => { do abstract_self.with_mut_iframe_element |iframe| { - iframe.AfterSetAttr(raw_name, raw_value); + iframe.AfterSetAttr(&Some(local_name.clone()), value); } } _ => () @@ -210,19 +284,24 @@ impl Element { Some(self.tag_name.to_owned().to_ascii_upper()) } - pub fn Id(&self) -> DOMString { - None + pub fn Id(&self, _abstract_self: AbstractNode<ScriptView>) -> DOMString { + match self.get_attr(&"id") { + Some(x) => Some(x), + None => Some(~"") + } } - pub fn SetId(&self, _id: &DOMString) { + pub fn SetId(&mut self, abstract_self: AbstractNode<ScriptView>, id: &DOMString) { + self.set_attribute(abstract_self, namespace::Null, &Some(~"id"), id); } pub fn GetAttribute(&self, name: &DOMString) -> DOMString { - self.get_attr(null_str_as_empty_ref(name)).map(|s| s.to_owned()) + self.get_attr(null_str_as_empty_ref(name)) } - pub fn GetAttributeNS(&self, _namespace: &DOMString, _localname: &DOMString) -> DOMString { - None + pub fn GetAttributeNS(&self, namespace: &DOMString, local_name: &DOMString) -> DOMString { + self.get_attribute(namespace, null_str_as_empty_ref(local_name)) + .map(|attr| attr.value.clone()) } pub fn SetAttribute(&mut self, @@ -233,8 +312,20 @@ impl Element { Ok(()) } - pub fn SetAttributeNS(&self, _namespace: &DOMString, _localname: &DOMString, _value: &DOMString) -> ErrorResult { - Ok(()) + pub fn SetAttributeNS(&mut self, + abstract_self: AbstractNode<ScriptView>, + namespace_url: &DOMString, + name: &DOMString, + value: &DOMString) -> ErrorResult { + let name_type = xml_name_type(name.to_str()); + match name_type { + InvalidXMLName => return Err(InvalidCharacter), + Name => return Err(NamespaceError), + QName => {} + } + + let namespace = Namespace::from_str(namespace_url); + self.set_attribute(abstract_self, namespace, name, value) } pub fn RemoveAttribute(&self, _name: &DOMString) -> ErrorResult { @@ -245,12 +336,12 @@ impl Element { Ok(()) } - pub fn HasAttribute(&self, _name: &DOMString) -> bool { - false + pub fn HasAttribute(&self, name: &DOMString) -> bool { + self.GetAttribute(name).is_some() } - pub fn HasAttributeNS(&self, _nameapce: &DOMString, _localname: &DOMString) -> bool { - false + pub fn HasAttributeNS(&self, namespace: &DOMString, local_name: &DOMString) -> bool { + self.GetAttributeNS(namespace, local_name).is_some() } pub fn GetElementsByTagName(&self, _localname: &DOMString) -> @mut HTMLCollection { @@ -385,17 +476,3 @@ impl Element { Ok(None) } } - -pub struct Attr { - name: ~str, - value: ~str, -} - -impl Attr { - pub fn new(name: ~str, value: ~str) -> Attr { - Attr { - name: name, - value: value - } - } -} |