diff options
Diffstat (limited to 'components/script/dom')
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); } } |