aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/script/dom/element.rs
diff options
context:
space:
mode:
authorJames Graham <james@hoppipolla.co.uk>2013-09-10 10:24:40 +0100
committerJosh Matthews <josh@joshmatthews.net>2013-11-12 17:39:04 +0900
commit15b9d4d19967ed96bd459bf15cae5b793567c227 (patch)
tree5e1237d9bd62386b327e30e52bf8870f5ff9f107 /src/components/script/dom/element.rs
parent433f19f5a310f71273f03504261568d393e648ea (diff)
downloadservo-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.rs175
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
- }
- }
-}