Implement SetNamedItem, SetNamedItemNS, SetAttributeNode and SetAttributeNodeNS

This commit is contained in:
Mathieu Hordesseaux 2015-12-23 17:21:45 +01:00
parent bc44ae679f
commit 322b120f8a
11 changed files with 105 additions and 67 deletions

View file

@ -193,16 +193,12 @@ impl Attr {
pub fn set_owner(&self, owner: Option<&Element>) { pub fn set_owner(&self, owner: Option<&Element>) {
let ns = &self.identifier.namespace; let ns = &self.identifier.namespace;
match (self.owner().r(), owner) { match (self.owner().r(), owner) {
(None, Some(new)) => {
// Already in the list of attributes of new owner.
assert!(new.get_attribute(&ns, &self.identifier.local_name) ==
Some(Root::from_ref(self)))
}
(Some(old), None) => { (Some(old), None) => {
// Already gone from the list of attributes of old owner. // Already gone from the list of attributes of old owner.
assert!(old.get_attribute(&ns, &self.identifier.local_name).is_none()) assert!(old.get_attribute(&ns, &self.identifier.local_name).r() != Some(self))
} }
(old, new) => assert!(old == new), (Some(old), Some(new)) => assert!(old == new),
_ => {},
} }
self.owner.set(owner); self.owner.set(owner);
} }

View file

@ -863,9 +863,7 @@ impl Element {
name: Atom, name: Atom,
namespace: Namespace, namespace: Namespace,
prefix: Option<Atom>) { prefix: Option<Atom>) {
self.will_mutate_attr();
let window = window_from_node(self); let window = window_from_node(self);
let in_empty_ns = namespace == ns!();
let attr = Attr::new(&window, let attr = Attr::new(&window,
local_name, local_name,
value, value,
@ -873,9 +871,15 @@ impl Element {
namespace, namespace,
prefix, prefix,
Some(self)); Some(self));
self.attrs.borrow_mut().push(JS::from_rooted(&attr)); self.push_attribute(&attr);
if in_empty_ns { }
vtable_for(self.upcast()).attribute_mutated(&attr, AttributeMutation::Set(None));
pub fn push_attribute(&self, attr: &Attr) {
assert!(attr.GetOwnerElement().r() == Some(self));
self.will_mutate_attr();
self.attrs.borrow_mut().push(JS::from_ref(attr));
if attr.namespace() == &ns!() {
vtable_for(self.upcast()).attribute_mutated(attr, AttributeMutation::Set(None));
} }
} }
@ -1269,6 +1273,56 @@ impl ElementMethods for Element {
Ok(()) Ok(())
} }
// https://dom.spec.whatwg.org/#dom-element-setattributenode
fn SetAttributeNode(&self, attr: &Attr) -> Fallible<Option<Root<Attr>>> {
// Step 1.
if let Some(owner) = attr.GetOwnerElement() {
if &*owner != self {
return Err(Error::InUseAttribute);
}
}
// Step 2.
let position = self.attrs.borrow().iter().position(|old_attr| {
attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
});
if let Some(position) = position {
let old_attr = Root::from_ref(&*self.attrs.borrow()[position]);
// Step 3.
if old_attr.r() == attr {
return Ok(Some(Root::from_ref(attr)));
}
// Step 4.
self.will_mutate_attr();
attr.set_owner(Some(self));
self.attrs.borrow_mut()[position] = JS::from_ref(attr);
old_attr.set_owner(None);
if attr.namespace() == &ns!() {
vtable_for(self.upcast()).attribute_mutated(
&attr, AttributeMutation::Set(Some(&old_attr.value())));
}
// Step 6.
Ok(Some(old_attr))
} else {
// Step 5.
attr.set_owner(Some(self));
self.push_attribute(attr);
// Step 6.
Ok(None)
}
}
// https://dom.spec.whatwg.org/#dom-element-setattributenodens
fn SetAttributeNodeNS(&self, attr: &Attr) -> Fallible<Option<Root<Attr>>> {
self.SetAttributeNode(attr)
}
// https://dom.spec.whatwg.org/#dom-element-removeattribute // https://dom.spec.whatwg.org/#dom-element-removeattribute
fn RemoveAttribute(&self, name: DOMString) { fn RemoveAttribute(&self, name: DOMString) {
let name = self.parsed_name(name); let name = self.parsed_name(name);

View file

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::attr::Attr; use dom::attr::Attr;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::NamedNodeMapBinding; use dom::bindings::codegen::Bindings::NamedNodeMapBinding;
use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods; use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
use dom::bindings::error::{Error, Fallible}; use dom::bindings::error::{Error, Fallible};
@ -58,6 +59,16 @@ impl NamedNodeMapMethods for NamedNodeMap {
self.owner.get_attribute(&ns, &Atom::from(&*local_name)) self.owner.get_attribute(&ns, &Atom::from(&*local_name))
} }
// https://dom.spec.whatwg.org/#dom-namednodemap-setnameditem
fn SetNamedItem(&self, attr: &Attr) -> Fallible<Option<Root<Attr>>> {
self.owner.SetAttributeNode(attr)
}
// https://dom.spec.whatwg.org/#dom-namednodemap-setnameditemns
fn SetNamedItemNS(&self, attr: &Attr) -> Fallible<Option<Root<Attr>>> {
self.SetNamedItem(attr)
}
// https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem // https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem
fn RemoveNamedItem(&self, name: DOMString) -> Fallible<Root<Attr>> { fn RemoveNamedItem(&self, name: DOMString) -> Fallible<Root<Attr>> {
let name = self.owner.parsed_name(name); let name = self.owner.parsed_name(name);

View file

@ -50,6 +50,11 @@ interface Element : Node {
boolean hasAttribute(DOMString name); boolean hasAttribute(DOMString name);
boolean hasAttributeNS(DOMString? namespace, DOMString localName); boolean hasAttributeNS(DOMString? namespace, DOMString localName);
[Throws]
Attr? setAttributeNode(Attr attr);
[Throws]
Attr? setAttributeNodeNS(Attr attr);
[Pure, Throws] [Pure, Throws]
Element? closest(DOMString selectors); Element? closest(DOMString selectors);

View file

@ -13,10 +13,10 @@ interface NamedNodeMap {
getter Attr? getNamedItem(DOMString name); getter Attr? getNamedItem(DOMString name);
[Pure] [Pure]
Attr? getNamedItemNS(DOMString? namespace, DOMString localName); Attr? getNamedItemNS(DOMString? namespace, DOMString localName);
//[Throws] [Throws]
//Attr? setNamedItem(Attr attr); Attr? setNamedItem(Attr attr);
//[Throws] [Throws]
//Attr? setNamedItemNS(Attr attr); Attr? setNamedItemNS(Attr attr);
[Throws] [Throws]
Attr removeNamedItem(DOMString name); Attr removeNamedItem(DOMString name);
[Throws] [Throws]

View file

@ -108,12 +108,6 @@
[Element interface: operation hasAttributes()] [Element interface: operation hasAttributes()]
expected: FAIL expected: FAIL
[Element interface: operation setAttributeNode(Attr)]
expected: FAIL
[Element interface: operation setAttributeNodeNS(Attr)]
expected: FAIL
[Element interface: operation removeAttributeNode(Attr)] [Element interface: operation removeAttributeNode(Attr)]
expected: FAIL expected: FAIL
@ -126,18 +120,6 @@
[Element interface: element must inherit property "hasAttributes" with the proper type (7)] [Element interface: element must inherit property "hasAttributes" with the proper type (7)]
expected: FAIL expected: FAIL
[Element interface: element must inherit property "setAttributeNode" with the proper type (19)]
expected: FAIL
[Element interface: calling setAttributeNode(Attr) on element with too few arguments must throw TypeError]
expected: FAIL
[Element interface: element must inherit property "setAttributeNodeNS" with the proper type (20)]
expected: FAIL
[Element interface: calling setAttributeNodeNS(Attr) on element with too few arguments must throw TypeError]
expected: FAIL
[Element interface: element must inherit property "removeAttributeNode" with the proper type (21)] [Element interface: element must inherit property "removeAttributeNode" with the proper type (21)]
expected: FAIL expected: FAIL
@ -156,12 +138,6 @@
[Element interface: calling queryAll(DOMString) on element with too few arguments must throw TypeError] [Element interface: calling queryAll(DOMString) on element with too few arguments must throw TypeError]
expected: FAIL expected: FAIL
[NamedNodeMap interface: operation setNamedItem(Attr)]
expected: FAIL
[NamedNodeMap interface: operation setNamedItemNS(Attr)]
expected: FAIL
[Range interface: stringifier] [Range interface: stringifier]
expected: FAIL expected: FAIL

View file

@ -1,17 +1,8 @@
[attributes.html] [attributes.html]
type: testharness type: testharness
[Basic functionality of setAttributeNode]
expected: FAIL
[Basic functionality of removeAttributeNode] [Basic functionality of removeAttributeNode]
expected: FAIL expected: FAIL
[setAttributeNode on bound attribute should throw InUseAttributeError]
expected: FAIL
[Basic functionality of setAttributeNodeNS]
expected: FAIL
[getAttributeNames tests] [getAttributeNames tests]
expected: FAIL expected: FAIL
@ -27,12 +18,6 @@
[Own property correctness with two namespaced attributes with the same name-with-prefix] [Own property correctness with two namespaced attributes with the same name-with-prefix]
expected: FAIL expected: FAIL
[setAttributeNode, if it fires mutation events, should fire one with the new node when resetting an existing attribute (outer shell)]
expected: FAIL
[setAttributeNode called with an Attr that has the same name as an existing one should not change attribute order]
expected: FAIL
[Own property names should only include all-lowercase qualified names for an HTML element in an HTML document] [Own property names should only include all-lowercase qualified names for an HTML element in an HTML document]
expected: FAIL expected: FAIL

View file

@ -2013,18 +2013,6 @@
[Element interface: document.createElement("noscript") must inherit property "hasAttributes" with the proper type (7)] [Element interface: document.createElement("noscript") must inherit property "hasAttributes" with the proper type (7)]
expected: FAIL expected: FAIL
[Element interface: document.createElement("noscript") must inherit property "setAttributeNode" with the proper type (19)]
expected: FAIL
[Element interface: calling setAttributeNode(Attr) on document.createElement("noscript") with too few arguments must throw TypeError]
expected: FAIL
[Element interface: document.createElement("noscript") must inherit property "setAttributeNodeNS" with the proper type (20)]
expected: FAIL
[Element interface: calling setAttributeNodeNS(Attr) on document.createElement("noscript") with too few arguments must throw TypeError]
expected: FAIL
[Element interface: document.createElement("noscript") must inherit property "removeAttributeNode" with the proper type (21)] [Element interface: document.createElement("noscript") must inherit property "removeAttributeNode" with the proper type (21)]
expected: FAIL expected: FAIL
@ -9101,3 +9089,4 @@
[WebSocket interface: new WebSocket("ws://foo") must inherit property "extensions" with the proper type (10)] [WebSocket interface: new WebSocket("ws://foo") must inherit property "extensions" with the proper type (10)]
expected: FAIL expected: FAIL

View file

@ -12998,3 +12998,4 @@
[dialog.itemId: IDL set to object "test-valueOf" followed by IDL get] [dialog.itemId: IDL set to object "test-valueOf" followed by IDL get]
expected: FAIL expected: FAIL

View file

@ -59,3 +59,4 @@
[Interfaces for RTC] [Interfaces for RTC]
expected: FAIL expected: FAIL

View file

@ -446,6 +446,26 @@ test(function() {
assert_equals(el2.getAttributeNS("x", "foo"), "bar"); assert_equals(el2.getAttributeNS("x", "foo"), "bar");
}, "Basic functionality of setAttributeNodeNS") }, "Basic functionality of setAttributeNodeNS")
test(function() {
var el = document.createElement("div");
var other = document.createElement("div");
attr = document.createAttribute("foo");
assert_equals(el.setAttributeNode(attr), null);
assert_equals(attr.ownerElement, el);
assert_throws("INUSE_ATTRIBUTE_ERR",
function() { other.setAttributeNode(attr) },
"Attribute already associated with el")
}, "If attrs element is neither null nor element, throw an InUseAttributeError.");
test(function() {
var el = document.createElement("div");
attr = document.createAttribute("foo");
assert_equals(el.setAttributeNode(attr), null);
el.setAttribute("bar", "qux");
assert_equals(el.setAttributeNode(attr), attr);
assert_equals(el.attributes[0], attr);
}, "Replacing an attr by itself");
test(function() { test(function() {
var el = document.createElement("div") var el = document.createElement("div")
el.setAttribute("foo", "bar") el.setAttribute("foo", "bar")