Skip to content
Permalink
Browse files

auto merge of #983 : saneyuki/servo/getid, r=jdm

This has not implemented these features yet:
* Update hashmap which is used for `getElementById()` when updating element attributes.
* Keep node order in tree.

However, this has the quality of that we can use this method for testcases.
  • Loading branch information...
bors-servo
bors-servo committed Oct 14, 2013
2 parents 5d00b52 + db3b5c3 commit 90baad3fffcf9b4fc1e1eb88979b7e60291e2eac
@@ -26,6 +26,8 @@ use js::jsapi::{JSTRACE_OBJECT, JSTracer, JS_CallTracer};
use js::glue::RUST_OBJECT_TO_JSVAL;
use servo_util::tree::TreeNodeRef;

use std::hashmap::HashMap;

use std::cast;
use std::ptr;
use std::str::eq_slice;
@@ -93,6 +95,9 @@ impl AbstractDocument {
}));
self.with_mut_base(|document| {
document.root = Some(root);
// Register elements having "id" attribute to the owner doc.
document.register_nodes_with_id(&root);

document.content_changed();
});
}
@@ -109,7 +114,8 @@ pub struct Document {
reflector_: Reflector,
window: Option<@mut Window>,
doctype: DocumentType,
title: ~str
title: ~str,
idmap: HashMap<~str, AbstractNode<ScriptView>>
}

impl Document {
@@ -120,7 +126,8 @@ impl Document {
reflector_: Reflector::new(),
window: window,
doctype: doctype,
title: ~""
title: ~"",
idmap: HashMap::new()
}
}

@@ -264,8 +271,11 @@ impl Document {
HTMLCollection::new(~[], cx, scope)
}

pub fn GetElementById(&self, _id: &DOMString) -> Option<AbstractNode<ScriptView>> {
None
pub fn GetElementById(&self, id: &DOMString) -> Option<AbstractNode<ScriptView>> {
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>> {
@@ -518,6 +528,41 @@ impl Document {
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 {
@@ -170,6 +170,9 @@ impl<'self> Element {
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.
// This hardcoding is awful.
match abstract_self.type_id() {
@@ -461,7 +461,7 @@ impl Node<ScriptView> {
node
}

pub fn add_to_doc(&mut self, doc: AbstractDocument) {
pub fn add_to_doc(&mut self, abstract_self: AbstractNode<ScriptView>, doc: AbstractDocument) {
let old_doc = self.owner_doc;
self.owner_doc = doc;
let mut cur_node = self.first_child;
@@ -474,6 +474,16 @@ impl Node<ScriptView> {
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
if old_doc != doc {
do old_doc.with_base |old_doc| {
@@ -756,7 +766,7 @@ impl Node<ScriptView> {
node.parent_node().map(|parent| parent.remove_child(node));
abstract_self.add_child(node);
do node.with_mut_base |node| {
node.add_to_doc(self.owner_doc);
node.add_to_doc(abstract_self, self.owner_doc);
}
Ok(node)
}
@@ -782,6 +792,12 @@ impl Node<ScriptView> {

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);
// Signal the document that it needs to update its display.
do self.owner_doc.with_base |document| {
@@ -13,6 +13,12 @@ function is(a, b, c) {
f(a + " == " + b, m);
}

function isnot(a, b, c) {
let f = (a != b) ? _pass : _fail;
let m = !c ? "" : c;
f(a + " != " + b, m);
}

var _test_complete = false;
var _test_timeout = 10000; //10 seconds
function finish() {
@@ -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>

0 comments on commit 90baad3

Please sign in to comment.
You can’t perform that action at this time.