mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
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.
This commit is contained in:
parent
5672142042
commit
58e1bd0e57
27 changed files with 565 additions and 862 deletions
|
@ -9,7 +9,7 @@ use dom::bindings::global::GlobalRef;
|
||||||
use dom::bindings::js::{JS, MutNullableHeap};
|
use dom::bindings::js::{JS, MutNullableHeap};
|
||||||
use dom::bindings::js::{Root, RootedReference, LayoutJS};
|
use dom::bindings::js::{Root, RootedReference, LayoutJS};
|
||||||
use dom::bindings::utils::{Reflector, reflect_dom_object};
|
use dom::bindings::utils::{Reflector, reflect_dom_object};
|
||||||
use dom::element::Element;
|
use dom::element::{AttributeMutation, Element};
|
||||||
use dom::virtualmethods::vtable_for;
|
use dom::virtualmethods::vtable_for;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
|
|
||||||
|
@ -23,12 +23,6 @@ use std::cell::Ref;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[derive(HeapSizeOf)]
|
|
||||||
pub enum AttrSettingType {
|
|
||||||
FirstSetAttr,
|
|
||||||
ReplacedAttr,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(JSTraceable, PartialEq, Clone, HeapSizeOf)]
|
#[derive(JSTraceable, PartialEq, Clone, HeapSizeOf)]
|
||||||
pub enum AttrValue {
|
pub enum AttrValue {
|
||||||
String(DOMString),
|
String(DOMString),
|
||||||
|
@ -94,6 +88,17 @@ impl AttrValue {
|
||||||
_ => None
|
_ => 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 {
|
impl Deref for AttrValue {
|
||||||
|
@ -179,7 +184,7 @@ impl AttrMethods for Attr {
|
||||||
None => *self.value.borrow_mut() = AttrValue::String(value),
|
None => *self.value.borrow_mut() = AttrValue::String(value),
|
||||||
Some(owner) => {
|
Some(owner) => {
|
||||||
let value = owner.r().parse_attribute(&self.namespace, self.local_name(), value);
|
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 {
|
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());
|
assert!(Some(owner) == self.owner().r());
|
||||||
|
mem::swap(&mut *self.value.borrow_mut(), &mut value);
|
||||||
let node = NodeCast::from_ref(owner);
|
if self.namespace == ns!("") {
|
||||||
let namespace_is_null = self.namespace == ns!("");
|
vtable_for(NodeCast::from_ref(owner)).attribute_mutated(
|
||||||
|
self, AttributeMutation::Set(Some(&value)));
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -297,6 +297,7 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refresh the cached first base element in the DOM.
|
/// Refresh the cached first base element in the DOM.
|
||||||
|
/// https://github.com/w3c/web-platform-tests/issues/2122
|
||||||
pub fn refresh_base_element(&self) {
|
pub fn refresh_base_element(&self) {
|
||||||
let base = NodeCast::from_ref(self)
|
let base = NodeCast::from_ref(self)
|
||||||
.traverse_preorder()
|
.traverse_preorder()
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
use dom::activation::Activatable;
|
use dom::activation::Activatable;
|
||||||
use dom::attr::AttrValue;
|
use dom::attr::AttrValue;
|
||||||
use dom::attr::{Attr, AttrSettingType, AttrHelpersForLayout};
|
use dom::attr::{Attr, AttrHelpersForLayout};
|
||||||
use dom::bindings::cell::DOMRefCell;
|
use dom::bindings::cell::DOMRefCell;
|
||||||
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
|
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
|
||||||
use dom::bindings::codegen::Bindings::ElementBinding;
|
use dom::bindings::codegen::Bindings::ElementBinding;
|
||||||
|
@ -825,6 +825,22 @@ impl Element {
|
||||||
|
|
||||||
|
|
||||||
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>> {
|
pub fn get_attribute(&self, namespace: &Namespace, local_name: &Atom) -> Option<Root<Attr>> {
|
||||||
self.attrs.borrow().iter().map(JS::root).find(|attr| {
|
self.attrs.borrow().iter().map(JS::root).find(|attr| {
|
||||||
attr.local_name() == local_name && attr.namespace() == namespace
|
attr.local_name() == local_name && attr.namespace() == namespace
|
||||||
|
@ -839,9 +855,9 @@ impl Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_attribute_from_parser(&self,
|
pub fn set_attribute_from_parser(&self,
|
||||||
qname: QualName,
|
qname: QualName,
|
||||||
value: DOMString,
|
value: DOMString,
|
||||||
prefix: Option<Atom>) {
|
prefix: Option<Atom>) {
|
||||||
// Don't set if the attribute already exists, so we can handle add_attrs_if_missing
|
// Don't set if the attribute already exists, so we can handle add_attrs_if_missing
|
||||||
if self.attrs.borrow().iter().map(JS::root)
|
if self.attrs.borrow().iter().map(JS::root)
|
||||||
.any(|a| *a.r().local_name() == qname.local && *a.r().namespace() == qname.ns) {
|
.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);
|
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) {
|
pub fn set_attribute(&self, name: &Atom, value: AttrValue) {
|
||||||
assert!(&**name == name.to_ascii_lowercase());
|
assert!(&**name == name.to_ascii_lowercase());
|
||||||
assert!(!name.contains(":"));
|
assert!(!name.contains(":"));
|
||||||
|
|
||||||
self.do_set_attribute(name.clone(), value, name.clone(),
|
self.set_first_matching_attribute(
|
||||||
ns!(""), None, |attr| attr.local_name() == name);
|
name.clone(), value, name.clone(), ns!(""), None,
|
||||||
|
|attr| attr.local_name() == name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#attr-data-*
|
// https://html.spec.whatwg.org/multipage/#attr-data-*
|
||||||
|
@ -878,34 +895,27 @@ impl Element {
|
||||||
// Steps 2-5.
|
// Steps 2-5.
|
||||||
let name = Atom::from_slice(&name);
|
let name = Atom::from_slice(&name);
|
||||||
let value = self.parse_attribute(&ns!(""), &name, value);
|
let value = self.parse_attribute(&ns!(""), &name, value);
|
||||||
self.do_set_attribute(name.clone(), value, name.clone(), ns!(""), None, |attr| {
|
self.set_first_matching_attribute(
|
||||||
*attr.name() == name && *attr.namespace() == ns!("")
|
name.clone(), value, name.clone(), ns!(""), None,
|
||||||
});
|
|attr| *attr.name() == name && *attr.namespace() == ns!(""));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_set_attribute<F>(&self,
|
fn set_first_matching_attribute<F>(&self,
|
||||||
local_name: Atom,
|
local_name: Atom,
|
||||||
value: AttrValue,
|
value: AttrValue,
|
||||||
name: Atom,
|
name: Atom,
|
||||||
namespace: Namespace,
|
namespace: Namespace,
|
||||||
prefix: Option<Atom>,
|
prefix: Option<Atom>,
|
||||||
cb: F)
|
find: F)
|
||||||
where F: Fn(&Attr) -> bool
|
where F: Fn(&Attr)
|
||||||
{
|
-> bool {
|
||||||
let idx = self.attrs.borrow().iter().map(JS::root).position(|attr| cb(&attr));
|
let attr = self.attrs.borrow().iter().map(JS::root).find(|attr| find(&attr));
|
||||||
let (idx, set_type) = match idx {
|
if let Some(attr) = attr {
|
||||||
Some(idx) => (idx, AttrSettingType::ReplacedAttr),
|
attr.set_value(value, self);
|
||||||
None => {
|
} else {
|
||||||
let window = window_from_node(self);
|
self.push_new_attribute(local_name, value, name, namespace, prefix);
|
||||||
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)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
(*self.attrs.borrow())[idx].root().r().set_value(set_type, value, self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_attribute(&self, namespace: &Namespace, local_name: &Atom,
|
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)
|
pub fn remove_attribute(&self, namespace: &Namespace, local_name: &Atom)
|
||||||
-> Option<Root<Attr>> {
|
-> Option<Root<Attr>> {
|
||||||
self.do_remove_attribute(|attr| {
|
self.remove_first_matching_attribute(|attr| {
|
||||||
attr.namespace() == namespace && attr.local_name() == local_name
|
attr.namespace() == namespace && attr.local_name() == local_name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_attribute_by_name(&self, name: &Atom) -> Option<Root<Attr>> {
|
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
|
where F: Fn(&Attr) -> bool
|
||||||
{
|
{
|
||||||
let idx = self.attrs.borrow().iter().map(JS::root).position(|attr| find(&attr));
|
let idx = self.attrs.borrow().iter().map(JS::root).position(|attr| find(&attr));
|
||||||
|
|
||||||
idx.map(|idx| {
|
idx.map(|idx| {
|
||||||
let attr = (*self.attrs.borrow())[idx].root();
|
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);
|
self.attrs.borrow_mut().remove(idx);
|
||||||
attr.r().set_owner(None);
|
attr.set_owner(None);
|
||||||
if attr.r().namespace() == &ns!("") {
|
|
||||||
vtable_for(&NodeCast::from_ref(self)).after_remove_attr(attr.r().name());
|
|
||||||
}
|
|
||||||
|
|
||||||
let node = NodeCast::from_ref(self);
|
let node = NodeCast::from_ref(self);
|
||||||
if node.is_in_doc() {
|
if attr.namespace() == &ns!("") {
|
||||||
let document = document_from_node(self);
|
vtable_for(node).attribute_mutated(&attr, AttributeMutation::Removed);
|
||||||
let damage = if attr.r().local_name() == &atom!("style") {
|
|
||||||
NodeDamage::NodeStyleDamaged
|
|
||||||
} else {
|
|
||||||
NodeDamage::OtherNodeDamage
|
|
||||||
};
|
|
||||||
document.r().content_changed(node, damage);
|
|
||||||
}
|
}
|
||||||
attr
|
attr
|
||||||
})
|
})
|
||||||
|
@ -1168,9 +1164,9 @@ impl ElementMethods for Element {
|
||||||
|
|
||||||
// Step 3-5.
|
// Step 3-5.
|
||||||
let value = self.parse_attribute(&ns!(""), &name, value);
|
let value = self.parse_attribute(&ns!(""), &name, value);
|
||||||
self.do_set_attribute(name.clone(), value, name.clone(), ns!(""), None, |attr| {
|
self.set_first_matching_attribute(
|
||||||
*attr.name() == name
|
name.clone(), value, name.clone(), ns!(""), None,
|
||||||
});
|
|attr| *attr.name() == name);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1183,11 +1179,9 @@ impl ElementMethods for Element {
|
||||||
try!(validate_and_extract(namespace, &qualified_name));
|
try!(validate_and_extract(namespace, &qualified_name));
|
||||||
let qualified_name = Atom::from_slice(&qualified_name);
|
let qualified_name = Atom::from_slice(&qualified_name);
|
||||||
let value = self.parse_attribute(&namespace, &local_name, value);
|
let value = self.parse_attribute(&namespace, &local_name, value);
|
||||||
self.do_set_attribute(local_name.clone(), value, qualified_name,
|
self.set_first_matching_attribute(
|
||||||
namespace.clone(), prefix, |attr| {
|
local_name.clone(), value, qualified_name, namespace.clone(), prefix,
|
||||||
*attr.local_name() == local_name &&
|
|attr| *attr.local_name() == local_name && *attr.namespace() == namespace);
|
||||||
*attr.namespace() == namespace
|
|
||||||
});
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1454,96 +1448,52 @@ impl VirtualMethods for Element {
|
||||||
Some(node as &VirtualMethods)
|
Some(node as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
let node = NodeCast::from_ref(self);
|
let node = NodeCast::from_ref(self);
|
||||||
match attr.local_name() {
|
let doc = node.owner_doc();
|
||||||
&atom!("style") => {
|
let damage = match attr.local_name() {
|
||||||
|
&atom!(style) => {
|
||||||
// Modifying the `style` attribute might change style.
|
// Modifying the `style` attribute might change style.
|
||||||
let doc = document_from_node(self);
|
*self.style_attribute.borrow_mut() =
|
||||||
let base_url = doc.r().base_url();
|
mutation.new_value(attr).map(|value| {
|
||||||
let value = attr.value();
|
parse_style_attribute(&value, &doc.base_url())
|
||||||
let style = Some(parse_style_attribute(&value, &base_url));
|
});
|
||||||
*self.style_attribute.borrow_mut() = style;
|
NodeDamage::NodeStyleDamaged
|
||||||
|
},
|
||||||
if node.is_in_doc() {
|
&atom!(class) => {
|
||||||
doc.r().content_changed(node, NodeDamage::NodeStyleDamaged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&atom!("class") => {
|
|
||||||
// Modifying a class can change style.
|
// Modifying a class can change style.
|
||||||
|
NodeDamage::NodeStyleDamaged
|
||||||
|
},
|
||||||
|
&atom!(id) => {
|
||||||
if node.is_in_doc() {
|
if node.is_in_doc() {
|
||||||
let document = document_from_node(self);
|
let value = attr.value().atom().unwrap().clone();
|
||||||
document.r().content_changed(node, NodeDamage::NodeStyleDamaged);
|
match mutation {
|
||||||
}
|
AttributeMutation::Set(old_value) => {
|
||||||
}
|
if let Some(old_value) = old_value {
|
||||||
&atom!("id") => {
|
let old_value = old_value.atom().unwrap().clone();
|
||||||
// Modifying an ID might change style.
|
doc.unregister_named_element(self, old_value);
|
||||||
let value = attr.value();
|
}
|
||||||
if node.is_in_doc() {
|
if value != atom!("") {
|
||||||
let doc = document_from_node(self);
|
doc.register_named_element(self, value);
|
||||||
if !value.is_empty() {
|
}
|
||||||
let value = value.atom().unwrap().clone();
|
},
|
||||||
doc.r().register_named_element(self, value);
|
AttributeMutation::Removed => {
|
||||||
|
if value != atom!("") {
|
||||||
|
doc.unregister_named_element(self, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
doc.r().content_changed(node, NodeDamage::NodeStyleDamaged);
|
|
||||||
}
|
}
|
||||||
}
|
NodeDamage::NodeStyleDamaged
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// Modifying any other attribute might change arbitrary things.
|
// Modifying any other attribute might change arbitrary things.
|
||||||
if node.is_in_doc() {
|
NodeDamage::OtherNodeDamage
|
||||||
let document = document_from_node(self);
|
},
|
||||||
document.r().content_changed(node, NodeDamage::OtherNodeDamage);
|
};
|
||||||
}
|
if node.is_in_doc() {
|
||||||
}
|
doc.content_changed(node, damage);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1853,3 +1803,23 @@ impl Element {
|
||||||
self.set_click_in_progress(false);
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use dom::bindings::codegen::InheritTypes::HTMLBaseElementDerived;
|
||||||
use dom::bindings::codegen::InheritTypes::HTMLElementCast;
|
use dom::bindings::codegen::InheritTypes::HTMLElementCast;
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId, document_from_node};
|
use dom::node::{Node, NodeTypeId, document_from_node};
|
||||||
|
@ -56,15 +56,6 @@ impl HTMLBaseElement {
|
||||||
parsed.unwrap_or(base)
|
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
|
/// Update the cached base element in response to binding or unbinding from
|
||||||
/// a tree.
|
/// a tree.
|
||||||
pub fn bind_unbind(&self, tree_in_doc: bool) {
|
pub fn bind_unbind(&self, tree_in_doc: bool) {
|
||||||
|
@ -84,14 +75,11 @@ impl VirtualMethods for HTMLBaseElement {
|
||||||
Some(HTMLElementCast::from_ref(self) as &VirtualMethods)
|
Some(HTMLElementCast::from_ref(self) as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
self.super_type().unwrap().after_set_attr(attr);
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
self.add_remove_attr(attr);
|
if *attr.local_name() == atom!(href) {
|
||||||
}
|
document_from_node(self).refresh_base_element();
|
||||||
|
}
|
||||||
fn before_remove_attr(&self, attr: &Attr) {
|
|
||||||
self.super_type().unwrap().before_remove_attr(attr);
|
|
||||||
self.add_remove_attr(attr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind_to_tree(&self, tree_in_doc: bool) {
|
fn bind_to_tree(&self, tree_in_doc: bool) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLElementCa
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::bindings::utils::Reflectable;
|
use dom::bindings::utils::Reflectable;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId, window_from_node, document_from_node};
|
use dom::node::{Node, NodeTypeId, window_from_node, document_from_node};
|
||||||
|
@ -123,56 +123,40 @@ impl VirtualMethods for HTMLBodyElement {
|
||||||
chan.send(event).unwrap();
|
chan.send(event).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
match (attr.local_name(), mutation) {
|
||||||
}
|
(&atom!(bgcolor), _) => {
|
||||||
|
self.background_color.set(mutation.new_value(attr).and_then(|value| {
|
||||||
let name = attr.local_name();
|
str::parse_legacy_color(&value).ok()
|
||||||
if name.starts_with("on") {
|
}));
|
||||||
static FORWARDED_EVENTS: &'static [&'static str] =
|
},
|
||||||
&["onfocus", "onload", "onscroll", "onafterprint", "onbeforeprint",
|
(&atom!(background), _) => {
|
||||||
"onbeforeunload", "onhashchange", "onlanguagechange", "onmessage",
|
*self.background.borrow_mut() = mutation.new_value(attr).and_then(|value| {
|
||||||
"onoffline", "ononline", "onpagehide", "onpageshow", "onpopstate",
|
let base = document_from_node(self).url();
|
||||||
"onstorage", "onresize", "onunload", "onerror"];
|
UrlParser::new().base_url(&base).parse(&value).ok()
|
||||||
let window = window_from_node(self);
|
});
|
||||||
let (cx, url, reflector) = (window.r().get_cx(),
|
},
|
||||||
window.r().get_url(),
|
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
|
||||||
window.r().reflector().get_jsobject());
|
static FORWARDED_EVENTS: &'static [&'static str] =
|
||||||
let evtarget =
|
&["onfocus", "onload", "onscroll", "onafterprint", "onbeforeprint",
|
||||||
if FORWARDED_EVENTS.iter().any(|&event| &**name == event) {
|
"onbeforeunload", "onhashchange", "onlanguagechange", "onmessage",
|
||||||
EventTargetCast::from_ref(window.r())
|
"onoffline", "ononline", "onpagehide", "onpageshow", "onpopstate",
|
||||||
} else {
|
"onstorage", "onresize", "onunload", "onerror"];
|
||||||
EventTargetCast::from_ref(self)
|
let window = window_from_node(self);
|
||||||
};
|
let (cx, url, reflector) = (window.get_cx(),
|
||||||
evtarget.set_event_handler_uncompiled(cx, url, reflector,
|
window.get_url(),
|
||||||
&name[2..],
|
window.reflector().get_jsobject());
|
||||||
(**attr.value()).to_owned());
|
let evtarget =
|
||||||
}
|
if FORWARDED_EVENTS.iter().any(|&event| &**name == event) {
|
||||||
|
EventTargetCast::from_ref(window.r())
|
||||||
match attr.local_name() {
|
} else {
|
||||||
&atom!("bgcolor") => {
|
EventTargetCast::from_ref(self)
|
||||||
self.background_color.set(str::parse_legacy_color(&attr.value()).ok())
|
};
|
||||||
}
|
evtarget.set_event_handler_uncompiled(cx, url, reflector,
|
||||||
&atom!("background") => {
|
&name[2..],
|
||||||
let doc = document_from_node(self);
|
(**attr.value()).to_owned());
|
||||||
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,
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLBut
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLButtonElementDerived, HTMLFieldSetElementDerived};
|
use dom::bindings::codegen::InheritTypes::{HTMLButtonElementDerived, HTMLFieldSetElementDerived};
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::{Element, ElementTypeId};
|
use dom::element::{AttributeMutation, Element, ElementTypeId};
|
||||||
use dom::event::Event;
|
use dom::event::Event;
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
|
@ -56,7 +56,7 @@ impl HTMLButtonElement {
|
||||||
HTMLButtonElement {
|
HTMLButtonElement {
|
||||||
htmlelement:
|
htmlelement:
|
||||||
HTMLElement::new_inherited(HTMLElementTypeId::HTMLButtonElement, localName, prefix, document),
|
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)
|
button_type: Cell::new(ButtonType::ButtonSubmit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,34 +135,25 @@ impl VirtualMethods for HTMLButtonElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("disabled") => {
|
&atom!(disabled) => {
|
||||||
let node = NodeCast::from_ref(self);
|
let node = NodeCast::from_ref(self);
|
||||||
node.set_disabled_state(true);
|
match mutation {
|
||||||
node.set_enabled_state(false);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => ()
|
_ => {},
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, HeapGCValue, Root};
|
||||||
use dom::bindings::utils::{Reflectable};
|
use dom::bindings::utils::{Reflectable};
|
||||||
use dom::canvasrenderingcontext2d::{CanvasRenderingContext2D, LayoutCanvasRenderingContext2DHelpers};
|
use dom::canvasrenderingcontext2d::{CanvasRenderingContext2D, LayoutCanvasRenderingContext2DHelpers};
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId, window_from_node};
|
use dom::node::{Node, NodeTypeId, window_from_node};
|
||||||
|
@ -256,46 +256,25 @@ impl VirtualMethods for HTMLCanvasElement {
|
||||||
Some(element as &VirtualMethods)
|
Some(element as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn before_remove_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.before_remove_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
let recreate = match attr.local_name() {
|
let recreate = match attr.local_name() {
|
||||||
&atom!("width") => {
|
&atom!(width) => {
|
||||||
self.width.set(DEFAULT_WIDTH);
|
let width = mutation.new_value(attr).and_then(|value| {
|
||||||
|
parse_unsigned_integer(value.chars())
|
||||||
|
});
|
||||||
|
self.width.set(width.unwrap_or(DEFAULT_WIDTH));
|
||||||
true
|
true
|
||||||
}
|
},
|
||||||
&atom!("height") => {
|
&atom!(height) => {
|
||||||
self.height.set(DEFAULT_HEIGHT);
|
let height = mutation.new_value(attr).and_then(|value| {
|
||||||
|
parse_unsigned_integer(value.chars())
|
||||||
|
});
|
||||||
|
self.height.set(height.unwrap_or(DEFAULT_HEIGHT));
|
||||||
true
|
true
|
||||||
}
|
},
|
||||||
_ => false,
|
_ => 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 {
|
if recreate {
|
||||||
self.recreate_contexts();
|
self.recreate_contexts();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ use dom::bindings::utils::Reflectable;
|
||||||
use dom::cssstyledeclaration::{CSSStyleDeclaration, CSSModificationAccess};
|
use dom::cssstyledeclaration::{CSSStyleDeclaration, CSSModificationAccess};
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::domstringmap::DOMStringMap;
|
use dom::domstringmap::DOMStringMap;
|
||||||
use dom::element::{Element, ElementTypeId};
|
use dom::element::{AttributeMutation, Element, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlinputelement::HTMLInputElement;
|
use dom::htmlinputelement::HTMLInputElement;
|
||||||
use dom::htmlmediaelement::HTMLMediaElementTypeId;
|
use dom::htmlmediaelement::HTMLMediaElementTypeId;
|
||||||
|
@ -313,37 +313,23 @@ impl VirtualMethods for HTMLElement {
|
||||||
Some(element as &VirtualMethods)
|
Some(element as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn before_remove_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.before_remove_attr(attr);
|
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());
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_remove_attr(&self, name: &Atom) {
|
|
||||||
if let Some(ref s) = self.super_type() {
|
|
||||||
s.after_remove_attr(name);
|
|
||||||
}
|
|
||||||
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) {
|
fn bind_to_tree(&self, tree_in_doc: bool) {
|
||||||
if let Some(ref s) = self.super_type() {
|
if let Some(ref s) = self.super_type() {
|
||||||
s.bind_to_tree(tree_in_doc);
|
s.bind_to_tree(tree_in_doc);
|
||||||
|
|
|
@ -9,7 +9,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLLegendElementDer
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLFieldSetElementDerived, NodeCast};
|
use dom::bindings::codegen::InheritTypes::{HTMLFieldSetElementDerived, NodeCast};
|
||||||
use dom::bindings::js::{Root, RootedReference};
|
use dom::bindings::js::{Root, RootedReference};
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::{Element, ElementTypeId};
|
use dom::element::{AttributeMutation, Element, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlcollection::{HTMLCollection, CollectionFilter};
|
use dom::htmlcollection::{HTMLCollection, CollectionFilter};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
|
@ -88,83 +88,66 @@ impl VirtualMethods for HTMLFieldSetElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
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);
|
let node = NodeCast::from_ref(self);
|
||||||
node.set_disabled_state(true);
|
node.set_disabled_state(disabled_state);
|
||||||
node.set_enabled_state(false);
|
node.set_enabled_state(!disabled_state);
|
||||||
let maybe_legend = node.children()
|
let mut found_legend = false;
|
||||||
.find(|node| node.r().is_htmllegendelement());
|
let children = node.children().filter(|node| {
|
||||||
|
if found_legend {
|
||||||
for child in node.children() {
|
true
|
||||||
if Some(child.r()) == maybe_legend.r() {
|
} else if node.is_htmllegendelement() {
|
||||||
continue;
|
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() {
|
match descendant.r().type_id() {
|
||||||
NodeTypeId::Element(
|
NodeTypeId::Element(
|
||||||
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) |
|
ElementTypeId::HTMLElement(
|
||||||
|
HTMLElementTypeId::HTMLButtonElement)) |
|
||||||
NodeTypeId::Element(
|
NodeTypeId::Element(
|
||||||
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
|
ElementTypeId::HTMLElement(
|
||||||
|
HTMLElementTypeId::HTMLInputElement)) |
|
||||||
NodeTypeId::Element(
|
NodeTypeId::Element(
|
||||||
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
|
ElementTypeId::HTMLElement(
|
||||||
|
HTMLElementTypeId::HTMLSelectElement)) |
|
||||||
NodeTypeId::Element(
|
NodeTypeId::Element(
|
||||||
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
|
ElementTypeId::HTMLElement(
|
||||||
descendant.r().set_disabled_state(true);
|
HTMLElementTypeId::HTMLTextAreaElement)) => {
|
||||||
descendant.r().set_enabled_state(false);
|
true
|
||||||
},
|
},
|
||||||
_ => ()
|
_ => false,
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if disabled_state {
|
||||||
|
for field in fields {
|
||||||
|
field.set_disabled_state(true);
|
||||||
|
field.set_enabled_state(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for field in fields {
|
||||||
|
field.check_disabled_attribute();
|
||||||
|
field.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);
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use dom::bindings::codegen::Bindings::HTMLFontElementBinding::HTMLFontElementMet
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLFontElementDerived};
|
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLFontElementDerived};
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId};
|
use dom::node::{Node, NodeTypeId};
|
||||||
|
@ -60,27 +60,15 @@ impl VirtualMethods for HTMLFontElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("color") => {
|
&atom!(color) => {
|
||||||
self.color.set(str::parse_legacy_color(&attr.value()).ok())
|
self.color.set(mutation.new_value(attr).and_then(|value| {
|
||||||
}
|
str::parse_legacy_color(&value).ok()
|
||||||
_ => {}
|
}));
|
||||||
}
|
},
|
||||||
}
|
_ => {},
|
||||||
|
|
||||||
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!("color") => self.color.set(None),
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ use dom::bindings::js::{Root};
|
||||||
use dom::bindings::utils::Reflectable;
|
use dom::bindings::utils::Reflectable;
|
||||||
use dom::customevent::CustomEvent;
|
use dom::customevent::CustomEvent;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::{ElementTypeId, self};
|
use dom::element::{AttributeMutation, ElementTypeId, self};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId, window_from_node};
|
use dom::node::{Node, NodeTypeId, window_from_node};
|
||||||
|
@ -354,16 +354,13 @@ impl VirtualMethods for HTMLIFrameElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("sandbox") => {
|
&atom!(sandbox) => {
|
||||||
let mut modes = SandboxAllowance::AllowNothing as u8;
|
self.sandbox.set(mutation.new_value(attr).map(|value| {
|
||||||
if let Some(ref tokens) = attr.value().tokens() {
|
let mut modes = SandboxAllowance::AllowNothing as u8;
|
||||||
for token in *tokens {
|
for token in value.tokens().unwrap() {
|
||||||
modes |= match &*token.to_ascii_lowercase() {
|
modes |= match &*token.to_ascii_lowercase() {
|
||||||
"allow-same-origin" => SandboxAllowance::AllowSameOrigin,
|
"allow-same-origin" => SandboxAllowance::AllowSameOrigin,
|
||||||
"allow-forms" => SandboxAllowance::AllowForms,
|
"allow-forms" => SandboxAllowance::AllowForms,
|
||||||
|
@ -374,16 +371,17 @@ impl VirtualMethods for HTMLIFrameElement {
|
||||||
_ => SandboxAllowance::AllowNothing
|
_ => SandboxAllowance::AllowNothing
|
||||||
} as u8;
|
} as u8;
|
||||||
}
|
}
|
||||||
}
|
modes
|
||||||
self.sandbox.set(Some(modes));
|
}));
|
||||||
}
|
},
|
||||||
&atom!("src") => {
|
&atom!(src) => {
|
||||||
let node = NodeCast::from_ref(self);
|
if let AttributeMutation::Set(_) = mutation {
|
||||||
if node.is_in_doc() {
|
if NodeCast::from_ref(self).is_in_doc() {
|
||||||
self.process_the_iframe_attributes()
|
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) {
|
fn bind_to_tree(&self, tree_in_doc: bool) {
|
||||||
if let Some(ref s) = self.super_type() {
|
if let Some(ref s) = self.super_type() {
|
||||||
s.bind_to_tree(tree_in_doc);
|
s.bind_to_tree(tree_in_doc);
|
||||||
|
|
|
@ -15,7 +15,7 @@ use dom::bindings::global::GlobalRef;
|
||||||
use dom::bindings::js::{LayoutJS, Root};
|
use dom::bindings::js::{LayoutJS, Root};
|
||||||
use dom::bindings::refcounted::Trusted;
|
use dom::bindings::refcounted::Trusted;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
|
@ -108,7 +108,7 @@ impl Runnable for ImageResponseHandlerRunnable {
|
||||||
impl HTMLImageElement {
|
impl HTMLImageElement {
|
||||||
/// Makes the local `image` member match the status of the `src` attribute and starts
|
/// 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.
|
/// 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 node = NodeCast::from_ref(self);
|
||||||
let document = node.owner_doc();
|
let document = node.owner_doc();
|
||||||
let window = document.r().window();
|
let window = document.r().window();
|
||||||
|
@ -120,7 +120,7 @@ impl HTMLImageElement {
|
||||||
*self.image.borrow_mut() = None;
|
*self.image.borrow_mut() = None;
|
||||||
}
|
}
|
||||||
Some((src, base_url)) => {
|
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.
|
// FIXME: handle URL parse errors more gracefully.
|
||||||
let img_url = img_url.unwrap();
|
let img_url = img_url.unwrap();
|
||||||
*self.url.borrow_mut() = Some(img_url.clone());
|
*self.url.borrow_mut() = Some(img_url.clone());
|
||||||
|
@ -300,29 +300,15 @@ impl VirtualMethods for HTMLImageElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("src") => {
|
&atom!(src) => {
|
||||||
let window = window_from_node(self);
|
self.update_image(mutation.new_value(attr).map(|value| {
|
||||||
let url = window.r().get_url();
|
((**value).to_owned(), window_from_node(self).get_url())
|
||||||
self.update_image(Some(((**attr.value()).to_owned(), &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),
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSet
|
||||||
use dom::bindings::global::GlobalRef;
|
use dom::bindings::global::GlobalRef;
|
||||||
use dom::bindings::js::{JS, LayoutJS, Root, RootedReference};
|
use dom::bindings::js::{JS, LayoutJS, Root, RootedReference};
|
||||||
use dom::document::Document;
|
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::event::{Event, EventBubbles, EventCancelable};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
|
@ -447,113 +447,87 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
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);
|
let node = NodeCast::from_ref(self);
|
||||||
node.set_disabled_state(true);
|
node.set_disabled_state(disabled_state);
|
||||||
node.set_enabled_state(false);
|
node.set_enabled_state(!disabled_state);
|
||||||
}
|
|
||||||
&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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
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") => {
|
&atom!(type) => {
|
||||||
// https://html.spec.whatwg.org/multipage/#the-input-element:concept-input-checked-dirty
|
match mutation {
|
||||||
if !self.checked_changed.get() {
|
AttributeMutation::Set(_) => {
|
||||||
self.update_checked_state(false, false);
|
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!("size") => {
|
&atom!(value) if !self.value_changed.get() => {
|
||||||
self.size.set(DEFAULT_INPUT_SIZE);
|
let value = mutation.new_value(attr).map(|value| (**value).to_owned());
|
||||||
}
|
self.textinput.borrow_mut().set_content(
|
||||||
&atom!("type") => {
|
value.unwrap_or_else(|| "".to_owned()));
|
||||||
if self.input_type.get() == InputType::InputRadio {
|
},
|
||||||
broadcast_radio_checked(self,
|
&atom!(name) if self.input_type.get() == InputType::InputRadio => {
|
||||||
self.get_radio_group_name()
|
self.radio_group_updated(
|
||||||
.as_ref()
|
mutation.new_value(attr).as_ref().map(|value| &***value));
|
||||||
.map(|group| &**group));
|
},
|
||||||
|
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'));
|
||||||
}
|
}
|
||||||
self.input_type.set(InputType::InputText);
|
},
|
||||||
}
|
_ => {},
|
||||||
&atom!("value") => {
|
|
||||||
if !self.value_changed.get() {
|
|
||||||
self.textinput.borrow_mut().set_content("".to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&atom!("name") => {
|
|
||||||
if self.input_type.get() == InputType::InputRadio {
|
|
||||||
self.radio_group_updated(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ if attr.local_name() == &Atom::from_slice("placeholder") => {
|
|
||||||
self.placeholder.borrow_mut().clear();
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ use dom::bindings::js::{RootedReference};
|
||||||
use dom::bindings::refcounted::Trusted;
|
use dom::bindings::refcounted::Trusted;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::domtokenlist::DOMTokenList;
|
use dom::domtokenlist::DOMTokenList;
|
||||||
use dom::element::{Element, ElementTypeId};
|
use dom::element::{AttributeMutation, Element, ElementTypeId};
|
||||||
use dom::event::{EventBubbles, EventCancelable, Event};
|
use dom::event::{EventBubbles, EventCancelable, Event};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
|
@ -103,33 +103,26 @@ impl VirtualMethods for HTMLLinkElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
if !NodeCast::from_ref(self).is_in_doc() || mutation == AttributeMutation::Removed {
|
||||||
}
|
|
||||||
|
|
||||||
let node = NodeCast::from_ref(self);
|
|
||||||
if !node.is_in_doc() {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let rel = get_attr(ElementCast::from_ref(self), &atom!(rel));
|
||||||
let element = ElementCast::from_ref(self);
|
match attr.local_name() {
|
||||||
let rel = get_attr(element, &atom!("rel"));
|
&atom!(href) => {
|
||||||
|
if is_stylesheet(&rel) {
|
||||||
match (rel, attr.local_name()) {
|
|
||||||
(ref rel, &atom!("href")) => {
|
|
||||||
if is_stylesheet(rel) {
|
|
||||||
self.handle_stylesheet_url(&attr.value());
|
self.handle_stylesheet_url(&attr.value());
|
||||||
} else if is_favicon(rel) {
|
} else if is_favicon(&rel) {
|
||||||
self.handle_favicon_url(&attr.value());
|
self.handle_favicon_url(&attr.value());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
(ref rel, &atom!("media")) => {
|
&atom!(media) => {
|
||||||
if is_stylesheet(rel) {
|
if is_stylesheet(&rel) {
|
||||||
self.handle_stylesheet_url(&attr.value());
|
self.handle_stylesheet_url(&attr.value());
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
(_, _) => ()
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use dom::bindings::codegen::InheritTypes::HTMLObjectElementDerived;
|
||||||
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
|
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId, window_from_node};
|
use dom::node::{Node, NodeTypeId, window_from_node};
|
||||||
|
@ -101,16 +101,15 @@ impl VirtualMethods for HTMLObjectElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("data") => {
|
&atom!(data) => {
|
||||||
self.process_data_url();
|
if let AttributeMutation::Set(_) = mutation {
|
||||||
|
self.process_data_url();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => ()
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLElementCast, NodeCast};
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLOptGroupElementDerived, HTMLOptionElementDerived};
|
use dom::bindings::codegen::InheritTypes::{HTMLOptGroupElementDerived, HTMLOptionElementDerived};
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId};
|
use dom::node::{Node, NodeTypeId};
|
||||||
|
@ -63,44 +63,36 @@ impl VirtualMethods for HTMLOptGroupElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
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);
|
let node = NodeCast::from_ref(self);
|
||||||
node.set_disabled_state(true);
|
node.set_disabled_state(disabled_state);
|
||||||
node.set_enabled_state(false);
|
node.set_enabled_state(!disabled_state);
|
||||||
for child in node.children() {
|
let options = node.children().filter(|child| {
|
||||||
if child.r().is_htmloptionelement() {
|
child.is_htmloptionelement()
|
||||||
child.r().set_disabled_state(true);
|
});
|
||||||
child.r().set_enabled_state(false);
|
if disabled_state {
|
||||||
|
for option in options {
|
||||||
|
option.set_disabled_state(true);
|
||||||
|
option.set_enabled_state(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for option in options {
|
||||||
|
option.check_disabled_attribute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => {},
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLOptionElementDerived};
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived};
|
use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived};
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId};
|
use dom::node::{Node, NodeTypeId};
|
||||||
|
@ -131,34 +131,24 @@ impl VirtualMethods for HTMLOptionElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("disabled") => {
|
&atom!(disabled) => {
|
||||||
let node = NodeCast::from_ref(self);
|
let node = NodeCast::from_ref(self);
|
||||||
node.set_disabled_state(true);
|
match mutation {
|
||||||
node.set_enabled_state(false);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => ()
|
_ => {},
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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_parent_disabled_state_for_option();
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ use dom::bindings::js::{JS, Root};
|
||||||
use dom::bindings::refcounted::Trusted;
|
use dom::bindings::refcounted::Trusted;
|
||||||
use dom::bindings::trace::JSTraceable;
|
use dom::bindings::trace::JSTraceable;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::{ElementCreator, ElementTypeId};
|
use dom::element::{AttributeMutation, ElementCreator, ElementTypeId};
|
||||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
|
@ -532,13 +532,17 @@ impl VirtualMethods for HTMLScriptElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
match attr.local_name() {
|
||||||
}
|
&atom!("src") => {
|
||||||
let node = NodeCast::from_ref(self);
|
if let AttributeMutation::Set(_) = mutation {
|
||||||
if attr.local_name() == &atom!("src") && !self.parser_inserted.get() && node.is_in_doc() {
|
if !self.parser_inserted.get() && NodeCast::from_ref(self).is_in_doc() {
|
||||||
self.prepare();
|
self.prepare();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ use dom::bindings::codegen::UnionTypes::HTMLElementOrLong;
|
||||||
use dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement;
|
use dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement;
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId, window_from_node};
|
use dom::node::{Node, NodeTypeId, window_from_node};
|
||||||
|
@ -109,34 +109,21 @@ impl VirtualMethods for HTMLSelectElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
if attr.local_name() == &atom!(disabled) {
|
||||||
}
|
let node = NodeCast::from_ref(self);
|
||||||
|
match mutation {
|
||||||
match attr.local_name() {
|
AttributeMutation::Set(_) => {
|
||||||
&atom!("disabled") => {
|
node.set_disabled_state(true);
|
||||||
let node = NodeCast::from_ref(self);
|
node.set_enabled_state(false);
|
||||||
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();
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use dom::attr::{Attr, AttrValue};
|
||||||
use dom::bindings::codegen::Bindings::HTMLTableCellElementBinding::HTMLTableCellElementMethods;
|
use dom::bindings::codegen::Bindings::HTMLTableCellElementBinding::HTMLTableCellElementMethods;
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableCellElementDerived};
|
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableCellElementDerived};
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::NodeTypeId;
|
use dom::node::NodeTypeId;
|
||||||
|
@ -101,38 +101,26 @@ impl VirtualMethods for HTMLTableCellElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("bgcolor") => {
|
&atom!(bgcolor) => {
|
||||||
self.background_color.set(str::parse_legacy_color(&attr.value()).ok())
|
self.background_color.set(mutation.new_value(attr).and_then(|value| {
|
||||||
}
|
str::parse_legacy_color(&value).ok()
|
||||||
&atom!("colspan") => {
|
}));
|
||||||
match *attr.value() {
|
|
||||||
AttrValue::UInt(_, colspan) => {
|
|
||||||
self.colspan.set(Some(max(DEFAULT_COLSPAN, colspan)))
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
&atom!("width") => self.width.set(str::parse_length(&attr.value())),
|
&atom!(colspan) => {
|
||||||
_ => ()
|
self.colspan.set(mutation.new_value(attr).map(|value| {
|
||||||
}
|
max(DEFAULT_COLSPAN, value.uint().unwrap())
|
||||||
}
|
}));
|
||||||
|
},
|
||||||
fn before_remove_attr(&self, attr: &Attr) {
|
&atom!(width) => {
|
||||||
if let Some(ref s) = self.super_type() {
|
let width = mutation.new_value(attr).map(|value| {
|
||||||
s.before_remove_attr(attr);
|
str::parse_length(&value)
|
||||||
}
|
});
|
||||||
|
self.width.set(width.unwrap_or(LengthOrPercentageOrAuto::Auto));
|
||||||
match attr.local_name() {
|
},
|
||||||
&atom!("bgcolor") => self.background_color.set(None),
|
_ => {},
|
||||||
&atom!("colspan") => self.colspan.set(None),
|
|
||||||
&atom!("width") => self.width.set(LengthOrPercentageOrAuto::Auto),
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLTab
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast};
|
use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, NodeCast};
|
||||||
use dom::bindings::js::{Root, RootedReference};
|
use dom::bindings::js::{Root, RootedReference};
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::htmltablecaptionelement::HTMLTableCaptionElement;
|
use dom::htmltablecaptionelement::HTMLTableCaptionElement;
|
||||||
|
@ -157,39 +157,32 @@ impl VirtualMethods for HTMLTableElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("bgcolor") => {
|
&atom!(bgcolor) => {
|
||||||
self.background_color.set(str::parse_legacy_color(&attr.value()).ok())
|
self.background_color.set(mutation.new_value(attr).and_then(|value| {
|
||||||
}
|
str::parse_legacy_color(&value).ok()
|
||||||
&atom!("border") => {
|
}));
|
||||||
|
},
|
||||||
|
&atom!(border) => {
|
||||||
// According to HTML5 § 14.3.9, invalid values map to 1px.
|
// According to HTML5 § 14.3.9, invalid values map to 1px.
|
||||||
self.border.set(Some(str::parse_unsigned_integer(attr.value()
|
self.border.set(mutation.new_value(attr).map(|value| {
|
||||||
.chars()).unwrap_or(1)))
|
str::parse_unsigned_integer(value.chars()).unwrap_or(1)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
&atom!("cellspacing") => {
|
&atom!(cellspacing) => {
|
||||||
self.cellspacing.set(str::parse_unsigned_integer(attr.value().chars()))
|
self.cellspacing.set(mutation.new_value(attr).and_then(|value| {
|
||||||
}
|
str::parse_unsigned_integer(value.chars())
|
||||||
&atom!("width") => self.width.set(str::parse_length(&attr.value())),
|
}));
|
||||||
_ => ()
|
},
|
||||||
}
|
&atom!(width) => {
|
||||||
}
|
let width = mutation.new_value(attr).map(|value| {
|
||||||
|
str::parse_length(&value)
|
||||||
fn before_remove_attr(&self, attr: &Attr) {
|
});
|
||||||
if let Some(ref s) = self.super_type() {
|
self.width.set(width.unwrap_or(LengthOrPercentageOrAuto::Auto));
|
||||||
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),
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use dom::bindings::codegen::Bindings::HTMLTableRowElementBinding;
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableRowElementDerived};
|
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableRowElementDerived};
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId};
|
use dom::node::{Node, NodeTypeId};
|
||||||
|
@ -62,29 +62,15 @@ impl VirtualMethods for HTMLTableRowElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("bgcolor") => {
|
&atom!(bgcolor) => {
|
||||||
self.background_color.set(str::parse_legacy_color(&attr.value()).ok());
|
self.background_color.set(mutation.new_value(attr).and_then(|value| {
|
||||||
|
str::parse_legacy_color(&value).ok()
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
_ => ()
|
_ => {},
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use dom::bindings::codegen::Bindings::HTMLTableSectionElementBinding;
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableSectionElementDerived};
|
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTableSectionElementDerived};
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
use dom::node::{Node, NodeTypeId};
|
use dom::node::{Node, NodeTypeId};
|
||||||
|
@ -61,29 +61,15 @@ impl VirtualMethods for HTMLTableSectionElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("bgcolor") => {
|
&atom!(bgcolor) => {
|
||||||
self.background_color.set(str::parse_legacy_color(&attr.value()).ok());
|
self.background_color.set(mutation.new_value(attr).and_then(|value| {
|
||||||
|
str::parse_legacy_color(&value).ok()
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
_ => ()
|
_ => {},
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use dom::bindings::global::GlobalRef;
|
||||||
use dom::bindings::js::{LayoutJS, Root};
|
use dom::bindings::js::{LayoutJS, Root};
|
||||||
use dom::bindings::refcounted::Trusted;
|
use dom::bindings::refcounted::Trusted;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::{Element, ElementTypeId};
|
use dom::element::{AttributeMutation, Element, ElementTypeId};
|
||||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||||
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
use dom::eventtarget::{EventTarget, EventTargetTypeId};
|
||||||
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
|
||||||
|
@ -242,52 +242,36 @@ impl VirtualMethods for HTMLTextAreaElement {
|
||||||
Some(htmlelement as &VirtualMethods)
|
Some(htmlelement as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
if let Some(ref s) = self.super_type() {
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
s.after_set_attr(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
&atom!("disabled") => {
|
&atom!(disabled) => {
|
||||||
let node = NodeCast::from_ref(self);
|
let node = NodeCast::from_ref(self);
|
||||||
node.set_disabled_state(true);
|
match mutation {
|
||||||
node.set_enabled_state(false);
|
AttributeMutation::Set(_) => {
|
||||||
},
|
node.set_disabled_state(true);
|
||||||
&atom!("cols") => {
|
node.set_enabled_state(false);
|
||||||
match *attr.value() {
|
},
|
||||||
AttrValue::UInt(_, value) => self.cols.set(value),
|
AttributeMutation::Removed => {
|
||||||
_ => panic!("Expected an AttrValue::UInt"),
|
node.set_disabled_state(false);
|
||||||
|
node.set_enabled_state(true);
|
||||||
|
node.check_ancestors_disabled_state_for_form_control();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&atom!("rows") => {
|
&atom!(cols) => {
|
||||||
match *attr.value() {
|
let cols = mutation.new_value(attr).map(|value| {
|
||||||
AttrValue::UInt(_, value) => self.rows.set(value),
|
value.uint().expect("Expected an AttrValue::UInt")
|
||||||
_ => panic!("Expected an AttrValue::UInt"),
|
});
|
||||||
}
|
self.cols.set(cols.unwrap_or(DEFAULT_COLS));
|
||||||
},
|
},
|
||||||
_ => ()
|
&atom!(rows) => {
|
||||||
}
|
let rows = mutation.new_value(attr).map(|value| {
|
||||||
}
|
value.uint().expect("Expected an AttrValue::UInt")
|
||||||
|
});
|
||||||
fn before_remove_attr(&self, attr: &Attr) {
|
self.rows.set(rows.unwrap_or(DEFAULT_ROWS));
|
||||||
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!("rows") => {
|
|
||||||
self.rows.set(DEFAULT_ROWS);
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ use dom::bindings::codegen::InheritTypes::HTMLTableSectionElementCast;
|
||||||
use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
|
use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
|
||||||
use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast;
|
use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::ElementTypeId;
|
use dom::element::{AttributeMutation, ElementTypeId};
|
||||||
use dom::event::Event;
|
use dom::event::Event;
|
||||||
use dom::htmlelement::HTMLElementTypeId;
|
use dom::htmlelement::HTMLElementTypeId;
|
||||||
use dom::node::NodeTypeId;
|
use dom::node::NodeTypeId;
|
||||||
|
@ -50,27 +50,12 @@ pub trait VirtualMethods {
|
||||||
/// if any.
|
/// if any.
|
||||||
fn super_type(&self) -> Option<&VirtualMethods>;
|
fn super_type(&self) -> Option<&VirtualMethods>;
|
||||||
|
|
||||||
/// Called when changing or adding attributes, after the attribute's value
|
/// Called when attributes of a node are mutated.
|
||||||
/// has been updated.
|
/// https://dom.spec.whatwg.org/#attribute-is-set
|
||||||
fn after_set_attr(&self, attr: &Attr) {
|
/// https://dom.spec.whatwg.org/#attribute-is-removed
|
||||||
if let Some(ref s) = self.super_type() {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
s.after_set_attr(attr);
|
if let Some(s) = self.super_type() {
|
||||||
}
|
s.attribute_mutated(attr, mutation);
|
||||||
}
|
|
||||||
|
|
||||||
/// 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,7 @@
|
||||||
test(function() {
|
test(function() {
|
||||||
var optgroup = document.createElement("optgroup");
|
var optgroup = document.createElement("optgroup");
|
||||||
optgroup.disabled = true;
|
optgroup.disabled = true;
|
||||||
|
check_disabled_selector(optgroup, true);
|
||||||
|
|
||||||
var option = document.createElement("option");
|
var option = document.createElement("option");
|
||||||
check_disabled_selector(option, false);
|
check_disabled_selector(option, false);
|
||||||
|
|
|
@ -48,6 +48,9 @@
|
||||||
document.getElementById("button1").setAttribute("disabled", "disabled");
|
document.getElementById("button1").setAttribute("disabled", "disabled");
|
||||||
testSelector(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set");
|
testSelector(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set");
|
||||||
|
|
||||||
|
document.getElementById("button1").setAttribute("disabled", "disabled");
|
||||||
|
testSelector(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match elements whose disabled attribute has been set twice");
|
||||||
|
|
||||||
document.getElementById("input2").setAttribute("type", "submit"); // change input type to submit
|
document.getElementById("input2").setAttribute("type", "submit"); // change input type to submit
|
||||||
testSelector(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match disabled elements whose type has changed");
|
testSelector(":disabled", ["button1", "input2", "select2", "optgroup2", "option2", "textarea2", "fieldset2", "clubname", "clubnum"], "':disabled' should also match disabled elements whose type has changed");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue