mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Cargoify servo
This commit is contained in:
parent
db2f642c32
commit
c6ab60dbfc
1761 changed files with 8423 additions and 2294 deletions
958
components/script/dom/element.rs
Normal file
958
components/script/dom/element.rs
Normal file
|
@ -0,0 +1,958 @@
|
|||
/* 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::attr::{Attr, ReplacedAttr, FirstSetAttr, AttrHelpersForLayout};
|
||||
use dom::attr::{AttrValue, StringAttrValue, UIntAttrValue, AtomAttrValue};
|
||||
use dom::namednodemap::NamedNodeMap;
|
||||
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
|
||||
use dom::bindings::codegen::Bindings::ElementBinding;
|
||||
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
|
||||
use dom::bindings::codegen::InheritTypes::{ElementDerived, NodeCast};
|
||||
use dom::bindings::js::{JS, JSRef, Temporary, TemporaryPushable};
|
||||
use dom::bindings::js::{OptionalSettable, OptionalRootable, Root};
|
||||
use dom::bindings::trace::Traceable;
|
||||
use dom::bindings::utils::{Reflectable, Reflector};
|
||||
use dom::bindings::error::{ErrorResult, Fallible, NamespaceError, InvalidCharacter, Syntax};
|
||||
use dom::bindings::utils::{QName, Name, InvalidXMLName, xml_name_type};
|
||||
use dom::domrect::DOMRect;
|
||||
use dom::domrectlist::DOMRectList;
|
||||
use dom::document::{Document, DocumentHelpers};
|
||||
use dom::domtokenlist::DOMTokenList;
|
||||
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
|
||||
use dom::htmlcollection::HTMLCollection;
|
||||
use dom::htmlserializer::serialize;
|
||||
use dom::node::{ElementNodeTypeId, Node, NodeHelpers, NodeIterator, document_from_node};
|
||||
use dom::node::{window_from_node, LayoutNodeHelpers};
|
||||
use dom::nodelist::NodeList;
|
||||
use dom::virtualmethods::{VirtualMethods, vtable_for};
|
||||
use layout_interface::ContentChangedDocumentDamage;
|
||||
use layout_interface::MatchSelectorsDocumentDamage;
|
||||
use style::{matches, parse_selector_list_from_str};
|
||||
use style;
|
||||
use servo_util::atom::Atom;
|
||||
use servo_util::namespace;
|
||||
use servo_util::namespace::{Namespace, Null};
|
||||
use servo_util::str::{DOMString, null_str_as_empty_ref};
|
||||
|
||||
use std::ascii::StrAsciiExt;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::mem;
|
||||
|
||||
#[deriving(Encodable)]
|
||||
pub struct Element {
|
||||
pub node: Node,
|
||||
pub local_name: Atom,
|
||||
pub namespace: Namespace,
|
||||
pub prefix: Option<DOMString>,
|
||||
pub attrs: RefCell<Vec<JS<Attr>>>,
|
||||
pub style_attribute: Traceable<RefCell<Option<style::PropertyDeclarationBlock>>>,
|
||||
pub attr_list: Cell<Option<JS<NamedNodeMap>>>,
|
||||
class_list: Cell<Option<JS<DOMTokenList>>>,
|
||||
}
|
||||
|
||||
impl ElementDerived for EventTarget {
|
||||
fn is_element(&self) -> bool {
|
||||
match self.type_id {
|
||||
NodeTargetTypeId(ElementNodeTypeId(_)) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Reflectable for Element {
|
||||
fn reflector<'a>(&'a self) -> &'a Reflector {
|
||||
self.node.reflector()
|
||||
}
|
||||
}
|
||||
|
||||
#[deriving(PartialEq,Encodable)]
|
||||
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,
|
||||
HTMLTableDataCellElementTypeId,
|
||||
HTMLTableHeaderCellElementTypeId,
|
||||
HTMLTableColElementTypeId,
|
||||
HTMLTableRowElementTypeId,
|
||||
HTMLTableSectionElementTypeId,
|
||||
HTMLTemplateElementTypeId,
|
||||
HTMLTextAreaElementTypeId,
|
||||
HTMLTimeElementTypeId,
|
||||
HTMLTitleElementTypeId,
|
||||
HTMLTrackElementTypeId,
|
||||
HTMLUListElementTypeId,
|
||||
HTMLVideoElementTypeId,
|
||||
HTMLUnknownElementTypeId,
|
||||
|
||||
ElementTypeId,
|
||||
}
|
||||
|
||||
//
|
||||
// Element methods
|
||||
//
|
||||
|
||||
impl Element {
|
||||
pub fn new_inherited(type_id: ElementTypeId, local_name: DOMString, namespace: Namespace, prefix: Option<DOMString>, document: &JSRef<Document>) -> Element {
|
||||
Element {
|
||||
node: Node::new_inherited(ElementNodeTypeId(type_id), document),
|
||||
local_name: Atom::from_slice(local_name.as_slice()),
|
||||
namespace: namespace,
|
||||
prefix: prefix,
|
||||
attrs: RefCell::new(vec!()),
|
||||
attr_list: Cell::new(None),
|
||||
class_list: Cell::new(None),
|
||||
style_attribute: Traceable::new(RefCell::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(local_name: DOMString, namespace: Namespace, prefix: Option<DOMString>, document: &JSRef<Document>) -> Temporary<Element> {
|
||||
let element = Element::new_inherited(ElementTypeId, local_name, namespace, prefix, document);
|
||||
Node::reflect_node(box element, document, ElementBinding::Wrap)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RawLayoutElementHelpers {
|
||||
unsafe fn get_attr_val_for_layout(&self, namespace: &Namespace, name: &str) -> Option<&'static str>;
|
||||
unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &str) -> Option<Atom>;
|
||||
}
|
||||
|
||||
impl RawLayoutElementHelpers for Element {
|
||||
#[inline]
|
||||
unsafe fn get_attr_val_for_layout(&self, namespace: &Namespace, name: &str)
|
||||
-> Option<&'static str> {
|
||||
// cast to point to T in RefCell<T> directly
|
||||
let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs);
|
||||
(*attrs).iter().find(|attr: & &JS<Attr>| {
|
||||
let attr = attr.unsafe_get();
|
||||
name == (*attr).local_name().as_slice() &&
|
||||
(*attr).namespace == *namespace
|
||||
}).map(|attr| {
|
||||
let attr = attr.unsafe_get();
|
||||
(*attr).value_ref_forever()
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &str)
|
||||
-> Option<Atom> {
|
||||
// cast to point to T in RefCell<T> directly
|
||||
let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs);
|
||||
(*attrs).iter().find(|attr: & &JS<Attr>| {
|
||||
let attr = attr.unsafe_get();
|
||||
name == (*attr).local_name().as_slice() &&
|
||||
(*attr).namespace == *namespace
|
||||
}).and_then(|attr| {
|
||||
let attr = attr.unsafe_get();
|
||||
(*attr).value_atom_forever()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LayoutElementHelpers {
|
||||
unsafe fn html_element_in_html_document_for_layout(&self) -> bool;
|
||||
}
|
||||
|
||||
impl LayoutElementHelpers for JS<Element> {
|
||||
unsafe fn html_element_in_html_document_for_layout(&self) -> bool {
|
||||
if (*self.unsafe_get()).namespace != namespace::HTML {
|
||||
return false
|
||||
}
|
||||
let node: JS<Node> = self.transmute_copy();
|
||||
let owner_doc = node.owner_doc_for_layout().unsafe_get();
|
||||
(*owner_doc).is_html_document
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ElementHelpers {
|
||||
fn html_element_in_html_document(&self) -> bool;
|
||||
fn get_local_name<'a>(&'a self) -> &'a Atom;
|
||||
fn get_namespace<'a>(&'a self) -> &'a Namespace;
|
||||
}
|
||||
|
||||
impl<'a> ElementHelpers for JSRef<'a, Element> {
|
||||
fn html_element_in_html_document(&self) -> bool {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
self.namespace == namespace::HTML && node.is_in_html_doc()
|
||||
}
|
||||
|
||||
fn get_local_name<'a>(&'a self) -> &'a Atom {
|
||||
&self.deref().local_name
|
||||
}
|
||||
|
||||
fn get_namespace<'a>(&'a self) -> &'a Namespace {
|
||||
&self.deref().namespace
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AttributeHandlers {
|
||||
fn get_attribute(&self, namespace: Namespace, name: &str) -> Option<Temporary<Attr>>;
|
||||
fn set_attribute_from_parser(&self, local_name: Atom,
|
||||
value: DOMString, namespace: Namespace,
|
||||
prefix: Option<DOMString>);
|
||||
fn set_attribute(&self, name: &str, value: AttrValue);
|
||||
fn do_set_attribute(&self, local_name: Atom, value: AttrValue,
|
||||
name: Atom, namespace: Namespace,
|
||||
prefix: Option<DOMString>, cb: |&JSRef<Attr>| -> bool);
|
||||
fn parse_attribute(&self, namespace: &Namespace, local_name: &Atom,
|
||||
value: DOMString) -> AttrValue;
|
||||
|
||||
fn remove_attribute(&self, namespace: Namespace, name: &str);
|
||||
fn notify_attribute_changed(&self, local_name: &Atom);
|
||||
fn has_class(&self, name: &str) -> bool;
|
||||
|
||||
fn set_atomic_attribute(&self, name: &str, value: DOMString);
|
||||
|
||||
// http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes
|
||||
fn has_attribute(&self, name: &str) -> bool;
|
||||
fn set_bool_attribute(&self, name: &str, value: bool);
|
||||
fn get_url_attribute(&self, name: &str) -> DOMString;
|
||||
fn set_url_attribute(&self, name: &str, value: DOMString);
|
||||
fn get_string_attribute(&self, name: &str) -> DOMString;
|
||||
fn set_string_attribute(&self, name: &str, value: DOMString);
|
||||
fn set_tokenlist_attribute(&self, name: &str, value: DOMString);
|
||||
fn get_uint_attribute(&self, name: &str) -> u32;
|
||||
fn set_uint_attribute(&self, name: &str, value: u32);
|
||||
}
|
||||
|
||||
impl<'a> AttributeHandlers for JSRef<'a, Element> {
|
||||
fn get_attribute(&self, namespace: Namespace, name: &str) -> Option<Temporary<Attr>> {
|
||||
let element: &Element = self.deref();
|
||||
let local_name = match self.html_element_in_html_document() {
|
||||
true => Atom::from_slice(name.to_ascii_lower().as_slice()),
|
||||
false => Atom::from_slice(name)
|
||||
};
|
||||
element.attrs.borrow().iter().map(|attr| attr.root()).find(|attr| {
|
||||
*attr.local_name() == local_name && attr.namespace == namespace
|
||||
}).map(|x| Temporary::from_rooted(&*x))
|
||||
}
|
||||
|
||||
fn set_attribute_from_parser(&self, local_name: Atom,
|
||||
value: DOMString, namespace: Namespace,
|
||||
prefix: Option<DOMString>) {
|
||||
let name = match prefix {
|
||||
None => local_name.clone(),
|
||||
Some(ref prefix) => {
|
||||
let name = format!("{:s}:{:s}", *prefix, local_name.as_slice());
|
||||
Atom::from_slice(name.as_slice())
|
||||
},
|
||||
};
|
||||
let value = self.parse_attribute(&namespace, &local_name, value);
|
||||
self.do_set_attribute(local_name, value, name, namespace, prefix, |_| false)
|
||||
}
|
||||
|
||||
fn set_attribute(&self, name: &str, value: AttrValue) {
|
||||
assert!(name == name.to_ascii_lower().as_slice());
|
||||
assert!(!name.contains(":"));
|
||||
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
node.wait_until_safe_to_modify_dom();
|
||||
|
||||
let name = Atom::from_slice(name);
|
||||
self.do_set_attribute(name.clone(), value, name.clone(),
|
||||
namespace::Null, None, |attr| *attr.local_name() == name);
|
||||
}
|
||||
|
||||
fn do_set_attribute(&self, local_name: Atom, value: AttrValue,
|
||||
name: Atom, namespace: Namespace,
|
||||
prefix: Option<DOMString>, cb: |&JSRef<Attr>| -> bool) {
|
||||
let idx = self.deref().attrs.borrow().iter()
|
||||
.map(|attr| attr.root())
|
||||
.position(|attr| cb(&*attr));
|
||||
let (idx, set_type) = match idx {
|
||||
Some(idx) => (idx, ReplacedAttr),
|
||||
None => {
|
||||
let window = window_from_node(self).root();
|
||||
let attr = Attr::new(&*window, local_name, value.clone(),
|
||||
name, namespace.clone(), prefix, self);
|
||||
self.deref().attrs.borrow_mut().push_unrooted(&attr);
|
||||
(self.deref().attrs.borrow().len() - 1, FirstSetAttr)
|
||||
}
|
||||
};
|
||||
|
||||
(*self.deref().attrs.borrow())[idx].root().set_value(set_type, value);
|
||||
}
|
||||
|
||||
fn parse_attribute(&self, namespace: &Namespace, local_name: &Atom,
|
||||
value: DOMString) -> AttrValue {
|
||||
if *namespace == namespace::Null {
|
||||
vtable_for(NodeCast::from_ref(self))
|
||||
.parse_plain_attribute(local_name.as_slice(), value)
|
||||
} else {
|
||||
StringAttrValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_attribute(&self, namespace: Namespace, name: &str) {
|
||||
let (_, local_name) = get_attribute_parts(name);
|
||||
let local_name = Atom::from_slice(local_name);
|
||||
|
||||
let idx = self.deref().attrs.borrow().iter().map(|attr| attr.root()).position(|attr| {
|
||||
*attr.local_name() == local_name
|
||||
});
|
||||
|
||||
match idx {
|
||||
None => (),
|
||||
Some(idx) => {
|
||||
{
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
node.wait_until_safe_to_modify_dom();
|
||||
}
|
||||
|
||||
if namespace == namespace::Null {
|
||||
let removed_raw_value = (*self.deref().attrs.borrow())[idx].root().Value();
|
||||
vtable_for(NodeCast::from_ref(self))
|
||||
.before_remove_attr(&local_name,
|
||||
removed_raw_value);
|
||||
}
|
||||
|
||||
self.deref().attrs.borrow_mut().remove(idx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn notify_attribute_changed(&self, local_name: &Atom) {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
if node.is_in_doc() {
|
||||
let damage = match local_name.as_slice() {
|
||||
"style" | "id" | "class" => MatchSelectorsDocumentDamage,
|
||||
_ => ContentChangedDocumentDamage
|
||||
};
|
||||
let document = node.owner_doc().root();
|
||||
document.deref().damage_and_reflow(damage);
|
||||
}
|
||||
}
|
||||
|
||||
fn has_class(&self, name: &str) -> bool {
|
||||
self.get_attribute(Null, "class").root().map(|attr| {
|
||||
attr.deref().value().tokens().map(|mut tokens| {
|
||||
tokens.any(|atom| atom.as_slice() == name)
|
||||
}).unwrap_or(false)
|
||||
}).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn set_atomic_attribute(&self, name: &str, value: DOMString) {
|
||||
assert!(name == name.to_ascii_lower().as_slice());
|
||||
let value = AttrValue::from_atomic(value);
|
||||
self.set_attribute(name, value);
|
||||
}
|
||||
|
||||
fn has_attribute(&self, name: &str) -> bool {
|
||||
let name = match self.html_element_in_html_document() {
|
||||
true => Atom::from_slice(name.to_ascii_lower().as_slice()),
|
||||
false => Atom::from_slice(name)
|
||||
};
|
||||
self.deref().attrs.borrow().iter().map(|attr| attr.root()).any(|attr| {
|
||||
*attr.local_name() == name && attr.namespace == Null
|
||||
})
|
||||
}
|
||||
|
||||
fn set_bool_attribute(&self, name: &str, value: bool) {
|
||||
if self.has_attribute(name) == value { return; }
|
||||
if value {
|
||||
self.set_string_attribute(name, String::new());
|
||||
} else {
|
||||
self.remove_attribute(Null, name);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_url_attribute(&self, name: &str) -> DOMString {
|
||||
// XXX Resolve URL.
|
||||
self.get_string_attribute(name)
|
||||
}
|
||||
fn set_url_attribute(&self, name: &str, value: DOMString) {
|
||||
self.set_string_attribute(name, value);
|
||||
}
|
||||
|
||||
fn get_string_attribute(&self, name: &str) -> DOMString {
|
||||
match self.get_attribute(Null, name) {
|
||||
Some(x) => {
|
||||
let x = x.root();
|
||||
x.deref().Value()
|
||||
}
|
||||
None => "".to_string()
|
||||
}
|
||||
}
|
||||
fn set_string_attribute(&self, name: &str, value: DOMString) {
|
||||
assert!(name == name.to_ascii_lower().as_slice());
|
||||
self.set_attribute(name, StringAttrValue(value));
|
||||
}
|
||||
|
||||
fn set_tokenlist_attribute(&self, name: &str, value: DOMString) {
|
||||
assert!(name == name.to_ascii_lower().as_slice());
|
||||
self.set_attribute(name, AttrValue::from_tokenlist(value));
|
||||
}
|
||||
|
||||
fn get_uint_attribute(&self, name: &str) -> u32 {
|
||||
assert!(name == name.to_ascii_lower().as_slice());
|
||||
let attribute = self.get_attribute(Null, name).root();
|
||||
match attribute {
|
||||
Some(attribute) => {
|
||||
match *attribute.deref().value() {
|
||||
UIntAttrValue(_, value) => value,
|
||||
_ => fail!("Expected a UIntAttrValue"),
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
fn set_uint_attribute(&self, name: &str, value: u32) {
|
||||
assert!(name == name.to_ascii_lower().as_slice());
|
||||
self.set_attribute(name, UIntAttrValue(value.to_string(), value));
|
||||
}
|
||||
}
|
||||
|
||||
impl Element {
|
||||
pub fn is_void(&self) -> bool {
|
||||
if self.namespace != namespace::HTML {
|
||||
return false
|
||||
}
|
||||
match self.local_name.as_slice() {
|
||||
/* List of void elements from
|
||||
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#html-fragment-serialization-algorithm */
|
||||
"area" | "base" | "basefont" | "bgsound" | "br" | "col" | "embed" |
|
||||
"frame" | "hr" | "img" | "input" | "keygen" | "link" | "menuitem" |
|
||||
"meta" | "param" | "source" | "track" | "wbr" => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ElementMethods for JSRef<'a, Element> {
|
||||
// http://dom.spec.whatwg.org/#dom-element-namespaceuri
|
||||
fn GetNamespaceURI(&self) -> Option<DOMString> {
|
||||
match self.namespace {
|
||||
Null => None,
|
||||
ref ns => Some(ns.to_str().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn LocalName(&self) -> DOMString {
|
||||
self.local_name.as_slice().to_string()
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-prefix
|
||||
fn GetPrefix(&self) -> Option<DOMString> {
|
||||
self.prefix.clone()
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-tagname
|
||||
fn TagName(&self) -> DOMString {
|
||||
let qualified_name = match self.prefix {
|
||||
Some(ref prefix) => format!("{}:{}", prefix, self.local_name).into_maybe_owned(),
|
||||
None => self.local_name.as_slice().into_maybe_owned()
|
||||
};
|
||||
if self.html_element_in_html_document() {
|
||||
qualified_name.as_slice().to_ascii_upper()
|
||||
} else {
|
||||
qualified_name.into_string()
|
||||
}
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-id
|
||||
fn Id(&self) -> DOMString {
|
||||
self.get_string_attribute("id")
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-id
|
||||
fn SetId(&self, id: DOMString) {
|
||||
self.set_atomic_attribute("id", id);
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-classname
|
||||
fn ClassName(&self) -> DOMString {
|
||||
self.get_string_attribute("class")
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-classname
|
||||
fn SetClassName(&self, class: DOMString) {
|
||||
self.set_tokenlist_attribute("class", class);
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-classlist
|
||||
fn ClassList(&self) -> Temporary<DOMTokenList> {
|
||||
match self.class_list.get() {
|
||||
Some(class_list) => Temporary::new(class_list),
|
||||
None => {
|
||||
let class_list = DOMTokenList::new(self, "class").root();
|
||||
self.class_list.assign(Some(class_list.deref().clone()));
|
||||
Temporary::from_rooted(&*class_list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-attributes
|
||||
fn Attributes(&self) -> Temporary<NamedNodeMap> {
|
||||
match self.attr_list.get() {
|
||||
None => (),
|
||||
Some(ref list) => return Temporary::new(list.clone()),
|
||||
}
|
||||
|
||||
let doc = {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
node.owner_doc().root()
|
||||
};
|
||||
let window = doc.deref().window.root();
|
||||
let list = NamedNodeMap::new(&*window, self);
|
||||
self.attr_list.assign(Some(list));
|
||||
Temporary::new(self.attr_list.get().get_ref().clone())
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-getattribute
|
||||
fn GetAttribute(&self, name: DOMString) -> Option<DOMString> {
|
||||
let name = if self.html_element_in_html_document() {
|
||||
name.as_slice().to_ascii_lower()
|
||||
} else {
|
||||
name
|
||||
};
|
||||
self.get_attribute(Null, name.as_slice()).root()
|
||||
.map(|s| s.deref().Value())
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-getattributens
|
||||
fn GetAttributeNS(&self,
|
||||
namespace: Option<DOMString>,
|
||||
local_name: DOMString) -> Option<DOMString> {
|
||||
let namespace = Namespace::from_str(null_str_as_empty_ref(&namespace));
|
||||
self.get_attribute(namespace, local_name.as_slice()).root()
|
||||
.map(|attr| attr.deref().Value())
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-setattribute
|
||||
fn SetAttribute(&self,
|
||||
name: DOMString,
|
||||
value: DOMString) -> ErrorResult {
|
||||
{
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
node.wait_until_safe_to_modify_dom();
|
||||
}
|
||||
|
||||
// Step 1.
|
||||
match xml_name_type(name.as_slice()) {
|
||||
InvalidXMLName => return Err(InvalidCharacter),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Step 2.
|
||||
let name = if self.html_element_in_html_document() {
|
||||
name.as_slice().to_ascii_lower()
|
||||
} else {
|
||||
name
|
||||
};
|
||||
|
||||
// Step 3-5.
|
||||
let name = Atom::from_slice(name.as_slice());
|
||||
let value = self.parse_attribute(&namespace::Null, &name, value);
|
||||
self.do_set_attribute(name.clone(), value, name.clone(), namespace::Null, None, |attr| {
|
||||
attr.deref().name.as_slice() == name.as_slice()
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-setattributens
|
||||
fn SetAttributeNS(&self,
|
||||
namespace_url: Option<DOMString>,
|
||||
name: DOMString,
|
||||
value: DOMString) -> ErrorResult {
|
||||
{
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
node.wait_until_safe_to_modify_dom();
|
||||
}
|
||||
|
||||
// Step 1.
|
||||
let namespace = Namespace::from_str(null_str_as_empty_ref(&namespace_url));
|
||||
|
||||
let name_type = xml_name_type(name.as_slice());
|
||||
match name_type {
|
||||
// Step 2.
|
||||
InvalidXMLName => return Err(InvalidCharacter),
|
||||
// Step 3.
|
||||
Name => return Err(NamespaceError),
|
||||
QName => {}
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
let (prefix, local_name) = get_attribute_parts(name.as_slice());
|
||||
match prefix {
|
||||
Some(ref prefix_str) => {
|
||||
// Step 5.
|
||||
if namespace == namespace::Null {
|
||||
return Err(NamespaceError);
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
if "xml" == prefix_str.as_slice() && namespace != namespace::XML {
|
||||
return Err(NamespaceError);
|
||||
}
|
||||
|
||||
// Step 7b.
|
||||
if "xmlns" == prefix_str.as_slice() && namespace != namespace::XMLNS {
|
||||
return Err(NamespaceError);
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
||||
let name = Atom::from_slice(name.as_slice());
|
||||
let local_name = Atom::from_slice(local_name);
|
||||
let xmlns = Atom::from_slice("xmlns"); // TODO: Make this a static atom type
|
||||
|
||||
// Step 7a.
|
||||
if xmlns == name && namespace != namespace::XMLNS {
|
||||
return Err(NamespaceError);
|
||||
}
|
||||
|
||||
// Step 8.
|
||||
if namespace == namespace::XMLNS && xmlns != name && Some("xmlns") != prefix {
|
||||
return Err(NamespaceError);
|
||||
}
|
||||
|
||||
// Step 9.
|
||||
let value = self.parse_attribute(&namespace, &local_name, value);
|
||||
self.do_set_attribute(local_name.clone(), value, name,
|
||||
namespace.clone(), prefix.map(|s| s.to_string()),
|
||||
|attr| {
|
||||
*attr.local_name() == local_name &&
|
||||
attr.namespace == namespace
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-removeattribute
|
||||
fn RemoveAttribute(&self, name: DOMString) {
|
||||
let name = if self.html_element_in_html_document() {
|
||||
name.as_slice().to_ascii_lower()
|
||||
} else {
|
||||
name
|
||||
};
|
||||
self.remove_attribute(namespace::Null, name.as_slice())
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-removeattributens
|
||||
fn RemoveAttributeNS(&self,
|
||||
namespace: Option<DOMString>,
|
||||
localname: DOMString) {
|
||||
let namespace = Namespace::from_str(null_str_as_empty_ref(&namespace));
|
||||
self.remove_attribute(namespace, localname.as_slice())
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-hasattribute
|
||||
fn HasAttribute(&self,
|
||||
name: DOMString) -> bool {
|
||||
self.has_attribute(name.as_slice())
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-hasattributens
|
||||
fn HasAttributeNS(&self,
|
||||
namespace: Option<DOMString>,
|
||||
local_name: DOMString) -> bool {
|
||||
self.GetAttributeNS(namespace, local_name).is_some()
|
||||
}
|
||||
|
||||
fn GetElementsByTagName(&self, localname: DOMString) -> Temporary<HTMLCollection> {
|
||||
let window = window_from_node(self).root();
|
||||
HTMLCollection::by_tag_name(&*window, NodeCast::from_ref(self), localname)
|
||||
}
|
||||
|
||||
fn GetElementsByTagNameNS(&self, maybe_ns: Option<DOMString>,
|
||||
localname: DOMString) -> Temporary<HTMLCollection> {
|
||||
let window = window_from_node(self).root();
|
||||
HTMLCollection::by_tag_name_ns(&*window, NodeCast::from_ref(self), localname, maybe_ns)
|
||||
}
|
||||
|
||||
fn GetElementsByClassName(&self, classes: DOMString) -> Temporary<HTMLCollection> {
|
||||
let window = window_from_node(self).root();
|
||||
HTMLCollection::by_class_name(&*window, NodeCast::from_ref(self), classes)
|
||||
}
|
||||
|
||||
// http://dev.w3.org/csswg/cssom-view/#dom-element-getclientrects
|
||||
fn GetClientRects(&self) -> Temporary<DOMRectList> {
|
||||
let win = window_from_node(self).root();
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
let rects = node.get_content_boxes();
|
||||
let rects: Vec<Root<DOMRect>> = rects.iter().map(|r| {
|
||||
DOMRect::new(
|
||||
&*win,
|
||||
r.origin.y,
|
||||
r.origin.y + r.size.height,
|
||||
r.origin.x,
|
||||
r.origin.x + r.size.width).root()
|
||||
}).collect();
|
||||
|
||||
DOMRectList::new(&*win, rects.iter().map(|rect| rect.deref().clone()).collect())
|
||||
}
|
||||
|
||||
// http://dev.w3.org/csswg/cssom-view/#dom-element-getboundingclientrect
|
||||
fn GetBoundingClientRect(&self) -> Temporary<DOMRect> {
|
||||
let win = window_from_node(self).root();
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
let rect = node.get_bounding_content_box();
|
||||
DOMRect::new(
|
||||
&*win,
|
||||
rect.origin.y,
|
||||
rect.origin.y + rect.size.height,
|
||||
rect.origin.x,
|
||||
rect.origin.x + rect.size.width)
|
||||
}
|
||||
|
||||
fn GetInnerHTML(&self) -> Fallible<DOMString> {
|
||||
//XXX TODO: XML case
|
||||
Ok(serialize(&mut NodeIterator::new(NodeCast::from_ref(self), false, false)))
|
||||
}
|
||||
|
||||
fn GetOuterHTML(&self) -> Fallible<DOMString> {
|
||||
Ok(serialize(&mut NodeIterator::new(NodeCast::from_ref(self), true, false)))
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-parentnode-children
|
||||
fn Children(&self) -> Temporary<HTMLCollection> {
|
||||
let window = window_from_node(self).root();
|
||||
HTMLCollection::children(&*window, NodeCast::from_ref(self))
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector
|
||||
fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
|
||||
let root: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
root.query_selector(selectors)
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
|
||||
fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>> {
|
||||
let root: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
root.query_selector_all(selectors)
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-childnode-remove
|
||||
fn Remove(&self) {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
node.remove_self();
|
||||
}
|
||||
|
||||
// http://dom.spec.whatwg.org/#dom-element-matches
|
||||
fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
|
||||
match parse_selector_list_from_str(selectors.as_slice()) {
|
||||
Err(()) => Err(Syntax),
|
||||
Ok(ref selectors) => {
|
||||
let root: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
Ok(matches(selectors, root))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
let (prefix, local_name) = if name.contains(":") {
|
||||
let mut parts = name.splitn(':', 1);
|
||||
(Some(parts.next().unwrap()), parts.next().unwrap())
|
||||
} else {
|
||||
(None, name)
|
||||
};
|
||||
|
||||
(prefix, local_name)
|
||||
}
|
||||
|
||||
impl<'a> VirtualMethods for JSRef<'a, Element> {
|
||||
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
Some(node as &VirtualMethods)
|
||||
}
|
||||
|
||||
fn after_set_attr(&self, name: &Atom, value: DOMString) {
|
||||
match self.super_type() {
|
||||
Some(ref s) => s.after_set_attr(name, value.clone()),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match name.as_slice() {
|
||||
"style" => {
|
||||
let doc = document_from_node(self).root();
|
||||
let base_url = doc.deref().url().clone();
|
||||
let style = Some(style::parse_style_attribute(value.as_slice(), &base_url));
|
||||
*self.deref().style_attribute.deref().borrow_mut() = style;
|
||||
}
|
||||
"id" => {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
if node.is_in_doc() && !value.is_empty() {
|
||||
let doc = document_from_node(self).root();
|
||||
doc.register_named_element(self, value.clone());
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
self.notify_attribute_changed(name);
|
||||
}
|
||||
|
||||
fn before_remove_attr(&self, name: &Atom, value: DOMString) {
|
||||
match self.super_type() {
|
||||
Some(ref s) => s.before_remove_attr(name, value.clone()),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match name.as_slice() {
|
||||
"style" => {
|
||||
*self.deref().style_attribute.deref().borrow_mut() = None;
|
||||
}
|
||||
"id" => {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
if node.is_in_doc() && !value.is_empty() {
|
||||
let doc = document_from_node(self).root();
|
||||
doc.unregister_named_element(self, value);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
self.notify_attribute_changed(name);
|
||||
}
|
||||
|
||||
fn parse_plain_attribute(&self, name: &str, value: DOMString) -> AttrValue {
|
||||
match name {
|
||||
"id" => AttrValue::from_atomic(value),
|
||||
"class" => AttrValue::from_tokenlist(value),
|
||||
_ => self.super_type().unwrap().parse_plain_attribute(name, value),
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_to_tree(&self, tree_in_doc: bool) {
|
||||
match self.super_type() {
|
||||
Some(ref s) => s.bind_to_tree(tree_in_doc),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if !tree_in_doc { return; }
|
||||
|
||||
match self.get_attribute(Null, "id").root() {
|
||||
Some(attr) => {
|
||||
let doc = document_from_node(self).root();
|
||||
let value = attr.deref().Value();
|
||||
if !value.is_empty() {
|
||||
doc.deref().register_named_element(self, value);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
fn unbind_from_tree(&self, tree_in_doc: bool) {
|
||||
match self.super_type() {
|
||||
Some(ref s) => s.unbind_from_tree(tree_in_doc),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if !tree_in_doc { return; }
|
||||
|
||||
match self.get_attribute(Null, "id").root() {
|
||||
Some(attr) => {
|
||||
let doc = document_from_node(self).root();
|
||||
let value = attr.deref().Value();
|
||||
if !value.is_empty() {
|
||||
doc.deref().unregister_named_element(self, value);
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> style::TElement for JSRef<'a, Element> {
|
||||
fn get_attr(&self, namespace: &Namespace, attr: &str) -> Option<&'static str> {
|
||||
self.get_attribute(namespace.clone(), attr).root().map(|attr| {
|
||||
unsafe { mem::transmute(attr.deref().value().as_slice()) }
|
||||
})
|
||||
}
|
||||
fn get_link(&self) -> Option<&'static str> {
|
||||
// FIXME: This is HTML only.
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
match node.type_id() {
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/multipage/selectors.html#
|
||||
// selector-link
|
||||
ElementNodeTypeId(HTMLAnchorElementTypeId) |
|
||||
ElementNodeTypeId(HTMLAreaElementTypeId) |
|
||||
ElementNodeTypeId(HTMLLinkElementTypeId) => self.get_attr(&namespace::Null, "href"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn get_local_name<'a>(&'a self) -> &'a Atom {
|
||||
(self as &ElementHelpers).get_local_name()
|
||||
}
|
||||
fn get_namespace<'a>(&'a self) -> &'a Namespace {
|
||||
(self as &ElementHelpers).get_namespace()
|
||||
}
|
||||
fn get_hover_state(&self) -> bool {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
node.get_hover_state()
|
||||
}
|
||||
fn get_id<'a>(&self) -> Option<Atom> {
|
||||
self.get_attribute(namespace::Null, "id").map(|attr| {
|
||||
let attr = attr.root();
|
||||
match *attr.value() {
|
||||
AtomAttrValue(ref val) => val.clone(),
|
||||
_ => fail!("`id` attribute should be AtomAttrValue"),
|
||||
}
|
||||
})
|
||||
}
|
||||
fn get_disabled_state(&self) -> bool {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
node.get_disabled_state()
|
||||
}
|
||||
fn get_enabled_state(&self) -> bool {
|
||||
let node: &JSRef<Node> = NodeCast::from_ref(self);
|
||||
node.get_enabled_state()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue