aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/attr.rs41
-rw-r--r--components/script/dom/document.rs1
-rw-r--r--components/script/dom/element.rs252
-rw-r--r--components/script/dom/htmlbaseelement.rs24
-rw-r--r--components/script/dom/htmlbodyelement.rs86
-rw-r--r--components/script/dom/htmlbuttonelement.rs45
-rw-r--r--components/script/dom/htmlcanvaselement.rs51
-rw-r--r--components/script/dom/htmlelement.rs44
-rw-r--r--components/script/dom/htmlfieldsetelement.rs111
-rw-r--r--components/script/dom/htmlfontelement.rs30
-rw-r--r--components/script/dom/htmliframeelement.rs45
-rw-r--r--components/script/dom/htmlimageelement.rs34
-rw-r--r--components/script/dom/htmlinputelement.rs178
-rw-r--r--components/script/dom/htmllinkelement.rs35
-rw-r--r--components/script/dom/htmlobjectelement.rs17
-rw-r--r--components/script/dom/htmloptgroupelement.rs58
-rw-r--r--components/script/dom/htmloptionelement.rs42
-rw-r--r--components/script/dom/htmlscriptelement.rs20
-rw-r--r--components/script/dom/htmlselectelement.rs45
-rw-r--r--components/script/dom/htmltablecellelement.rs50
-rw-r--r--components/script/dom/htmltableelement.rs55
-rw-r--r--components/script/dom/htmltablerowelement.rs30
-rw-r--r--components/script/dom/htmltablesectionelement.rs30
-rw-r--r--components/script/dom/htmltextareaelement.rs66
-rw-r--r--components/script/dom/virtualmethods.rs29
25 files changed, 559 insertions, 860 deletions
diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs
index e1baff2bffd..134d7156475 100644
--- a/components/script/dom/attr.rs
+++ b/components/script/dom/attr.rs
@@ -9,7 +9,7 @@ use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutNullableHeap};
use dom::bindings::js::{Root, RootedReference, LayoutJS};
use dom::bindings::utils::{Reflector, reflect_dom_object};
-use dom::element::Element;
+use dom::element::{AttributeMutation, Element};
use dom::virtualmethods::vtable_for;
use dom::window::Window;
@@ -23,12 +23,6 @@ use std::cell::Ref;
use std::mem;
use std::ops::Deref;
-#[derive(HeapSizeOf)]
-pub enum AttrSettingType {
- FirstSetAttr,
- ReplacedAttr,
-}
-
#[derive(JSTraceable, PartialEq, Clone, HeapSizeOf)]
pub enum AttrValue {
String(DOMString),
@@ -94,6 +88,17 @@ impl AttrValue {
_ => None
}
}
+
+ /// Return the AttrValue as its integer representation, if any.
+ /// This corresponds to attribute values returned as `AttrValue::UInt(_)`
+ /// by `VirtualMethods::parse_plain_attribute()`.
+ pub fn uint(&self) -> Option<u32> {
+ if let AttrValue::UInt(_, value) = *self {
+ Some(value)
+ } else {
+ None
+ }
+ }
}
impl Deref for AttrValue {
@@ -179,7 +184,7 @@ impl AttrMethods for Attr {
None => *self.value.borrow_mut() = AttrValue::String(value),
Some(owner) => {
let value = owner.r().parse_attribute(&self.namespace, self.local_name(), value);
- self.set_value(AttrSettingType::ReplacedAttr, value, owner.r());
+ self.set_value(value, owner.r());
}
}
}
@@ -236,22 +241,12 @@ impl AttrMethods for Attr {
impl Attr {
- pub fn set_value(&self, set_type: AttrSettingType, value: AttrValue, owner: &Element) {
+ pub fn set_value(&self, mut value: AttrValue, owner: &Element) {
assert!(Some(owner) == self.owner().r());
-
- let node = NodeCast::from_ref(owner);
- let namespace_is_null = self.namespace == ns!("");
-
- match set_type {
- AttrSettingType::ReplacedAttr if namespace_is_null =>
- vtable_for(&node).before_remove_attr(self),
- _ => ()
- }
-
- *self.value.borrow_mut() = value;
-
- if namespace_is_null {
- vtable_for(&node).after_set_attr(self)
+ mem::swap(&mut *self.value.borrow_mut(), &mut value);
+ if self.namespace == ns!("") {
+ vtable_for(NodeCast::from_ref(owner)).attribute_mutated(
+ self, AttributeMutation::Set(Some(&value)));
}
}
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 23df1732104..6cff3f00ece 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -297,6 +297,7 @@ impl Document {
}
/// Refresh the cached first base element in the DOM.
+ /// https://github.com/w3c/web-platform-tests/issues/2122
pub fn refresh_base_element(&self) {
let base = NodeCast::from_ref(self)
.traverse_preorder()
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index e97b59accf8..44be8b2e74b 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -6,7 +6,7 @@
use dom::activation::Activatable;
use dom::attr::AttrValue;
-use dom::attr::{Attr, AttrSettingType, AttrHelpersForLayout};
+use dom::attr::{Attr, AttrHelpersForLayout};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::ElementBinding;
@@ -825,6 +825,22 @@ impl Element {
impl Element {
+ pub fn push_new_attribute(&self,
+ local_name: Atom,
+ value: AttrValue,
+ name: Atom,
+ namespace: Namespace,
+ prefix: Option<Atom>) {
+ let window = window_from_node(self);
+ let in_empty_ns = namespace == ns!("");
+ let attr = Attr::new(&window, local_name, value, name, namespace, prefix, Some(self));
+ self.attrs.borrow_mut().push(JS::from_rooted(&attr));
+ if in_empty_ns {
+ vtable_for(NodeCast::from_ref(self)).attribute_mutated(
+ &attr, AttributeMutation::Set(None));
+ }
+ }
+
pub fn get_attribute(&self, namespace: &Namespace, local_name: &Atom) -> Option<Root<Attr>> {
self.attrs.borrow().iter().map(JS::root).find(|attr| {
attr.local_name() == local_name && attr.namespace() == namespace
@@ -839,9 +855,9 @@ impl Element {
}
pub fn set_attribute_from_parser(&self,
- qname: QualName,
- value: DOMString,
- prefix: Option<Atom>) {
+ qname: QualName,
+ value: DOMString,
+ prefix: Option<Atom>) {
// Don't set if the attribute already exists, so we can handle add_attrs_if_missing
if self.attrs.borrow().iter().map(JS::root)
.any(|a| *a.r().local_name() == qname.local && *a.r().namespace() == qname.ns) {
@@ -856,15 +872,16 @@ impl Element {
},
};
let value = self.parse_attribute(&qname.ns, &qname.local, value);
- self.do_set_attribute(qname.local, value, name, qname.ns, prefix, |_| false)
+ self.push_new_attribute(qname.local, value, name, qname.ns, prefix);
}
pub fn set_attribute(&self, name: &Atom, value: AttrValue) {
assert!(&**name == name.to_ascii_lowercase());
assert!(!name.contains(":"));
- self.do_set_attribute(name.clone(), value, name.clone(),
- ns!(""), None, |attr| attr.local_name() == name);
+ self.set_first_matching_attribute(
+ name.clone(), value, name.clone(), ns!(""), None,
+ |attr| attr.local_name() == name);
}
// https://html.spec.whatwg.org/multipage/#attr-data-*
@@ -878,34 +895,27 @@ impl Element {
// Steps 2-5.
let name = Atom::from_slice(&name);
let value = self.parse_attribute(&ns!(""), &name, value);
- self.do_set_attribute(name.clone(), value, name.clone(), ns!(""), None, |attr| {
- *attr.name() == name && *attr.namespace() == ns!("")
- });
+ self.set_first_matching_attribute(
+ name.clone(), value, name.clone(), ns!(""), None,
+ |attr| *attr.name() == name && *attr.namespace() == ns!(""));
Ok(())
}
- pub fn do_set_attribute<F>(&self,
- local_name: Atom,
- value: AttrValue,
- name: Atom,
- namespace: Namespace,
- prefix: Option<Atom>,
- cb: F)
- where F: Fn(&Attr) -> bool
- {
- let idx = self.attrs.borrow().iter().map(JS::root).position(|attr| cb(&attr));
- let (idx, set_type) = match idx {
- Some(idx) => (idx, AttrSettingType::ReplacedAttr),
- None => {
- let window = window_from_node(self);
- let attr = Attr::new(window.r(), local_name, value.clone(),
- name, namespace.clone(), prefix, Some(self));
- self.attrs.borrow_mut().push(JS::from_rooted(&attr));
- (self.attrs.borrow().len() - 1, AttrSettingType::FirstSetAttr)
- }
+ fn set_first_matching_attribute<F>(&self,
+ local_name: Atom,
+ value: AttrValue,
+ name: Atom,
+ namespace: Namespace,
+ prefix: Option<Atom>,
+ find: F)
+ where F: Fn(&Attr)
+ -> bool {
+ let attr = self.attrs.borrow().iter().map(JS::root).find(|attr| find(&attr));
+ if let Some(attr) = attr {
+ attr.set_value(value, self);
+ } else {
+ self.push_new_attribute(local_name, value, name, namespace, prefix);
};
-
- (*self.attrs.borrow())[idx].root().r().set_value(set_type, value, self);
}
pub fn parse_attribute(&self, namespace: &Namespace, local_name: &Atom,
@@ -920,41 +930,27 @@ impl Element {
pub fn remove_attribute(&self, namespace: &Namespace, local_name: &Atom)
-> Option<Root<Attr>> {
- self.do_remove_attribute(|attr| {
+ self.remove_first_matching_attribute(|attr| {
attr.namespace() == namespace && attr.local_name() == local_name
})
}
pub fn remove_attribute_by_name(&self, name: &Atom) -> Option<Root<Attr>> {
- self.do_remove_attribute(|attr| attr.name() == name)
+ self.remove_first_matching_attribute(|attr| attr.name() == name)
}
- pub fn do_remove_attribute<F>(&self, find: F) -> Option<Root<Attr>>
+ fn remove_first_matching_attribute<F>(&self, find: F) -> Option<Root<Attr>>
where F: Fn(&Attr) -> bool
{
let idx = self.attrs.borrow().iter().map(JS::root).position(|attr| find(&attr));
idx.map(|idx| {
let attr = (*self.attrs.borrow())[idx].root();
- if attr.r().namespace() == &ns!("") {
- vtable_for(&NodeCast::from_ref(self)).before_remove_attr(attr.r());
- }
-
self.attrs.borrow_mut().remove(idx);
- attr.r().set_owner(None);
- if attr.r().namespace() == &ns!("") {
- vtable_for(&NodeCast::from_ref(self)).after_remove_attr(attr.r().name());
- }
-
+ attr.set_owner(None);
let node = NodeCast::from_ref(self);
- if node.is_in_doc() {
- let document = document_from_node(self);
- let damage = if attr.r().local_name() == &atom!("style") {
- NodeDamage::NodeStyleDamaged
- } else {
- NodeDamage::OtherNodeDamage
- };
- document.r().content_changed(node, damage);
+ if attr.namespace() == &ns!("") {
+ vtable_for(node).attribute_mutated(&attr, AttributeMutation::Removed);
}
attr
})
@@ -1168,9 +1164,9 @@ impl ElementMethods for Element {
// Step 3-5.
let value = self.parse_attribute(&ns!(""), &name, value);
- self.do_set_attribute(name.clone(), value, name.clone(), ns!(""), None, |attr| {
- *attr.name() == name
- });
+ self.set_first_matching_attribute(
+ name.clone(), value, name.clone(), ns!(""), None,
+ |attr| *attr.name() == name);
Ok(())
}
@@ -1183,11 +1179,9 @@ impl ElementMethods for Element {
try!(validate_and_extract(namespace, &qualified_name));
let qualified_name = Atom::from_slice(&qualified_name);
let value = self.parse_attribute(&namespace, &local_name, value);
- self.do_set_attribute(local_name.clone(), value, qualified_name,
- namespace.clone(), prefix, |attr| {
- *attr.local_name() == local_name &&
- *attr.namespace() == namespace
- });
+ self.set_first_matching_attribute(
+ local_name.clone(), value, qualified_name, namespace.clone(), prefix,
+ |attr| *attr.local_name() == local_name && *attr.namespace() == namespace);
Ok(())
}
@@ -1454,96 +1448,52 @@ impl VirtualMethods for Element {
Some(node as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
let node = NodeCast::from_ref(self);
- match attr.local_name() {
- &atom!("style") => {
+ let doc = node.owner_doc();
+ let damage = match attr.local_name() {
+ &atom!(style) => {
// Modifying the `style` attribute might change style.
- let doc = document_from_node(self);
- let base_url = doc.r().base_url();
- let value = attr.value();
- let style = Some(parse_style_attribute(&value, &base_url));
- *self.style_attribute.borrow_mut() = style;
-
- if node.is_in_doc() {
- doc.r().content_changed(node, NodeDamage::NodeStyleDamaged);
- }
- }
- &atom!("class") => {
+ *self.style_attribute.borrow_mut() =
+ mutation.new_value(attr).map(|value| {
+ parse_style_attribute(&value, &doc.base_url())
+ });
+ NodeDamage::NodeStyleDamaged
+ },
+ &atom!(class) => {
// Modifying a class can change style.
+ NodeDamage::NodeStyleDamaged
+ },
+ &atom!(id) => {
if node.is_in_doc() {
- let document = document_from_node(self);
- document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
- }
- }
- &atom!("id") => {
- // Modifying an ID might change style.
- let value = attr.value();
- if node.is_in_doc() {
- let doc = document_from_node(self);
- if !value.is_empty() {
- let value = value.atom().unwrap().clone();
- doc.r().register_named_element(self, value);
- }
- doc.r().content_changed(node, NodeDamage::NodeStyleDamaged);
- }
- }
- _ => {
- // Modifying any other attribute might change arbitrary things.
- if node.is_in_doc() {
- let document = document_from_node(self);
- document.r().content_changed(node, NodeDamage::OtherNodeDamage);
- }
- }
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
- let node = NodeCast::from_ref(self);
- match attr.local_name() {
- &atom!("style") => {
- // Modifying the `style` attribute might change style.
- *self.style_attribute.borrow_mut() = None;
-
- if node.is_in_doc() {
- let doc = document_from_node(self);
- doc.r().content_changed(node, NodeDamage::NodeStyleDamaged);
- }
- }
- &atom!("id") => {
- // Modifying an ID can change style.
- let value = attr.value();
- if node.is_in_doc() {
- let doc = document_from_node(self);
- if !value.is_empty() {
- let value = value.atom().unwrap().clone();
- doc.r().unregister_named_element(self, value);
+ let value = attr.value().atom().unwrap().clone();
+ match mutation {
+ AttributeMutation::Set(old_value) => {
+ if let Some(old_value) = old_value {
+ let old_value = old_value.atom().unwrap().clone();
+ doc.unregister_named_element(self, old_value);
+ }
+ if value != atom!("") {
+ doc.register_named_element(self, value);
+ }
+ },
+ AttributeMutation::Removed => {
+ if value != atom!("") {
+ doc.unregister_named_element(self, value);
+ }
+ }
}
- doc.r().content_changed(node, NodeDamage::NodeStyleDamaged);
}
- }
- &atom!("class") => {
- // Modifying a class can change style.
- if node.is_in_doc() {
- let document = document_from_node(self);
- document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
- }
- }
+ NodeDamage::NodeStyleDamaged
+ },
_ => {
// Modifying any other attribute might change arbitrary things.
- if node.is_in_doc() {
- let doc = document_from_node(self);
- doc.r().content_changed(node, NodeDamage::OtherNodeDamage);
- }
- }
+ NodeDamage::OtherNodeDamage
+ },
+ };
+ if node.is_in_doc() {
+ doc.content_changed(node, damage);
}
}
@@ -1853,3 +1803,23 @@ impl Element {
self.set_click_in_progress(false);
}
}
+
+#[derive(Clone, Copy, PartialEq)]
+pub enum AttributeMutation<'a> {
+ /// The attribute is set, keep track of old value.
+ /// https://dom.spec.whatwg.org/#attribute-is-set
+ Set(Option<&'a AttrValue>),
+
+ /// The attribute is removed.
+ /// https://dom.spec.whatwg.org/#attribute-is-removed
+ Removed
+}
+
+impl<'a> AttributeMutation<'a> {
+ pub fn new_value<'b>(&self, attr: &'b Attr) -> Option<Ref<'b, AttrValue>> {
+ match *self {
+ AttributeMutation::Set(_) => Some(attr.value()),
+ AttributeMutation::Removed => None,
+ }
+ }
+}
diff --git a/components/script/dom/htmlbaseelement.rs b/components/script/dom/htmlbaseelement.rs
index 0a6e0ed8d0a..62db191aa05 100644
--- a/components/script/dom/htmlbaseelement.rs
+++ b/components/script/dom/htmlbaseelement.rs
@@ -9,7 +9,7 @@ use dom::bindings::codegen::InheritTypes::HTMLBaseElementDerived;
use dom::bindings::codegen::InheritTypes::HTMLElementCast;
use dom::bindings::js::Root;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId, document_from_node};
@@ -56,15 +56,6 @@ impl HTMLBaseElement {
parsed.unwrap_or(base)
}
- /// Update the cached base element in response to adding or removing an
- /// attribute.
- pub fn add_remove_attr(&self, attr: &Attr) {
- if *attr.local_name() == atom!("href") {
- let document = document_from_node(self);
- document.refresh_base_element();
- }
- }
-
/// Update the cached base element in response to binding or unbinding from
/// a tree.
pub fn bind_unbind(&self, tree_in_doc: bool) {
@@ -84,14 +75,11 @@ impl VirtualMethods for HTMLBaseElement {
Some(HTMLElementCast::from_ref(self) as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- self.super_type().unwrap().after_set_attr(attr);
- self.add_remove_attr(attr);
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- self.super_type().unwrap().before_remove_attr(attr);
- self.add_remove_attr(attr);
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
+ if *attr.local_name() == atom!(href) {
+ document_from_node(self).refresh_base_element();
+ }
}
fn bind_to_tree(&self, tree_in_doc: bool) {
diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs
index a38e385bc9c..28484a59f48 100644
--- a/components/script/dom/htmlbodyelement.rs
+++ b/components/script/dom/htmlbodyelement.rs
@@ -12,7 +12,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLElementCa
use dom::bindings::js::Root;
use dom::bindings::utils::Reflectable;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId, window_from_node, document_from_node};
@@ -123,56 +123,40 @@ impl VirtualMethods for HTMLBodyElement {
chan.send(event).unwrap();
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- let name = attr.local_name();
- if name.starts_with("on") {
- static FORWARDED_EVENTS: &'static [&'static str] =
- &["onfocus", "onload", "onscroll", "onafterprint", "onbeforeprint",
- "onbeforeunload", "onhashchange", "onlanguagechange", "onmessage",
- "onoffline", "ononline", "onpagehide", "onpageshow", "onpopstate",
- "onstorage", "onresize", "onunload", "onerror"];
- let window = window_from_node(self);
- let (cx, url, reflector) = (window.r().get_cx(),
- window.r().get_url(),
- window.r().reflector().get_jsobject());
- let evtarget =
- if FORWARDED_EVENTS.iter().any(|&event| &**name == event) {
- EventTargetCast::from_ref(window.r())
- } else {
- EventTargetCast::from_ref(self)
- };
- evtarget.set_event_handler_uncompiled(cx, url, reflector,
- &name[2..],
- (**attr.value()).to_owned());
- }
-
- match attr.local_name() {
- &atom!("bgcolor") => {
- self.background_color.set(str::parse_legacy_color(&attr.value()).ok())
- }
- &atom!("background") => {
- let doc = document_from_node(self);
- let base = doc.r().url();
-
- *self.background.borrow_mut() = UrlParser::new().base_url(&base).parse(&attr.value()).ok();
- }
- _ => {}
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- match self.super_type() {
- Some(ref s) => s.before_remove_attr(attr),
- _ => {}
- }
-
- match attr.local_name() {
- &atom!("bgcolor") => self.background_color.set(None),
- &atom!("background") => *self.background.borrow_mut() = None,
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
+ match (attr.local_name(), mutation) {
+ (&atom!(bgcolor), _) => {
+ self.background_color.set(mutation.new_value(attr).and_then(|value| {
+ str::parse_legacy_color(&value).ok()
+ }));
+ },
+ (&atom!(background), _) => {
+ *self.background.borrow_mut() = mutation.new_value(attr).and_then(|value| {
+ let base = document_from_node(self).url();
+ UrlParser::new().base_url(&base).parse(&value).ok()
+ });
+ },
+ (name, AttributeMutation::Set(_)) if name.starts_with("on") => {
+ static FORWARDED_EVENTS: &'static [&'static str] =
+ &["onfocus", "onload", "onscroll", "onafterprint", "onbeforeprint",
+ "onbeforeunload", "onhashchange", "onlanguagechange", "onmessage",
+ "onoffline", "ononline", "onpagehide", "onpageshow", "onpopstate",
+ "onstorage", "onresize", "onunload", "onerror"];
+ let window = window_from_node(self);
+ let (cx, url, reflector) = (window.get_cx(),
+ window.get_url(),
+ window.reflector().get_jsobject());
+ let evtarget =
+ if FORWARDED_EVENTS.iter().any(|&event| &**name == event) {
+ EventTargetCast::from_ref(window.r())
+ } else {
+ EventTargetCast::from_ref(self)
+ };
+ evtarget.set_event_handler_uncompiled(cx, url, reflector,
+ &name[2..],
+ (**attr.value()).to_owned());
+ },
_ => {}
}
}
diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs
index a8c95eb6d3b..c158ad36fb0 100644
--- a/components/script/dom/htmlbuttonelement.rs
+++ b/components/script/dom/htmlbuttonelement.rs
@@ -10,7 +10,7 @@ use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLBut
use dom::bindings::codegen::InheritTypes::{HTMLButtonElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::js::Root;
use dom::document::Document;
-use dom::element::{Element, ElementTypeId};
+use dom::element::{AttributeMutation, Element, ElementTypeId};
use dom::event::Event;
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
@@ -56,7 +56,7 @@ impl HTMLButtonElement {
HTMLButtonElement {
htmlelement:
HTMLElement::new_inherited(HTMLElementTypeId::HTMLButtonElement, localName, prefix, document),
- //TODO: implement button_type in after_set_attr
+ //TODO: implement button_type in attribute_mutated
button_type: Cell::new(ButtonType::ButtonSubmit)
}
}
@@ -135,34 +135,25 @@ impl VirtualMethods for HTMLButtonElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("disabled") => {
- let node = NodeCast::from_ref(self);
- node.set_disabled_state(true);
- node.set_enabled_state(false);
- },
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("disabled") => {
+ &atom!(disabled) => {
let node = NodeCast::from_ref(self);
- node.set_disabled_state(false);
- node.set_enabled_state(true);
- node.check_ancestors_disabled_state_for_form_control();
+ match mutation {
+ AttributeMutation::Set(Some(_)) => {}
+ AttributeMutation::Set(None) => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ },
+ AttributeMutation::Removed => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ node.check_ancestors_disabled_state_for_form_control();
+ }
+ }
},
- _ => ()
+ _ => {},
}
}
diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs
index 8da4c2cd155..123cf6b5e32 100644
--- a/components/script/dom/htmlcanvaselement.rs
+++ b/components/script/dom/htmlcanvaselement.rs
@@ -14,7 +14,7 @@ use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, HeapGCValue, Root};
use dom::bindings::utils::{Reflectable};
use dom::canvasrenderingcontext2d::{CanvasRenderingContext2D, LayoutCanvasRenderingContext2DHelpers};
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId, window_from_node};
@@ -256,46 +256,25 @@ impl VirtualMethods for HTMLCanvasElement {
Some(element as &VirtualMethods)
}
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
let recreate = match attr.local_name() {
- &atom!("width") => {
- self.width.set(DEFAULT_WIDTH);
+ &atom!(width) => {
+ let width = mutation.new_value(attr).and_then(|value| {
+ parse_unsigned_integer(value.chars())
+ });
+ self.width.set(width.unwrap_or(DEFAULT_WIDTH));
true
- }
- &atom!("height") => {
- self.height.set(DEFAULT_HEIGHT);
+ },
+ &atom!(height) => {
+ let height = mutation.new_value(attr).and_then(|value| {
+ parse_unsigned_integer(value.chars())
+ });
+ self.height.set(height.unwrap_or(DEFAULT_HEIGHT));
true
- }
+ },
_ => false,
};
-
- if recreate {
- self.recreate_contexts();
- }
- }
-
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- let value = attr.value();
- let recreate = match attr.local_name() {
- &atom!("width") => {
- self.width.set(parse_unsigned_integer(value.chars()).unwrap_or(DEFAULT_WIDTH));
- true
- }
- &atom!("height") => {
- self.height.set(parse_unsigned_integer(value.chars()).unwrap_or(DEFAULT_HEIGHT));
- true
- }
- _ => false,
- };
-
if recreate {
self.recreate_contexts();
}
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index 0b9ea1e6b8c..ec6a4c53454 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -19,7 +19,7 @@ use dom::bindings::utils::Reflectable;
use dom::cssstyledeclaration::{CSSStyleDeclaration, CSSModificationAccess};
use dom::document::Document;
use dom::domstringmap::DOMStringMap;
-use dom::element::{Element, ElementTypeId};
+use dom::element::{AttributeMutation, Element, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlinputelement::HTMLInputElement;
use dom::htmlmediaelement::HTMLMediaElementTypeId;
@@ -313,37 +313,23 @@ impl VirtualMethods for HTMLElement {
Some(element as &VirtualMethods)
}
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
- }
-
- fn after_remove_attr(&self, name: &Atom) {
- if let Some(ref s) = self.super_type() {
- s.after_remove_attr(name);
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
+ match (attr.local_name(), mutation) {
+ (name, AttributeMutation::Set(_)) if name.starts_with("on") => {
+ let window = window_from_node(self);
+ let (cx, url, reflector) = (window.r().get_cx(),
+ window.r().get_url(),
+ window.r().reflector().get_jsobject());
+ let evtarget = EventTargetCast::from_ref(self);
+ evtarget.set_event_handler_uncompiled(cx, url, reflector,
+ &name[2..],
+ (**attr.value()).to_owned());
+ },
+ _ => {}
}
- self.update_sequentially_focusable_status();
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- let name = attr.local_name();
- if name.starts_with("on") {
- let window = window_from_node(self);
- let (cx, url, reflector) = (window.r().get_cx(),
- window.r().get_url(),
- window.r().reflector().get_jsobject());
- let evtarget = EventTargetCast::from_ref(self);
- evtarget.set_event_handler_uncompiled(cx, url, reflector,
- &name[2..],
- (**attr.value()).to_owned());
- }
- self.update_sequentially_focusable_status();
- }
fn bind_to_tree(&self, tree_in_doc: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
diff --git a/components/script/dom/htmlfieldsetelement.rs b/components/script/dom/htmlfieldsetelement.rs
index 13ba6c17e76..d3f438b5862 100644
--- a/components/script/dom/htmlfieldsetelement.rs
+++ b/components/script/dom/htmlfieldsetelement.rs
@@ -9,7 +9,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLLegendElementDer
use dom::bindings::codegen::InheritTypes::{HTMLFieldSetElementDerived, NodeCast};
use dom::bindings::js::{Root, RootedReference};
use dom::document::Document;
-use dom::element::{Element, ElementTypeId};
+use dom::element::{AttributeMutation, Element, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlcollection::{HTMLCollection, CollectionFilter};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
@@ -88,83 +88,66 @@ impl VirtualMethods for HTMLFieldSetElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("disabled") => {
+ &atom!(disabled) => {
+ let disabled_state = match mutation {
+ AttributeMutation::Set(None) => true,
+ AttributeMutation::Set(Some(_)) => {
+ // Fieldset was already disabled before.
+ return;
+ },
+ AttributeMutation::Removed => false,
+ };
let node = NodeCast::from_ref(self);
- node.set_disabled_state(true);
- node.set_enabled_state(false);
- let maybe_legend = node.children()
- .find(|node| node.r().is_htmllegendelement());
-
- for child in node.children() {
- if Some(child.r()) == maybe_legend.r() {
- continue;
+ node.set_disabled_state(disabled_state);
+ node.set_enabled_state(!disabled_state);
+ let mut found_legend = false;
+ let children = node.children().filter(|node| {
+ if found_legend {
+ true
+ } else if node.is_htmllegendelement() {
+ found_legend = true;
+ false
+ } else {
+ true
}
-
- for descendant in child.r().traverse_preorder() {
+ });
+ let fields = children.flat_map(|child| {
+ child.traverse_preorder().filter(|descendant| {
match descendant.r().type_id() {
NodeTypeId::Element(
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) |
+ ElementTypeId::HTMLElement(
+ HTMLElementTypeId::HTMLButtonElement)) |
NodeTypeId::Element(
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
+ ElementTypeId::HTMLElement(
+ HTMLElementTypeId::HTMLInputElement)) |
NodeTypeId::Element(
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
+ ElementTypeId::HTMLElement(
+ HTMLElementTypeId::HTMLSelectElement)) |
NodeTypeId::Element(
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
- descendant.r().set_disabled_state(true);
- descendant.r().set_enabled_state(false);
+ ElementTypeId::HTMLElement(
+ HTMLElementTypeId::HTMLTextAreaElement)) => {
+ true
},
- _ => ()
+ _ => false,
}
+ })
+ });
+ if disabled_state {
+ for field in fields {
+ field.set_disabled_state(true);
+ field.set_enabled_state(false);
}
- }
- },
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("disabled") => {
- let node = NodeCast::from_ref(self);
- node.set_disabled_state(false);
- node.set_enabled_state(true);
- let maybe_legend = node.children()
- .find(|node| node.r().is_htmllegendelement());
-
- for child in node.children() {
- if Some(child.r()) == maybe_legend.r() {
- continue;
- }
-
- for descendant in child.r().traverse_preorder() {
- match descendant.r().type_id() {
- NodeTypeId::Element(
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) |
- NodeTypeId::Element(
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
- NodeTypeId::Element(
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
- NodeTypeId::Element(
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
- descendant.r().check_disabled_attribute();
- descendant.r().check_ancestors_disabled_state_for_form_control();
- },
- _ => ()
- }
+ } else {
+ for field in fields {
+ field.check_disabled_attribute();
+ field.check_ancestors_disabled_state_for_form_control();
}
}
},
- _ => ()
+ _ => {},
}
}
}
diff --git a/components/script/dom/htmlfontelement.rs b/components/script/dom/htmlfontelement.rs
index 03750d382b8..8bda280acdc 100644
--- a/components/script/dom/htmlfontelement.rs
+++ b/components/script/dom/htmlfontelement.rs
@@ -8,7 +8,7 @@ use dom::bindings::codegen::Bindings::HTMLFontElementBinding::HTMLFontElementMet
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLFontElementDerived};
use dom::bindings::js::Root;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId};
@@ -60,27 +60,15 @@ impl VirtualMethods for HTMLFontElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("color") => {
- self.color.set(str::parse_legacy_color(&attr.value()).ok())
- }
- _ => {}
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("color") => self.color.set(None),
- _ => ()
+ &atom!(color) => {
+ self.color.set(mutation.new_value(attr).and_then(|value| {
+ str::parse_legacy_color(&value).ok()
+ }));
+ },
+ _ => {},
}
}
}
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
index 6abf4f7b877..9795b0a9008 100644
--- a/components/script/dom/htmliframeelement.rs
+++ b/components/script/dom/htmliframeelement.rs
@@ -17,7 +17,7 @@ use dom::bindings::js::{Root};
use dom::bindings::utils::Reflectable;
use dom::customevent::CustomEvent;
use dom::document::Document;
-use dom::element::{ElementTypeId, self};
+use dom::element::{AttributeMutation, ElementTypeId, self};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId, window_from_node};
@@ -354,16 +354,13 @@ impl VirtualMethods for HTMLIFrameElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("sandbox") => {
- let mut modes = SandboxAllowance::AllowNothing as u8;
- if let Some(ref tokens) = attr.value().tokens() {
- for token in *tokens {
+ &atom!(sandbox) => {
+ self.sandbox.set(mutation.new_value(attr).map(|value| {
+ let mut modes = SandboxAllowance::AllowNothing as u8;
+ for token in value.tokens().unwrap() {
modes |= match &*token.to_ascii_lowercase() {
"allow-same-origin" => SandboxAllowance::AllowSameOrigin,
"allow-forms" => SandboxAllowance::AllowForms,
@@ -374,16 +371,17 @@ impl VirtualMethods for HTMLIFrameElement {
_ => SandboxAllowance::AllowNothing
} as u8;
}
- }
- self.sandbox.set(Some(modes));
- }
- &atom!("src") => {
- let node = NodeCast::from_ref(self);
- if node.is_in_doc() {
- self.process_the_iframe_attributes()
+ modes
+ }));
+ },
+ &atom!(src) => {
+ if let AttributeMutation::Set(_) = mutation {
+ if NodeCast::from_ref(self).is_in_doc() {
+ self.process_the_iframe_attributes();
+ }
}
},
- _ => ()
+ _ => {},
}
}
@@ -394,17 +392,6 @@ impl VirtualMethods for HTMLIFrameElement {
}
}
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("sandbox") => self.sandbox.set(None),
- _ => ()
- }
- }
-
fn bind_to_tree(&self, tree_in_doc: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs
index dcb69a44104..fe811bf2cf8 100644
--- a/components/script/dom/htmlimageelement.rs
+++ b/components/script/dom/htmlimageelement.rs
@@ -15,7 +15,7 @@ use dom::bindings::global::GlobalRef;
use dom::bindings::js::{LayoutJS, Root};
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
@@ -108,7 +108,7 @@ impl Runnable for ImageResponseHandlerRunnable {
impl HTMLImageElement {
/// Makes the local `image` member match the status of the `src` attribute and starts
/// prefetching the image. This method must be called after `src` is changed.
- fn update_image(&self, value: Option<(DOMString, &Url)>) {
+ fn update_image(&self, value: Option<(DOMString, Url)>) {
let node = NodeCast::from_ref(self);
let document = node.owner_doc();
let window = document.r().window();
@@ -120,7 +120,7 @@ impl HTMLImageElement {
*self.image.borrow_mut() = None;
}
Some((src, base_url)) => {
- let img_url = UrlParser::new().base_url(base_url).parse(&src);
+ let img_url = UrlParser::new().base_url(&base_url).parse(&src);
// FIXME: handle URL parse errors more gracefully.
let img_url = img_url.unwrap();
*self.url.borrow_mut() = Some(img_url.clone());
@@ -300,29 +300,15 @@ impl VirtualMethods for HTMLImageElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("src") => {
- let window = window_from_node(self);
- let url = window.r().get_url();
- self.update_image(Some(((**attr.value()).to_owned(), &url)));
+ &atom!(src) => {
+ self.update_image(mutation.new_value(attr).map(|value| {
+ ((**value).to_owned(), window_from_node(self).get_url())
+ }));
},
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("src") => self.update_image(None),
- _ => ()
+ _ => {},
}
}
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index becbf7c4947..17d0144fa47 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -16,7 +16,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSet
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, LayoutJS, Root, RootedReference};
use dom::document::Document;
-use dom::element::{Element, ElementTypeId, RawLayoutElementHelpers};
+use dom::element::{AttributeMutation, Element, ElementTypeId, RawLayoutElementHelpers};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
@@ -447,113 +447,87 @@ impl VirtualMethods for HTMLInputElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("disabled") => {
- let node = NodeCast::from_ref(self);
- node.set_disabled_state(true);
- node.set_enabled_state(false);
- }
- &atom!("checked") => {
- // https://html.spec.whatwg.org/multipage/#the-input-element:concept-input-checked-dirty
- if !self.checked_changed.get() {
- self.update_checked_state(true, false);
- }
- }
- &atom!("size") => {
- match *attr.value() {
- AttrValue::UInt(_, value) => self.size.set(value),
- _ => panic!("Expected an AttrValue::UInt"),
- }
- }
- &atom!("type") => {
- let value = attr.value();
- self.input_type.set(match &**value {
- "button" => InputType::InputButton,
- "submit" => InputType::InputSubmit,
- "reset" => InputType::InputReset,
- "file" => InputType::InputFile,
- "radio" => InputType::InputRadio,
- "checkbox" => InputType::InputCheckbox,
- "password" => InputType::InputPassword,
- _ => InputType::InputText,
- });
- if self.input_type.get() == InputType::InputRadio {
- self.radio_group_updated(self.get_radio_group_name()
- .as_ref()
- .map(|group| &**group));
- }
- }
- &atom!("value") => {
- if !self.value_changed.get() {
- self.textinput.borrow_mut().set_content((**attr.value()).to_owned());
- }
- }
- &atom!("name") => {
- if self.input_type.get() == InputType::InputRadio {
- let value = attr.value();
- self.radio_group_updated(Some(&value));
- }
- }
- _ if attr.local_name() == &Atom::from_slice("placeholder") => {
- let value = attr.value();
- let stripped = value.chars()
- .filter(|&c| c != '\n' && c != '\r')
- .collect::<String>();
- *self.placeholder.borrow_mut() = stripped;
- }
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("disabled") => {
+ &atom!(disabled) => {
+ let disabled_state = match mutation {
+ AttributeMutation::Set(None) => true,
+ AttributeMutation::Set(Some(_)) => {
+ // Input was already disabled before.
+ return;
+ },
+ AttributeMutation::Removed => false,
+ };
let node = NodeCast::from_ref(self);
- node.set_disabled_state(false);
- node.set_enabled_state(true);
+ node.set_disabled_state(disabled_state);
+ node.set_enabled_state(!disabled_state);
node.check_ancestors_disabled_state_for_form_control();
+ },
+ &atom!(checked) if !self.checked_changed.get() => {
+ let checked_state = match mutation {
+ AttributeMutation::Set(None) => true,
+ AttributeMutation::Set(Some(_)) => {
+ // Input was already checked before.
+ return;
+ },
+ AttributeMutation::Removed => false,
+ };
+ self.update_checked_state(checked_state, false);
+ },
+ &atom!(size) => {
+ let size = mutation.new_value(attr).map(|value| {
+ value.uint().expect("Expected an AttrValue::UInt")
+ });
+ self.size.set(size.unwrap_or(DEFAULT_INPUT_SIZE));
}
- &atom!("checked") => {
- // https://html.spec.whatwg.org/multipage/#the-input-element:concept-input-checked-dirty
- if !self.checked_changed.get() {
- self.update_checked_state(false, false);
- }
- }
- &atom!("size") => {
- self.size.set(DEFAULT_INPUT_SIZE);
- }
- &atom!("type") => {
- if self.input_type.get() == InputType::InputRadio {
- broadcast_radio_checked(self,
- self.get_radio_group_name()
- .as_ref()
- .map(|group| &**group));
- }
- self.input_type.set(InputType::InputText);
- }
- &atom!("value") => {
- if !self.value_changed.get() {
- self.textinput.borrow_mut().set_content("".to_owned());
+ &atom!(type) => {
+ match mutation {
+ AttributeMutation::Set(_) => {
+ let value = match &**attr.value() {
+ "button" => InputType::InputButton,
+ "submit" => InputType::InputSubmit,
+ "reset" => InputType::InputReset,
+ "file" => InputType::InputFile,
+ "radio" => InputType::InputRadio,
+ "checkbox" => InputType::InputCheckbox,
+ "password" => InputType::InputPassword,
+ _ => InputType::InputText,
+ };
+ self.input_type.set(value);
+ if value == InputType::InputRadio {
+ self.radio_group_updated(
+ self.get_radio_group_name().as_ref().map(|group| &**group));
+ }
+ },
+ AttributeMutation::Removed => {
+ if self.input_type.get() == InputType::InputRadio {
+ broadcast_radio_checked(
+ self,
+ self.get_radio_group_name().as_ref().map(|group| &**group));
+ }
+ self.input_type.set(InputType::InputText);
+ }
}
- }
- &atom!("name") => {
- if self.input_type.get() == InputType::InputRadio {
- self.radio_group_updated(None);
+ },
+ &atom!(value) if !self.value_changed.get() => {
+ let value = mutation.new_value(attr).map(|value| (**value).to_owned());
+ self.textinput.borrow_mut().set_content(
+ value.unwrap_or_else(|| "".to_owned()));
+ },
+ &atom!(name) if self.input_type.get() == InputType::InputRadio => {
+ self.radio_group_updated(
+ mutation.new_value(attr).as_ref().map(|value| &***value));
+ },
+ name if name == &Atom::from_slice("placeholder") => {
+ let mut placeholder = self.placeholder.borrow_mut();
+ placeholder.clear();
+ if let AttributeMutation::Set(_) = mutation {
+ placeholder.extend(
+ attr.value().chars().filter(|&c| c != '\n' && c != '\r'));
}
- }
- _ if attr.local_name() == &Atom::from_slice("placeholder") => {
- self.placeholder.borrow_mut().clear();
- }
- _ => ()
+ },
+ _ => {},
}
}
diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
index 072e14bab3b..787c1d70254 100644
--- a/components/script/dom/htmllinkelement.rs
+++ b/components/script/dom/htmllinkelement.rs
@@ -16,7 +16,7 @@ use dom::bindings::js::{RootedReference};
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
use dom::domtokenlist::DOMTokenList;
-use dom::element::{Element, ElementTypeId};
+use dom::element::{AttributeMutation, Element, ElementTypeId};
use dom::event::{EventBubbles, EventCancelable, Event};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
@@ -103,33 +103,26 @@ impl VirtualMethods for HTMLLinkElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- let node = NodeCast::from_ref(self);
- if !node.is_in_doc() {
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
+ if !NodeCast::from_ref(self).is_in_doc() || mutation == AttributeMutation::Removed {
return;
}
-
- let element = ElementCast::from_ref(self);
- let rel = get_attr(element, &atom!("rel"));
-
- match (rel, attr.local_name()) {
- (ref rel, &atom!("href")) => {
- if is_stylesheet(rel) {
+ let rel = get_attr(ElementCast::from_ref(self), &atom!(rel));
+ match attr.local_name() {
+ &atom!(href) => {
+ if is_stylesheet(&rel) {
self.handle_stylesheet_url(&attr.value());
- } else if is_favicon(rel) {
+ } else if is_favicon(&rel) {
self.handle_favicon_url(&attr.value());
}
- }
- (ref rel, &atom!("media")) => {
- if is_stylesheet(rel) {
+ },
+ &atom!(media) => {
+ if is_stylesheet(&rel) {
self.handle_stylesheet_url(&attr.value());
}
- }
- (_, _) => ()
+ },
+ _ => {},
}
}
diff --git a/components/script/dom/htmlobjectelement.rs b/components/script/dom/htmlobjectelement.rs
index 884087c298d..04c5228072b 100644
--- a/components/script/dom/htmlobjectelement.rs
+++ b/components/script/dom/htmlobjectelement.rs
@@ -10,7 +10,7 @@ use dom::bindings::codegen::InheritTypes::HTMLObjectElementDerived;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
use dom::bindings::js::Root;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId, window_from_node};
@@ -101,16 +101,15 @@ impl VirtualMethods for HTMLObjectElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("data") => {
- self.process_data_url();
+ &atom!(data) => {
+ if let AttributeMutation::Set(_) = mutation {
+ self.process_data_url();
+ }
},
- _ => ()
+ _ => {},
}
}
}
diff --git a/components/script/dom/htmloptgroupelement.rs b/components/script/dom/htmloptgroupelement.rs
index 547136cf931..3765c8fcb43 100644
--- a/components/script/dom/htmloptgroupelement.rs
+++ b/components/script/dom/htmloptgroupelement.rs
@@ -9,7 +9,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLOptGroupElementDerived, HTMLOptionElementDerived};
use dom::bindings::js::Root;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId};
@@ -63,44 +63,36 @@ impl VirtualMethods for HTMLOptGroupElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("disabled") => {
+ &atom!(disabled) => {
+ let disabled_state = match mutation {
+ AttributeMutation::Set(None) => true,
+ AttributeMutation::Set(Some(_)) => {
+ // Option group was already disabled.
+ return;
+ },
+ AttributeMutation::Removed => false,
+ };
let node = NodeCast::from_ref(self);
- node.set_disabled_state(true);
- node.set_enabled_state(false);
- for child in node.children() {
- if child.r().is_htmloptionelement() {
- child.r().set_disabled_state(true);
- child.r().set_enabled_state(false);
+ node.set_disabled_state(disabled_state);
+ node.set_enabled_state(!disabled_state);
+ let options = node.children().filter(|child| {
+ child.is_htmloptionelement()
+ });
+ if disabled_state {
+ for option in options {
+ option.set_disabled_state(true);
+ option.set_enabled_state(false);
}
- }
- },
- _ => (),
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("disabled") => {
- let node = NodeCast::from_ref(self);
- node.set_disabled_state(false);
- node.set_enabled_state(true);
- for child in node.children() {
- if child.r().is_htmloptionelement() {
- child.r().check_disabled_attribute();
+ } else {
+ for option in options {
+ option.check_disabled_attribute();
}
}
},
- _ => ()
+ _ => {},
}
}
}
diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs
index f830cc61cc1..b9855f84eb7 100644
--- a/components/script/dom/htmloptionelement.rs
+++ b/components/script/dom/htmloptionelement.rs
@@ -12,7 +12,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLOptionElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived};
use dom::bindings::js::Root;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId};
@@ -131,34 +131,24 @@ impl VirtualMethods for HTMLOptionElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("disabled") => {
- let node = NodeCast::from_ref(self);
- node.set_disabled_state(true);
- node.set_enabled_state(false);
- },
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("disabled") => {
+ &atom!(disabled) => {
let node = NodeCast::from_ref(self);
- node.set_disabled_state(false);
- node.set_enabled_state(true);
- node.check_parent_disabled_state_for_option();
+ match mutation {
+ AttributeMutation::Set(_) => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ },
+ AttributeMutation::Removed => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ node.check_parent_disabled_state_for_option();
+ }
+ }
},
- _ => ()
+ _ => {},
}
}
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 4bde701ac0b..221bd1a3d6c 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -21,7 +21,7 @@ use dom::bindings::js::{JS, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::trace::JSTraceable;
use dom::document::Document;
-use dom::element::{ElementCreator, ElementTypeId};
+use dom::element::{AttributeMutation, ElementCreator, ElementTypeId};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
@@ -532,13 +532,17 @@ impl VirtualMethods for HTMLScriptElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
- let node = NodeCast::from_ref(self);
- if attr.local_name() == &atom!("src") && !self.parser_inserted.get() && node.is_in_doc() {
- self.prepare();
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
+ match attr.local_name() {
+ &atom!("src") => {
+ if let AttributeMutation::Set(_) = mutation {
+ if !self.parser_inserted.get() && NodeCast::from_ref(self).is_in_doc() {
+ self.prepare();
+ }
+ }
+ },
+ _ => {},
}
}
diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs
index bed90a07c6e..b71760fdc09 100644
--- a/components/script/dom/htmlselectelement.rs
+++ b/components/script/dom/htmlselectelement.rs
@@ -11,7 +11,7 @@ use dom::bindings::codegen::UnionTypes::HTMLElementOrLong;
use dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement;
use dom::bindings::js::Root;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId, window_from_node};
@@ -109,34 +109,21 @@ impl VirtualMethods for HTMLSelectElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("disabled") => {
- let node = NodeCast::from_ref(self);
- node.set_disabled_state(true);
- node.set_enabled_state(false);
- },
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("disabled") => {
- let node = NodeCast::from_ref(self);
- node.set_disabled_state(false);
- node.set_enabled_state(true);
- node.check_ancestors_disabled_state_for_form_control();
- },
- _ => ()
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
+ if attr.local_name() == &atom!(disabled) {
+ let node = NodeCast::from_ref(self);
+ match mutation {
+ AttributeMutation::Set(_) => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ },
+ AttributeMutation::Removed => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ node.check_ancestors_disabled_state_for_form_control();
+ }
+ }
}
}
diff --git a/components/script/dom/htmltablecellelement.rs b/components/script/dom/htmltablecellelement.rs
index 153593f5e58..f35f019c175 100644
--- a/components/script/dom/htmltablecellelement.rs
+++ b/components/script/dom/htmltablecellelement.rs
@@ -6,7 +6,7 @@ use dom::attr::{Attr, AttrValue};
use dom::bindings::codegen::Bindings::HTMLTableCellElementBinding::HTMLTableCellElementMethods;
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableCellElementDerived};
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::NodeTypeId;
@@ -101,38 +101,26 @@ impl VirtualMethods for HTMLTableCellElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("bgcolor") => {
- self.background_color.set(str::parse_legacy_color(&attr.value()).ok())
- }
- &atom!("colspan") => {
- match *attr.value() {
- AttrValue::UInt(_, colspan) => {
- self.colspan.set(Some(max(DEFAULT_COLSPAN, colspan)))
- },
- _ => unreachable!(),
- }
+ &atom!(bgcolor) => {
+ self.background_color.set(mutation.new_value(attr).and_then(|value| {
+ str::parse_legacy_color(&value).ok()
+ }));
},
- &atom!("width") => self.width.set(str::parse_length(&attr.value())),
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("bgcolor") => self.background_color.set(None),
- &atom!("colspan") => self.colspan.set(None),
- &atom!("width") => self.width.set(LengthOrPercentageOrAuto::Auto),
- _ => ()
+ &atom!(colspan) => {
+ self.colspan.set(mutation.new_value(attr).map(|value| {
+ max(DEFAULT_COLSPAN, value.uint().unwrap())
+ }));
+ },
+ &atom!(width) => {
+ let width = mutation.new_value(attr).map(|value| {
+ str::parse_length(&value)
+ });
+ self.width.set(width.unwrap_or(LengthOrPercentageOrAuto::Auto));
+ },
+ _ => {},
}
}
diff --git a/components/script/dom/htmltableelement.rs b/components/script/dom/htmltableelement.rs
index b55f9ac341f..e5788bd0e56 100644
--- a/components/script/dom/htmltableelement.rs
+++ b/components/script/dom/htmltableelement.rs
@@ -11,7 +11,7 @@ use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLTab
use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast};
use dom::bindings::js::{Root, RootedReference};
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::htmltablecaptionelement::HTMLTableCaptionElement;
@@ -157,39 +157,32 @@ impl VirtualMethods for HTMLTableElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("bgcolor") => {
- self.background_color.set(str::parse_legacy_color(&attr.value()).ok())
- }
- &atom!("border") => {
+ &atom!(bgcolor) => {
+ self.background_color.set(mutation.new_value(attr).and_then(|value| {
+ str::parse_legacy_color(&value).ok()
+ }));
+ },
+ &atom!(border) => {
// According to HTML5 § 14.3.9, invalid values map to 1px.
- self.border.set(Some(str::parse_unsigned_integer(attr.value()
- .chars()).unwrap_or(1)))
+ self.border.set(mutation.new_value(attr).map(|value| {
+ str::parse_unsigned_integer(value.chars()).unwrap_or(1)
+ }));
}
- &atom!("cellspacing") => {
- self.cellspacing.set(str::parse_unsigned_integer(attr.value().chars()))
- }
- &atom!("width") => self.width.set(str::parse_length(&attr.value())),
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("bgcolor") => self.background_color.set(None),
- &atom!("border") => self.border.set(None),
- &atom!("cellspacing") => self.cellspacing.set(None),
- &atom!("width") => self.width.set(LengthOrPercentageOrAuto::Auto),
- _ => ()
+ &atom!(cellspacing) => {
+ self.cellspacing.set(mutation.new_value(attr).and_then(|value| {
+ str::parse_unsigned_integer(value.chars())
+ }));
+ },
+ &atom!(width) => {
+ let width = mutation.new_value(attr).map(|value| {
+ str::parse_length(&value)
+ });
+ self.width.set(width.unwrap_or(LengthOrPercentageOrAuto::Auto));
+ },
+ _ => {},
}
}
diff --git a/components/script/dom/htmltablerowelement.rs b/components/script/dom/htmltablerowelement.rs
index 160b9a3833b..93a4fddc094 100644
--- a/components/script/dom/htmltablerowelement.rs
+++ b/components/script/dom/htmltablerowelement.rs
@@ -7,7 +7,7 @@ use dom::bindings::codegen::Bindings::HTMLTableRowElementBinding;
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableRowElementDerived};
use dom::bindings::js::Root;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId};
@@ -62,29 +62,15 @@ impl VirtualMethods for HTMLTableRowElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("bgcolor") => {
- self.background_color.set(str::parse_legacy_color(&attr.value()).ok());
- },
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("bgcolor") => {
- self.background_color.set(None);
+ &atom!(bgcolor) => {
+ self.background_color.set(mutation.new_value(attr).and_then(|value| {
+ str::parse_legacy_color(&value).ok()
+ }));
},
- _ => ()
+ _ => {},
}
}
}
diff --git a/components/script/dom/htmltablesectionelement.rs b/components/script/dom/htmltablesectionelement.rs
index 5922ffd4b42..137d25541fa 100644
--- a/components/script/dom/htmltablesectionelement.rs
+++ b/components/script/dom/htmltablesectionelement.rs
@@ -7,7 +7,7 @@ use dom::bindings::codegen::Bindings::HTMLTableSectionElementBinding;
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableSectionElementDerived};
use dom::bindings::js::Root;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeTypeId};
@@ -61,29 +61,15 @@ impl VirtualMethods for HTMLTableSectionElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("bgcolor") => {
- self.background_color.set(str::parse_legacy_color(&attr.value()).ok());
- },
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("bgcolor") => {
- self.background_color.set(None);
+ &atom!(bgcolor) => {
+ self.background_color.set(mutation.new_value(attr).and_then(|value| {
+ str::parse_legacy_color(&value).ok()
+ }));
},
- _ => ()
+ _ => {},
}
}
}
diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs
index 333e5259394..533ac1f8bcd 100644
--- a/components/script/dom/htmltextareaelement.rs
+++ b/components/script/dom/htmltextareaelement.rs
@@ -15,7 +15,7 @@ use dom::bindings::global::GlobalRef;
use dom::bindings::js::{LayoutJS, Root};
use dom::bindings::refcounted::Trusted;
use dom::document::Document;
-use dom::element::{Element, ElementTypeId};
+use dom::element::{AttributeMutation, Element, ElementTypeId};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
@@ -242,52 +242,36 @@ impl VirtualMethods for HTMLTextAreaElement {
Some(htmlelement as &VirtualMethods)
}
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
-
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ self.super_type().unwrap().attribute_mutated(attr, mutation);
match attr.local_name() {
- &atom!("disabled") => {
+ &atom!(disabled) => {
let node = NodeCast::from_ref(self);
- node.set_disabled_state(true);
- node.set_enabled_state(false);
- },
- &atom!("cols") => {
- match *attr.value() {
- AttrValue::UInt(_, value) => self.cols.set(value),
- _ => panic!("Expected an AttrValue::UInt"),
- }
- },
- &atom!("rows") => {
- match *attr.value() {
- AttrValue::UInt(_, value) => self.rows.set(value),
- _ => panic!("Expected an AttrValue::UInt"),
+ match mutation {
+ AttributeMutation::Set(_) => {
+ node.set_disabled_state(true);
+ node.set_enabled_state(false);
+ },
+ AttributeMutation::Removed => {
+ node.set_disabled_state(false);
+ node.set_enabled_state(true);
+ node.check_ancestors_disabled_state_for_form_control();
+ }
}
},
- _ => ()
- }
- }
-
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
-
- match attr.local_name() {
- &atom!("disabled") => {
- let node = NodeCast::from_ref(self);
- node.set_disabled_state(false);
- node.set_enabled_state(true);
- node.check_ancestors_disabled_state_for_form_control();
- },
- &atom!("cols") => {
- self.cols.set(DEFAULT_COLS);
+ &atom!(cols) => {
+ let cols = mutation.new_value(attr).map(|value| {
+ value.uint().expect("Expected an AttrValue::UInt")
+ });
+ self.cols.set(cols.unwrap_or(DEFAULT_COLS));
},
- &atom!("rows") => {
- self.rows.set(DEFAULT_ROWS);
+ &atom!(rows) => {
+ let rows = mutation.new_value(attr).map(|value| {
+ value.uint().expect("Expected an AttrValue::UInt")
+ });
+ self.rows.set(rows.unwrap_or(DEFAULT_ROWS));
},
- _ => ()
+ _ => {},
}
}
diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs
index 589a2b2461c..6812703ffe6 100644
--- a/components/script/dom/virtualmethods.rs
+++ b/components/script/dom/virtualmethods.rs
@@ -33,7 +33,7 @@ use dom::bindings::codegen::InheritTypes::HTMLTableSectionElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast;
use dom::document::Document;
-use dom::element::ElementTypeId;
+use dom::element::{AttributeMutation, ElementTypeId};
use dom::event::Event;
use dom::htmlelement::HTMLElementTypeId;
use dom::node::NodeTypeId;
@@ -50,27 +50,12 @@ pub trait VirtualMethods {
/// if any.
fn super_type(&self) -> Option<&VirtualMethods>;
- /// Called when changing or adding attributes, after the attribute's value
- /// has been updated.
- fn after_set_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.after_set_attr(attr);
- }
- }
-
- /// Called when changing or removing attributes, before any modification
- /// has taken place.
- fn before_remove_attr(&self, attr: &Attr) {
- if let Some(ref s) = self.super_type() {
- s.before_remove_attr(attr);
- }
- }
-
- /// Called when changing or removing attributes, after all modification
- /// has taken place.
- fn after_remove_attr(&self, name: &Atom) {
- if let Some(ref s) = self.super_type() {
- s.after_remove_attr(name);
+ /// Called when attributes of a node are mutated.
+ /// https://dom.spec.whatwg.org/#attribute-is-set
+ /// https://dom.spec.whatwg.org/#attribute-is-removed
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
+ if let Some(s) = self.super_type() {
+ s.attribute_mutated(attr, mutation);
}
}