Implements dirty value/checked flags for input

And modifies test-inputs.html to test.

Fixes wpt breaking mistake
This commit is contained in:
Matthew Rasmus 2014-12-08 10:20:09 -08:00
parent 38e4d86b14
commit 0c8e1aeda3
3 changed files with 76 additions and 15 deletions

View file

@ -63,7 +63,9 @@ pub struct HTMLInputElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
input_type: Cell<InputType>, input_type: Cell<InputType>,
checked: Cell<bool>, checked: Cell<bool>,
checked_changed: Cell<bool>,
indeterminate: Cell<bool>, indeterminate: Cell<bool>,
value_changed: Cell<bool>,
size: Cell<u32>, size: Cell<u32>,
textinput: DOMRefCell<TextInput>, textinput: DOMRefCell<TextInput>,
activation_state: DOMRefCell<InputActivationState>, activation_state: DOMRefCell<InputActivationState>,
@ -74,6 +76,7 @@ pub struct HTMLInputElement {
struct InputActivationState { struct InputActivationState {
indeterminate: bool, indeterminate: bool,
checked: bool, checked: bool,
checked_changed: bool,
checked_radio: MutNullableJS<HTMLInputElement>, checked_radio: MutNullableJS<HTMLInputElement>,
// In case mutability changed // In case mutability changed
was_mutable: bool, was_mutable: bool,
@ -86,6 +89,7 @@ impl InputActivationState {
InputActivationState { InputActivationState {
indeterminate: false, indeterminate: false,
checked: false, checked: false,
checked_changed: false,
checked_radio: Default::default(), checked_radio: Default::default(),
was_mutable: false, was_mutable: false,
old_type: InputText old_type: InputText
@ -108,6 +112,8 @@ impl HTMLInputElement {
input_type: Cell::new(InputText), input_type: Cell::new(InputText),
checked: Cell::new(false), checked: Cell::new(false),
indeterminate: Cell::new(false), indeterminate: Cell::new(false),
checked_changed: Cell::new(false),
value_changed: Cell::new(false),
size: Cell::new(DEFAULT_INPUT_SIZE), size: Cell::new(DEFAULT_INPUT_SIZE),
textinput: DOMRefCell::new(TextInput::new(Single, "".to_string())), textinput: DOMRefCell::new(TextInput::new(Single, "".to_string())),
activation_state: DOMRefCell::new(InputActivationState::new()) activation_state: DOMRefCell::new(InputActivationState::new())
@ -196,7 +202,7 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-checked // https://html.spec.whatwg.org/multipage/forms.html#dom-input-checked
fn SetChecked(self, checked: bool) { fn SetChecked(self, checked: bool) {
self.update_checked_state(checked); self.update_checked_state(checked, true);
} }
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-readonly // https://html.spec.whatwg.org/multipage/forms.html#dom-input-readonly
@ -231,6 +237,7 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-value // https://html.spec.whatwg.org/multipage/forms.html#dom-input-value
fn SetValue(self, value: DOMString) { fn SetValue(self, value: DOMString) {
self.textinput.borrow_mut().set_content(value); self.textinput.borrow_mut().set_content(value);
self.value_changed.set(true);
self.force_relayout(); self.force_relayout();
} }
@ -286,7 +293,7 @@ pub trait HTMLInputElementHelpers {
fn force_relayout(self); fn force_relayout(self);
fn radio_group_updated(self, group: Option<&str>); fn radio_group_updated(self, group: Option<&str>);
fn get_radio_group_name(self) -> Option<String>; fn get_radio_group_name(self) -> Option<String>;
fn update_checked_state(self, checked: bool); fn update_checked_state(self, checked: bool, dirty: bool);
fn get_size(&self) -> u32; fn get_size(&self) -> u32;
} }
@ -346,8 +353,13 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
.map(|name| name.Value()) .map(|name| name.Value())
} }
fn update_checked_state(self, checked: bool) { fn update_checked_state(self, checked: bool, dirty: bool) {
self.checked.set(checked); self.checked.set(checked);
if dirty {
self.checked_changed.set(true);
}
if self.input_type.get() == InputRadio && checked { if self.input_type.get() == InputRadio && checked {
broadcast_radio_checked(self, broadcast_radio_checked(self,
self.get_radio_group_name() self.get_radio_group_name()
@ -382,7 +394,10 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
node.set_enabled_state(false); node.set_enabled_state(false);
} }
&atom!("checked") => { &atom!("checked") => {
self.update_checked_state(true); // https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-input-checked-dirty
if !self.checked_changed.get() {
self.update_checked_state(true, false);
}
} }
&atom!("size") => { &atom!("size") => {
match *attr.value() { match *attr.value() {
@ -411,8 +426,10 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
self.force_relayout(); self.force_relayout();
} }
&atom!("value") => { &atom!("value") => {
self.textinput.borrow_mut().set_content(attr.value().as_slice().to_string()); if !self.value_changed.get() {
self.force_relayout(); self.textinput.borrow_mut().set_content(attr.value().as_slice().to_string());
self.force_relayout();
}
} }
&atom!("name") => { &atom!("name") => {
if self.input_type.get() == InputRadio { if self.input_type.get() == InputRadio {
@ -438,7 +455,10 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
node.check_ancestors_disabled_state_for_form_control(); node.check_ancestors_disabled_state_for_form_control();
} }
&atom!("checked") => { &atom!("checked") => {
self.update_checked_state(false); // https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-input-checked-dirty
if !self.checked_changed.get() {
self.update_checked_state(false, false);
}
} }
&atom!("size") => { &atom!("size") => {
self.size.set(DEFAULT_INPUT_SIZE); self.size.set(DEFAULT_INPUT_SIZE);
@ -455,8 +475,10 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
self.force_relayout(); self.force_relayout();
} }
&atom!("value") => { &atom!("value") => {
self.textinput.borrow_mut().set_content("".to_string()); if !self.value_changed.get() {
self.force_relayout(); self.textinput.borrow_mut().set_content("".to_string());
self.force_relayout();
}
} }
&atom!("name") => { &atom!("name") => {
if self.input_type.get() == InputRadio { if self.input_type.get() == InputRadio {
@ -508,7 +530,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
if "click" == event.Type().as_slice() && !event.DefaultPrevented() { if "click" == event.Type().as_slice() && !event.DefaultPrevented() {
match self.input_type.get() { match self.input_type.get() {
InputRadio => self.SetChecked(true), InputRadio => self.update_checked_state(true, true),
_ => {} _ => {}
} }
@ -526,6 +548,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
match self.textinput.borrow_mut().handle_keydown(keyevent) { match self.textinput.borrow_mut().handle_keydown(keyevent) {
TriggerDefaultAction => (), TriggerDefaultAction => (),
DispatchInput => { DispatchInput => {
self.value_changed.set(true);
self.force_relayout(); self.force_relayout();
event.PreventDefault(); event.PreventDefault();
} }
@ -581,13 +604,15 @@ impl<'a> FormControl<'a> for JSRef<'a, HTMLInputElement> {
fn reset(self) { fn reset(self) {
match self.input_type.get() { match self.input_type.get() {
InputRadio | InputCheckbox => { InputRadio | InputCheckbox => {
self.SetChecked(self.DefaultChecked()); self.update_checked_state(self.DefaultChecked(), false);
self.checked_changed.set(false);
}, },
InputImage => (), InputImage => (),
_ => () _ => ()
} }
self.SetValue(self.DefaultValue()); self.SetValue(self.DefaultValue());
self.value_changed.set(false);
} }
} }
@ -613,6 +638,7 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
// we may need to restore them later // we may need to restore them later
cache.indeterminate = self.Indeterminate(); cache.indeterminate = self.Indeterminate();
cache.checked = self.Checked(); cache.checked = self.Checked();
cache.checked_changed = self.checked_changed.get();
self.SetIndeterminate(false); self.SetIndeterminate(false);
self.SetChecked(!cache.checked); self.SetChecked(!cache.checked);
}, },
@ -633,6 +659,7 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
.find(|r| r.Checked()) .find(|r| r.Checked())
}; };
cache.checked_radio.assign(checked_member); cache.checked_radio.assign(checked_member);
cache.checked_changed = self.checked_changed.get();
self.SetChecked(true); self.SetChecked(true);
} }
_ => () _ => ()
@ -658,6 +685,7 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
if cache.was_mutable { if cache.was_mutable {
self.SetIndeterminate(cache.indeterminate); self.SetIndeterminate(cache.indeterminate);
self.SetChecked(cache.checked); self.SetChecked(cache.checked);
self.checked_changed.set(cache.checked_changed);
} }
}, },
// https://html.spec.whatwg.org/multipage/forms.html#radio-button-state-(type=radio):canceled-activation-steps // https://html.spec.whatwg.org/multipage/forms.html#radio-button-state-(type=radio):canceled-activation-steps
@ -681,6 +709,7 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
}, },
None => self.SetChecked(false) None => self.SetChecked(false)
}; };
self.checked_changed.set(cache.checked_changed);
} }
} }
_ => () _ => ()

View file

@ -109,7 +109,7 @@ impl<'a> HTMLTextAreaElementMethods for JSRef<'a, HTMLTextAreaElement> {
make_bool_getter!(ReadOnly) make_bool_getter!(ReadOnly)
// https://html.spec.whatwg.org/multipage/forms.html#attr-textarea-readonly // https://html.spec.whatwg.org/multipage/forms.html#attr-textarea-readonly
make_bool_setter!(SetReadOnly, "readOnly") make_bool_setter!(SetReadOnly, "readonly")
// https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-required // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-required
make_bool_getter!(Required) make_bool_getter!(Required)

View file

@ -1,12 +1,43 @@
<style> <style>
</style> </style>
<form>
<div><input type="checkbox"></div> <div><input type="checkbox"></div>
<div><input type="text" size="30" value="placeholder"></div> <div><input type="text" size="30" value="placeholder"></div>
<div><input type="text" size="10" value="whefghijklmnopqrstuvwxyzabcdefg"></div> <div><input type="text" size="10" value="whefghijklmnopqrstuvwxyzabcdefg"></div>
<div><input type="text" value=""><div> <div><input id=bar1 type="text" value=""></div>
<div><button id=setdefaultvalue type=button>setDefaultValue</button>
<button id=setvalue type=button>setValue</button></div>
<div><input id=foo1 type="checkbox"></div>
<div><input id=foo2 type="checkbox"></div>
<div><input id=foo3 type="checkbox"></div>
<div><button id=setdefault type=button>setDefaultChecked</button></div>
<div><input type="submit"><input type="reset"><div> <div><input type="submit"><input type="reset"><div>
<div><input type="checkbox"></div> <script>
<div><input type="checkbox" checked></div> var checkboxes = [document.getElementById("foo1"),
document.getElementById("foo2"),
document.getElementById("foo3")];
var textinput = document.getElementById("bar1");
var set_default_checked = document.getElementById("setdefault");
var set_default_value = document.getElementById("setdefaultvalue");
var set_value = document.getElementById("setvalue");
var x = 0;
set_default_checked.addEventListener("click", function () {
for (var i = 0; i < 3; i++) {
checkboxes[i].defaultChecked = (i == x);
}
x = (x + 1) % 3;
});
set_default_value.addEventListener("click", function () {
textinput.defaultValue = x;
x = (x + 1) % 3;
});
set_value.addEventListener("click", function () {
textinput.value = "new value!";
});
</script>
<div>group 1 <div>group 1
<div><input type="radio"></div> <div><input type="radio"></div>
<div><input type="radio" checked></div> <div><input type="radio" checked></div>
@ -15,3 +46,4 @@
<div><input type="radio" name="a" checked></div> <div><input type="radio" name="a" checked></div>
<div><input type="radio" name="a"></div> <div><input type="radio" name="a"></div>
</div> </div>
</form>