Make sure getElementById always returns the first element with the given ID in tree order.(fixes #1822)

This commit is contained in:
lpy 2014-03-14 22:32:15 +08:00
parent 86c83f7bfc
commit 50aea70f98
3 changed files with 77 additions and 8 deletions

View file

@ -7,6 +7,7 @@ use dom::bindings::codegen::InheritTypes::{DocumentBase, NodeCast, DocumentCast}
use dom::bindings::codegen::InheritTypes::{HTMLHeadElementCast, TextCast, ElementCast};
use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, HTMLHtmlElementCast};
use dom::bindings::codegen::DocumentBinding;
use dom::bindings::codegen::NodeBinding::NodeConstants::{DOCUMENT_POSITION_CONTAINS, DOCUMENT_POSITION_PRECEDING};
use dom::bindings::js::JS;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::error::{ErrorResult, Fallible, NotSupported, InvalidCharacter, HierarchyRequest};
@ -58,7 +59,7 @@ pub struct Document {
node: Node,
reflector_: Reflector,
window: JS<Window>,
idmap: HashMap<DOMString, JS<Element>>,
idmap: HashMap<DOMString, ~[JS<Element>]>,
implementation: Option<JS<DOMImplementation>>,
content_type: DOMString,
encoding_name: DOMString,
@ -248,7 +249,7 @@ impl Document {
// http://dom.spec.whatwg.org/#dom-document-getelementbyid.
match self.idmap.find_equiv(&id) {
None => None,
Some(node) => Some(node.clone()),
Some(ref elements) => Some(elements[0].clone()),
}
}
@ -600,8 +601,22 @@ impl Document {
/// Remove any existing association between the provided id and any elements in this document.
pub fn unregister_named_element(&mut self,
abstract_self: &JS<Element>,
id: DOMString) {
self.idmap.remove(&id);
let mut is_empty = false;
match self.idmap.find_mut(&id) {
None => {},
Some(elements) => {
let position = elements.iter()
.position(|element| element == abstract_self)
.expect("This element should be in registered.");
elements.remove(position);
is_empty = elements.is_empty();
}
}
if is_empty {
self.idmap.remove(&id);
}
}
/// Associate an element present in this document with the provided id.
@ -618,12 +633,26 @@ impl Document {
// FIXME https://github.com/mozilla/rust/issues/13195
// Use mangle() when it exists again.
match self.idmap.find_mut(&id) {
Some(v) => {
*v = element.clone();
Some(elements) => {
let new_node = NodeCast::from(element);
let mut head : uint = 0u;
let mut tail : uint = elements.len();
while head < tail {
let middle = ((head + tail) / 2) as int;
let elem = &elements[middle];
let js_node = NodeCast::from(elem);
let position = elem.get().node.CompareDocumentPosition(&js_node, &new_node);
if position == DOCUMENT_POSITION_PRECEDING || position == DOCUMENT_POSITION_PRECEDING + DOCUMENT_POSITION_CONTAINS {
tail = middle as uint;
} else {
head = middle as uint + 1u;
}
}
elements.insert(tail, element.clone());
return;
},
None => (),
}
self.idmap.insert(id, element.clone());
self.idmap.insert(id, ~[element.clone()]);
}
}

View file

@ -352,7 +352,7 @@ impl AttributeHandlers for JS<Element> {
// "borrowed value does not live long enough"
let mut doc = node.get().owner_doc().clone();
let doc = doc.get_mut();
doc.unregister_named_element(old_value);
doc.unregister_named_element(self, old_value);
}
_ => ()
}
@ -640,7 +640,7 @@ impl IElement for JS<Element> {
match self.get_attribute(Null, "id") {
Some(attr) => {
let mut doc = document_from_node(self);
doc.get_mut().unregister_named_element(attr.get().Value());
doc.get_mut().unregister_named_element(self, attr.get().Value());
}
_ => ()
}