/* 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/. */ //! Element nodes. use dom::bindings::utils::{BindingObject, Reflectable, DOMString, ErrorResult, Fallible, Reflector}; use dom::bindings::utils::{null_str_as_empty, null_str_as_empty_ref}; use dom::htmlcollection::HTMLCollection; use dom::clientrect::ClientRect; use dom::clientrectlist::ClientRectList; use dom::document::AbstractDocument; use dom::node::{ElementNodeTypeId, Node, ScriptView, AbstractNode}; use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery}; use layout_interface::{ContentBoxesResponse}; use newcss::stylesheet::Stylesheet; use js::jsapi::{JSContext, JSObject}; use std::cell::Cell; use std::comm; use std::str::eq_slice; use std::ascii::StrAsciiExt; pub struct Element { node: Node, tag_name: ~str, // TODO: This should be an atom, not a ~str. attrs: ~[Attr], style_attribute: Option, } impl Reflectable for Element { fn reflector<'a>(&'a self) -> &'a Reflector { self.node.reflector() } fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector { self.node.mut_reflector() } fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { fail!("no wrapping") } } impl BindingObject for Element { fn GetParentObject(&self, cx: *JSContext) -> Option<@mut Reflectable> { self.node.GetParentObject(cx) } } #[deriving(Eq)] pub enum ElementTypeId { HTMLElementTypeId, HTMLAnchorElementTypeId, HTMLAppletElementTypeId, HTMLAreaElementTypeId, HTMLAudioElementTypeId, HTMLBaseElementTypeId, HTMLBRElementTypeId, HTMLBodyElementTypeId, HTMLButtonElementTypeId, HTMLCanvasElementTypeId, HTMLDataElementTypeId, HTMLDataListElementTypeId, HTMLDirectoryElementTypeId, HTMLDListElementTypeId, HTMLDivElementTypeId, HTMLEmbedElementTypeId, HTMLFieldSetElementTypeId, HTMLFontElementTypeId, HTMLFormElementTypeId, HTMLFrameElementTypeId, HTMLFrameSetElementTypeId, HTMLHRElementTypeId, HTMLHeadElementTypeId, HTMLHeadingElementTypeId, HTMLHtmlElementTypeId, HTMLIframeElementTypeId, HTMLImageElementTypeId, HTMLInputElementTypeId, HTMLLabelElementTypeId, HTMLLegendElementTypeId, HTMLLinkElementTypeId, HTMLLIElementTypeId, HTMLMapElementTypeId, HTMLMediaElementTypeId, HTMLMetaElementTypeId, HTMLMeterElementTypeId, HTMLModElementTypeId, HTMLObjectElementTypeId, HTMLOListElementTypeId, HTMLOptGroupElementTypeId, HTMLOptionElementTypeId, HTMLOutputElementTypeId, HTMLParagraphElementTypeId, HTMLParamElementTypeId, HTMLPreElementTypeId, HTMLProgressElementTypeId, HTMLQuoteElementTypeId, HTMLScriptElementTypeId, HTMLSelectElementTypeId, HTMLSourceElementTypeId, HTMLSpanElementTypeId, HTMLStyleElementTypeId, HTMLTableElementTypeId, HTMLTableCaptionElementTypeId, HTMLTableCellElementTypeId, HTMLTableColElementTypeId, HTMLTableRowElementTypeId, HTMLTableSectionElementTypeId, HTMLTemplateElementTypeId, HTMLTextAreaElementTypeId, HTMLTimeElementTypeId, HTMLTitleElementTypeId, HTMLTrackElementTypeId, HTMLUListElementTypeId, HTMLVideoElementTypeId, HTMLUnknownElementTypeId, } // // Element methods // impl<'self> Element { pub fn new(type_id: ElementTypeId, tag_name: ~str, document: AbstractDocument) -> Element { Element { node: Node::new(ElementNodeTypeId(type_id), document), tag_name: tag_name, attrs: ~[], style_attribute: None, } } pub fn get_attr(&'self self, name: &str) -> Option<&'self str> { // FIXME: Need an each() that links lifetimes in Rust. for attr in self.attrs.iter() { // FIXME: only case-insensitive in the HTML namespace (as opposed to SVG, etc.) if attr.name.eq_ignore_ascii_case(name) { let val: &str = attr.value; return Some(val); } } return None; } pub fn set_attr(&mut self, abstract_self: AbstractNode, raw_name: &DOMString, raw_value: &DOMString) { let name = null_str_as_empty(raw_name); let value_cell = Cell::new(null_str_as_empty(raw_value)); let mut found = false; for attr in self.attrs.mut_iter() { if eq_slice(attr.name, name) { attr.value = value_cell.take().clone(); found = true; break; } } if !found { self.attrs.push(Attr::new(name.to_str(), value_cell.take().clone())); } if "style" == name { self.style_attribute = Some( Stylesheet::from_attribute( FromStr::from_str("http://www.example.com/").unwrap(), null_str_as_empty_ref(raw_value))); } //XXXjdm We really need something like a vtable so we can call AfterSetAttr. // This hardcoding is awful. match abstract_self.type_id() { ElementNodeTypeId(HTMLImageElementTypeId) => { do abstract_self.with_mut_image_element |image| { image.AfterSetAttr(raw_name, raw_value); } } ElementNodeTypeId(HTMLIframeElementTypeId) => { do abstract_self.with_mut_iframe_element |iframe| { iframe.AfterSetAttr(raw_name, raw_value); } } _ => () } if abstract_self.is_in_doc() { do self.node.owner_doc.with_base |owner| { owner.content_changed(); } } } } impl Element { pub fn TagName(&self) -> DOMString { Some(self.tag_name.to_owned().to_ascii_upper()) } pub fn Id(&self) -> DOMString { None } pub fn SetId(&self, _id: &DOMString) { } pub fn GetAttribute(&self, name: &DOMString) -> DOMString { self.get_attr(null_str_as_empty_ref(name)).map(|s| s.to_owned()) } pub fn GetAttributeNS(&self, _namespace: &DOMString, _localname: &DOMString) -> DOMString { None } pub fn SetAttribute(&mut self, abstract_self: AbstractNode, name: &DOMString, value: &DOMString) -> ErrorResult { self.set_attr(abstract_self, name, value); Ok(()) } pub fn SetAttributeNS(&self, _namespace: &DOMString, _localname: &DOMString, _value: &DOMString) -> ErrorResult { Ok(()) } pub fn RemoveAttribute(&self, _name: &DOMString) -> ErrorResult { Ok(()) } pub fn RemoveAttributeNS(&self, _namespace: &DOMString, _localname: &DOMString) -> ErrorResult { Ok(()) } pub fn HasAttribute(&self, _name: &DOMString) -> bool { false } pub fn HasAttributeNS(&self, _nameapce: &DOMString, _localname: &DOMString) -> bool { false } pub fn GetElementsByTagName(&self, _localname: &DOMString) -> @mut HTMLCollection { let (scope, cx) = self.node.get_scope_and_cx(); HTMLCollection::new(~[], cx, scope) } pub fn GetElementsByTagNameNS(&self, _namespace: &DOMString, _localname: &DOMString) -> Fallible<@mut HTMLCollection> { let (scope, cx) = self.node.get_scope_and_cx(); Ok(HTMLCollection::new(~[], cx, scope)) } pub fn GetElementsByClassName(&self, _names: &DOMString) -> @mut HTMLCollection { let (scope, cx) = self.node.get_scope_and_cx(); HTMLCollection::new(~[], cx, scope) } pub fn MozMatchesSelector(&self, _selector: &DOMString) -> Fallible { Ok(false) } pub fn SetCapture(&self, _retargetToElement: bool) { } pub fn ReleaseCapture(&self) { } pub fn MozRequestFullScreen(&self) { } pub fn MozRequestPointerLock(&self) { } pub fn GetClientRects(&self, abstract_self: AbstractNode) -> @mut ClientRectList { let document = self.node.owner_doc; let win = document.with_base(|doc| doc.window).expect("no window"); let node = abstract_self; assert!(node.is_element()); let (port, chan) = comm::stream(); let (rects, cx, scope) = match win.page.query_layout(ContentBoxesQuery(node, chan), port) { ContentBoxesResponse(rects) => { let cx = win.get_cx(); let scope = win.reflector().get_jsobject(); let rects = do rects.map |r| { ClientRect::new( r.origin.y.to_f32(), (r.origin.y + r.size.height).to_f32(), r.origin.x.to_f32(), (r.origin.x + r.size.width).to_f32(), cx, scope) }; (rects, cx, scope) }, }; ClientRectList::new(rects, cx, scope) } pub fn GetBoundingClientRect(&self, abstract_self: AbstractNode) -> @mut ClientRect { let document = self.node.owner_doc; let win = document.with_base(|doc| doc.window).expect("no window"); let node = abstract_self; assert!(node.is_element()); let (port, chan) = comm::stream(); match win.page.query_layout(ContentBoxQuery(node, chan), port) { ContentBoxResponse(rect) => { let cx = win.get_cx(); let scope = win.reflector().get_jsobject(); ClientRect::new( rect.origin.y.to_f32(), (rect.origin.y + rect.size.height).to_f32(), rect.origin.x.to_f32(), (rect.origin.x + rect.size.width).to_f32(), cx, scope) } } } pub fn ScrollIntoView(&self, _top: bool) { } pub fn ScrollTop(&self) -> i32 { 0 } pub fn SetScrollTop(&mut self, _scroll_top: i32) { } pub fn ScrollLeft(&self) -> i32 { 0 } pub fn SetScrollLeft(&mut self, _scroll_left: i32) { } pub fn ScrollWidth(&self) -> i32 { 0 } pub fn ScrollHeight(&self) -> i32 { 0 } pub fn ClientTop(&self) -> i32 { 0 } pub fn ClientLeft(&self) -> i32 { 0 } pub fn ClientWidth(&self) -> i32 { 0 } pub fn ClientHeight(&self) -> i32 { 0 } pub fn GetInnerHTML(&self) -> Fallible { Ok(None) } pub fn SetInnerHTML(&mut self, _value: &DOMString) -> ErrorResult { Ok(()) } pub fn GetOuterHTML(&self) -> Fallible { Ok(None) } pub fn SetOuterHTML(&mut self, _value: &DOMString) -> ErrorResult { Ok(()) } pub fn InsertAdjacentHTML(&mut self, _position: &DOMString, _text: &DOMString) -> ErrorResult { Ok(()) } pub fn QuerySelector(&self, _selectors: &DOMString) -> Fallible>> { Ok(None) } } pub struct Attr { name: ~str, value: ~str, } impl Attr { pub fn new(name: ~str, value: ~str) -> Attr { Attr { name: name, value: value } } }