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::{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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
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::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();
|
||||
}
|
||||
|
|
|
@ -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 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());
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if let Some(ref s) = self.super_type() {
|
||||
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::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);
|
||||
}
|
||||
} 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::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);
|
||||
}
|
||||
|
||||
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(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);
|
||||
}
|
||||
|
||||
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()
|
||||
}));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
_ => ()
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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(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);
|
||||
}
|
||||
|
||||
match attr.local_name() {
|
||||
&atom!("disabled") => {
|
||||
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!(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!("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));
|
||||
},
|
||||
&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'));
|
||||
}
|
||||
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::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());
|
||||
}
|
||||
}
|
||||
(_, _) => ()
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
} 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::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);
|
||||
}
|
||||
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
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::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();
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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!(bgcolor) => {
|
||||
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::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);
|
||||
}
|
||||
|
||||
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!(bgcolor) => {
|
||||
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::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"),
|
||||
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();
|
||||
}
|
||||
}
|
||||
},
|
||||
&atom!("rows") => {
|
||||
match *attr.value() {
|
||||
AttrValue::UInt(_, value) => self.rows.set(value),
|
||||
_ => panic!("Expected an AttrValue::UInt"),
|
||||
}
|
||||
&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));
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
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!(rows) => {
|
||||
let rows = mutation.new_value(attr).map(|value| {
|
||||
value.uint().expect("Expected an AttrValue::UInt")
|
||||
});
|
||||
self.rows.set(rows.unwrap_or(DEFAULT_ROWS));
|
||||
},
|
||||
&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::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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -170,6 +170,7 @@
|
|||
test(function() {
|
||||
var optgroup = document.createElement("optgroup");
|
||||
optgroup.disabled = true;
|
||||
check_disabled_selector(optgroup, true);
|
||||
|
||||
var option = document.createElement("option");
|
||||
check_disabled_selector(option, false);
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
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");
|
||||
|
||||
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
|
||||
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