servo: Merge #3061 - Implement DOMTokenList.contains; r=Ms2ger (from brunoabinader:domtokenlist)

Source-Repo: https://github.com/servo/servo
Source-Revision: ce452801109d0d828b36aa08ed9242b921dc4bc3
This commit is contained in:
Bruno de Oliveira Abinader 2014-08-25 22:23:32 +02:00
Родитель adc3335def
Коммит 977edc8d5c
6 изменённых файлов: 118 добавлений и 40 удалений

Просмотреть файл

@ -16,9 +16,10 @@ use dom::virtualmethods::vtable_for;
use servo_util::atom::Atom;
use servo_util::namespace;
use servo_util::namespace::Namespace;
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
use servo_util::str::{DOMString, split_html_space_chars};
use std::cell::{Ref, RefCell};
use std::mem;
use std::slice::Items;
pub enum AttrSettingType {
FirstSetAttr,
@ -28,22 +29,16 @@ pub enum AttrSettingType {
#[deriving(PartialEq, Clone, Encodable)]
pub enum AttrValue {
StringAttrValue(DOMString),
TokenListAttrValue(DOMString, Vec<(uint, uint)>),
TokenListAttrValue(DOMString, Vec<Atom>),
UIntAttrValue(DOMString, u32),
AtomAttrValue(Atom),
}
impl AttrValue {
pub fn from_tokenlist(list: DOMString) -> AttrValue {
let mut indexes = vec![];
let mut last_index: uint = 0;
for (index, ch) in list.as_slice().char_indices() {
if HTML_SPACE_CHARACTERS.iter().any(|&space| space == ch) {
indexes.push((last_index, index));
last_index = index + 1;
}
}
return TokenListAttrValue(list, indexes);
pub fn from_tokenlist(tokens: DOMString) -> AttrValue {
let atoms = split_html_space_chars(tokens.as_slice())
.map(|token| Atom::from_slice(token)).collect();
TokenListAttrValue(tokens, atoms)
}
pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
@ -56,6 +51,12 @@ impl AttrValue {
AtomAttrValue(value)
}
pub fn tokens<'a>(&'a self) -> Option<Items<'a, Atom>> {
match *self {
TokenListAttrValue(_, ref tokens) => Some(tokens.iter()),
_ => None
}
}
}
impl Str for AttrValue {

Просмотреть файл

@ -2,17 +2,19 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::attr::{Attr, TokenListAttrValue};
use dom::attr::Attr;
use dom::bindings::codegen::Bindings::DOMTokenListBinding;
use dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenListMethods;
use dom::bindings::error::{Fallible, InvalidCharacter, Syntax};
use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable};
use dom::bindings::utils::{Reflector, Reflectable, reflect_dom_object};
use dom::element::{Element, AttributeHandlers};
use dom::node::window_from_node;
use servo_util::atom::Atom;
use servo_util::namespace::Null;
use servo_util::str::DOMString;
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
#[deriving(Encodable)]
pub struct DOMTokenList {
@ -47,6 +49,7 @@ impl Reflectable for DOMTokenList {
trait PrivateDOMTokenListHelpers {
fn attribute(&self) -> Option<Temporary<Attr>>;
fn check_token_exceptions<'a>(&self, token: &'a str) -> Fallible<&'a str>;
}
impl<'a> PrivateDOMTokenListHelpers for JSRef<'a, DOMTokenList> {
@ -54,37 +57,30 @@ impl<'a> PrivateDOMTokenListHelpers for JSRef<'a, DOMTokenList> {
let element = self.element.root();
element.deref().get_attribute(Null, self.local_name)
}
fn check_token_exceptions<'a>(&self, token: &'a str) -> Fallible<&'a str> {
match token {
"" => Err(Syntax),
token if token.find(HTML_SPACE_CHARACTERS).is_some() => Err(InvalidCharacter),
token => Ok(token)
}
}
}
// http://dom.spec.whatwg.org/#domtokenlist
impl<'a> DOMTokenListMethods for JSRef<'a, DOMTokenList> {
// http://dom.spec.whatwg.org/#dom-domtokenlist-length
fn Length(&self) -> u32 {
let attribute = self.attribute().root();
match attribute {
Some(attribute) => {
match *attribute.deref().value() {
TokenListAttrValue(_, ref indexes) => indexes.len() as u32,
_ => fail!("Expected a TokenListAttrValue"),
}
}
None => 0,
}
self.attribute().root().map(|attr| {
attr.value().tokens().map(|tokens| tokens.len()).unwrap_or(0)
}).unwrap_or(0) as u32
}
// http://dom.spec.whatwg.org/#dom-domtokenlist-item
fn Item(&self, index: u32) -> Option<DOMString> {
let attribute = self.attribute().root();
attribute.and_then(|attribute| {
match *attribute.deref().value() {
TokenListAttrValue(ref value, ref indexes) => {
indexes.as_slice().get(index as uint).map(|&(start, end)| {
value.as_slice().slice(start, end).to_string()
})
},
_ => fail!("Expected a TokenListAttrValue"),
}
})
self.attribute().root().and_then(|attr| attr.value().tokens().and_then(|mut tokens| {
tokens.idx(index as uint).map(|token| token.as_slice().to_string())
}))
}
fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<DOMString> {
@ -92,4 +88,14 @@ impl<'a> DOMTokenListMethods for JSRef<'a, DOMTokenList> {
*found = item.is_some();
item
}
// http://dom.spec.whatwg.org/#dom-domtokenlist-contains
fn Contains(&self, token: DOMString) -> Fallible<bool> {
self.check_token_exceptions(token.as_slice()).map(|slice| {
self.attribute().root().and_then(|attr| attr.value().tokens().map(|mut tokens| {
let atom = Atom::from_slice(slice);
tokens.any(|token| *token == atom)
})).unwrap_or(false)
})
}
}

Просмотреть файл

@ -35,7 +35,7 @@ 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, split_html_space_chars};
use servo_util::str::{DOMString, null_str_as_empty_ref};
use std::ascii::StrAsciiExt;
use std::cell::{Cell, RefCell};
@ -377,9 +377,11 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
}
fn has_class(&self, name: &str) -> bool {
let class_names = self.get_string_attribute("class");
let mut classes = split_html_space_chars(class_names.as_slice());
classes.any(|class| name == class)
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) {

Просмотреть файл

@ -7,7 +7,10 @@
interface DOMTokenList {
readonly attribute unsigned long length;
getter DOMString? item(unsigned long index);
//boolean contains(DOMString token);
[Throws]
boolean contains(DOMString token);
//void add(DOMString... tokens);
//void remove(DOMString... tokens);
//boolean toggle(DOMString token, optional boolean force);

Просмотреть файл

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<script src="harness.js"></script>
<script>
var div = document.createElement("div");
var classList = div.classList;
is(classList.length, 0);
is(classList.item(0), null);
should_throw(function () {
classList.contains("");
});
should_throw(function () {
classList.contains(" ");
});
var list = ["foo", " foo", "foo ", " foo ", " foo "];
for(var i = 0; i < list.length; i++) {
div.className = list[i];
is(div.className, list[i]);
is(classList.length, 1);
is(classList.item(0), "foo");
is(classList.item(1), null);
is(classList.contains("foo"), true);
is(classList.contains("bar"), false);
}
list = ["foo bar", " foo bar", " foo bar ", " foo bar "];
for(var i = 0; i < list.length; i++) {
div.className = list[i];
is(div.className, list[i]);
is(classList.length, 2);
is(classList.item(0), "foo");
is(classList.item(1), "bar");
is(classList.item(2), null);
is(classList.contains("foo"), true);
is(classList.contains("bar"), true);
is(classList.contains("baz"), false);
}
finish();
</script>
</head>
</html>

Просмотреть файл

@ -0,0 +1,21 @@
<!-- Remove this and update WPT metadata once DOMTokenList.toggle (#3138) is implemented. -->
<!DOCTYPE html>
<html>
<head>
<script src="harness.js"></script>
<script>
let div = document.createElement("div");
div.className = "foo bar";
let classList = div.classList;
div.className = "";
is(classList.item(0), null, "classList.item(0) must return null when all classes have been removed");
is(classList[0], undefined, "classList[0] must be undefined when all classes have been removed");
finish();
</script>
</head>
<body>
</body>
</html>