script: Use atom comparison in more places, especially for attributes.

75% improvement in style recalc for Guardians of the Galaxy.
This commit is contained in:
Patrick Walton 2014-09-12 13:28:37 -07:00
parent d168501555
commit ee2ccc4f87
31 changed files with 305 additions and 237 deletions

View file

@ -226,7 +226,7 @@ impl<'a> FlowConstructor<'a> {
//FIXME: would it make more sense to use HTMLInputElement::input_type instead of the raw //FIXME: would it make more sense to use HTMLInputElement::input_type instead of the raw
// value? definitely for string comparisons. // value? definitely for string comparisons.
let elem = node.as_element(); let elem = node.as_element();
let data = match elem.get_attr(&ns!(""), "type") { let data = match elem.get_attr(&ns!(""), &atom!("type")) {
Some("checkbox") | Some("radio") => None, Some("checkbox") | Some("radio") => None,
Some("button") | Some("submit") | Some("reset") => Some("button") | Some("submit") | Some("reset") =>
Some(node.get_input_value().len() as u32), Some(node.get_input_value().len() as u32),
@ -1158,7 +1158,7 @@ trait ObjectElement<'a> {
impl<'ln> ObjectElement<'ln> for ThreadSafeLayoutNode<'ln> { impl<'ln> ObjectElement<'ln> for ThreadSafeLayoutNode<'ln> {
fn get_type_and_data(&self) -> (Option<&'ln str>, Option<&'ln str>) { fn get_type_and_data(&self) -> (Option<&'ln str>, Option<&'ln str>) {
let elem = self.as_element(); let elem = self.as_element();
(elem.get_attr(&ns!(""), "type"), elem.get_attr(&ns!(""), "data")) (elem.get_attr(&ns!(""), &atom!("type")), elem.get_attr(&ns!(""), &atom!("data")))
} }
fn has_object_data(&self) -> bool { fn has_object_data(&self) -> bool {

View file

@ -15,7 +15,6 @@ use script::dom::node::{TextNodeTypeId};
use servo_util::bloom::BloomFilter; use servo_util::bloom::BloomFilter;
use servo_util::cache::{Cache, LRUCache, SimpleHashCache}; use servo_util::cache::{Cache, LRUCache, SimpleHashCache};
use servo_util::smallvec::{SmallVec, SmallVec16}; use servo_util::smallvec::{SmallVec, SmallVec16};
use servo_util::str::DOMString;
use std::mem; use std::mem;
use std::hash::{Hash, sip}; use std::hash::{Hash, sip};
use std::slice::Items; use std::slice::Items;
@ -165,7 +164,8 @@ pub struct StyleSharingCandidate {
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub parent_style: Arc<ComputedValues>, pub parent_style: Arc<ComputedValues>,
pub local_name: Atom, pub local_name: Atom,
pub class: Option<DOMString>, // FIXME(pcwalton): Should be a list of atoms instead.
pub class: Option<String>,
} }
impl PartialEq for StyleSharingCandidate { impl PartialEq for StyleSharingCandidate {
@ -222,7 +222,7 @@ impl StyleSharingCandidate {
style: style, style: style,
parent_style: parent_style, parent_style: parent_style,
local_name: element.get_local_name().clone(), local_name: element.get_local_name().clone(),
class: element.get_attr(&ns!(""), "class") class: element.get_attr(&ns!(""), &atom!("class"))
.map(|string| string.to_string()), .map(|string| string.to_string()),
}) })
} }
@ -231,10 +231,12 @@ impl StyleSharingCandidate {
if *element.get_local_name() != self.local_name { if *element.get_local_name() != self.local_name {
return false return false
} }
match (&self.class, element.get_attr(&ns!(""), "class")) {
// FIXME(pcwalton): Use `each_class` here instead of slow string comparison.
match (&self.class, element.get_attr(&ns!(""), &atom!("class"))) {
(&None, Some(_)) | (&Some(_), None) => return false, (&None, Some(_)) | (&Some(_), None) => return false,
(&Some(ref this_class), Some(element_class)) (&Some(ref this_class), Some(element_class)) if
if element_class != this_class.as_slice() => { element_class != this_class.as_slice() => {
return false return false
} }
(&Some(_), Some(_)) | (&None, None) => {} (&Some(_), Some(_)) | (&None, None) => {}
@ -457,7 +459,8 @@ impl<'ln> MatchMethods for LayoutNode<'ln> {
} }
let ok = { let ok = {
let element = self.as_element(); let element = self.as_element();
element.style_attribute().is_none() && element.get_attr(&ns!(""), "id").is_none() element.style_attribute().is_none() &&
element.get_attr(&ns!(""), &atom!("id")).is_none()
}; };
if !ok { if !ok {
return CannotShare(false) return CannotShare(false)

View file

@ -48,6 +48,7 @@ use std::cmp::{max, min};
use std::fmt; use std::fmt;
use std::from_str::FromStr; use std::from_str::FromStr;
use std::num::Zero; use std::num::Zero;
use string_cache::Atom;
use style::{ComputedValues, TElement, TNode, cascade_anonymous, RGBA}; use style::{ComputedValues, TElement, TNode, cascade_anonymous, RGBA};
use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto}; use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto};
use style::computed_values::{LengthOrPercentageOrNone}; use style::computed_values::{LengthOrPercentageOrNone};
@ -220,7 +221,7 @@ impl ImageFragmentInfo {
image_url: Url, image_url: Url,
local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>) local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>)
-> ImageFragmentInfo { -> ImageFragmentInfo {
fn convert_length(node: &ThreadSafeLayoutNode, name: &str) -> Option<Au> { fn convert_length(node: &ThreadSafeLayoutNode, name: &Atom) -> Option<Au> {
let element = node.as_element(); let element = node.as_element();
element.get_attr(&ns!(""), name).and_then(|string| { element.get_attr(&ns!(""), name).and_then(|string| {
let n: Option<int> = FromStr::from_str(string); let n: Option<int> = FromStr::from_str(string);
@ -229,8 +230,8 @@ impl ImageFragmentInfo {
} }
let is_vertical = node.style().writing_mode.is_vertical(); let is_vertical = node.style().writing_mode.is_vertical();
let dom_width = convert_length(node, "width"); let dom_width = convert_length(node, &atom!("width"));
let dom_height = convert_length(node, "height"); let dom_height = convert_length(node, &atom!("height"));
let opaque_node: OpaqueNode = OpaqueNodeMethods::from_thread_safe_layout_node(node); let opaque_node: OpaqueNode = OpaqueNodeMethods::from_thread_safe_layout_node(node);
let untrusted_node: UntrustedNodeAddress = opaque_node.to_untrusted_node_address(); let untrusted_node: UntrustedNodeAddress = opaque_node.to_untrusted_node_address();
@ -412,7 +413,7 @@ impl TableColumnFragmentInfo {
pub fn new(node: &ThreadSafeLayoutNode) -> TableColumnFragmentInfo { pub fn new(node: &ThreadSafeLayoutNode) -> TableColumnFragmentInfo {
let span = { let span = {
let element = node.as_element(); let element = node.as_element();
element.get_attr(&ns!(""), "span").and_then(|string| { element.get_attr(&ns!(""), &atom!("span")).and_then(|string| {
let n: Option<int> = FromStr::from_str(string); let n: Option<int> = FromStr::from_str(string);
n n
}) })

View file

@ -28,9 +28,10 @@ extern crate "net" as servo_net;
extern crate "msg" as servo_msg; extern crate "msg" as servo_msg;
#[phase(plugin, link)] #[phase(plugin, link)]
extern crate "util" as servo_util; extern crate "util" as servo_util;
extern crate string_cache;
#[phase(plugin)] #[phase(plugin)]
extern crate string_cache_macros; extern crate string_cache_macros;
extern crate string_cache;
extern crate collections; extern crate collections;
extern crate encoding; extern crate encoding;

View file

@ -317,15 +317,14 @@ impl<'ln> TNode<'ln, LayoutElement<'ln>> for LayoutNode<'ln> {
fn match_attr(self, attr: &AttrSelector, test: |&str| -> bool) -> bool { fn match_attr(self, attr: &AttrSelector, test: |&str| -> bool) -> bool {
assert!(self.is_element()) assert!(self.is_element())
let name = if self.is_html_element_in_html_document() { let name = if self.is_html_element_in_html_document() {
attr.lower_name.as_slice() &attr.lower_name
} else { } else {
attr.name.as_slice() &attr.name
}; };
match attr.namespace { match attr.namespace {
SpecificNamespace(ref ns) => { SpecificNamespace(ref ns) => {
let element = self.as_element(); let element = self.as_element();
element.get_attr(ns, name) element.get_attr(ns, name).map_or(false, |attr| test(attr))
.map_or(false, |attr| test(attr))
}, },
AnyNamespace => { AnyNamespace => {
let element = self.as_element(); let element = self.as_element();
@ -445,13 +444,15 @@ impl<'le> TElement<'le> for LayoutElement<'le> {
} }
#[inline] #[inline]
fn get_attr(self, namespace: &Namespace, name: &str) -> Option<&'le str> { fn get_attr(self, namespace: &Namespace, name: &Atom) -> Option<&'le str> {
unsafe { self.element.get_attr_val_for_layout(namespace, name) } unsafe { self.element.get_attr_val_for_layout(namespace, name) }
} }
#[inline] #[inline]
fn get_attrs(self, name: &str) -> Vec<&'le str> { fn get_attrs(self, name: &Atom) -> Vec<&'le str> {
unsafe { self.element.get_attr_vals_for_layout(name) } unsafe {
self.element.get_attr_vals_for_layout(name)
}
} }
fn get_link(self) -> Option<&'le str> { fn get_link(self) -> Option<&'le str> {
@ -462,7 +463,9 @@ impl<'le> TElement<'le> for LayoutElement<'le> {
ElementNodeTypeId(HTMLAnchorElementTypeId) | ElementNodeTypeId(HTMLAnchorElementTypeId) |
ElementNodeTypeId(HTMLAreaElementTypeId) | ElementNodeTypeId(HTMLAreaElementTypeId) |
ElementNodeTypeId(HTMLLinkElementTypeId) => { ElementNodeTypeId(HTMLLinkElementTypeId) => {
unsafe { self.element.get_attr_val_for_layout(&ns!(""), "href") } unsafe {
self.element.get_attr_val_for_layout(&ns!(""), &atom!("href"))
}
} }
_ => None, _ => None,
} }
@ -476,7 +479,9 @@ impl<'le> TElement<'le> for LayoutElement<'le> {
#[inline] #[inline]
fn get_id(self) -> Option<Atom> { fn get_id(self) -> Option<Atom> {
unsafe { self.element.get_attr_atom_for_layout(&ns!(""), "id") } unsafe {
self.element.get_attr_atom_for_layout(&ns!(""), &atom!("id"))
}
} }
fn get_disabled_state(self) -> bool { fn get_disabled_state(self) -> bool {
@ -491,7 +496,7 @@ impl<'le> TElement<'le> for LayoutElement<'le> {
} }
} }
fn has_class(self, name: &str) -> bool { fn has_class(self, name: &Atom) -> bool {
unsafe { unsafe {
self.element.has_class_for_layout(name) self.element.has_class_for_layout(name)
} }
@ -502,8 +507,8 @@ impl<'le> TElement<'le> for LayoutElement<'le> {
unsafe { unsafe {
match self.element.get_classes_for_layout() { match self.element.get_classes_for_layout() {
None => {} None => {}
Some(mut classes) => { Some(ref classes) => {
for class in classes { for class in classes.iter() {
callback(class) callback(class)
} }
} }
@ -867,8 +872,10 @@ pub struct ThreadSafeLayoutElement<'le> {
impl<'le> ThreadSafeLayoutElement<'le> { impl<'le> ThreadSafeLayoutElement<'le> {
#[inline] #[inline]
pub fn get_attr(&self, namespace: &Namespace, name: &str) -> Option<&'le str> { pub fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&'le str> {
unsafe { self.element.get_attr_val_for_layout(namespace, name) } unsafe {
self.element.get_attr_val_for_layout(namespace, name)
}
} }
} }

View file

@ -17,7 +17,6 @@ use devtools_traits::AttrInfo;
use servo_util::str::{DOMString, split_html_space_chars}; use servo_util::str::{DOMString, split_html_space_chars};
use std::cell::{Ref, RefCell}; use std::cell::{Ref, RefCell};
use std::mem; use std::mem;
use std::slice::Items;
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
pub enum AttrSettingType { pub enum AttrSettingType {
@ -51,9 +50,9 @@ impl AttrValue {
AtomAttrValue(value) AtomAttrValue(value)
} }
pub fn tokens<'a>(&'a self) -> Option<Items<'a, Atom>> { pub fn tokens<'a>(&'a self) -> Option<&'a [Atom]> {
match *self { match *self {
TokenListAttrValue(_, ref tokens) => Some(tokens.iter()), TokenListAttrValue(_, ref tokens) => Some(tokens.as_slice()),
_ => None _ => None
} }
} }
@ -215,17 +214,19 @@ impl<'a> AttrHelpers<'a> for JSRef<'a, Attr> {
pub trait AttrHelpersForLayout { pub trait AttrHelpersForLayout {
unsafe fn value_ref_forever(&self) -> &'static str; unsafe fn value_ref_forever(&self) -> &'static str;
unsafe fn value_atom_forever(&self) -> Option<Atom>; unsafe fn value_atom_forever(&self) -> Option<Atom>;
unsafe fn value_tokens_forever(&self) -> Option<Items<Atom>>; unsafe fn value_tokens_forever(&self) -> Option<&'static [Atom]>;
unsafe fn local_name_atom_forever(&self) -> Atom; unsafe fn local_name_atom_forever(&self) -> Atom;
} }
impl AttrHelpersForLayout for Attr { impl AttrHelpersForLayout for Attr {
#[inline]
unsafe fn value_ref_forever(&self) -> &'static str { unsafe fn value_ref_forever(&self) -> &'static str {
// cast to point to T in RefCell<T> directly // cast to point to T in RefCell<T> directly
let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(&self.value); let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(&self.value);
value.as_slice() value.as_slice()
} }
#[inline]
unsafe fn value_atom_forever(&self) -> Option<Atom> { unsafe fn value_atom_forever(&self) -> Option<Atom> {
// cast to point to T in RefCell<T> directly // cast to point to T in RefCell<T> directly
let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(&self.value); let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(&self.value);
@ -235,15 +236,17 @@ impl AttrHelpersForLayout for Attr {
} }
} }
unsafe fn value_tokens_forever(&self) -> Option<Items<Atom>> { #[inline]
unsafe fn value_tokens_forever(&self) -> Option<&'static [Atom]> {
// cast to point to T in RefCell<T> directly // cast to point to T in RefCell<T> directly
let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(&self.value); let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(&self.value);
match *value { match *value {
TokenListAttrValue(_, ref tokens) => Some(tokens.iter()), TokenListAttrValue(_, ref tokens) => Some(tokens.as_slice()),
_ => None, _ => None,
} }
} }
#[inline]
unsafe fn local_name_atom_forever(&self) -> Atom { unsafe fn local_name_atom_forever(&self) -> Atom {
self.local_name.clone() self.local_name.clone()
} }

View file

@ -5451,6 +5451,7 @@ class GlobalGenRoots():
for protoName in descriptor.prototypeChain[1:-1]: for protoName in descriptor.prototypeChain[1:-1]:
protoDescriptor = config.getDescriptor(protoName) protoDescriptor = config.getDescriptor(protoName)
delegate = string.Template('''impl ${selfName} for ${baseName} { delegate = string.Template('''impl ${selfName} for ${baseName} {
#[inline]
fn ${fname}(&self) -> bool { fn ${fname}(&self) -> bool {
self.${parentName}().${fname}() self.${parentName}().${fname}()
} }

View file

@ -123,7 +123,8 @@ impl CollectionFilter for EmbedsFilter {
struct LinksFilter; struct LinksFilter;
impl CollectionFilter for LinksFilter { impl CollectionFilter for LinksFilter {
fn filter(&self, elem: JSRef<Element>, _root: JSRef<Node>) -> bool { fn filter(&self, elem: JSRef<Element>, _root: JSRef<Node>) -> bool {
(elem.is_htmlanchorelement() || elem.is_htmlareaelement()) && elem.has_attribute("href") (elem.is_htmlanchorelement() || elem.is_htmlareaelement()) &&
elem.has_attribute(&atom!("href"))
} }
} }
@ -147,7 +148,7 @@ impl CollectionFilter for ScriptsFilter {
struct AnchorsFilter; struct AnchorsFilter;
impl CollectionFilter for AnchorsFilter { impl CollectionFilter for AnchorsFilter {
fn filter(&self, elem: JSRef<Element>, _root: JSRef<Node>) -> bool { fn filter(&self, elem: JSRef<Element>, _root: JSRef<Node>) -> bool {
elem.is_htmlanchorelement() && elem.has_attribute("href") elem.is_htmlanchorelement() && elem.has_attribute(&atom!("href"))
} }
} }
@ -278,7 +279,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
self.GetElementById(fragid.clone()).or_else(|| { self.GetElementById(fragid.clone()).or_else(|| {
let check_anchor = |&node: &JSRef<HTMLAnchorElement>| { let check_anchor = |&node: &JSRef<HTMLAnchorElement>| {
let elem: JSRef<Element> = ElementCast::from_ref(node); let elem: JSRef<Element> = ElementCast::from_ref(node);
elem.get_attribute(ns!(""), "name").root().map_or(false, |attr| { elem.get_attribute(ns!(""), &atom!("name")).root().map_or(false, |attr| {
attr.value().as_slice() == fragid.as_slice() attr.value().as_slice() == fragid.as_slice()
}) })
}; };
@ -785,7 +786,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
} }
let element: JSRef<Element> = ElementCast::to_ref(node).unwrap(); let element: JSRef<Element> = ElementCast::to_ref(node).unwrap();
element.get_attribute(ns!(""), "name").root().map_or(false, |attr| { element.get_attribute(ns!(""), &atom!("name")).root().map_or(false, |attr| {
attr.value().as_slice() == name.as_slice() attr.value().as_slice() == name.as_slice()
}) })
}) })

View file

@ -21,12 +21,11 @@ use string_cache::Atom;
pub struct DOMTokenList { pub struct DOMTokenList {
reflector_: Reflector, reflector_: Reflector,
element: JS<Element>, element: JS<Element>,
local_name: &'static str, local_name: Atom,
} }
impl DOMTokenList { impl DOMTokenList {
fn new_inherited(element: JSRef<Element>, pub fn new_inherited(element: JSRef<Element>, local_name: Atom) -> DOMTokenList {
local_name: &'static str) -> DOMTokenList {
DOMTokenList { DOMTokenList {
reflector_: Reflector::new(), reflector_: Reflector::new(),
element: JS::from_rooted(element), element: JS::from_rooted(element),
@ -34,11 +33,11 @@ impl DOMTokenList {
} }
} }
pub fn new(element: JSRef<Element>, pub fn new(element: JSRef<Element>, local_name: &Atom) -> Temporary<DOMTokenList> {
local_name: &'static str) -> Temporary<DOMTokenList> {
let window = window_from_node(element).root(); let window = window_from_node(element).root();
reflect_dom_object(box DOMTokenList::new_inherited(element, local_name), reflect_dom_object(box DOMTokenList::new_inherited(element, local_name.clone()),
&Window(*window), DOMTokenListBinding::Wrap) &Window(*window),
DOMTokenListBinding::Wrap)
} }
} }
@ -56,7 +55,7 @@ trait PrivateDOMTokenListHelpers {
impl<'a> PrivateDOMTokenListHelpers for JSRef<'a, DOMTokenList> { impl<'a> PrivateDOMTokenListHelpers for JSRef<'a, DOMTokenList> {
fn attribute(self) -> Option<Temporary<Attr>> { fn attribute(self) -> Option<Temporary<Attr>> {
let element = self.element.root(); let element = self.element.root();
element.get_attribute(ns!(""), self.local_name) element.get_attribute(ns!(""), &self.local_name)
} }
fn check_token_exceptions<'a>(self, token: &'a str) -> Fallible<&'a str> { fn check_token_exceptions<'a>(self, token: &'a str) -> Fallible<&'a str> {
@ -79,8 +78,8 @@ impl<'a> DOMTokenListMethods for JSRef<'a, DOMTokenList> {
// http://dom.spec.whatwg.org/#dom-domtokenlist-item // http://dom.spec.whatwg.org/#dom-domtokenlist-item
fn Item(self, index: u32) -> Option<DOMString> { fn Item(self, index: u32) -> Option<DOMString> {
self.attribute().root().and_then(|attr| attr.value().tokens().and_then(|mut tokens| { self.attribute().root().and_then(|attr| attr.value().tokens().and_then(|tokens| {
tokens.idx(index as uint).map(|token| token.as_slice().to_string()) tokens.get(index as uint).map(|token| token.as_slice().to_string())
})) }))
} }
@ -93,9 +92,9 @@ impl<'a> DOMTokenListMethods for JSRef<'a, DOMTokenList> {
// http://dom.spec.whatwg.org/#dom-domtokenlist-contains // http://dom.spec.whatwg.org/#dom-domtokenlist-contains
fn Contains(self, token: DOMString) -> Fallible<bool> { fn Contains(self, token: DOMString) -> Fallible<bool> {
self.check_token_exceptions(token.as_slice()).map(|slice| { self.check_token_exceptions(token.as_slice()).map(|slice| {
self.attribute().root().and_then(|attr| attr.value().tokens().map(|mut tokens| { self.attribute().root().and_then(|attr| attr.value().tokens().map(|tokens| {
let atom = Atom::from_slice(slice); let atom = Atom::from_slice(slice);
tokens.any(|token| *token == atom) tokens.iter().any(|token| *token == atom)
})).unwrap_or(false) })).unwrap_or(false)
}) })
} }

View file

@ -38,7 +38,6 @@ use std::ascii::StrAsciiExt;
use std::cell::{Ref, RefMut, RefCell}; use std::cell::{Ref, RefMut, RefCell};
use std::default::Default; use std::default::Default;
use std::mem; use std::mem;
use std::slice::Items;
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
use url::UrlParser; use url::UrlParser;
@ -57,6 +56,7 @@ pub struct Element {
} }
impl ElementDerived for EventTarget { impl ElementDerived for EventTarget {
#[inline]
fn is_element(&self) -> bool { fn is_element(&self) -> bool {
match *self.type_id() { match *self.type_id() {
NodeTargetTypeId(ElementNodeTypeId(_)) => true, NodeTargetTypeId(ElementNodeTypeId(_)) => true,
@ -205,24 +205,25 @@ impl Element {
} }
pub trait RawLayoutElementHelpers { pub trait RawLayoutElementHelpers {
unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &str) -> Option<&'a str>; unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &Atom)
unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &str) -> Vec<&'a str>; -> Option<&'a str>;
unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &str) -> Option<Atom>; unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &Atom) -> Vec<&'a str>;
unsafe fn has_class_for_layout(&self, name: &str) -> bool; unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &Atom) -> Option<Atom>;
unsafe fn get_classes_for_layout<'a>(&'a self) -> Option<Items<'a,Atom>>; unsafe fn has_class_for_layout(&self, name: &Atom) -> bool;
unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]>;
} }
impl RawLayoutElementHelpers for Element { impl RawLayoutElementHelpers for Element {
#[inline] #[inline]
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &str) unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &Atom)
-> Option<&'a str> { -> Option<&'a str> {
// cast to point to T in RefCell<T> directly // cast to point to T in RefCell<T> directly
let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs); let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs);
(*attrs).iter().find(|attr: & &JS<Attr>| { (*attrs).iter().find(|attr: & &JS<Attr>| {
let attr = attr.unsafe_get(); let attr = attr.unsafe_get();
name == (*attr).local_name_atom_forever().as_slice() && *name == (*attr).local_name_atom_forever() &&
*(*attr).namespace() == *namespace (*attr).namespace() == namespace
}).map(|attr| { }).map(|attr| {
let attr = attr.unsafe_get(); let attr = attr.unsafe_get();
(*attr).value_ref_forever() (*attr).value_ref_forever()
@ -231,12 +232,12 @@ impl RawLayoutElementHelpers for Element {
#[inline] #[inline]
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &str) -> Vec<&'a str> { unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &Atom) -> Vec<&'a str> {
// cast to point to T in RefCell<T> directly // cast to point to T in RefCell<T> directly
let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs); let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs);
(*attrs).iter().filter_map(|attr: &JS<Attr>| { (*attrs).iter().filter_map(|attr: &JS<Attr>| {
let attr = attr.unsafe_get(); let attr = attr.unsafe_get();
if name == (*attr).local_name_atom_forever().as_slice() { if *name == (*attr).local_name_atom_forever() {
Some((*attr).value_ref_forever()) Some((*attr).value_ref_forever())
} else { } else {
None None
@ -246,14 +247,14 @@ impl RawLayoutElementHelpers for Element {
#[inline] #[inline]
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &str) unsafe fn get_attr_atom_for_layout(&self, namespace: &Namespace, name: &Atom)
-> Option<Atom> { -> Option<Atom> {
// cast to point to T in RefCell<T> directly // cast to point to T in RefCell<T> directly
let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs); let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs);
(*attrs).iter().find(|attr: & &JS<Attr>| { (*attrs).iter().find(|attr: & &JS<Attr>| {
let attr = attr.unsafe_get(); let attr = attr.unsafe_get();
name == (*attr).local_name_atom_forever().as_slice() && *name == (*attr).local_name_atom_forever() &&
*(*attr).namespace() == *namespace (*attr).namespace() == namespace
}).and_then(|attr| { }).and_then(|attr| {
let attr = attr.unsafe_get(); let attr = attr.unsafe_get();
(*attr).value_atom_forever() (*attr).value_atom_forever()
@ -262,24 +263,26 @@ impl RawLayoutElementHelpers for Element {
#[inline] #[inline]
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn has_class_for_layout(&self, name: &str) -> bool { unsafe fn has_class_for_layout(&self, name: &Atom) -> bool {
let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs); let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs);
(*attrs).iter().find(|attr: & &JS<Attr>| { (*attrs).iter().find(|attr: & &JS<Attr>| {
let attr = attr.unsafe_get(); let attr = attr.unsafe_get();
(*attr).local_name_atom_forever().as_slice() == "class" (*attr).local_name_atom_forever() == atom!("class")
}).map_or(false, |attr| { }).map_or(false, |attr| {
let attr = attr.unsafe_get(); let attr = attr.unsafe_get();
(*attr).value_tokens_forever().map(|mut tokens| { tokens.any(|atom| atom.as_slice() == name) }) (*attr).value_tokens_forever().map(|tokens| {
tokens.iter().any(|atom| atom == name)
})
}.take().unwrap()) }.take().unwrap())
} }
#[inline] #[inline]
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn get_classes_for_layout<'a>(&'a self) -> Option<Items<'a,Atom>> { unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]> {
let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs); let attrs: *const Vec<JS<Attr>> = mem::transmute(&self.attrs);
(*attrs).iter().find(|attr: & &JS<Attr>| { (*attrs).iter().find(|attr: & &JS<Attr>| {
let attr = attr.unsafe_get(); let attr = attr.unsafe_get();
(*attr).local_name_atom_forever().as_slice() == "class" (*attr).local_name_atom_forever() == atom!("class")
}).and_then(|attr| { }).and_then(|attr| {
let attr = attr.unsafe_get(); let attr = attr.unsafe_get();
(*attr).value_tokens_forever() (*attr).value_tokens_forever()
@ -293,6 +296,7 @@ pub trait LayoutElementHelpers {
impl LayoutElementHelpers for JS<Element> { impl LayoutElementHelpers for JS<Element> {
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
#[inline]
unsafe fn html_element_in_html_document_for_layout(&self) -> bool { unsafe fn html_element_in_html_document_for_layout(&self) -> bool {
if (*self.unsafe_get()).namespace != ns!(HTML) { if (*self.unsafe_get()).namespace != ns!(HTML) {
return false return false
@ -355,14 +359,16 @@ impl<'a> ElementHelpers<'a> for JSRef<'a, Element> {
pub trait AttributeHandlers { pub trait AttributeHandlers {
/// Returns the attribute with given namespace and case-sensitive local /// Returns the attribute with given namespace and case-sensitive local
/// name, if any. /// name, if any.
fn get_attribute(self, namespace: Namespace, local_name: &str) fn get_attribute(self, namespace: Namespace, local_name: &Atom)
-> Option<Temporary<Attr>>; -> Option<Temporary<Attr>>;
fn get_attributes(self, local_name: &str) fn get_attributes(self, local_name: &Atom)
-> Vec<Temporary<Attr>>; -> Vec<Temporary<Attr>>;
fn set_attribute_from_parser(self, local_name: Atom, fn set_attribute_from_parser(self,
value: DOMString, namespace: Namespace, local_name: Atom,
value: DOMString,
namespace: Namespace,
prefix: Option<DOMString>); prefix: Option<DOMString>);
fn set_attribute(self, name: &str, value: AttrValue); fn set_attribute(self, name: &Atom, value: AttrValue);
fn do_set_attribute(self, local_name: Atom, value: AttrValue, fn do_set_attribute(self, local_name: Atom, value: AttrValue,
name: Atom, namespace: Namespace, name: Atom, namespace: Namespace,
prefix: Option<DOMString>, cb: |JSRef<Attr>| -> bool); prefix: Option<DOMString>, cb: |JSRef<Attr>| -> bool);
@ -371,34 +377,33 @@ pub trait AttributeHandlers {
fn remove_attribute(self, namespace: Namespace, name: &str); fn remove_attribute(self, namespace: Namespace, name: &str);
fn notify_attribute_changed(self, local_name: &Atom); fn notify_attribute_changed(self, local_name: &Atom);
fn has_class(&self, name: &str) -> bool; fn has_class(&self, name: &Atom) -> bool;
fn notify_attribute_removed(self); fn notify_attribute_removed(self);
fn set_atomic_attribute(self, name: &str, value: DOMString); fn set_atomic_attribute(self, name: &Atom, value: DOMString);
// http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes // http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes
fn has_attribute(self, name: &str) -> bool; fn has_attribute(self, name: &Atom) -> bool;
fn set_bool_attribute(self, name: &str, value: bool); fn set_bool_attribute(self, name: &Atom, value: bool);
fn get_url_attribute(self, name: &str) -> DOMString; fn get_url_attribute(self, name: &Atom) -> DOMString;
fn set_url_attribute(self, name: &str, value: DOMString); fn set_url_attribute(self, name: &Atom, value: DOMString);
fn get_string_attribute(self, name: &str) -> DOMString; fn get_string_attribute(self, name: &Atom) -> DOMString;
fn set_string_attribute(self, name: &str, value: DOMString); fn set_string_attribute(self, name: &Atom, value: DOMString);
fn set_tokenlist_attribute(self, name: &str, value: DOMString); fn set_tokenlist_attribute(self, name: &Atom, value: DOMString);
fn get_uint_attribute(self, name: &str) -> u32; fn get_uint_attribute(self, name: &Atom) -> u32;
fn set_uint_attribute(self, name: &str, value: u32); fn set_uint_attribute(self, name: &Atom, value: u32);
} }
impl<'a> AttributeHandlers for JSRef<'a, Element> { impl<'a> AttributeHandlers for JSRef<'a, Element> {
fn get_attribute(self, namespace: Namespace, local_name: &str) -> Option<Temporary<Attr>> { fn get_attribute(self, namespace: Namespace, local_name: &Atom) -> Option<Temporary<Attr>> {
self.get_attributes(local_name).iter().map(|attr| attr.root()) self.get_attributes(local_name).iter().map(|attr| attr.root())
.find(|attr| *attr.namespace() == namespace) .find(|attr| *attr.namespace() == namespace)
.map(|x| Temporary::from_rooted(*x)) .map(|x| Temporary::from_rooted(*x))
} }
fn get_attributes(self, local_name: &str) -> Vec<Temporary<Attr>> { fn get_attributes(self, local_name: &Atom) -> Vec<Temporary<Attr>> {
let local_name = Atom::from_slice(local_name);
self.attrs.borrow().iter().map(|attr| attr.root()).filter_map(|attr| { self.attrs.borrow().iter().map(|attr| attr.root()).filter_map(|attr| {
if *attr.local_name() == local_name { if *attr.local_name() == *local_name {
Some(Temporary::from_rooted(*attr)) Some(Temporary::from_rooted(*attr))
} else { } else {
None None
@ -406,8 +411,10 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
}).collect() }).collect()
} }
fn set_attribute_from_parser(self, local_name: Atom, fn set_attribute_from_parser(self,
value: DOMString, namespace: Namespace, local_name: Atom,
value: DOMString,
namespace: Namespace,
prefix: Option<DOMString>) { prefix: Option<DOMString>) {
let name = match prefix { let name = match prefix {
None => local_name.clone(), None => local_name.clone(),
@ -420,16 +427,15 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
self.do_set_attribute(local_name, value, name, namespace, prefix, |_| false) self.do_set_attribute(local_name, value, name, namespace, prefix, |_| false)
} }
fn set_attribute(self, name: &str, value: AttrValue) { fn set_attribute(self, name: &Atom, value: AttrValue) {
assert!(name == name.to_ascii_lower().as_slice()); assert!(name.as_slice() == name.as_slice().to_ascii_lower().as_slice());
assert!(!name.contains(":")); assert!(!name.as_slice().contains(":"));
let node: JSRef<Node> = NodeCast::from_ref(self); let node: JSRef<Node> = NodeCast::from_ref(self);
node.wait_until_safe_to_modify_dom(); node.wait_until_safe_to_modify_dom();
let name = Atom::from_slice(name);
self.do_set_attribute(name.clone(), value, name.clone(), self.do_set_attribute(name.clone(), value, name.clone(),
ns!(""), None, |attr| *attr.local_name() == name); ns!(""), None, |attr| *attr.local_name() == *name);
} }
fn do_set_attribute(self, local_name: Atom, value: AttrValue, fn do_set_attribute(self, local_name: Atom, value: AttrValue,
@ -507,41 +513,40 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
} }
} }
fn has_class(&self, name: &str) -> bool { fn has_class(&self, name: &Atom) -> bool {
self.get_attribute(ns!(""), "class").root().map(|attr| { self.get_attribute(ns!(""), &atom!("class")).root().map(|attr| {
attr.value().tokens().map(|mut tokens| { attr.value().tokens().map(|tokens| {
tokens.any(|atom| atom.as_slice() == name) tokens.iter().any(|atom| atom == name)
}).unwrap_or(false) }).unwrap_or(false)
}).unwrap_or(false) }).unwrap_or(false)
} }
fn set_atomic_attribute(self, name: &str, value: DOMString) { fn set_atomic_attribute(self, name: &Atom, value: DOMString) {
assert!(name == name.to_ascii_lower().as_slice()); assert!(name.as_slice().eq_ignore_ascii_case(name.as_slice()));
let value = AttrValue::from_atomic(value); let value = AttrValue::from_atomic(value);
self.set_attribute(name, value); self.set_attribute(name, value);
} }
fn has_attribute(self, name: &str) -> bool { fn has_attribute(self, name: &Atom) -> bool {
let name = match self.html_element_in_html_document() { assert!(name.as_slice().chars().all(|ch| {
true => Atom::from_slice(name.to_ascii_lower().as_slice()), !ch.is_ascii() || ch.to_ascii().to_lowercase() == ch.to_ascii()
false => Atom::from_slice(name) }));
};
self.attrs.borrow().iter().map(|attr| attr.root()).any(|attr| { self.attrs.borrow().iter().map(|attr| attr.root()).any(|attr| {
*attr.local_name() == name && *attr.namespace() == ns!("") *attr.local_name() == *name && *attr.namespace() == ns!("")
}) })
} }
fn set_bool_attribute(self, name: &str, value: bool) { fn set_bool_attribute(self, name: &Atom, value: bool) {
if self.has_attribute(name) == value { return; } if self.has_attribute(name) == value { return; }
if value { if value {
self.set_string_attribute(name, String::new()); self.set_string_attribute(name, String::new());
} else { } else {
self.remove_attribute(ns!(""), name); self.remove_attribute(ns!(""), name.as_slice());
} }
} }
fn get_url_attribute(self, name: &str) -> DOMString { fn get_url_attribute(self, name: &Atom) -> DOMString {
assert!(name == name.to_ascii_lower().as_slice()); assert!(name.as_slice() == name.as_slice().to_ascii_lower().as_slice());
if !self.has_attribute(name) { if !self.has_attribute(name) {
return "".to_string(); return "".to_string();
} }
@ -555,29 +560,30 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
Err(_) => "".to_string() Err(_) => "".to_string()
} }
} }
fn set_url_attribute(self, name: &str, value: DOMString) { fn set_url_attribute(self, name: &Atom, value: DOMString) {
self.set_string_attribute(name, value); self.set_string_attribute(name, value);
} }
fn get_string_attribute(self, name: &str) -> DOMString { fn get_string_attribute(self, name: &Atom) -> DOMString {
assert!(name == name.to_ascii_lower().as_slice());
match self.get_attribute(ns!(""), name) { match self.get_attribute(ns!(""), name) {
Some(x) => x.root().Value(), Some(x) => x.root().Value(),
None => "".to_string() None => "".to_string()
} }
} }
fn set_string_attribute(self, name: &str, value: DOMString) { fn set_string_attribute(self, name: &Atom, value: DOMString) {
assert!(name == name.to_ascii_lower().as_slice()); assert!(name.as_slice() == name.as_slice().to_ascii_lower().as_slice());
self.set_attribute(name, StringAttrValue(value)); self.set_attribute(name, StringAttrValue(value));
} }
fn set_tokenlist_attribute(self, name: &str, value: DOMString) { fn set_tokenlist_attribute(self, name: &Atom, value: DOMString) {
assert!(name == name.to_ascii_lower().as_slice()); assert!(name.as_slice() == name.as_slice().to_ascii_lower().as_slice());
self.set_attribute(name, AttrValue::from_tokenlist(value)); self.set_attribute(name, AttrValue::from_tokenlist(value));
} }
fn get_uint_attribute(self, name: &str) -> u32 { fn get_uint_attribute(self, name: &Atom) -> u32 {
assert!(name == name.to_ascii_lower().as_slice()); assert!(name.as_slice().chars().all(|ch| {
!ch.is_ascii() || ch.to_ascii().to_lowercase() == ch.to_ascii()
}));
let attribute = self.get_attribute(ns!(""), name).root(); let attribute = self.get_attribute(ns!(""), name).root();
match attribute { match attribute {
Some(attribute) => { Some(attribute) => {
@ -589,8 +595,8 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
None => 0, None => 0,
} }
} }
fn set_uint_attribute(self, name: &str, value: u32) { fn set_uint_attribute(self, name: &Atom, value: u32) {
assert!(name == name.to_ascii_lower().as_slice()); assert!(name.as_slice() == name.as_slice().to_ascii_lower().as_slice());
self.set_attribute(name, UIntAttrValue(value.to_string(), value)); self.set_attribute(name, UIntAttrValue(value.to_string(), value));
} }
} }
@ -628,28 +634,28 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
// http://dom.spec.whatwg.org/#dom-element-id // http://dom.spec.whatwg.org/#dom-element-id
fn Id(self) -> DOMString { fn Id(self) -> DOMString {
self.get_string_attribute("id") self.get_string_attribute(&atom!("id"))
} }
// http://dom.spec.whatwg.org/#dom-element-id // http://dom.spec.whatwg.org/#dom-element-id
fn SetId(self, id: DOMString) { fn SetId(self, id: DOMString) {
self.set_atomic_attribute("id", id); self.set_atomic_attribute(&atom!("id"), id);
} }
// http://dom.spec.whatwg.org/#dom-element-classname // http://dom.spec.whatwg.org/#dom-element-classname
fn ClassName(self) -> DOMString { fn ClassName(self) -> DOMString {
self.get_string_attribute("class") self.get_string_attribute(&atom!("class"))
} }
// http://dom.spec.whatwg.org/#dom-element-classname // http://dom.spec.whatwg.org/#dom-element-classname
fn SetClassName(self, class: DOMString) { fn SetClassName(self, class: DOMString) {
self.set_tokenlist_attribute("class", class); self.set_tokenlist_attribute(&atom!("class"), class);
} }
// http://dom.spec.whatwg.org/#dom-element-classlist // http://dom.spec.whatwg.org/#dom-element-classlist
fn ClassList(self) -> Temporary<DOMTokenList> { fn ClassList(self) -> Temporary<DOMTokenList> {
if self.class_list.get().is_none() { if self.class_list.get().is_none() {
let class_list = DOMTokenList::new(self, "class"); let class_list = DOMTokenList::new(self, &atom!("class"));
self.class_list.assign(Some(class_list)); self.class_list.assign(Some(class_list));
} }
self.class_list.get().unwrap() self.class_list.get().unwrap()
@ -676,7 +682,7 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
} else { } else {
name name
}; };
self.get_attribute(ns!(""), name.as_slice()).root() self.get_attribute(ns!(""), &Atom::from_slice(name.as_slice())).root()
.map(|s| s.Value()) .map(|s| s.Value())
} }
@ -685,7 +691,7 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
namespace: Option<DOMString>, namespace: Option<DOMString>,
local_name: DOMString) -> Option<DOMString> { local_name: DOMString) -> Option<DOMString> {
let namespace = namespace::from_domstring(namespace); let namespace = namespace::from_domstring(namespace);
self.get_attribute(namespace, local_name.as_slice()).root() self.get_attribute(namespace, &Atom::from_slice(local_name.as_slice())).root()
.map(|attr| attr.Value()) .map(|attr| attr.Value())
} }
@ -808,9 +814,17 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
} }
// http://dom.spec.whatwg.org/#dom-element-hasattribute // http://dom.spec.whatwg.org/#dom-element-hasattribute
fn HasAttribute(self, fn HasAttribute(self, name: DOMString) -> bool {
name: DOMString) -> bool { // Step 1.
self.has_attribute(name.as_slice()) if self.html_element_in_html_document() {
// TODO(pcwalton): Small string optimization here.
return self.has_attribute(&Atom::from_slice(name.as_slice()
.to_ascii_lower()
.as_slice()))
}
// Step 2.
self.has_attribute(&Atom::from_slice(name.as_slice()))
} }
// http://dom.spec.whatwg.org/#dom-element-hasattributens // http://dom.spec.whatwg.org/#dom-element-hasattributens
@ -997,7 +1011,7 @@ impl<'a> VirtualMethods for JSRef<'a, Element> {
if !tree_in_doc { return; } if !tree_in_doc { return; }
match self.get_attribute(ns!(""), "id").root() { match self.get_attribute(ns!(""), &atom!("id")).root() {
Some(attr) => { Some(attr) => {
let doc = document_from_node(*self).root(); let doc = document_from_node(*self).root();
let value = attr.Value(); let value = attr.Value();
@ -1018,7 +1032,7 @@ impl<'a> VirtualMethods for JSRef<'a, Element> {
if !tree_in_doc { return; } if !tree_in_doc { return; }
match self.get_attribute(ns!(""), "id").root() { match self.get_attribute(ns!(""), &atom!("id")).root() {
Some(attr) => { Some(attr) => {
let doc = document_from_node(*self).root(); let doc = document_from_node(*self).root();
let value = attr.Value(); let value = attr.Value();
@ -1033,12 +1047,12 @@ impl<'a> VirtualMethods for JSRef<'a, Element> {
} }
impl<'a> style::TElement<'a> for JSRef<'a, Element> { impl<'a> style::TElement<'a> for JSRef<'a, Element> {
fn get_attr(self, namespace: &Namespace, attr: &str) -> Option<&'a str> { fn get_attr(self, namespace: &Namespace, attr: &Atom) -> Option<&'a str> {
self.get_attribute(namespace.clone(), attr).root().map(|attr| { self.get_attribute(namespace.clone(), attr).root().map(|attr| {
unsafe { mem::transmute(attr.value().as_slice()) } unsafe { mem::transmute(attr.value().as_slice()) }
}) })
} }
fn get_attrs(self, attr: &str) -> Vec<&'a str> { fn get_attrs(self, attr: &Atom) -> Vec<&'a str> {
self.get_attributes(attr).iter().map(|attr| attr.root()).map(|attr| { self.get_attributes(attr).iter().map(|attr| attr.root()).map(|attr| {
unsafe { mem::transmute(attr.value().as_slice()) } unsafe { mem::transmute(attr.value().as_slice()) }
}).collect() }).collect()
@ -1051,7 +1065,7 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> {
// selector-link // selector-link
ElementNodeTypeId(HTMLAnchorElementTypeId) | ElementNodeTypeId(HTMLAnchorElementTypeId) |
ElementNodeTypeId(HTMLAreaElementTypeId) | ElementNodeTypeId(HTMLAreaElementTypeId) |
ElementNodeTypeId(HTMLLinkElementTypeId) => self.get_attr(&ns!(""), "href"), ElementNodeTypeId(HTMLLinkElementTypeId) => self.get_attr(&ns!(""), &atom!("href")),
_ => None, _ => None,
} }
} }
@ -1078,7 +1092,7 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> {
node.get_hover_state() node.get_hover_state()
} }
fn get_id(self) -> Option<Atom> { fn get_id(self) -> Option<Atom> {
self.get_attribute(ns!(""), "id").map(|attr| { self.get_attribute(ns!(""), &atom!("id")).map(|attr| {
let attr = attr.root(); let attr = attr.root();
match *attr.value() { match *attr.value() {
AtomAttrValue(ref val) => val.clone(), AtomAttrValue(ref val) => val.clone(),
@ -1094,23 +1108,23 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> {
let node: JSRef<Node> = NodeCast::from_ref(self); let node: JSRef<Node> = NodeCast::from_ref(self);
node.get_enabled_state() node.get_enabled_state()
} }
fn has_class(self, name: &str) -> bool { fn has_class(self, name: &Atom) -> bool {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way // FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods. // of disambiguating methods.
fn has_class<T: AttributeHandlers>(this: T, name: &str) -> bool { fn has_class<T: AttributeHandlers>(this: T, name: &Atom) -> bool {
this.has_class(name) this.has_class(name)
} }
has_class(self, name) has_class(self, name)
} }
fn each_class(self, callback: |&Atom|) { fn each_class(self, callback: |&Atom|) {
match self.get_attribute(ns!(""), "class").root() { match self.get_attribute(ns!(""), &atom!("class")).root() {
None => {} None => {}
Some(attr) => { Some(ref attr) => {
match attr.deref().value().tokens() { match attr.value().tokens() {
None => {} None => {}
Some(mut tokens) => { Some(tokens) => {
for token in tokens { for token in tokens.iter() {
callback(token) callback(token)
} }
} }

View file

@ -56,7 +56,7 @@ impl<'a> PrivateHTMLAnchorElementHelpers for JSRef<'a, HTMLAnchorElement> {
fn handle_event_impl(self, event: JSRef<Event>) { fn handle_event_impl(self, event: JSRef<Event>) {
if "click" == event.Type().as_slice() && !event.DefaultPrevented() { if "click" == event.Type().as_slice() && !event.DefaultPrevented() {
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
let attr = element.get_attribute(ns!(""), "href").root(); let attr = element.get_attribute(ns!(""), &atom!("href")).root();
match attr { match attr {
Some(ref href) => { Some(ref href) => {
let value = href.Value(); let value = href.Value();

View file

@ -62,7 +62,7 @@ impl<'a> HTMLButtonElementMethods for JSRef<'a, HTMLButtonElement> {
// https://html.spec.whatwg.org/multipage/forms.html#dom-button-type // https://html.spec.whatwg.org/multipage/forms.html#dom-button-type
fn Type(self) -> DOMString { fn Type(self) -> DOMString {
let elem: JSRef<Element> = ElementCast::from_ref(self); let elem: JSRef<Element> = ElementCast::from_ref(self);
let ty = elem.get_string_attribute("type").into_ascii_lower(); let ty = elem.get_string_attribute(&atom!("type")).into_ascii_lower();
// https://html.spec.whatwg.org/multipage/forms.html#attr-button-type // https://html.spec.whatwg.org/multipage/forms.html#attr-button-type
match ty.as_slice() { match ty.as_slice() {
"reset" | "button" | "menu" => ty, "reset" | "button" | "menu" => ty,

View file

@ -68,7 +68,7 @@ impl<'a> HTMLCanvasElementMethods for JSRef<'a, HTMLCanvasElement> {
fn SetWidth(self, width: u32) { fn SetWidth(self, width: u32) {
let elem: JSRef<Element> = ElementCast::from_ref(self); let elem: JSRef<Element> = ElementCast::from_ref(self);
elem.set_uint_attribute("width", width) elem.set_uint_attribute(&atom!("width"), width)
} }
fn Height(self) -> u32 { fn Height(self) -> u32 {
@ -77,7 +77,7 @@ impl<'a> HTMLCanvasElementMethods for JSRef<'a, HTMLCanvasElement> {
fn SetHeight(self, height: u32) { fn SetHeight(self, height: u32) {
let elem: JSRef<Element> = ElementCast::from_ref(self); let elem: JSRef<Element> = ElementCast::from_ref(self);
elem.set_uint_attribute("height", height) elem.set_uint_attribute(&atom!("height"), height)
} }
fn GetContext(self, id: DOMString) -> Option<Temporary<CanvasRenderingContext2D>> { fn GetContext(self, id: DOMString) -> Option<Temporary<CanvasRenderingContext2D>> {

View file

@ -139,15 +139,17 @@ impl HTMLCollection {
-> Temporary<HTMLCollection> { -> Temporary<HTMLCollection> {
#[jstraceable] #[jstraceable]
struct ClassNameFilter { struct ClassNameFilter {
classes: Vec<DOMString> classes: Vec<Atom>
} }
impl CollectionFilter for ClassNameFilter { impl CollectionFilter for ClassNameFilter {
fn filter(&self, elem: JSRef<Element>, _root: JSRef<Node>) -> bool { fn filter(&self, elem: JSRef<Element>, _root: JSRef<Node>) -> bool {
self.classes.iter().all(|class| elem.has_class(class.as_slice())) self.classes.iter().all(|class| elem.has_class(class))
} }
} }
let filter = ClassNameFilter { let filter = ClassNameFilter {
classes: split_html_space_chars(classes.as_slice()).map(|class| class.to_string()).collect() classes: split_html_space_chars(classes.as_slice()).map(|class| {
Atom::from_slice(class)
}).collect()
}; };
HTMLCollection::create(window, root, box filter) HTMLCollection::create(window, root, box filter)
} }
@ -216,8 +218,8 @@ impl<'a> HTMLCollectionMethods for JSRef<'a, HTMLCollection> {
Static(ref elems) => elems.iter() Static(ref elems) => elems.iter()
.map(|elem| elem.root()) .map(|elem| elem.root())
.find(|elem| { .find(|elem| {
elem.get_string_attribute("name") == key || elem.get_string_attribute(&atom!("name")) == key ||
elem.get_string_attribute("id") == key }) elem.get_string_attribute(&atom!("id")) == key })
.map(|maybe_elem| Temporary::from_rooted(*maybe_elem)), .map(|maybe_elem| Temporary::from_rooted(*maybe_elem)),
Live(ref root, ref filter) => { Live(ref root, ref filter) => {
let root = root.root(); let root = root.root();
@ -230,8 +232,8 @@ impl<'a> HTMLCollectionMethods for JSRef<'a, HTMLCollection> {
} }
}) })
.find(|elem| { .find(|elem| {
elem.get_string_attribute("name") == key || elem.get_string_attribute(&atom!("name")) == key ||
elem.get_string_attribute("id") == key }) elem.get_string_attribute(&atom!("id")) == key })
.map(|maybe_elem| Temporary::from_rooted(maybe_elem)) .map(|maybe_elem| Temporary::from_rooted(maybe_elem))
} }
} }

View file

@ -28,6 +28,7 @@ use std::ascii::OwnedStrAsciiExt;
use std::str::StrSlice; use std::str::StrSlice;
use url::UrlParser; use url::UrlParser;
use url::form_urlencoded::serialize; use url::form_urlencoded::serialize;
use string_cache::Atom;
#[jstraceable] #[jstraceable]
#[must_root] #[must_root]
@ -349,14 +350,24 @@ impl<'a> FormSubmitter<'a> {
fn action(&self) -> DOMString { fn action(&self) -> DOMString {
match *self { match *self {
FormElement(form) => form.Action(), FormElement(form) => form.Action(),
InputElement(input_element) => input_element.get_form_attribute("formaction", |i| i.FormAction(), |f| f.Action()) InputElement(input_element) => {
// FIXME(pcwalton): Make this a static atom.
input_element.get_form_attribute(&Atom::from_slice("formaction"),
|i| i.FormAction(),
|f| f.Action())
}
} }
} }
fn enctype(&self) -> FormEncType { fn enctype(&self) -> FormEncType {
let attr = match *self { let attr = match *self {
FormElement(form) => form.Enctype(), FormElement(form) => form.Enctype(),
InputElement(input_element) => input_element.get_form_attribute("formenctype", |i| i.FormEnctype(), |f| f.Enctype()) InputElement(input_element) => {
// FIXME(pcwalton): Make this a static atom.
input_element.get_form_attribute(&Atom::from_slice("formenctype"),
|i| i.FormEnctype(),
|f| f.Enctype())
}
}; };
match attr.as_slice() { match attr.as_slice() {
"multipart/form-data" => FormDataEncoded, "multipart/form-data" => FormDataEncoded,
@ -370,7 +381,12 @@ impl<'a> FormSubmitter<'a> {
fn method(&self) -> FormMethod { fn method(&self) -> FormMethod {
let attr = match *self { let attr = match *self {
FormElement(form) => form.Method(), FormElement(form) => form.Method(),
InputElement(input_element) => input_element.get_form_attribute("formmethod", |i| i.FormMethod(), |f| f.Method()) InputElement(input_element) => {
// FIXME(pcwalton): Make this a static atom.
input_element.get_form_attribute(&Atom::from_slice("formmethod"),
|i| i.FormMethod(),
|f| f.Method())
}
}; };
match attr.as_slice() { match attr.as_slice() {
"dialog" => FormDialog, "dialog" => FormDialog,
@ -382,14 +398,20 @@ impl<'a> FormSubmitter<'a> {
fn target(&self) -> DOMString { fn target(&self) -> DOMString {
match *self { match *self {
FormElement(form) => form.Target(), FormElement(form) => form.Target(),
InputElement(input_element) => input_element.get_form_attribute("formtarget", |i| i.FormTarget(), |f| f.Target()) InputElement(input_element) => {
// FIXME(pcwalton): Make this a static atom.
input_element.get_form_attribute(&Atom::from_slice("formtarget"),
|i| i.FormTarget(),
|f| f.Target())
}
} }
} }
} }
pub trait FormOwner<'a> : Copy { pub trait FormOwner<'a> : Copy {
fn form_owner(self) -> Option<Temporary<HTMLFormElement>>; fn form_owner(self) -> Option<Temporary<HTMLFormElement>>;
fn get_form_attribute(self, attr: &str, fn get_form_attribute(self,
attr: &Atom,
input: |Self| -> DOMString, input: |Self| -> DOMString,
owner: |JSRef<HTMLFormElement>| -> DOMString) -> DOMString { owner: |JSRef<HTMLFormElement>| -> DOMString) -> DOMString {
if self.to_element().has_attribute(attr) { if self.to_element().has_attribute(attr) {

View file

@ -87,7 +87,7 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
fn get_url(self) -> Option<Url> { fn get_url(self) -> Option<Url> {
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_attribute(ns!(""), "src").root().and_then(|src| { element.get_attribute(ns!(""), &atom!("src")).root().and_then(|src| {
let url = src.value(); let url = src.value();
if url.as_slice().is_empty() { if url.as_slice().is_empty() {
None None
@ -150,22 +150,22 @@ impl HTMLIFrameElement {
impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> { impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> {
fn Src(self) -> DOMString { fn Src(self) -> DOMString {
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_string_attribute("src") element.get_string_attribute(&atom!("src"))
} }
fn SetSrc(self, src: DOMString) { fn SetSrc(self, src: DOMString) {
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.set_url_attribute("src", src) element.set_url_attribute(&atom!("src"), src)
} }
fn Sandbox(self) -> DOMString { fn Sandbox(self) -> DOMString {
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_string_attribute("sandbox") element.get_string_attribute(&atom!("sandbox"))
} }
fn SetSandbox(self, sandbox: DOMString) { fn SetSandbox(self, sandbox: DOMString) {
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.set_string_attribute("sandbox", sandbox); element.set_string_attribute(&atom!("sandbox"), sandbox);
} }
fn GetContentWindow(self) -> Option<Temporary<Window>> { fn GetContentWindow(self) -> Option<Temporary<Window>> {

View file

@ -113,7 +113,7 @@ impl<'a> HTMLImageElementMethods for JSRef<'a, HTMLImageElement> {
fn SetIsMap(self, is_map: bool) { fn SetIsMap(self, is_map: bool) {
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.set_string_attribute("ismap", is_map.to_string()) element.set_string_attribute(&atom!("ismap"), is_map.to_string())
} }
fn Width(self) -> u32 { fn Width(self) -> u32 {
@ -124,7 +124,7 @@ impl<'a> HTMLImageElementMethods for JSRef<'a, HTMLImageElement> {
fn SetWidth(self, width: u32) { fn SetWidth(self, width: u32) {
let elem: JSRef<Element> = ElementCast::from_ref(self); let elem: JSRef<Element> = ElementCast::from_ref(self);
elem.set_uint_attribute("width", width) elem.set_uint_attribute(&atom!("width"), width)
} }
fn Height(self) -> u32 { fn Height(self) -> u32 {
@ -135,7 +135,7 @@ impl<'a> HTMLImageElementMethods for JSRef<'a, HTMLImageElement> {
fn SetHeight(self, height: u32) { fn SetHeight(self, height: u32) {
let elem: JSRef<Element> = ElementCast::from_ref(self); let elem: JSRef<Element> = ElementCast::from_ref(self);
elem.set_uint_attribute("height", height) elem.set_uint_attribute(&atom!("height"), height)
} }
make_getter!(Name) make_getter!(Name)
@ -188,7 +188,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLImageElement> {
_ => (), _ => (),
} }
if "src" == name.as_slice() { if atom!("src") == *name {
self.update_image(None); self.update_image(None);
} }
} }

View file

@ -232,7 +232,7 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
fn get_radio_group(self) -> Option<String> { fn get_radio_group(self) -> Option<String> {
//TODO: determine form owner //TODO: determine form owner
let elem: JSRef<Element> = ElementCast::from_ref(self); let elem: JSRef<Element> = ElementCast::from_ref(self);
elem.get_attribute(ns!(""), "name") elem.get_attribute(ns!(""), &atom!("name"))
.root() .root()
.map(|name| name.Value()) .map(|name| name.Value())
} }
@ -409,7 +409,7 @@ impl<'a> FormOwner<'a> for JSRef<'a, HTMLInputElement> {
fn form_owner(self) -> Option<Temporary<HTMLFormElement>> { fn form_owner(self) -> Option<Temporary<HTMLFormElement>> {
// https://html.spec.whatwg.org/multipage/forms.html#reset-the-form-owner // https://html.spec.whatwg.org/multipage/forms.html#reset-the-form-owner
let elem: JSRef<Element> = ElementCast::from_ref(self); let elem: JSRef<Element> = ElementCast::from_ref(self);
let owner = elem.get_string_attribute("form"); let owner = elem.get_string_attribute(&atom!("form"));
if !owner.is_empty() { if !owner.is_empty() {
let doc = document_from_node(self).root(); let doc = document_from_node(self).root();
let owner = doc.GetElementById(owner).root(); let owner = doc.GetElementById(owner).root();

View file

@ -48,7 +48,7 @@ impl HTMLLinkElement {
} }
} }
fn get_attr(element: JSRef<Element>, name: &str) -> Option<String> { fn get_attr(element: JSRef<Element>, name: &Atom) -> Option<String> {
let elem = element.get_attribute(ns!(""), name).root(); let elem = element.get_attribute(ns!(""), name).root();
elem.map(|e| e.value().as_slice().to_string()) elem.map(|e| e.value().as_slice().to_string())
} }
@ -76,7 +76,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLLinkElement> {
} }
let element: JSRef<Element> = ElementCast::from_ref(*self); let element: JSRef<Element> = ElementCast::from_ref(*self);
let rel = get_attr(element, "rel"); let rel = get_attr(element, &atom!("rel"));
match (rel, name.as_slice()) { match (rel, name.as_slice()) {
(ref rel, "href") => { (ref rel, "href") => {
@ -97,8 +97,8 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLLinkElement> {
if tree_in_doc { if tree_in_doc {
let element: JSRef<Element> = ElementCast::from_ref(*self); let element: JSRef<Element> = ElementCast::from_ref(*self);
let rel = get_attr(element, "rel"); let rel = get_attr(element, &atom!("rel"));
let href = get_attr(element, "href"); let href = get_attr(element, &atom!("href"));
match (rel, href) { match (rel, href) {
(ref rel, Some(ref href)) if is_stylesheet(rel) => { (ref rel, Some(ref href)) if is_stylesheet(rel) => {

View file

@ -63,8 +63,8 @@ impl<'a> ProcessDataURL for JSRef<'a, HTMLObjectElement> {
let elem: JSRef<Element> = ElementCast::from_ref(*self); let elem: JSRef<Element> = ElementCast::from_ref(*self);
// TODO: support other values // TODO: support other values
match (elem.get_attribute(ns!(""), "type").map(|x| x.root().Value()), match (elem.get_attribute(ns!(""), &atom!("type")).map(|x| x.root().Value()),
elem.get_attribute(ns!(""), "data").map(|x| x.root().Value())) { elem.get_attribute(ns!(""), &atom!("data")).map(|x| x.root().Value())) {
(None, Some(uri)) => { (None, Some(uri)) => {
if is_image_data(uri.as_slice()) { if is_image_data(uri.as_slice()) {
let data_url = Url::parse(uri.as_slice()).unwrap(); let data_url = Url::parse(uri.as_slice()).unwrap();

View file

@ -74,7 +74,7 @@ impl<'a> HTMLOptionElementMethods for JSRef<'a, HTMLOptionElement> {
// http://www.whatwg.org/html/#dom-option-disabled // http://www.whatwg.org/html/#dom-option-disabled
fn SetDisabled(self, disabled: bool) { fn SetDisabled(self, disabled: bool) {
let elem: JSRef<Element> = ElementCast::from_ref(self); let elem: JSRef<Element> = ElementCast::from_ref(self);
elem.set_bool_attribute("disabled", disabled) elem.set_bool_attribute(&atom!("disabled"), disabled)
} }
// http://www.whatwg.org/html/#dom-option-text // http://www.whatwg.org/html/#dom-option-text

View file

@ -75,7 +75,7 @@ static SCRIPT_JS_MIMES: StaticStringVec = &[
impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> {
fn is_javascript(self) -> bool { fn is_javascript(self) -> bool {
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
match element.get_attribute(ns!(""), "type").root().map(|s| s.Value()) { match element.get_attribute(ns!(""), &atom!("type")).root().map(|s| s.Value()) {
Some(ref s) if s.is_empty() => { Some(ref s) if s.is_empty() => {
// type attr exists, but empty means js // type attr exists, but empty means js
debug!("script type empty, inferring js"); debug!("script type empty, inferring js");
@ -87,7 +87,9 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> {
}, },
None => { None => {
debug!("no script type"); debug!("no script type");
match element.get_attribute(ns!(""), "language").root().map(|s| s.Value()) { match element.get_attribute(ns!(""), &atom!("language"))
.root()
.map(|s| s.Value()) {
Some(ref s) if s.is_empty() => { Some(ref s) if s.is_empty() => {
debug!("script language empty, inferring js"); debug!("script language empty, inferring js");
true true
@ -109,7 +111,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> {
impl<'a> HTMLScriptElementMethods for JSRef<'a, HTMLScriptElement> { impl<'a> HTMLScriptElementMethods for JSRef<'a, HTMLScriptElement> {
fn Src(self) -> DOMString { fn Src(self) -> DOMString {
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_url_attribute("src") element.get_url_attribute(&atom!("src"))
} }
// http://www.whatwg.org/html/#dom-script-text // http://www.whatwg.org/html/#dom-script-text

View file

@ -67,7 +67,7 @@ impl<'a> HTMLSelectElementMethods for JSRef<'a, HTMLSelectElement> {
// https://html.spec.whatwg.org/multipage/forms.html#dom-select-type // https://html.spec.whatwg.org/multipage/forms.html#dom-select-type
fn Type(self) -> DOMString { fn Type(self) -> DOMString {
let elem: JSRef<Element> = ElementCast::from_ref(self); let elem: JSRef<Element> = ElementCast::from_ref(self);
if elem.has_attribute("multiple") { if elem.has_attribute(&atom!("multiple")) {
"select-multiple".to_string() "select-multiple".to_string()
} else { } else {
"select-one".to_string() "select-one".to_string()

View file

@ -11,7 +11,7 @@ macro_rules! make_getter(
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_string_attribute($htmlname) element.get_string_attribute(&Atom::from_slice($htmlname.to_ascii_lower().as_slice()))
} }
); );
($attr:ident) => { ($attr:ident) => {
@ -28,7 +28,8 @@ macro_rules! make_bool_getter(
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.has_attribute($htmlname) // FIXME(pcwalton): Do this at compile time, not runtime.
element.has_attribute(&Atom::from_slice($htmlname))
} }
); );
($attr:ident) => { ($attr:ident) => {
@ -45,7 +46,8 @@ macro_rules! make_uint_getter(
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_uint_attribute($htmlname) // FIXME(pcwalton): Do this at compile time, not runtime.
element.get_uint_attribute(&Atom::from_slice($htmlname))
} }
); );
($attr:ident) => { ($attr:ident) => {
@ -62,10 +64,12 @@ macro_rules! make_url_getter(
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_url_attribute($htmlname) // FIXME(pcwalton): Do this at compile time, not runtime.
element.get_url_attribute(&Atom::from_slice($htmlname))
} }
); );
($attr:ident) => { ($attr:ident) => {
// FIXME(pcwalton): Do this at compile time, not runtime.
make_url_getter!($attr, stringify!($attr).to_ascii_lower().as_slice()) make_url_getter!($attr, stringify!($attr).to_ascii_lower().as_slice())
} }
) )
@ -79,7 +83,7 @@ macro_rules! make_url_or_base_getter(
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
let url = element.get_url_attribute($htmlname); let url = element.get_url_attribute(&Atom::from_slice($htmlname));
match url.as_slice() { match url.as_slice() {
"" => { "" => {
let window = window_from_node(self).root(); let window = window_from_node(self).root();
@ -103,7 +107,8 @@ macro_rules! make_enumerated_getter(
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
let val = element.get_string_attribute($htmlname).into_ascii_lower(); let val = element.get_string_attribute(&Atom::from_slice($htmlname))
.into_ascii_lower();
// https://html.spec.whatwg.org/multipage/forms.html#attr-fs-method // https://html.spec.whatwg.org/multipage/forms.html#attr-fs-method
match val.as_slice() { match val.as_slice() {
$($choices)|+ => val, $($choices)|+ => val,
@ -125,7 +130,8 @@ macro_rules! make_setter(
use dom::element::{Element, AttributeHandlers}; use dom::element::{Element, AttributeHandlers};
use dom::bindings::codegen::InheritTypes::ElementCast; use dom::bindings::codegen::InheritTypes::ElementCast;
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.set_string_attribute($htmlname, value) // FIXME(pcwalton): Do this at compile time, not at runtime.
element.set_string_attribute(&Atom::from_slice($htmlname), value)
} }
); );
) )
@ -137,7 +143,8 @@ macro_rules! make_bool_setter(
use dom::element::{Element, AttributeHandlers}; use dom::element::{Element, AttributeHandlers};
use dom::bindings::codegen::InheritTypes::ElementCast; use dom::bindings::codegen::InheritTypes::ElementCast;
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.set_bool_attribute($htmlname, value) // FIXME(pcwalton): Do this at compile time, not at runtime.
element.set_bool_attribute(&Atom::from_slice($htmlname), value)
} }
); );
) )
@ -149,7 +156,8 @@ macro_rules! make_uint_setter(
use dom::element::{Element, AttributeHandlers}; use dom::element::{Element, AttributeHandlers};
use dom::bindings::codegen::InheritTypes::ElementCast; use dom::bindings::codegen::InheritTypes::ElementCast;
let element: JSRef<Element> = ElementCast::from_ref(self); let element: JSRef<Element> = ElementCast::from_ref(self);
element.set_uint_attribute($htmlname, value) // FIXME(pcwalton): Do this at compile time, not at runtime.
element.set_uint_attribute(&Atom::from_slice($htmlname), value)
} }
); );
) )

View file

@ -919,16 +919,19 @@ pub trait RawLayoutNodeHelpers {
} }
impl RawLayoutNodeHelpers for Node { impl RawLayoutNodeHelpers for Node {
#[inline]
unsafe fn get_hover_state_for_layout(&self) -> bool { unsafe fn get_hover_state_for_layout(&self) -> bool {
(*self.unsafe_get_flags()).contains(InHoverState) (*self.unsafe_get_flags()).contains(InHoverState)
} }
#[inline]
unsafe fn get_disabled_state_for_layout(&self) -> bool { unsafe fn get_disabled_state_for_layout(&self) -> bool {
(*self.unsafe_get_flags()).contains(InDisabledState) (*self.unsafe_get_flags()).contains(InDisabledState)
} }
#[inline]
unsafe fn get_enabled_state_for_layout(&self) -> bool { unsafe fn get_enabled_state_for_layout(&self) -> bool {
(*self.unsafe_get_flags()).contains(InEnabledState) (*self.unsafe_get_flags()).contains(InEnabledState)
} }
#[inline]
fn type_id_for_layout(&self) -> NodeTypeId { fn type_id_for_layout(&self) -> NodeTypeId {
self.type_id self.type_id
} }
@ -1581,6 +1584,7 @@ impl Node {
} }
} }
#[inline]
pub unsafe fn unsafe_get_flags(&self) -> *const NodeFlags { pub unsafe fn unsafe_get_flags(&self) -> *const NodeFlags {
mem::transmute(&self.flags) mem::transmute(&self.flags)
} }
@ -2221,9 +2225,9 @@ impl<'a> style::TNode<'a, JSRef<'a, Element>> for JSRef<'a, Node> {
fn match_attr(self, attr: &style::AttrSelector, test: |&str| -> bool) -> bool { fn match_attr(self, attr: &style::AttrSelector, test: |&str| -> bool) -> bool {
let name = { let name = {
if self.is_html_element_in_html_document() { if self.is_html_element_in_html_document() {
attr.lower_name.as_slice() &attr.lower_name
} else { } else {
attr.name.as_slice() &attr.name
} }
}; };
match attr.namespace { match attr.namespace {
@ -2294,7 +2298,7 @@ impl<'a> DisabledStateHelpers for JSRef<'a, Node> {
fn check_disabled_attribute(self) { fn check_disabled_attribute(self) {
let elem: JSRef<'a, Element> = ElementCast::to_ref(self).unwrap(); let elem: JSRef<'a, Element> = ElementCast::to_ref(self).unwrap();
let has_disabled_attrib = elem.has_attribute("disabled"); let has_disabled_attrib = elem.has_attribute(&atom!("disabled"));
self.set_disabled_state(has_disabled_attrib); self.set_disabled_state(has_disabled_attrib);
self.set_enabled_state(!has_disabled_attrib); self.set_enabled_state(!has_disabled_attrib);
} }

View file

@ -481,7 +481,7 @@ pub fn parse_html(page: &Page,
}; };
let script_element: JSRef<Element> = ElementCast::from_ref(script); let script_element: JSRef<Element> = ElementCast::from_ref(script);
match script_element.get_attribute(ns!(""), "src").root() { match script_element.get_attribute(ns!(""), &atom!("src")).root() {
Some(src) => { Some(src) => {
debug!("found script: {:s}", src.Value()); debug!("found script: {:s}", src.Value());
let mut url_parser = UrlParser::new(); let mut url_parser = UrlParser::new();

View file

@ -35,3 +35,4 @@ git = "https://github.com/servo/string-cache"
[dependencies.string_cache_macros] [dependencies.string_cache_macros]
git = "https://github.com/servo/string-cache" git = "https://github.com/servo/string-cache"

View file

@ -11,6 +11,7 @@
#![feature(phase)] #![feature(phase)]
#[phase(plugin, link)] extern crate log; #[phase(plugin, link)] extern crate log;
#[phase(plugin)] extern crate string_cache_macros;
extern crate debug; extern crate debug;
extern crate collections; extern crate collections;

View file

@ -8,7 +8,6 @@
use selectors::AttrSelector; use selectors::AttrSelector;
use string_cache::{Atom, Namespace}; use string_cache::{Atom, Namespace};
pub trait TNode<'a, E: TElement<'a>> : Clone + Copy { pub trait TNode<'a, E: TElement<'a>> : Clone + Copy {
fn parent_node(self) -> Option<Self>; fn parent_node(self) -> Option<Self>;
fn first_child(self) -> Option<Self>; fn first_child(self) -> Option<Self>;
@ -28,8 +27,8 @@ pub trait TNode<'a, E: TElement<'a>> : Clone + Copy {
} }
pub trait TElement<'a> : Copy { pub trait TElement<'a> : Copy {
fn get_attr(self, namespace: &Namespace, attr: &str) -> Option<&'a str>; fn get_attr(self, namespace: &Namespace, attr: &Atom) -> Option<&'a str>;
fn get_attrs(self, attr: &str) -> Vec<&'a str>; fn get_attrs(self, attr: &Atom) -> Vec<&'a str>;
fn get_link(self) -> Option<&'a str>; fn get_link(self) -> Option<&'a str>;
fn get_local_name(self) -> &'a Atom; fn get_local_name(self) -> &'a Atom;
fn get_namespace(self) -> &'a Namespace; fn get_namespace(self) -> &'a Namespace;
@ -37,6 +36,13 @@ pub trait TElement<'a> : Copy {
fn get_id(self) -> Option<Atom>; fn get_id(self) -> Option<Atom>;
fn get_disabled_state(self) -> bool; fn get_disabled_state(self) -> bool;
fn get_enabled_state(self) -> bool; fn get_enabled_state(self) -> bool;
fn has_class(self, name: &str) -> bool; fn has_class(self, name: &Atom) -> bool;
// Ordinarily I wouldn't use callbacks like this, but the alternative is
// really messy, since there is a `JSRef` and a `RefCell` involved. Maybe
// in the future when we have associated types and/or a more convenient
// JS GC story... --pcwalton
fn each_class(self, callback: |&Atom|); fn each_class(self, callback: |&Atom|);
} }

View file

@ -108,20 +108,14 @@ impl SelectorMap {
None => {} None => {}
} }
match element.get_attr(&ns!(""), "class") { element.each_class(|class| {
Some(ref class_attr) => { SelectorMap::get_matching_rules_from_hash(node,
// FIXME: Store classes pre-split as atoms to make the loop below faster. parent_bf,
for class in class_attr.split(SELECTOR_WHITESPACE) { &self.class_hash,
SelectorMap::get_matching_rules_from_hash(node, class,
parent_bf, matching_rules_list,
&self.class_hash, shareable);
&Atom::from_slice(class), });
matching_rules_list,
shareable);
}
}
None => {}
}
let local_name_hash = if node.is_html_element_in_html_document() { let local_name_hash = if node.is_html_element_in_html_document() {
&self.lower_local_name_hash &self.lower_local_name_hash
@ -699,12 +693,12 @@ fn matches_compound_selector_internal<'a,
/// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in /// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in
/// `main/css/matching.rs`.) /// `main/css/matching.rs`.)
#[inline] #[inline]
pub fn matches_simple_selector<'a, E:TElement<'a>, pub fn matches_simple_selector<'a,E,N>(
N:TNode<'a, E>>( selector: &SimpleSelector,
selector: &SimpleSelector, element: &N,
element: &N, shareable: &mut bool)
shareable: &mut bool) -> bool
-> bool { where E:TElement<'a>, N:TNode<'a,E> {
match *selector { match *selector {
LocalNameSelector(LocalName { ref name, ref lower_name }) => { LocalNameSelector(LocalName { ref name, ref lower_name }) => {
let name = if element.is_html_element_in_html_document() { lower_name } else { name }; let name = if element.is_html_element_in_html_document() { lower_name } else { name };
@ -718,7 +712,6 @@ pub fn matches_simple_selector<'a, E:TElement<'a>,
element.get_namespace() == namespace element.get_namespace() == namespace
} }
// TODO: case-sensitivity depends on the document type and quirks mode // TODO: case-sensitivity depends on the document type and quirks mode
// TODO: cache and intern IDs on elements.
IDSelector(ref id) => { IDSelector(ref id) => {
*shareable = false; *shareable = false;
let element = element.as_element(); let element = element.as_element();
@ -726,10 +719,9 @@ pub fn matches_simple_selector<'a, E:TElement<'a>,
attr == *id attr == *id
}) })
} }
// TODO: cache and intern class names on elements.
ClassSelector(ref class) => { ClassSelector(ref class) => {
let element = element.as_element(); let element = element.as_element();
element.has_class(class.as_slice()) element.has_class(class)
} }
AttrExists(ref attr) => { AttrExists(ref attr) => {
@ -876,6 +868,7 @@ pub fn matches_simple_selector<'a, E:TElement<'a>,
} }
} }
#[inline]
fn url_is_visited(_url: &str) -> bool { fn url_is_visited(_url: &str) -> bool {
// FIXME: implement this. // FIXME: implement this.
// This function will probably need to take a "session" // This function will probably need to take a "session"
@ -884,15 +877,14 @@ fn url_is_visited(_url: &str) -> bool {
} }
#[inline] #[inline]
fn matches_generic_nth_child<'a, fn matches_generic_nth_child<'a,E,N>(
E:TElement<'a>,
N:TNode<'a, E>>(
element: &N, element: &N,
a: i32, a: i32,
b: i32, b: i32,
is_of_type: bool, is_of_type: bool,
is_from_end: bool) is_from_end: bool)
-> bool { -> bool
where E: TElement<'a>, N: TNode<'a,E> {
let mut node = element.clone(); let mut node = element.clone();
// fail if we can't find a parent or if the node is the root element // fail if we can't find a parent or if the node is the root element
// of the document (Cf. Selectors Level 3) // of the document (Cf. Selectors Level 3)

View file

@ -106,8 +106,8 @@ pub struct LocalName {
#[deriving(Eq, PartialEq, Clone, Hash)] #[deriving(Eq, PartialEq, Clone, Hash)]
pub struct AttrSelector { pub struct AttrSelector {
pub name: String, pub name: Atom,
pub lower_name: String, pub lower_name: Atom,
pub namespace: NamespaceConstraint, pub namespace: NamespaceConstraint,
} }
@ -448,8 +448,8 @@ fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &Namespace
Some((_, None)) => fail!("Implementation error, this should not happen."), Some((_, None)) => fail!("Implementation error, this should not happen."),
Some((namespace, Some(local_name))) => AttrSelector { Some((namespace, Some(local_name))) => AttrSelector {
namespace: namespace, namespace: namespace,
lower_name: local_name.as_slice().to_ascii_lower(), lower_name: Atom::from_slice(local_name.as_slice().to_ascii_lower().as_slice()),
name: local_name, name: Atom::from_slice(local_name.as_slice()),
}, },
}; };
skip_whitespace(iter); skip_whitespace(iter);
@ -675,8 +675,8 @@ mod tests {
assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector { assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(AttrExists(AttrSelector { simple_selectors: vec!(AttrExists(AttrSelector {
name: String::from_str("Foo"), name: Atom::from_slice("Foo"),
lower_name: String::from_str("foo"), lower_name: Atom::from_slice("foo"),
namespace: SpecificNamespace(ns!("")), namespace: SpecificNamespace(ns!("")),
})), })),
next: None, next: None,
@ -690,8 +690,8 @@ mod tests {
assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector { assert!(parse_ns("[Foo]", &namespaces) == Ok(vec!(Selector {
compound_selectors: Arc::new(CompoundSelector { compound_selectors: Arc::new(CompoundSelector {
simple_selectors: vec!(AttrExists(AttrSelector { simple_selectors: vec!(AttrExists(AttrSelector {
name: String::from_str("Foo"), name: Atom::from_slice("Foo"),
lower_name: String::from_str("foo"), lower_name: Atom::from_slice("foo"),
namespace: SpecificNamespace(ns!("")), namespace: SpecificNamespace(ns!("")),
})), })),
next: None, next: None,