mirror of
https://github.com/servo/servo.git
synced 2025-06-20 15:18:58 +01:00
Basic Implementation of document.getElementById(), #740
This commit is contained in:
parent
f9be872e61
commit
db3b5c3c4c
4 changed files with 126 additions and 4 deletions
|
@ -25,6 +25,8 @@ use js::jsapi::{JSTRACE_OBJECT, JSTracer, JS_CallTracer};
|
||||||
use js::glue::RUST_OBJECT_TO_JSVAL;
|
use js::glue::RUST_OBJECT_TO_JSVAL;
|
||||||
use servo_util::tree::TreeNodeRef;
|
use servo_util::tree::TreeNodeRef;
|
||||||
|
|
||||||
|
use std::hashmap::HashMap;
|
||||||
|
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::str::eq_slice;
|
use std::str::eq_slice;
|
||||||
|
@ -92,6 +94,9 @@ impl AbstractDocument {
|
||||||
}));
|
}));
|
||||||
self.with_mut_base(|document| {
|
self.with_mut_base(|document| {
|
||||||
document.root = Some(root);
|
document.root = Some(root);
|
||||||
|
// Register elements having "id" attribute to the owner doc.
|
||||||
|
document.register_nodes_with_id(&root);
|
||||||
|
|
||||||
document.content_changed();
|
document.content_changed();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -108,7 +113,8 @@ pub struct Document {
|
||||||
reflector_: Reflector,
|
reflector_: Reflector,
|
||||||
window: Option<@mut Window>,
|
window: Option<@mut Window>,
|
||||||
doctype: DocumentType,
|
doctype: DocumentType,
|
||||||
title: ~str
|
title: ~str,
|
||||||
|
idmap: HashMap<~str, AbstractNode<ScriptView>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Document {
|
impl Document {
|
||||||
|
@ -119,7 +125,8 @@ impl Document {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
window: window,
|
window: window,
|
||||||
doctype: doctype,
|
doctype: doctype,
|
||||||
title: ~""
|
title: ~"",
|
||||||
|
idmap: HashMap::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,8 +272,11 @@ impl Document {
|
||||||
HTMLCollection::new(~[], cx, scope)
|
HTMLCollection::new(~[], cx, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn GetElementById(&self, _id: &DOMString) -> Option<AbstractNode<ScriptView>> {
|
pub fn GetElementById(&self, id: &DOMString) -> Option<AbstractNode<ScriptView>> {
|
||||||
None
|
let key: &~str = &null_str_as_empty(id);
|
||||||
|
// TODO: "in tree order, within the context object's tree"
|
||||||
|
// http://dom.spec.whatwg.org/#dom-document-getelementbyid.
|
||||||
|
self.idmap.find_equiv(key).map(|node| **node)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn CreateElement(&self, abstract_self: AbstractDocument, local_name: &DOMString) -> Fallible<AbstractNode<ScriptView>> {
|
pub fn CreateElement(&self, abstract_self: AbstractDocument, local_name: &DOMString) -> Fallible<AbstractNode<ScriptView>> {
|
||||||
|
@ -513,6 +523,41 @@ impl Document {
|
||||||
window.wait_until_safe_to_modify_dom();
|
window.wait_until_safe_to_modify_dom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn register_nodes_with_id(&mut self, root: &AbstractNode<ScriptView>) {
|
||||||
|
foreach_ided_elements(root, |id: &~str, abstract_node: &AbstractNode<ScriptView>| {
|
||||||
|
// TODO: "in tree order, within the context object's tree"
|
||||||
|
// http://dom.spec.whatwg.org/#dom-document-getelementbyid.
|
||||||
|
self.idmap.find_or_insert(id.clone(), *abstract_node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unregister_nodes_with_id(&mut self, root: &AbstractNode<ScriptView>) {
|
||||||
|
foreach_ided_elements(root, |id: &~str, _| {
|
||||||
|
// TODO: "in tree order, within the context object's tree"
|
||||||
|
// http://dom.spec.whatwg.org/#dom-document-getelementbyid.
|
||||||
|
self.idmap.pop(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn foreach_ided_elements(root: &AbstractNode<ScriptView>,
|
||||||
|
callback: &fn(&~str, &AbstractNode<ScriptView>)) {
|
||||||
|
for node in root.traverse_preorder() {
|
||||||
|
if !node.is_element() {
|
||||||
|
loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
do node.with_imm_element |element| {
|
||||||
|
match element.get_attr("id") {
|
||||||
|
Some(id) => {
|
||||||
|
callback(&id.to_str(), &node);
|
||||||
|
}
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Traceable for Document {
|
impl Traceable for Document {
|
||||||
|
|
|
@ -170,6 +170,9 @@ impl<'self> Element {
|
||||||
null_str_as_empty_ref(raw_value)));
|
null_str_as_empty_ref(raw_value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: update owner document's id hashmap for `document.getElementById()`
|
||||||
|
// if `name` == "id".
|
||||||
|
|
||||||
//XXXjdm We really need something like a vtable so we can call AfterSetAttr.
|
//XXXjdm We really need something like a vtable so we can call AfterSetAttr.
|
||||||
// This hardcoding is awful.
|
// This hardcoding is awful.
|
||||||
match abstract_self.type_id() {
|
match abstract_self.type_id() {
|
||||||
|
|
|
@ -473,6 +473,16 @@ impl Node<ScriptView> {
|
||||||
cur_node = cur_node.unwrap().next_sibling();
|
cur_node = cur_node.unwrap().next_sibling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unregister elements having "id' from the old doc.
|
||||||
|
do old_doc.with_mut_base |old_doc| {
|
||||||
|
old_doc.unregister_nodes_with_id(&abstract_self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register elements having "id" attribute to the owner doc.
|
||||||
|
do doc.with_mut_base |doc| {
|
||||||
|
doc.register_nodes_with_id(&abstract_self);
|
||||||
|
}
|
||||||
|
|
||||||
// Signal the old document that it needs to update its display
|
// Signal the old document that it needs to update its display
|
||||||
if old_doc != doc {
|
if old_doc != doc {
|
||||||
do old_doc.with_base |old_doc| {
|
do old_doc.with_base |old_doc| {
|
||||||
|
@ -779,6 +789,12 @@ impl Node<ScriptView> {
|
||||||
|
|
||||||
self.wait_until_safe_to_modify_dom();
|
self.wait_until_safe_to_modify_dom();
|
||||||
|
|
||||||
|
// Unregister elements having "id' from the owner doc.
|
||||||
|
// This need be called before target nodes are removed from tree.
|
||||||
|
do self.owner_doc.with_mut_base |doc| {
|
||||||
|
doc.unregister_nodes_with_id(&abstract_self);
|
||||||
|
}
|
||||||
|
|
||||||
abstract_self.remove_child(node);
|
abstract_self.remove_child(node);
|
||||||
// Signal the document that it needs to update its display.
|
// Signal the document that it needs to update its display.
|
||||||
do self.owner_doc.with_base |document| {
|
do self.owner_doc.with_base |document| {
|
||||||
|
|
58
src/test/html/content/test_document_getElementById.html
Normal file
58
src/test/html/content/test_document_getElementById.html
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<html>
|
||||||
|
<head id="foo">
|
||||||
|
<title></title>
|
||||||
|
<script src="harness.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="bar"></div>
|
||||||
|
<script>
|
||||||
|
let gBody = document.getElementsByTagName("body")[0];
|
||||||
|
|
||||||
|
// test1: on static page
|
||||||
|
{
|
||||||
|
let foo = document.getElementById("foo");
|
||||||
|
isnot(foo, null, "test-1-0, on static page");
|
||||||
|
is(foo && foo.tagName, "HEAD", "test1-1, on static page");
|
||||||
|
is(foo instanceof HTMLHeadElement, true, "test1-2, on static page");
|
||||||
|
|
||||||
|
let bar = document.getElementById("bar");
|
||||||
|
isnot(bar, null, "test1-3, on static page");
|
||||||
|
is(bar && bar.tagName, "DIV", "test1-4, on static page");
|
||||||
|
is(bar instanceof HTMLDivElement, true, "test1-5, on static page");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test2: scripted element
|
||||||
|
{
|
||||||
|
let TEST_ID = "test";
|
||||||
|
let test = document.createElement("div");
|
||||||
|
test.setAttribute("id", TEST_ID);
|
||||||
|
gBody.appendChild(test);
|
||||||
|
|
||||||
|
// test: appended element
|
||||||
|
let appended = document.getElementById(TEST_ID);
|
||||||
|
isnot(appended, null, "test2-0, appended element");
|
||||||
|
is(appended && appended.tagName, "DIV", "test2-1, appended element");
|
||||||
|
is(appended instanceof HTMLDivElement, true, "test2-2, appended element");
|
||||||
|
|
||||||
|
// test: removed element
|
||||||
|
gBody.removeChild(test);
|
||||||
|
let removed = document.getElementById(TEST_ID);
|
||||||
|
// `document.getElementById()` returns `null` if there is none.
|
||||||
|
// http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-getElBId
|
||||||
|
// http://dom.spec.whatwg.org/#dom-document-getelementbyid (2013-09-20)
|
||||||
|
is(removed, null, "test2-3, removed element");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// test3: update `id` attribute
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// test4: "in tree order, within the context object's tree"
|
||||||
|
// http://dom.spec.whatwg.org/#dom-document-getelementbyid.
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// test5: innerHTML
|
||||||
|
finish();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue