mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Expand InputType to cover all possible types
This came out of a conversation with nox in IRC: https://mozilla.logbot.info/servo/20171201#c13946454-c13946594 The code I was working on which motivated this change is here: https://github.com/servo/servo/pull/19461 Previously, InputType::Text was used to represent several different values of the type attribute on an input element. If an input element doesn't have a type attribute, or its type attribute doesn't contain a recognised value, then the input's type defaults to "text". Before this change, there were a number of checks in the code which directly looked at the type attribute. If those checks matched against the value "text", then they were potentially buggy, since an input with type=invalid should also behave like an input with type=text. Rather than have every conditional which cares about the input type also have to deal with invalid input types, we can convert the type attribute to an InputType enum once, and then match against the enum. A secondary benefit is that the compiler can tell us whether we've missed branches in a match expression. While working on this I discovered that the HTMLInputElement::value_mode() method misses a case for inputs with type=hidden (this resulted in a failing WPT test passing). I've also implemented the Default trait for InputType, so we now only have one place in the code which knows that InputType::Text is the default, where previously there were several.
This commit is contained in:
parent
005fd9cfa8
commit
05bfb8d07a
6 changed files with 235 additions and 141 deletions
|
@ -55,6 +55,7 @@ playing
|
||||||
print
|
print
|
||||||
progress
|
progress
|
||||||
radio
|
radio
|
||||||
|
range
|
||||||
readystatechange
|
readystatechange
|
||||||
reftest-wait
|
reftest-wait
|
||||||
reset
|
reset
|
||||||
|
|
|
@ -22,7 +22,7 @@ use dom::eventtarget::EventTarget;
|
||||||
use dom::htmlbodyelement::HTMLBodyElement;
|
use dom::htmlbodyelement::HTMLBodyElement;
|
||||||
use dom::htmlframesetelement::HTMLFrameSetElement;
|
use dom::htmlframesetelement::HTMLFrameSetElement;
|
||||||
use dom::htmlhtmlelement::HTMLHtmlElement;
|
use dom::htmlhtmlelement::HTMLHtmlElement;
|
||||||
use dom::htmlinputelement::HTMLInputElement;
|
use dom::htmlinputelement::{HTMLInputElement, InputType};
|
||||||
use dom::htmllabelelement::HTMLLabelElement;
|
use dom::htmllabelelement::HTMLLabelElement;
|
||||||
use dom::node::{Node, NodeFlags};
|
use dom::node::{Node, NodeFlags};
|
||||||
use dom::node::{document_from_node, window_from_node};
|
use dom::node::{document_from_node, window_from_node};
|
||||||
|
@ -497,7 +497,7 @@ impl HTMLElement {
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) =>
|
NodeTypeId::Element(ElementTypeId::HTMLElement(type_id)) =>
|
||||||
match type_id {
|
match type_id {
|
||||||
HTMLElementTypeId::HTMLInputElement =>
|
HTMLElementTypeId::HTMLInputElement =>
|
||||||
self.downcast::<HTMLInputElement>().unwrap().type_() != atom!("hidden"),
|
self.downcast::<HTMLInputElement>().unwrap().input_type() != InputType::Hidden,
|
||||||
HTMLElementTypeId::HTMLButtonElement |
|
HTMLElementTypeId::HTMLButtonElement |
|
||||||
HTMLElementTypeId::HTMLMeterElement |
|
HTMLElementTypeId::HTMLMeterElement |
|
||||||
HTMLElementTypeId::HTMLOutputElement |
|
HTMLElementTypeId::HTMLOutputElement |
|
||||||
|
|
|
@ -30,7 +30,7 @@ use dom::htmlelement::HTMLElement;
|
||||||
use dom::htmlfieldsetelement::HTMLFieldSetElement;
|
use dom::htmlfieldsetelement::HTMLFieldSetElement;
|
||||||
use dom::htmlformcontrolscollection::HTMLFormControlsCollection;
|
use dom::htmlformcontrolscollection::HTMLFormControlsCollection;
|
||||||
use dom::htmlimageelement::HTMLImageElement;
|
use dom::htmlimageelement::HTMLImageElement;
|
||||||
use dom::htmlinputelement::HTMLInputElement;
|
use dom::htmlinputelement::{HTMLInputElement, InputType};
|
||||||
use dom::htmllabelelement::HTMLLabelElement;
|
use dom::htmllabelelement::HTMLLabelElement;
|
||||||
use dom::htmllegendelement::HTMLLegendElement;
|
use dom::htmllegendelement::HTMLLegendElement;
|
||||||
use dom::htmlobjectelement::HTMLObjectElement;
|
use dom::htmlobjectelement::HTMLObjectElement;
|
||||||
|
@ -183,7 +183,7 @@ impl HTMLFormElementMethods for HTMLFormElement {
|
||||||
}
|
}
|
||||||
HTMLElementTypeId::HTMLInputElement => {
|
HTMLElementTypeId::HTMLInputElement => {
|
||||||
let input_elem = elem.downcast::<HTMLInputElement>().unwrap();
|
let input_elem = elem.downcast::<HTMLInputElement>().unwrap();
|
||||||
if input_elem.type_() == atom!("image") {
|
if input_elem.input_type() == InputType::Image {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
input_elem.form_owner()
|
input_elem.form_owner()
|
||||||
|
|
|
@ -63,16 +63,118 @@ const PASSWORD_REPLACEMENT_CHAR: char = '●';
|
||||||
#[derive(Clone, Copy, JSTraceable, PartialEq)]
|
#[derive(Clone, Copy, JSTraceable, PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(MallocSizeOf)]
|
#[derive(MallocSizeOf)]
|
||||||
enum InputType {
|
pub enum InputType {
|
||||||
InputSubmit,
|
Button,
|
||||||
InputReset,
|
Checkbox,
|
||||||
InputButton,
|
Color,
|
||||||
InputText,
|
Date,
|
||||||
InputFile,
|
Datetime,
|
||||||
InputImage,
|
DatetimeLocal,
|
||||||
InputCheckbox,
|
Email,
|
||||||
InputRadio,
|
File,
|
||||||
InputPassword
|
Hidden,
|
||||||
|
Image,
|
||||||
|
Month,
|
||||||
|
Number,
|
||||||
|
Password,
|
||||||
|
Radio,
|
||||||
|
Range,
|
||||||
|
Reset,
|
||||||
|
Search,
|
||||||
|
Submit,
|
||||||
|
Tel,
|
||||||
|
Text,
|
||||||
|
Time,
|
||||||
|
Url,
|
||||||
|
Week,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputType {
|
||||||
|
// Note that Password is not included here since it is handled
|
||||||
|
// slightly differently, with placeholder characters shown rather
|
||||||
|
// than the underlying value.
|
||||||
|
fn is_textual(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
InputType::Color | InputType::Date | InputType::Datetime
|
||||||
|
| InputType::DatetimeLocal | InputType::Email | InputType::Hidden
|
||||||
|
| InputType::Month | InputType::Number | InputType::Range
|
||||||
|
| InputType::Search | InputType::Tel | InputType::Text
|
||||||
|
| InputType::Time | InputType::Url | InputType::Week => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_textual_or_password(&self) -> bool {
|
||||||
|
self.is_textual() || *self == InputType::Password
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_str(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
InputType::Button => "button",
|
||||||
|
InputType::Checkbox => "checkbox",
|
||||||
|
InputType::Color => "color",
|
||||||
|
InputType::Date => "date",
|
||||||
|
InputType::Datetime => "datetime",
|
||||||
|
InputType::DatetimeLocal => "datetime-local",
|
||||||
|
InputType::Email => "email",
|
||||||
|
InputType::File => "file",
|
||||||
|
InputType::Hidden => "hidden",
|
||||||
|
InputType::Image => "image",
|
||||||
|
InputType::Month => "month",
|
||||||
|
InputType::Number => "number",
|
||||||
|
InputType::Password => "password",
|
||||||
|
InputType::Radio => "radio",
|
||||||
|
InputType::Range => "range",
|
||||||
|
InputType::Reset => "reset",
|
||||||
|
InputType::Search => "search",
|
||||||
|
InputType::Submit => "submit",
|
||||||
|
InputType::Tel => "tel",
|
||||||
|
InputType::Text => "text",
|
||||||
|
InputType::Time => "time",
|
||||||
|
InputType::Url => "url",
|
||||||
|
InputType::Week => "week",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Atom> for InputType {
|
||||||
|
fn from(value: &Atom) -> InputType {
|
||||||
|
match value.to_ascii_lowercase() {
|
||||||
|
atom!("button") => InputType::Button,
|
||||||
|
atom!("checkbox") => InputType::Checkbox,
|
||||||
|
atom!("color") => InputType::Color,
|
||||||
|
atom!("date") => InputType::Date,
|
||||||
|
atom!("datetime") => InputType::Datetime,
|
||||||
|
atom!("datetime-local") => InputType::DatetimeLocal,
|
||||||
|
atom!("email") => InputType::Email,
|
||||||
|
atom!("file") => InputType::File,
|
||||||
|
atom!("hidden") => InputType::Hidden,
|
||||||
|
atom!("image") => InputType::Image,
|
||||||
|
atom!("month") => InputType::Month,
|
||||||
|
atom!("number") => InputType::Number,
|
||||||
|
atom!("password") => InputType::Password,
|
||||||
|
atom!("radio") => InputType::Radio,
|
||||||
|
atom!("range") => InputType::Range,
|
||||||
|
atom!("reset") => InputType::Reset,
|
||||||
|
atom!("search") => InputType::Search,
|
||||||
|
atom!("submit") => InputType::Submit,
|
||||||
|
atom!("tel") => InputType::Tel,
|
||||||
|
atom!("text") => InputType::Text,
|
||||||
|
atom!("time") => InputType::Time,
|
||||||
|
atom!("url") => InputType::Url,
|
||||||
|
atom!("week") => InputType::Week,
|
||||||
|
_ => Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for InputType {
|
||||||
|
fn default() -> InputType {
|
||||||
|
InputType::Text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -125,7 +227,7 @@ impl InputActivationState {
|
||||||
checked_changed: false,
|
checked_changed: false,
|
||||||
checked_radio: None,
|
checked_radio: None,
|
||||||
was_mutable: false,
|
was_mutable: false,
|
||||||
old_type: InputType::InputText
|
old_type: Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,7 +244,7 @@ impl HTMLInputElement {
|
||||||
HTMLElement::new_inherited_with_state(ElementState::IN_ENABLED_STATE |
|
HTMLElement::new_inherited_with_state(ElementState::IN_ENABLED_STATE |
|
||||||
ElementState::IN_READ_WRITE_STATE,
|
ElementState::IN_READ_WRITE_STATE,
|
||||||
local_name, prefix, document),
|
local_name, prefix, document),
|
||||||
input_type: Cell::new(InputType::InputText),
|
input_type: Cell::new(Default::default()),
|
||||||
placeholder: DomRefCell::new(DOMString::new()),
|
placeholder: DomRefCell::new(DOMString::new()),
|
||||||
checked_changed: Cell::new(false),
|
checked_changed: Cell::new(false),
|
||||||
value_changed: Cell::new(false),
|
value_changed: Cell::new(false),
|
||||||
|
@ -171,25 +273,34 @@ impl HTMLInputElement {
|
||||||
HTMLInputElementBinding::Wrap)
|
HTMLInputElementBinding::Wrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_(&self) -> Atom {
|
// https://html.spec.whatwg.org/multipage/#dom-input-value
|
||||||
self.upcast::<Element>()
|
// https://html.spec.whatwg.org/multipage/#concept-input-apply
|
||||||
.get_attribute(&ns!(), &local_name!("type"))
|
fn value_mode(&self) -> ValueMode {
|
||||||
.map_or_else(|| atom!(""), |a| a.value().as_atom().to_owned())
|
match self.input_type() {
|
||||||
|
InputType::Submit | InputType::Reset | InputType::Button
|
||||||
|
| InputType::Image | InputType::Hidden => {
|
||||||
|
ValueMode::Default
|
||||||
|
},
|
||||||
|
|
||||||
|
InputType::Checkbox | InputType::Radio => {
|
||||||
|
ValueMode::DefaultOn
|
||||||
|
},
|
||||||
|
|
||||||
|
InputType::Color | InputType::Date | InputType::Datetime
|
||||||
|
| InputType::DatetimeLocal | InputType::Email | InputType::Month
|
||||||
|
| InputType::Number | InputType::Password | InputType::Range
|
||||||
|
| InputType::Search | InputType::Tel | InputType::Text
|
||||||
|
| InputType::Time | InputType::Url | InputType::Week => {
|
||||||
|
ValueMode::Value
|
||||||
|
}
|
||||||
|
|
||||||
|
InputType::File => ValueMode::Filename,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-input-value
|
#[inline]
|
||||||
fn value_mode(&self) -> ValueMode {
|
pub fn input_type(&self) -> InputType {
|
||||||
match self.input_type.get() {
|
self.input_type.get()
|
||||||
InputType::InputSubmit |
|
|
||||||
InputType::InputReset |
|
|
||||||
InputType::InputButton |
|
|
||||||
InputType::InputImage => ValueMode::Default,
|
|
||||||
InputType::InputCheckbox |
|
|
||||||
InputType::InputRadio => ValueMode::DefaultOn,
|
|
||||||
InputType::InputPassword |
|
|
||||||
InputType::InputText => ValueMode::Value,
|
|
||||||
InputType::InputFile => ValueMode::Filename,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,13 +334,13 @@ impl LayoutHTMLInputElementHelpers for LayoutDom<HTMLInputElement> {
|
||||||
String::from(value)
|
String::from(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
match (*self.unsafe_get()).input_type.get() {
|
match (*self.unsafe_get()).input_type() {
|
||||||
InputType::InputCheckbox | InputType::InputRadio => String::new(),
|
InputType::Checkbox | InputType::Radio => String::new(),
|
||||||
InputType::InputFile | InputType::InputImage => String::new(),
|
InputType::File | InputType::Image => String::new(),
|
||||||
InputType::InputButton => get_raw_attr_value(self, ""),
|
InputType::Button => get_raw_attr_value(self, ""),
|
||||||
InputType::InputSubmit => get_raw_attr_value(self, DEFAULT_SUBMIT_VALUE),
|
InputType::Submit => get_raw_attr_value(self, DEFAULT_SUBMIT_VALUE),
|
||||||
InputType::InputReset => get_raw_attr_value(self, DEFAULT_RESET_VALUE),
|
InputType::Reset => get_raw_attr_value(self, DEFAULT_RESET_VALUE),
|
||||||
InputType::InputPassword => {
|
InputType::Password => {
|
||||||
let text = get_raw_textinput_value(self);
|
let text = get_raw_textinput_value(self);
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
text.chars().map(|_| PASSWORD_REPLACEMENT_CHAR).collect()
|
text.chars().map(|_| PASSWORD_REPLACEMENT_CHAR).collect()
|
||||||
|
@ -263,8 +374,8 @@ impl LayoutHTMLInputElementHelpers for LayoutDom<HTMLInputElement> {
|
||||||
|
|
||||||
let textinput = (*self.unsafe_get()).textinput.borrow_for_layout();
|
let textinput = (*self.unsafe_get()).textinput.borrow_for_layout();
|
||||||
|
|
||||||
match (*self.unsafe_get()).input_type.get() {
|
match (*self.unsafe_get()).input_type() {
|
||||||
InputType::InputPassword => {
|
InputType::Password => {
|
||||||
let text = get_raw_textinput_value(self);
|
let text = get_raw_textinput_value(self);
|
||||||
let sel = textinput.get_absolute_selection_range();
|
let sel = textinput.get_absolute_selection_range();
|
||||||
|
|
||||||
|
@ -275,7 +386,7 @@ impl LayoutHTMLInputElementHelpers for LayoutDom<HTMLInputElement> {
|
||||||
let bytes_per_char = PASSWORD_REPLACEMENT_CHAR.len_utf8();
|
let bytes_per_char = PASSWORD_REPLACEMENT_CHAR.len_utf8();
|
||||||
Some(char_start * bytes_per_char .. char_end * bytes_per_char)
|
Some(char_start * bytes_per_char .. char_end * bytes_per_char)
|
||||||
}
|
}
|
||||||
InputType::InputText => Some(textinput.get_absolute_selection_range()),
|
input_type if input_type.is_textual() => Some(textinput.get_absolute_selection_range()),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,16 +477,9 @@ impl HTMLInputElementMethods for HTMLInputElement {
|
||||||
make_limited_uint_setter!(SetSize, "size", DEFAULT_INPUT_SIZE);
|
make_limited_uint_setter!(SetSize, "size", DEFAULT_INPUT_SIZE);
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-input-type
|
// https://html.spec.whatwg.org/multipage/#dom-input-type
|
||||||
make_enumerated_getter!(Type,
|
fn Type(&self) -> DOMString {
|
||||||
"type",
|
DOMString::from(self.input_type().to_str())
|
||||||
"text",
|
}
|
||||||
"hidden" | "search" | "tel" |
|
|
||||||
"url" | "email" | "password" |
|
|
||||||
"datetime" | "date" | "month" |
|
|
||||||
"week" | "time" | "datetime-local" |
|
|
||||||
"number" | "range" | "color" |
|
|
||||||
"checkbox" | "radio" | "file" |
|
|
||||||
"submit" | "image" | "reset" | "button");
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-input-type
|
// https://html.spec.whatwg.org/multipage/#dom-input-type
|
||||||
make_atomic_setter!(SetType, "type");
|
make_atomic_setter!(SetType, "type");
|
||||||
|
@ -566,7 +670,7 @@ impl HTMLInputElementMethods for HTMLInputElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
|
// https://html.spec.whatwg.org/multipage/#dom-lfe-labels
|
||||||
fn Labels(&self) -> DomRoot<NodeList> {
|
fn Labels(&self) -> DomRoot<NodeList> {
|
||||||
if self.type_() == atom!("hidden") {
|
if self.input_type() == InputType::Hidden {
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
NodeList::empty(&window)
|
NodeList::empty(&window)
|
||||||
} else {
|
} else {
|
||||||
|
@ -614,7 +718,7 @@ impl HTMLInputElementMethods for HTMLInputElement {
|
||||||
// used for test purpose.
|
// used for test purpose.
|
||||||
// check-tidy: no specs after this line
|
// check-tidy: no specs after this line
|
||||||
fn SelectFiles(&self, paths: Vec<DOMString>) {
|
fn SelectFiles(&self, paths: Vec<DOMString>) {
|
||||||
if self.input_type.get() == InputType::InputFile {
|
if self.input_type() == InputType::File {
|
||||||
self.select_files(Some(paths));
|
self.select_files(Some(paths));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -655,7 +759,7 @@ fn broadcast_radio_checked(broadcaster: &HTMLInputElement, group: Option<&Atom>)
|
||||||
// https://html.spec.whatwg.org/multipage/#radio-button-group
|
// https://html.spec.whatwg.org/multipage/#radio-button-group
|
||||||
fn in_same_group(other: &HTMLInputElement, owner: Option<&HTMLFormElement>,
|
fn in_same_group(other: &HTMLInputElement, owner: Option<&HTMLFormElement>,
|
||||||
group: Option<&Atom>) -> bool {
|
group: Option<&Atom>) -> bool {
|
||||||
other.input_type.get() == InputType::InputRadio &&
|
other.input_type() == InputType::Radio &&
|
||||||
// TODO Both a and b are in the same home subtree.
|
// TODO Both a and b are in the same home subtree.
|
||||||
other.form_owner().r() == owner &&
|
other.form_owner().r() == owner &&
|
||||||
match (other.radio_group_name(), group) {
|
match (other.radio_group_name(), group) {
|
||||||
|
@ -677,7 +781,8 @@ impl HTMLInputElement {
|
||||||
// 3.1: disabled state check is in get_unclean_dataset
|
// 3.1: disabled state check is in get_unclean_dataset
|
||||||
|
|
||||||
// Step 3.2
|
// Step 3.2
|
||||||
let ty = self.type_();
|
let ty = self.Type();
|
||||||
|
|
||||||
// Step 3.4
|
// Step 3.4
|
||||||
let name = self.Name();
|
let name = self.Name();
|
||||||
let is_submitter = match submitter {
|
let is_submitter = match submitter {
|
||||||
|
@ -687,25 +792,26 @@ impl HTMLInputElement {
|
||||||
_ => false
|
_ => false
|
||||||
};
|
};
|
||||||
|
|
||||||
match ty {
|
match self.input_type() {
|
||||||
// Step 3.1: it's a button but it is not submitter.
|
// Step 3.1: it's a button but it is not submitter.
|
||||||
atom!("submit") | atom!("button") | atom!("reset") if !is_submitter => return vec![],
|
InputType::Submit | InputType::Button | InputType::Reset if !is_submitter => return vec![],
|
||||||
|
|
||||||
// Step 3.1: it's the "Checkbox" or "Radio Button" and whose checkedness is false.
|
// Step 3.1: it's the "Checkbox" or "Radio Button" and whose checkedness is false.
|
||||||
atom!("radio") | atom!("checkbox") => if !self.Checked() || name.is_empty() {
|
InputType::Radio | InputType::Checkbox => if !self.Checked() || name.is_empty() {
|
||||||
return vec![];
|
return vec![];
|
||||||
},
|
},
|
||||||
atom!("file") => {
|
|
||||||
|
InputType::File => {
|
||||||
let mut datums = vec![];
|
let mut datums = vec![];
|
||||||
|
|
||||||
// Step 3.2-3.7
|
// Step 3.2-3.7
|
||||||
let name = self.Name();
|
let name = self.Name();
|
||||||
let type_ = self.Type();
|
|
||||||
|
|
||||||
match self.GetFiles() {
|
match self.GetFiles() {
|
||||||
Some(fl) => {
|
Some(fl) => {
|
||||||
for f in fl.iter_files() {
|
for f in fl.iter_files() {
|
||||||
datums.push(FormDatum {
|
datums.push(FormDatum {
|
||||||
ty: type_.clone(),
|
ty: ty.clone(),
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
value: FormDatumValue::File(DomRoot::from_ref(&f)),
|
value: FormDatumValue::File(DomRoot::from_ref(&f)),
|
||||||
});
|
});
|
||||||
|
@ -715,7 +821,7 @@ impl HTMLInputElement {
|
||||||
datums.push(FormDatum {
|
datums.push(FormDatum {
|
||||||
// XXX(izgzhen): Spec says 'application/octet-stream' as the type,
|
// XXX(izgzhen): Spec says 'application/octet-stream' as the type,
|
||||||
// but this is _type_ of element rather than content right?
|
// but this is _type_ of element rather than content right?
|
||||||
ty: type_.clone(),
|
ty: ty.clone(),
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
value: FormDatumValue::String(DOMString::from("")),
|
value: FormDatumValue::String(DOMString::from("")),
|
||||||
})
|
})
|
||||||
|
@ -724,7 +830,9 @@ impl HTMLInputElement {
|
||||||
|
|
||||||
return datums;
|
return datums;
|
||||||
}
|
}
|
||||||
atom!("image") => return vec![], // Unimplemented
|
|
||||||
|
InputType::Image => return vec![], // Unimplemented
|
||||||
|
|
||||||
// Step 3.1: it's not the "Image Button" and doesn't have a name attribute.
|
// Step 3.1: it's not the "Image Button" and doesn't have a name attribute.
|
||||||
_ => if name.is_empty() {
|
_ => if name.is_empty() {
|
||||||
return vec![];
|
return vec![];
|
||||||
|
@ -734,7 +842,7 @@ impl HTMLInputElement {
|
||||||
|
|
||||||
// Step 3.9
|
// Step 3.9
|
||||||
vec![FormDatum {
|
vec![FormDatum {
|
||||||
ty: DOMString::from(&*ty), // FIXME(ajeffrey): Convert directly from Atoms to DOMStrings
|
ty: ty.clone(),
|
||||||
name: name,
|
name: name,
|
||||||
value: FormDatumValue::String(self.Value())
|
value: FormDatumValue::String(self.Value())
|
||||||
}]
|
}]
|
||||||
|
@ -755,7 +863,7 @@ impl HTMLInputElement {
|
||||||
self.checked_changed.set(true);
|
self.checked_changed.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.input_type.get() == InputType::InputRadio && checked {
|
if self.input_type() == InputType::Radio && checked {
|
||||||
broadcast_radio_checked(self,
|
broadcast_radio_checked(self,
|
||||||
self.radio_group_name().as_ref());
|
self.radio_group_name().as_ref());
|
||||||
}
|
}
|
||||||
|
@ -773,12 +881,12 @@ impl HTMLInputElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#the-input-element:concept-form-reset-control
|
// https://html.spec.whatwg.org/multipage/#the-input-element:concept-form-reset-control
|
||||||
pub fn reset(&self) {
|
pub fn reset(&self) {
|
||||||
match self.input_type.get() {
|
match self.input_type() {
|
||||||
InputType::InputRadio | InputType::InputCheckbox => {
|
InputType::Radio | InputType::Checkbox => {
|
||||||
self.update_checked_state(self.DefaultChecked(), false);
|
self.update_checked_state(self.DefaultChecked(), false);
|
||||||
self.checked_changed.set(false);
|
self.checked_changed.set(false);
|
||||||
},
|
},
|
||||||
InputType::InputImage => (),
|
InputType::Image => (),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,13 +898,14 @@ impl HTMLInputElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_placeholder_shown_state(&self) {
|
fn update_placeholder_shown_state(&self) {
|
||||||
match self.input_type.get() {
|
if !self.input_type().is_textual_or_password() {
|
||||||
InputType::InputText | InputType::InputPassword => {},
|
return
|
||||||
_ => return,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let has_placeholder = !self.placeholder.borrow().is_empty();
|
let has_placeholder = !self.placeholder.borrow().is_empty();
|
||||||
let has_value = !self.textinput.borrow().is_empty();
|
let has_value = !self.textinput.borrow().is_empty();
|
||||||
let el = self.upcast::<Element>();
|
let el = self.upcast::<Element>();
|
||||||
|
|
||||||
el.set_placeholder_shown_state(has_placeholder && !has_value);
|
el.set_placeholder_shown_state(has_placeholder && !has_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,29 +974,29 @@ impl HTMLInputElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#value-sanitization-algorithm
|
// https://html.spec.whatwg.org/multipage/#value-sanitization-algorithm
|
||||||
fn sanitize_value(&self) {
|
fn sanitize_value(&self) {
|
||||||
match self.type_() {
|
match self.input_type() {
|
||||||
atom!("text") | atom!("search") | atom!("tel") | atom!("password") => {
|
InputType::Text | InputType::Search | InputType::Tel | InputType::Password => {
|
||||||
self.textinput.borrow_mut().single_line_content_mut().strip_newlines();
|
self.textinput.borrow_mut().single_line_content_mut().strip_newlines();
|
||||||
}
|
}
|
||||||
atom!("url") => {
|
InputType::Url => {
|
||||||
let mut textinput = self.textinput.borrow_mut();
|
let mut textinput = self.textinput.borrow_mut();
|
||||||
let content = textinput.single_line_content_mut();
|
let content = textinput.single_line_content_mut();
|
||||||
content.strip_newlines();
|
content.strip_newlines();
|
||||||
content.strip_leading_and_trailing_ascii_whitespace();
|
content.strip_leading_and_trailing_ascii_whitespace();
|
||||||
}
|
}
|
||||||
atom!("date") => {
|
InputType::Date => {
|
||||||
let mut textinput = self.textinput.borrow_mut();
|
let mut textinput = self.textinput.borrow_mut();
|
||||||
if !textinput.single_line_content().is_valid_date_string() {
|
if !textinput.single_line_content().is_valid_date_string() {
|
||||||
*textinput.single_line_content_mut() = "".into();
|
*textinput.single_line_content_mut() = "".into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
atom!("month") => {
|
InputType::Month => {
|
||||||
let mut textinput = self.textinput.borrow_mut();
|
let mut textinput = self.textinput.borrow_mut();
|
||||||
if !textinput.single_line_content().is_valid_month_string() {
|
if !textinput.single_line_content().is_valid_month_string() {
|
||||||
*textinput.single_line_content_mut() = "".into();
|
*textinput.single_line_content_mut() = "".into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
atom!("color") => {
|
InputType::Color => {
|
||||||
let mut textinput = self.textinput.borrow_mut();
|
let mut textinput = self.textinput.borrow_mut();
|
||||||
|
|
||||||
let is_valid = {
|
let is_valid = {
|
||||||
|
@ -907,7 +1016,7 @@ impl HTMLInputElement {
|
||||||
textinput.set_content("#000000".into());
|
textinput.set_content("#000000".into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
atom!("time") => {
|
InputType::Time => {
|
||||||
let mut textinput = self.textinput.borrow_mut();
|
let mut textinput = self.textinput.borrow_mut();
|
||||||
|
|
||||||
if ! textinput.single_line_content().is_valid_time_string() {
|
if ! textinput.single_line_content().is_valid_time_string() {
|
||||||
|
@ -943,7 +1052,7 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
el.set_enabled_state(!disabled_state);
|
el.set_enabled_state(!disabled_state);
|
||||||
el.check_ancestors_disabled_state_for_form_control();
|
el.check_ancestors_disabled_state_for_form_control();
|
||||||
|
|
||||||
if self.input_type.get() == InputType::InputText {
|
if self.input_type().is_textual() {
|
||||||
let read_write = !(self.ReadOnly() || el.disabled_state());
|
let read_write = !(self.ReadOnly() || el.disabled_state());
|
||||||
el.set_read_write_state(read_write);
|
el.set_read_write_state(read_write);
|
||||||
}
|
}
|
||||||
|
@ -969,29 +1078,20 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
let el = self.upcast::<Element>();
|
let el = self.upcast::<Element>();
|
||||||
match mutation {
|
match mutation {
|
||||||
AttributeMutation::Set(_) => {
|
AttributeMutation::Set(_) => {
|
||||||
let new_type = match attr.value().as_atom() {
|
let new_type = InputType::from(attr.value().as_atom());
|
||||||
&atom!("button") => InputType::InputButton,
|
|
||||||
&atom!("submit") => InputType::InputSubmit,
|
|
||||||
&atom!("reset") => InputType::InputReset,
|
|
||||||
&atom!("file") => InputType::InputFile,
|
|
||||||
&atom!("radio") => InputType::InputRadio,
|
|
||||||
&atom!("checkbox") => InputType::InputCheckbox,
|
|
||||||
&atom!("password") => InputType::InputPassword,
|
|
||||||
_ => InputType::InputText,
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#input-type-change
|
// https://html.spec.whatwg.org/multipage/#input-type-change
|
||||||
let (old_value_mode, old_idl_value) = (self.value_mode(), self.Value());
|
let (old_value_mode, old_idl_value) = (self.value_mode(), self.Value());
|
||||||
self.input_type.set(new_type);
|
self.input_type.set(new_type);
|
||||||
|
|
||||||
if new_type == InputType::InputText {
|
if new_type.is_textual() {
|
||||||
let read_write = !(self.ReadOnly() || el.disabled_state());
|
let read_write = !(self.ReadOnly() || el.disabled_state());
|
||||||
el.set_read_write_state(read_write);
|
el.set_read_write_state(read_write);
|
||||||
} else {
|
} else {
|
||||||
el.set_read_write_state(false);
|
el.set_read_write_state(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_type == InputType::InputFile {
|
if new_type == InputType::File {
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
let filelist = FileList::new(&window, vec![]);
|
let filelist = FileList::new(&window, vec![]);
|
||||||
self.filelist.set(Some(&filelist));
|
self.filelist.set(Some(&filelist));
|
||||||
|
@ -1026,7 +1126,7 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 5
|
// Step 5
|
||||||
if new_type == InputType::InputRadio {
|
if new_type == InputType::Radio {
|
||||||
self.radio_group_updated(
|
self.radio_group_updated(
|
||||||
self.radio_group_name().as_ref());
|
self.radio_group_name().as_ref());
|
||||||
}
|
}
|
||||||
|
@ -1035,12 +1135,12 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
self.sanitize_value();
|
self.sanitize_value();
|
||||||
},
|
},
|
||||||
AttributeMutation::Removed => {
|
AttributeMutation::Removed => {
|
||||||
if self.input_type.get() == InputType::InputRadio {
|
if self.input_type() == InputType::Radio {
|
||||||
broadcast_radio_checked(
|
broadcast_radio_checked(
|
||||||
self,
|
self,
|
||||||
self.radio_group_name().as_ref());
|
self.radio_group_name().as_ref());
|
||||||
}
|
}
|
||||||
self.input_type.set(InputType::InputText);
|
self.input_type.set(InputType::default());
|
||||||
let el = self.upcast::<Element>();
|
let el = self.upcast::<Element>();
|
||||||
|
|
||||||
let read_write = !(self.ReadOnly() || el.disabled_state());
|
let read_write = !(self.ReadOnly() || el.disabled_state());
|
||||||
|
@ -1057,7 +1157,7 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
self.sanitize_value();
|
self.sanitize_value();
|
||||||
self.update_placeholder_shown_state();
|
self.update_placeholder_shown_state();
|
||||||
},
|
},
|
||||||
&local_name!("name") if self.input_type.get() == InputType::InputRadio => {
|
&local_name!("name") if self.input_type() == InputType::Radio => {
|
||||||
self.radio_group_updated(
|
self.radio_group_updated(
|
||||||
mutation.new_value(attr).as_ref().map(|name| name.as_atom()));
|
mutation.new_value(attr).as_ref().map(|name| name.as_atom()));
|
||||||
},
|
},
|
||||||
|
@ -1096,7 +1196,7 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
}
|
}
|
||||||
self.update_placeholder_shown_state();
|
self.update_placeholder_shown_state();
|
||||||
},
|
},
|
||||||
&local_name!("readonly") if self.input_type.get() == InputType::InputText => {
|
&local_name!("readonly") if self.input_type().is_textual() => {
|
||||||
let el = self.upcast::<Element>();
|
let el = self.upcast::<Element>();
|
||||||
match mutation {
|
match mutation {
|
||||||
AttributeMutation::Set(_) => {
|
AttributeMutation::Set(_) => {
|
||||||
|
@ -1157,8 +1257,7 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
//TODO: set the editing position for text inputs
|
//TODO: set the editing position for text inputs
|
||||||
|
|
||||||
document_from_node(self).request_focus(self.upcast());
|
document_from_node(self).request_focus(self.upcast());
|
||||||
if (self.input_type.get() == InputType::InputText ||
|
if self.input_type().is_textual_or_password() &&
|
||||||
self.input_type.get() == InputType::InputPassword) &&
|
|
||||||
// Check if we display a placeholder. Layout doesn't know about this.
|
// Check if we display a placeholder. Layout doesn't know about this.
|
||||||
!self.textinput.borrow().is_empty() {
|
!self.textinput.borrow().is_empty() {
|
||||||
if let Some(mouse_event) = event.downcast::<MouseEvent>() {
|
if let Some(mouse_event) = event.downcast::<MouseEvent>() {
|
||||||
|
@ -1181,8 +1280,7 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if event.type_() == atom!("keydown") && !event.DefaultPrevented() &&
|
} else if event.type_() == atom!("keydown") && !event.DefaultPrevented() &&
|
||||||
(self.input_type.get() == InputType::InputText ||
|
self.input_type().is_textual_or_password() {
|
||||||
self.input_type.get() == InputType::InputPassword) {
|
|
||||||
if let Some(keyevent) = event.downcast::<KeyboardEvent>() {
|
if let Some(keyevent) = event.downcast::<KeyboardEvent>() {
|
||||||
// This can't be inlined, as holding on to textinput.borrow_mut()
|
// This can't be inlined, as holding on to textinput.borrow_mut()
|
||||||
// during self.implicit_submission will cause a panic.
|
// during self.implicit_submission will cause a panic.
|
||||||
|
@ -1208,8 +1306,7 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if event.type_() == atom!("keypress") && !event.DefaultPrevented() &&
|
} else if event.type_() == atom!("keypress") && !event.DefaultPrevented() &&
|
||||||
(self.input_type.get() == InputType::InputText ||
|
self.input_type().is_textual_or_password() {
|
||||||
self.input_type.get() == InputType::InputPassword) {
|
|
||||||
if event.IsTrusted() {
|
if event.IsTrusted() {
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
let _ = window.user_interaction_task_source()
|
let _ = window.user_interaction_task_source()
|
||||||
|
@ -1254,13 +1351,13 @@ impl Activatable for HTMLInputElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_instance_activatable(&self) -> bool {
|
fn is_instance_activatable(&self) -> bool {
|
||||||
match self.input_type.get() {
|
match self.input_type() {
|
||||||
// https://html.spec.whatwg.org/multipage/#submit-button-state-%28type=submit%29:activation-behaviour-2
|
// https://html.spec.whatwg.org/multipage/#submit-button-state-%28type=submit%29:activation-behaviour-2
|
||||||
// https://html.spec.whatwg.org/multipage/#reset-button-state-%28type=reset%29:activation-behaviour-2
|
// https://html.spec.whatwg.org/multipage/#reset-button-state-%28type=reset%29:activation-behaviour-2
|
||||||
// https://html.spec.whatwg.org/multipage/#checkbox-state-%28type=checkbox%29:activation-behaviour-2
|
// https://html.spec.whatwg.org/multipage/#checkbox-state-%28type=checkbox%29:activation-behaviour-2
|
||||||
// https://html.spec.whatwg.org/multipage/#radio-button-state-%28type=radio%29:activation-behaviour-2
|
// https://html.spec.whatwg.org/multipage/#radio-button-state-%28type=radio%29:activation-behaviour-2
|
||||||
InputType::InputSubmit | InputType::InputReset | InputType::InputFile
|
InputType::Submit | InputType::Reset | InputType::File
|
||||||
| InputType::InputCheckbox | InputType::InputRadio => self.is_mutable(),
|
| InputType::Checkbox | InputType::Radio => self.is_mutable(),
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1269,16 +1366,16 @@ impl Activatable for HTMLInputElement {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn pre_click_activation(&self) {
|
fn pre_click_activation(&self) {
|
||||||
let mut cache = self.activation_state.borrow_mut();
|
let mut cache = self.activation_state.borrow_mut();
|
||||||
let ty = self.input_type.get();
|
let ty = self.input_type();
|
||||||
cache.old_type = ty;
|
cache.old_type = ty;
|
||||||
cache.was_mutable = self.is_mutable();
|
cache.was_mutable = self.is_mutable();
|
||||||
if cache.was_mutable {
|
if cache.was_mutable {
|
||||||
match ty {
|
match ty {
|
||||||
// https://html.spec.whatwg.org/multipage/#submit-button-state-(type=submit):activation-behavior
|
// https://html.spec.whatwg.org/multipage/#submit-button-state-(type=submit):activation-behavior
|
||||||
// InputType::InputSubmit => (), // No behavior defined
|
// InputType::Submit => (), // No behavior defined
|
||||||
// https://html.spec.whatwg.org/multipage/#reset-button-state-(type=reset):activation-behavior
|
// https://html.spec.whatwg.org/multipage/#reset-button-state-(type=reset):activation-behavior
|
||||||
// InputType::InputSubmit => (), // No behavior defined
|
// InputType::Submit => (), // No behavior defined
|
||||||
InputType::InputCheckbox => {
|
InputType::Checkbox => {
|
||||||
/*
|
/*
|
||||||
https://html.spec.whatwg.org/multipage/#checkbox-state-(type=checkbox):pre-click-activation-steps
|
https://html.spec.whatwg.org/multipage/#checkbox-state-(type=checkbox):pre-click-activation-steps
|
||||||
cache current values of `checked` and `indeterminate`
|
cache current values of `checked` and `indeterminate`
|
||||||
|
@ -1291,7 +1388,7 @@ impl Activatable for HTMLInputElement {
|
||||||
self.SetChecked(!cache.checked);
|
self.SetChecked(!cache.checked);
|
||||||
},
|
},
|
||||||
// https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio):pre-click-activation-steps
|
// https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio):pre-click-activation-steps
|
||||||
InputType::InputRadio => {
|
InputType::Radio => {
|
||||||
//TODO: if not in document, use root ancestor instead of document
|
//TODO: if not in document, use root ancestor instead of document
|
||||||
let owner = self.form_owner();
|
let owner = self.form_owner();
|
||||||
let doc = document_from_node(self);
|
let doc = document_from_node(self);
|
||||||
|
@ -1318,7 +1415,7 @@ impl Activatable for HTMLInputElement {
|
||||||
// https://html.spec.whatwg.org/multipage/#run-canceled-activation-steps
|
// https://html.spec.whatwg.org/multipage/#run-canceled-activation-steps
|
||||||
fn canceled_activation(&self) {
|
fn canceled_activation(&self) {
|
||||||
let cache = self.activation_state.borrow();
|
let cache = self.activation_state.borrow();
|
||||||
let ty = self.input_type.get();
|
let ty = self.input_type();
|
||||||
if cache.old_type != ty {
|
if cache.old_type != ty {
|
||||||
// Type changed, abandon ship
|
// Type changed, abandon ship
|
||||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27414
|
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27414
|
||||||
|
@ -1326,11 +1423,11 @@ impl Activatable for HTMLInputElement {
|
||||||
}
|
}
|
||||||
match ty {
|
match ty {
|
||||||
// https://html.spec.whatwg.org/multipage/#submit-button-state-(type=submit):activation-behavior
|
// https://html.spec.whatwg.org/multipage/#submit-button-state-(type=submit):activation-behavior
|
||||||
// InputType::InputSubmit => (), // No behavior defined
|
// InputType::Submit => (), // No behavior defined
|
||||||
// https://html.spec.whatwg.org/multipage/#reset-button-state-(type=reset):activation-behavior
|
// https://html.spec.whatwg.org/multipage/#reset-button-state-(type=reset):activation-behavior
|
||||||
// InputType::InputReset => (), // No behavior defined
|
// InputType::Reset => (), // No behavior defined
|
||||||
// https://html.spec.whatwg.org/multipage/#checkbox-state-(type=checkbox):canceled-activation-steps
|
// https://html.spec.whatwg.org/multipage/#checkbox-state-(type=checkbox):canceled-activation-steps
|
||||||
InputType::InputCheckbox => {
|
InputType::Checkbox => {
|
||||||
// We want to restore state only if the element had been changed in the first place
|
// We want to restore state only if the element had been changed in the first place
|
||||||
if cache.was_mutable {
|
if cache.was_mutable {
|
||||||
self.SetIndeterminate(cache.indeterminate);
|
self.SetIndeterminate(cache.indeterminate);
|
||||||
|
@ -1339,7 +1436,7 @@ impl Activatable for HTMLInputElement {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio):canceled-activation-steps
|
// https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio):canceled-activation-steps
|
||||||
InputType::InputRadio => {
|
InputType::Radio => {
|
||||||
// We want to restore state only if the element had been changed in the first place
|
// We want to restore state only if the element had been changed in the first place
|
||||||
if cache.was_mutable {
|
if cache.was_mutable {
|
||||||
match cache.checked_radio.r() {
|
match cache.checked_radio.r() {
|
||||||
|
@ -1363,14 +1460,14 @@ impl Activatable for HTMLInputElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#run-post-click-activation-steps
|
// https://html.spec.whatwg.org/multipage/#run-post-click-activation-steps
|
||||||
fn activation_behavior(&self, _event: &Event, _target: &EventTarget) {
|
fn activation_behavior(&self, _event: &Event, _target: &EventTarget) {
|
||||||
let ty = self.input_type.get();
|
let ty = self.input_type();
|
||||||
if self.activation_state.borrow().old_type != ty || !self.is_mutable() {
|
if self.activation_state.borrow().old_type != ty || !self.is_mutable() {
|
||||||
// Type changed or input is immutable, abandon ship
|
// Type changed or input is immutable, abandon ship
|
||||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27414
|
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27414
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match ty {
|
match ty {
|
||||||
InputType::InputSubmit => {
|
InputType::Submit => {
|
||||||
// https://html.spec.whatwg.org/multipage/#submit-button-state-(type=submit):activation-behavior
|
// https://html.spec.whatwg.org/multipage/#submit-button-state-(type=submit):activation-behavior
|
||||||
// FIXME (Manishearth): support document owners (needs ability to get parent browsing context)
|
// FIXME (Manishearth): support document owners (needs ability to get parent browsing context)
|
||||||
// Check if document owner is fully active
|
// Check if document owner is fully active
|
||||||
|
@ -1379,7 +1476,7 @@ impl Activatable for HTMLInputElement {
|
||||||
FormSubmitter::InputElement(self.clone()))
|
FormSubmitter::InputElement(self.clone()))
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
InputType::InputReset => {
|
InputType::Reset => {
|
||||||
// https://html.spec.whatwg.org/multipage/#reset-button-state-(type=reset):activation-behavior
|
// https://html.spec.whatwg.org/multipage/#reset-button-state-(type=reset):activation-behavior
|
||||||
// FIXME (Manishearth): support document owners (needs ability to get parent browsing context)
|
// FIXME (Manishearth): support document owners (needs ability to get parent browsing context)
|
||||||
// Check if document owner is fully active
|
// Check if document owner is fully active
|
||||||
|
@ -1387,7 +1484,7 @@ impl Activatable for HTMLInputElement {
|
||||||
o.reset(ResetFrom::NotFromForm)
|
o.reset(ResetFrom::NotFromForm)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
InputType::InputCheckbox | InputType::InputRadio => {
|
InputType::Checkbox | InputType::Radio => {
|
||||||
// https://html.spec.whatwg.org/multipage/#checkbox-state-(type=checkbox):activation-behavior
|
// https://html.spec.whatwg.org/multipage/#checkbox-state-(type=checkbox):activation-behavior
|
||||||
// https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio):activation-behavior
|
// https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio):activation-behavior
|
||||||
// Check if document owner is fully active
|
// Check if document owner is fully active
|
||||||
|
@ -1395,7 +1492,7 @@ impl Activatable for HTMLInputElement {
|
||||||
target.fire_bubbling_event(atom!("input"));
|
target.fire_bubbling_event(atom!("input"));
|
||||||
target.fire_bubbling_event(atom!("change"));
|
target.fire_bubbling_event(atom!("change"));
|
||||||
},
|
},
|
||||||
InputType::InputFile => self.select_files(None),
|
InputType::File => self.select_files(None),
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1433,11 +1530,11 @@ impl Activatable for HTMLInputElement {
|
||||||
let inputs = node.query_selector_iter(DOMString::from("input")).unwrap()
|
let inputs = node.query_selector_iter(DOMString::from("input")).unwrap()
|
||||||
.filter_map(DomRoot::downcast::<HTMLInputElement>)
|
.filter_map(DomRoot::downcast::<HTMLInputElement>)
|
||||||
.filter(|input| {
|
.filter(|input| {
|
||||||
input.form_owner() == owner && match input.type_() {
|
input.form_owner() == owner && match input.input_type() {
|
||||||
atom!("text") | atom!("search") | atom!("url") | atom!("tel") |
|
InputType::Text | InputType::Search | InputType::Url | InputType::Tel
|
||||||
atom!("email") | atom!("password") | atom!("datetime") |
|
| InputType::Email | InputType::Password | InputType::Datetime
|
||||||
atom!("date") | atom!("month") | atom!("week") | atom!("time") |
|
| InputType::Date | InputType::Month | InputType::Week | InputType::Time
|
||||||
atom!("datetime-local") | atom!("number")
|
| InputType::DatetimeLocal | InputType::Number
|
||||||
=> true,
|
=> true,
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use dom::bindings::inheritance::Castable;
|
||||||
use dom::bindings::reflector::reflect_dom_object;
|
use dom::bindings::reflector::reflect_dom_object;
|
||||||
use dom::bindings::root::{Dom, DomRoot};
|
use dom::bindings::root::{Dom, DomRoot};
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
use dom::htmlinputelement::HTMLInputElement;
|
use dom::htmlinputelement::{HTMLInputElement, InputType};
|
||||||
use dom::node::Node;
|
use dom::node::Node;
|
||||||
use dom::nodelist::{NodeList, NodeListType};
|
use dom::nodelist::{NodeList, NodeListType};
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
|
@ -55,13 +55,12 @@ impl RadioNodeListMethods for RadioNodeList {
|
||||||
self.upcast::<NodeList>().as_simple_list().iter().filter_map(|node| {
|
self.upcast::<NodeList>().as_simple_list().iter().filter_map(|node| {
|
||||||
// Step 1
|
// Step 1
|
||||||
node.downcast::<HTMLInputElement>().and_then(|input| {
|
node.downcast::<HTMLInputElement>().and_then(|input| {
|
||||||
match input.type_() {
|
if input.input_type() == InputType::Radio && input.Checked() {
|
||||||
atom!("radio") if input.Checked() => {
|
// Step 3-4
|
||||||
// Step 3-4
|
let value = input.Value();
|
||||||
let value = input.Value();
|
Some(if value.is_empty() { DOMString::from("on") } else { value })
|
||||||
Some(if value.is_empty() { DOMString::from("on") } else { value })
|
} else {
|
||||||
}
|
None
|
||||||
_ => None
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}).next()
|
}).next()
|
||||||
|
@ -74,8 +73,8 @@ impl RadioNodeListMethods for RadioNodeList {
|
||||||
for node in self.upcast::<NodeList>().as_simple_list().iter() {
|
for node in self.upcast::<NodeList>().as_simple_list().iter() {
|
||||||
// Step 1
|
// Step 1
|
||||||
if let Some(input) = node.downcast::<HTMLInputElement>() {
|
if let Some(input) = node.downcast::<HTMLInputElement>() {
|
||||||
match input.type_() {
|
match input.input_type() {
|
||||||
atom!("radio") if value == DOMString::from("on") => {
|
InputType::Radio if value == DOMString::from("on") => {
|
||||||
// Step 2
|
// Step 2
|
||||||
let val = input.Value();
|
let val = input.Value();
|
||||||
if val.is_empty() || val == value {
|
if val.is_empty() || val == value {
|
||||||
|
@ -83,7 +82,7 @@ impl RadioNodeListMethods for RadioNodeList {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
atom!("radio") => {
|
InputType::Radio => {
|
||||||
// Step 2
|
// Step 2
|
||||||
if input.Value() == value {
|
if input.Value() == value {
|
||||||
input.SetChecked(true);
|
input.SetChecked(true);
|
||||||
|
|
|
@ -12,9 +12,6 @@
|
||||||
[Radiobutton must retain unchecked state.]
|
[Radiobutton must retain unchecked state.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Hidden field must retain changed value.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Text field must retain changed value.]
|
[Text field must retain changed value.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue