aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/htmlinputelement.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/htmlinputelement.rs')
-rw-r--r--components/script/dom/htmlinputelement.rs133
1 files changed, 94 insertions, 39 deletions
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 5b0133395a7..3ea2a38a686 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -7,12 +7,11 @@ use dom::attr::{Attr, AttrValue, UIntAttrValue};
use dom::attr::AttrHelpers;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
-use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
-use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLFormElementCast, HTMLInputElementCast, NodeCast};
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLInputElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived, EventTargetCast};
use dom::bindings::codegen::InheritTypes::KeyboardEventCast;
use dom::bindings::global::Window;
@@ -27,7 +26,7 @@ use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::keyboardevent::KeyboardEvent;
use dom::htmlformelement::{InputElement, FormControl, HTMLFormElement, HTMLFormElementHelpers};
-use dom::htmlformelement::{NotFromFormSubmitMethod};
+use dom::htmlformelement::{NotFromFormSubmitMethod, NotFromFormResetMethod};
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, OtherNodeDamage};
use dom::node::{document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
@@ -63,7 +62,9 @@ pub struct HTMLInputElement {
htmlelement: HTMLElement,
input_type: Cell<InputType>,
checked: Cell<bool>,
+ checked_changed: Cell<bool>,
indeterminate: Cell<bool>,
+ value_changed: Cell<bool>,
size: Cell<u32>,
textinput: DOMRefCell<TextInput>,
activation_state: DOMRefCell<InputActivationState>,
@@ -74,6 +75,7 @@ pub struct HTMLInputElement {
struct InputActivationState {
indeterminate: bool,
checked: bool,
+ checked_changed: bool,
checked_radio: MutNullableJS<HTMLInputElement>,
// In case mutability changed
was_mutable: bool,
@@ -86,6 +88,7 @@ impl InputActivationState {
InputActivationState {
indeterminate: false,
checked: false,
+ checked_changed: false,
checked_radio: Default::default(),
was_mutable: false,
old_type: InputText
@@ -108,6 +111,8 @@ impl HTMLInputElement {
input_type: Cell::new(InputText),
checked: Cell::new(false),
indeterminate: Cell::new(false),
+ checked_changed: Cell::new(false),
+ value_changed: Cell::new(false),
size: Cell::new(DEFAULT_INPUT_SIZE),
textinput: DOMRefCell::new(TextInput::new(Single, "".to_string())),
activation_state: DOMRefCell::new(InputActivationState::new())
@@ -128,6 +133,7 @@ pub trait LayoutHTMLInputElementHelpers {
pub trait RawLayoutHTMLInputElementHelpers {
unsafe fn get_checked_state_for_layout(&self) -> bool;
+ unsafe fn get_indeterminate_state_for_layout(&self) -> bool;
unsafe fn get_size_for_layout(&self) -> u32;
}
@@ -171,6 +177,11 @@ impl RawLayoutHTMLInputElementHelpers for HTMLInputElement {
}
#[allow(unrooted_must_root)]
+ unsafe fn get_indeterminate_state_for_layout(&self) -> bool {
+ self.indeterminate.get()
+ }
+
+ #[allow(unrooted_must_root)]
unsafe fn get_size_for_layout(&self) -> u32 {
self.size.get()
}
@@ -183,6 +194,12 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
// http://www.whatwg.org/html/#dom-fe-disabled
make_bool_setter!(SetDisabled, "disabled")
+ // https://html.spec.whatwg.org/multipage/forms.html#dom-input-defaultchecked
+ make_bool_getter!(DefaultChecked, "checked")
+
+ // https://html.spec.whatwg.org/multipage/forms.html#dom-input-defaultchecked
+ make_bool_setter!(SetDefaultChecked, "checked")
+
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-checked
fn Checked(self) -> bool {
self.checked.get()
@@ -190,7 +207,7 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-checked
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
@@ -223,7 +240,17 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
}
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-value
- make_setter!(SetValue, "value")
+ fn SetValue(self, value: DOMString) {
+ self.textinput.borrow_mut().set_content(value);
+ self.value_changed.set(true);
+ self.force_relayout();
+ }
+
+ // https://html.spec.whatwg.org/multipage/forms.html#dom-input-defaultvalue
+ make_getter!(DefaultValue, "value")
+
+ // https://html.spec.whatwg.org/multipage/forms.html#dom-input-defaultvalue
+ make_setter!(SetDefaultValue, "value")
// https://html.spec.whatwg.org/multipage/forms.html#attr-fe-name
make_getter!(Name)
@@ -262,7 +289,6 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-indeterminate
fn SetIndeterminate(self, val: bool) {
- // FIXME #4079 this should change the appearance
self.indeterminate.set(val)
}
}
@@ -271,8 +297,9 @@ pub trait HTMLInputElementHelpers {
fn force_relayout(self);
fn radio_group_updated(self, group: Option<&str>);
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_indeterminate_state(self) -> bool;
}
fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&str>) {
@@ -331,8 +358,13 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
.map(|name| name.Value())
}
- fn update_checked_state(self, checked: bool) {
+ fn update_checked_state(self, checked: bool, dirty: bool) {
self.checked.set(checked);
+
+ if dirty {
+ self.checked_changed.set(true);
+ }
+
if self.input_type.get() == InputRadio && checked {
broadcast_radio_checked(self,
self.get_radio_group_name()
@@ -346,6 +378,10 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
fn get_size(&self) -> u32 {
self.size.get()
}
+
+ fn get_indeterminate_state(self) -> bool {
+ self.indeterminate.get()
+ }
}
impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
@@ -367,7 +403,10 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
node.set_enabled_state(false);
}
&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") => {
match *attr.value() {
@@ -396,8 +435,10 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
self.force_relayout();
}
&atom!("value") => {
- self.textinput.borrow_mut().set_content(attr.value().as_slice().to_string());
- self.force_relayout();
+ if !self.value_changed.get() {
+ self.textinput.borrow_mut().set_content(attr.value().as_slice().to_string());
+ self.force_relayout();
+ }
}
&atom!("name") => {
if self.input_type.get() == InputRadio {
@@ -423,7 +464,10 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
node.check_ancestors_disabled_state_for_form_control();
}
&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") => {
self.size.set(DEFAULT_INPUT_SIZE);
@@ -440,8 +484,10 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
self.force_relayout();
}
&atom!("value") => {
- self.textinput.borrow_mut().set_content("".to_string());
- self.force_relayout();
+ if !self.value_changed.get() {
+ self.textinput.borrow_mut().set_content("".to_string());
+ self.force_relayout();
+ }
}
&atom!("name") => {
if self.input_type.get() == InputRadio {
@@ -493,7 +539,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
if "click" == event.Type().as_slice() && !event.DefaultPrevented() {
match self.input_type.get() {
- InputRadio => self.SetChecked(true),
+ InputRadio => self.update_checked_state(true, true),
_ => {}
}
@@ -511,6 +557,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
match self.textinput.borrow_mut().handle_keydown(keyevent) {
TriggerDefaultAction => (),
DispatchInput => {
+ self.value_changed.set(true);
self.force_relayout();
event.PreventDefault();
}
@@ -528,30 +575,6 @@ impl Reflectable for HTMLInputElement {
}
impl<'a> FormControl<'a> for JSRef<'a, HTMLInputElement> {
- // FIXME: This is wrong (https://github.com/servo/servo/issues/3553)
- // but we need html5ever to do it correctly
- fn form_owner(self) -> Option<Temporary<HTMLFormElement>> {
- // https://html.spec.whatwg.org/multipage/forms.html#reset-the-form-owner
- let elem: JSRef<Element> = ElementCast::from_ref(self);
- let owner = elem.get_string_attribute(&atom!("form"));
- if !owner.is_empty() {
- let doc = document_from_node(self).root();
- let owner = doc.GetElementById(owner).root();
- match owner {
- Some(o) => {
- let maybe_form: Option<JSRef<HTMLFormElement>> = HTMLFormElementCast::to_ref(*o);
- if maybe_form.is_some() {
- return maybe_form.map(Temporary::from_rooted);
- }
- },
- _ => ()
- }
- }
- let node: JSRef<Node> = NodeCast::from_ref(self);
- node.ancestors().filter_map(|a| HTMLFormElementCast::to_ref(a)).next()
- .map(Temporary::from_rooted)
- }
-
fn to_element(self) -> JSRef<'a, Element> {
ElementCast::from_ref(self)
}
@@ -562,6 +585,21 @@ impl<'a> FormControl<'a> for JSRef<'a, HTMLInputElement> {
// https://html.spec.whatwg.org/multipage/forms.html#the-readonly-attribute:concept-fe-mutable
!(self.Disabled() || self.ReadOnly())
}
+
+ // https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-form-reset-control
+ fn reset(self) {
+ match self.input_type.get() {
+ InputRadio | InputCheckbox => {
+ self.update_checked_state(self.DefaultChecked(), false);
+ self.checked_changed.set(false);
+ },
+ InputImage => (),
+ _ => ()
+ }
+
+ self.SetValue(self.DefaultValue());
+ self.value_changed.set(false);
+ }
}
@@ -580,12 +618,15 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
match ty {
// https://html.spec.whatwg.org/multipage/forms.html#submit-button-state-(type=submit):activation-behavior
// InputSubmit => (), // No behavior defined
+ // https://html.spec.whatwg.org/multipage/forms.html#reset-button-state-(type=reset):activation-behavior
+ // InputSubmit => (), // No behavior defined
InputCheckbox => {
// https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):pre-click-activation-steps
// cache current values of `checked` and `indeterminate`
// we may need to restore them later
cache.indeterminate = self.Indeterminate();
cache.checked = self.Checked();
+ cache.checked_changed = self.checked_changed.get();
self.SetIndeterminate(false);
self.SetChecked(!cache.checked);
},
@@ -606,6 +647,7 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
.find(|r| r.Checked())
};
cache.checked_radio.assign(checked_member);
+ cache.checked_changed = self.checked_changed.get();
self.SetChecked(true);
}
_ => ()
@@ -625,12 +667,15 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
match ty {
// https://html.spec.whatwg.org/multipage/forms.html#submit-button-state-(type=submit):activation-behavior
// InputSubmit => (), // No behavior defined
+ // https://html.spec.whatwg.org/multipage/forms.html#reset-button-state-(type=reset):activation-behavior
+ // InputReset => (), // No behavior defined
// https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):canceled-activation-steps
InputCheckbox => {
// We want to restore state only if the element had been changed in the first place
if cache.was_mutable {
self.SetIndeterminate(cache.indeterminate);
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
@@ -654,6 +699,7 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
},
None => self.SetChecked(false)
};
+ self.checked_changed.set(cache.checked_changed);
}
}
_ => ()
@@ -678,6 +724,15 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
});
}
},
+ InputReset => {
+ // https://html.spec.whatwg.org/multipage/forms.html#reset-button-state-(type=reset):activation-behavior
+ // FIXME (Manishearth): support document owners (needs ability to get parent browsing context)
+ if self.mutable() /* and document owner is fully active */ {
+ self.form_owner().map(|o| {
+ o.root().reset(NotFromFormResetMethod)
+ });
+ }
+ },
InputCheckbox | InputRadio => {
// https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):activation-behavior
// https://html.spec.whatwg.org/multipage/forms.html#radio-button-state-(type=radio):activation-behavior