diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/script/dom/attr.rs | 95 | ||||
-rw-r--r-- | src/components/script/dom/bindings/codegen/Attr.webidl | 18 | ||||
-rw-r--r-- | src/components/script/dom/bindings/codegen/Bindings.conf | 5 | ||||
-rw-r--r-- | src/components/script/dom/bindings/utils.rs | 34 | ||||
-rw-r--r-- | src/components/script/dom/document.rs | 5 | ||||
-rw-r--r-- | src/components/script/dom/element.rs | 175 | ||||
-rw-r--r-- | src/components/script/dom/namespace.rs | 44 | ||||
-rw-r--r-- | src/components/script/html/hubbub_html_parser.rs | 8 | ||||
-rw-r--r-- | src/components/script/script.rc | 2 | ||||
-rw-r--r-- | src/components/style/selector_matching.rs | 6 | ||||
-rw-r--r-- | src/components/util/tree.rs | 4 |
11 files changed, 333 insertions, 63 deletions
diff --git a/src/components/script/dom/attr.rs b/src/components/script/dom/attr.rs new file mode 100644 index 00000000000..310dc2f3de6 --- /dev/null +++ b/src/components/script/dom/attr.rs @@ -0,0 +1,95 @@ +/* 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/. */ + +use dom::bindings::codegen::AttrBinding; +use dom::bindings::utils::{Reflectable, Reflector, DOMString}; +use dom::bindings::utils::{reflect_dom_object, null_str_as_empty}; +use dom::namespace::{Namespace, Null}; +use dom::window::Window; + +use std::str::eq_slice; + +pub struct Attr { + reflector_: Reflector, + priv local_name: Option<~str>, + value: ~str, + name: ~str, + namespace: Namespace, + prefix: DOMString +} + +impl Reflectable for Attr { + fn reflector<'a>(&'a self) -> &'a Reflector { + &self.reflector_ + } + + fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector { + &mut self.reflector_ + } +} + +impl Attr { + fn new_inherited(name: ~str, value: ~str, local_name: Option<~str>, + namespace: Namespace, prefix: Option<~str>) -> Attr { + Attr { + reflector_: Reflector::new(), + local_name: local_name, + value: value, + name: name, //TODO: Intern attribute names + namespace: namespace, + prefix: prefix + } + } + + pub fn new(window: &Window, name: ~str, value: ~str) -> @mut Attr { + Attr::new_helper(window, name, value, None, Null, None) + } + + pub fn new_ns(window: &Window, name: ~str, value: ~str, local_name: ~str, namespace: Namespace, + prefix: Option<~str>) -> @mut Attr { + let local_name = if eq_slice(local_name, name) { + None + } else { + Some(local_name) + }; + Attr::new_helper(window, name, value, local_name, namespace, prefix) + } + + fn new_helper(window: &Window, name: ~str, value: ~str, local_name: Option<~str>, + namespace: Namespace, prefix: Option<~str>) -> @mut Attr { + let attr = Attr::new_inherited(name, value, local_name, namespace, prefix); + reflect_dom_object(@mut attr, window, AttrBinding::Wrap) + } + + pub fn local_name<'a>(&'a self) -> &'a str { + match self.local_name { + Some(ref x) => x.as_slice(), + None => self.name.as_slice() + } + } + + pub fn LocalName(&self) -> DOMString { + Some(self.local_name().to_owned()) + } + + pub fn Value(&self) -> DOMString { + Some(self.value.clone()) + } + + pub fn SetValue(&mut self, value: &DOMString) { + self.value = null_str_as_empty(value); + } + + pub fn Name(&self) -> DOMString { + Some(self.name.clone()) + } + + pub fn GetNamespaceURI(&self) -> DOMString { + self.namespace.to_str() + } + + pub fn GetPrefix(&self) -> DOMString { + self.prefix.clone() + } +} diff --git a/src/components/script/dom/bindings/codegen/Attr.webidl b/src/components/script/dom/bindings/codegen/Attr.webidl new file mode 100644 index 00000000000..2b3d18150d8 --- /dev/null +++ b/src/components/script/dom/bindings/codegen/Attr.webidl @@ -0,0 +1,18 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is + * http://dom.spec.whatwg.org/#interface-attr + * + */ + +interface Attr { + readonly attribute DOMString localName; + attribute DOMString value; + + readonly attribute DOMString name; + readonly attribute DOMString? namespaceURI; + readonly attribute DOMString? prefix; +}; diff --git a/src/components/script/dom/bindings/codegen/Bindings.conf b/src/components/script/dom/bindings/codegen/Bindings.conf index 6de264edc35..2c75ed85b8b 100644 --- a/src/components/script/dom/bindings/codegen/Bindings.conf +++ b/src/components/script/dom/bindings/codegen/Bindings.conf @@ -66,6 +66,9 @@ DOMInterfaces = { +'Attr' : { +}, + 'AudioBuffer' : { }, @@ -178,7 +181,7 @@ DOMInterfaces = { 'Element': { 'nativeType': 'AbstractNode<ScriptView>', 'pointerType': '', - 'needsAbstract': ['getClientRects', 'getBoundingClientRect', 'setAttribute'] + 'needsAbstract': ['getClientRects', 'getBoundingClientRect', 'setAttribute', 'setAttributeNS', 'id'] }, 'Event': { diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs index 716f16459d5..6fa2a8ec0c3 100644 --- a/src/components/script/dom/bindings/utils.rs +++ b/src/components/script/dom/bindings/utils.rs @@ -784,7 +784,8 @@ pub enum Error { HierarchyRequest, InvalidCharacter, NotSupported, - InvalidState + InvalidState, + NamespaceError } pub type Fallible<T> = Result<T, Error>; @@ -883,7 +884,14 @@ pub fn cx_for_dom_object<T: Reflectable>(obj: &mut T) -> *JSContext { /// 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 { +#[deriving(Eq)] +pub enum XMLName { + QName, + Name, + InvalidXMLName +} + +pub fn xml_name_type(name: &str) -> XMLName { fn is_valid_start(c: char) -> bool { match c { ':' | @@ -919,20 +927,34 @@ pub fn is_valid_element_name(name: &str) -> bool { } let mut iter = name.iter(); + let mut non_qname_colons = false; + let mut seen_colon = false; match iter.next() { - None => return false, + None => return InvalidXMLName, Some(c) => { if !is_valid_start(c) { - return false; + return InvalidXMLName; + } + if c == ':' { + non_qname_colons = true; } } } for c in name.iter() { if !is_valid_continuation(c) { - return false; + return InvalidXMLName; + } + if c == ':' { + match seen_colon { + true => non_qname_colons = true, + false => seen_colon = true + } } } - true + match non_qname_colons { + false => QName, + true => Name + } } diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index 9a978027b00..e7542fc8975 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -7,7 +7,7 @@ use dom::bindings::codegen::DocumentBinding; use dom::bindings::utils::{Reflectable, Reflector, Traceable, reflect_dom_object}; use dom::bindings::utils::{ErrorResult, Fallible, NotSupported, InvalidCharacter}; use dom::bindings::utils::{DOMString, null_str_as_empty_ref, null_str_as_empty, null_str_as_word_null}; -use dom::bindings::utils::is_valid_element_name; +use dom::bindings::utils::{xml_name_type, InvalidXMLName}; use dom::documentfragment::DocumentFragment; use dom::element::{Element}; use dom::element::{HTMLHeadElementTypeId, HTMLTitleElementTypeId}; @@ -90,6 +90,7 @@ impl AbstractDocument { } } +#[deriving(Eq)] pub enum DocumentType { HTML, SVG, @@ -203,7 +204,7 @@ impl Document { pub fn CreateElement(&self, abstract_self: AbstractDocument, local_name: &DOMString) -> Fallible<AbstractNode<ScriptView>> { let local_name = null_str_as_empty(local_name); - if !is_valid_element_name(local_name) { + if xml_name_type(local_name) == InvalidXMLName { return Err(InvalidCharacter); } let local_name = local_name.to_ascii_lower(); 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 - } - } -} diff --git a/src/components/script/dom/namespace.rs b/src/components/script/dom/namespace.rs new file mode 100644 index 00000000000..7fe8e68e232 --- /dev/null +++ b/src/components/script/dom/namespace.rs @@ -0,0 +1,44 @@ +/* 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/. */ + +use dom::bindings::utils::{DOMString, null_str_as_empty_ref}; + +#[deriving(Eq, Clone)] +pub enum Namespace { + Null, + HTML, + XML, + XMLNS, + XLink, + SVG, + MathML, + Other(~str) +} + +impl Namespace { + pub fn from_str(url: &DOMString) -> Namespace { + match null_str_as_empty_ref(url) { + &"http://www.w3.org/1999/xhtml" => HTML, + &"http://www.w3.org/XML/1998/namespace" => XML, + &"http://www.w3.org/2000/xmlns/" => XMLNS, + &"http://www.w3.org/1999/xlink" => XLink, + &"http://www.w3.org/2000/svg" => SVG, + &"http://www.w3.org/1998/Math/MathML" => MathML, + &"" => Null, + ns => Other(ns.to_owned()) + } + } + pub fn to_str(&self) -> DOMString { + match *self { + Null => None, + HTML => Some(~"http://www.w3.org/1999/xhtml"), + XML => Some(~"http://www.w3.org/XML/1998/namespace"), + XMLNS => Some(~"http://www.w3.org/2000/xmlns/"), + XLink => Some(~"http://www.w3.org/1999/xlink"), + SVG => Some(~"http://www.w3.org/2000/svg"), + MathML => Some(~"http://www.w3.org/1998/Math/MathML"), + Other(ref x) => Some(x.to_owned()) + } + } +} diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs index 07f5fb7f0df..44e06eb14fc 100644 --- a/src/components/script/html/hubbub_html_parser.rs +++ b/src/components/script/html/hubbub_html_parser.rs @@ -8,6 +8,7 @@ use dom::htmlelement::HTMLElement; use dom::htmlheadingelement::{Heading1, Heading2, Heading3, Heading4, Heading5, Heading6}; use dom::htmliframeelement::IFrameSize; use dom::htmlformelement::HTMLFormElement; +use dom::namespace; use dom::node::{AbstractNode, ElementNodeTypeId, ScriptView}; use dom::types::*; use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser}; @@ -339,7 +340,10 @@ pub fn parse_html(cx: *JSContext, debug!("-- attach attrs"); do node.as_mut_element |element| { for attr in tag.attributes.iter() { - element.set_attr(node, &Some(attr.name.clone()), &Some(attr.value.clone())); + element.set_attribute(node, + namespace::Null, + &Some(attr.name.clone()), + &Some(attr.value.clone())); } } @@ -350,7 +354,7 @@ pub fn parse_html(cx: *JSContext, do node.with_imm_element |element| { match (element.get_attr("rel"), element.get_attr("href")) { (Some(rel), Some(href)) => { - if rel == "stylesheet" { + if "stylesheet" == rel { debug!("found CSS stylesheet: {:s}", href); let url = make_url(href.to_str(), Some(url2.clone())); css_chan2.send(CSSTaskNewFile(UrlProvenance(url))); diff --git a/src/components/script/script.rc b/src/components/script/script.rc index 0307e70b580..a2617f0a9a3 100644 --- a/src/components/script/script.rc +++ b/src/components/script/script.rc @@ -44,6 +44,7 @@ pub mod dom { pub use super::bindings::codegen::InterfaceTypes::*; } + pub mod attr; pub mod blob; pub mod characterdata; pub mod clientrect; @@ -130,6 +131,7 @@ pub mod dom { pub mod htmlvideoelement; pub mod htmlunknownelement; pub mod mouseevent; + pub mod namespace; pub mod navigator; pub mod node; pub mod nodelist; diff --git a/src/components/style/selector_matching.rs b/src/components/style/selector_matching.rs index 7e93387f83b..94ebb4fd28a 100644 --- a/src/components/style/selector_matching.rs +++ b/src/components/style/selector_matching.rs @@ -12,6 +12,7 @@ use media_queries::{Device, Screen}; use properties::{PropertyDeclaration, PropertyDeclarationBlock}; use servo_util::tree::{TreeNodeRefAsElement, TreeNode, ElementLike}; +use std::str; pub enum StylesheetOrigin { UserAgentOrigin, @@ -207,7 +208,10 @@ fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: Ele // TODO: cache and intern IDs on elements. IDSelector(ref id) => { do element.with_imm_element_like |element: &E| { - element.get_attr("id") == Some(id.as_slice()) + match element.get_attr("id") { + Some(attr) => str::eq_slice(attr, *id), + None => false + } } } // TODO: cache and intern classe names on elements. diff --git a/src/components/util/tree.rs b/src/components/util/tree.rs index 4a156cfa6ca..c84de349579 100644 --- a/src/components/util/tree.rs +++ b/src/components/util/tree.rs @@ -348,6 +348,6 @@ pub trait TreeNode<Ref: TreeNodeRef<Self>> { pub trait ElementLike { fn get_local_name<'a>(&'a self) -> &'a str; - fn get_attr<'a>(&'a self, name: &str) -> Option<&'a str>; - fn get_link<'a>(&'a self) -> Option<&'a str>; + fn get_attr(&self, name: &str) -> Option<~str>; + fn get_link(&self) -> Option<~str>; } |