diff options
author | Tetsuharu OHZEKI <saneyuki.snyk@gmail.com> | 2013-09-16 23:00:18 +0900 |
---|---|---|
committer | Tetsuharu OHZEKI <saneyuki.snyk@gmail.com> | 2013-10-15 00:21:13 +0900 |
commit | db3b5c3c4cc10225d221b6579b82a13048adb35f (patch) | |
tree | 02bcceddd3e275f10969fb938111983f7684cd70 /src | |
parent | f9be872e614cc2ecf0487eb8878fedfd44525c53 (diff) | |
download | servo-db3b5c3c4cc10225d221b6579b82a13048adb35f.tar.gz servo-db3b5c3c4cc10225d221b6579b82a13048adb35f.zip |
Basic Implementation of document.getElementById(), #740
Diffstat (limited to 'src')
-rw-r--r-- | src/components/script/dom/document.rs | 53 | ||||
-rw-r--r-- | src/components/script/dom/element.rs | 3 | ||||
-rw-r--r-- | src/components/script/dom/node.rs | 16 | ||||
-rw-r--r-- | src/test/html/content/test_document_getElementById.html | 58 |
4 files changed, 126 insertions, 4 deletions
diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index a8c417b986b..99c20d280c8 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -25,6 +25,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; @@ -92,6 +94,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(); }); } @@ -108,7 +113,8 @@ pub struct Document { reflector_: Reflector, window: Option<@mut Window>, doctype: DocumentType, - title: ~str + title: ~str, + idmap: HashMap<~str, AbstractNode<ScriptView>> } impl Document { @@ -119,7 +125,8 @@ impl Document { reflector_: Reflector::new(), window: window, doctype: doctype, - title: ~"" + title: ~"", + idmap: HashMap::new() } } @@ -265,8 +272,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>> { @@ -513,6 +523,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 { diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 51d7c609c98..4c4238ca5d6 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -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() { diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 293da68d0e4..b68f8ffff12 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -473,6 +473,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| { @@ -779,6 +789,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| { diff --git a/src/test/html/content/test_document_getElementById.html b/src/test/html/content/test_document_getElementById.html new file mode 100644 index 00000000000..20140274477 --- /dev/null +++ b/src/test/html/content/test_document_getElementById.html @@ -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> |