aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
authorAnthony Ramine <n.oxyde@gmail.com>2015-08-28 13:32:38 +0200
committerAnthony Ramine <n.oxyde@gmail.com>2015-09-02 15:45:38 +0200
commit58e1bd0e57a3c69307e35a25ed23af3dedf05c50 (patch)
tree4a37e912de8a15479cc4b97024eb47b5b77b8c9e /components/script/dom
parent5672142042973bdb8e208357a702bb4a93b94658 (diff)
downloadservo-58e1bd0e57a3c69307e35a25ed23af3dedf05c50.tar.gz
servo-58e1bd0e57a3c69307e35a25ed23af3dedf05c50.zip
Introduce VirtualMethods::attribute_mutated()
This replaces before_remove_attr(), after_remove_attr() and after_set_attr(). The virtual method takes the mutated attribute and an AttributeMutation value to disambiguate between "attribute is changed", "attribute is added" and "attribute is removed". In the case of "attribute is changed", the mutation value contains a reference to the old value of the mutated attribute, which is used to unregister outdated named elements when the "id" attribute is changed on an element. This greatly simplifies the handling of attributes, which in many cases don't have any specific behaviour whether they are removed or changed or added. It also fixes a few bugs where things were put in before_remove_attr() instead of after_remove_attr() (e.g. when removing an href attribute from a base element). A few helper functions in Element were also renamed and made private.
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);
}
}