mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #9514 - g-k:html-input-value, r=KiChjang
HTML input value Ready for review. Fixes #9455. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.svg" height="40" alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9514) <!-- Reviewable:end -->
This commit is contained in:
commit
0d7a2eee2d
5 changed files with 114 additions and 152 deletions
|
@ -11,6 +11,7 @@ use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
|
||||||
use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
|
use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
|
||||||
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
||||||
use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
|
use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
|
||||||
|
use dom::bindings::error::{Error, ErrorResult};
|
||||||
use dom::bindings::global::GlobalRef;
|
use dom::bindings::global::GlobalRef;
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
use dom::bindings::js::{JS, LayoutJS, Root, RootedReference};
|
use dom::bindings::js::{JS, LayoutJS, Root, RootedReference};
|
||||||
|
@ -59,6 +60,14 @@ enum InputType {
|
||||||
InputPassword
|
InputPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum ValueMode {
|
||||||
|
Value,
|
||||||
|
Default,
|
||||||
|
DefaultOn,
|
||||||
|
Filename,
|
||||||
|
}
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct HTMLInputElement {
|
pub struct HTMLInputElement {
|
||||||
htmlelement: HTMLElement,
|
htmlelement: HTMLElement,
|
||||||
|
@ -71,6 +80,10 @@ pub struct HTMLInputElement {
|
||||||
#[ignore_heap_size_of = "#7193"]
|
#[ignore_heap_size_of = "#7193"]
|
||||||
textinput: DOMRefCell<TextInput<ConstellationChan<ConstellationMsg>>>,
|
textinput: DOMRefCell<TextInput<ConstellationChan<ConstellationMsg>>>,
|
||||||
activation_state: DOMRefCell<InputActivationState>,
|
activation_state: DOMRefCell<InputActivationState>,
|
||||||
|
// https://html.spec.whatwg.org/multipage/#concept-input-value-dirty-flag
|
||||||
|
value_dirty: Cell<bool>,
|
||||||
|
|
||||||
|
// TODO: selected files for file input
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable)]
|
#[derive(JSTraceable)]
|
||||||
|
@ -117,7 +130,8 @@ impl HTMLInputElement {
|
||||||
maxlength: Cell::new(DEFAULT_MAX_LENGTH),
|
maxlength: Cell::new(DEFAULT_MAX_LENGTH),
|
||||||
size: Cell::new(DEFAULT_INPUT_SIZE),
|
size: Cell::new(DEFAULT_INPUT_SIZE),
|
||||||
textinput: DOMRefCell::new(TextInput::new(Single, DOMString::new(), chan, None)),
|
textinput: DOMRefCell::new(TextInput::new(Single, DOMString::new(), chan, None)),
|
||||||
activation_state: DOMRefCell::new(InputActivationState::new())
|
activation_state: DOMRefCell::new(InputActivationState::new()),
|
||||||
|
value_dirty: Cell::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +148,22 @@ impl HTMLInputElement {
|
||||||
.get_attribute(&ns!(), &atom!("type"))
|
.get_attribute(&ns!(), &atom!("type"))
|
||||||
.map_or_else(|| atom!(""), |a| a.value().as_atom().to_owned())
|
.map_or_else(|| atom!(""), |a| a.value().as_atom().to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#input-type-attr-summary
|
||||||
|
fn get_value_mode(&self) -> ValueMode {
|
||||||
|
match 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait LayoutHTMLInputElementHelpers {
|
pub trait LayoutHTMLInputElementHelpers {
|
||||||
|
@ -292,14 +322,50 @@ impl HTMLInputElementMethods for HTMLInputElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-input-value
|
// https://html.spec.whatwg.org/multipage/#dom-input-value
|
||||||
fn Value(&self) -> DOMString {
|
fn Value(&self) -> DOMString {
|
||||||
self.textinput.borrow().get_content()
|
match self.get_value_mode() {
|
||||||
|
ValueMode::Value => self.textinput.borrow().get_content(),
|
||||||
|
ValueMode::Default => {
|
||||||
|
self.upcast::<Element>()
|
||||||
|
.get_attribute(&ns!(), &atom!("value"))
|
||||||
|
.map_or(DOMString::from(""),
|
||||||
|
|a| DOMString::from(a.summarize().value))
|
||||||
|
}
|
||||||
|
ValueMode::DefaultOn => {
|
||||||
|
self.upcast::<Element>()
|
||||||
|
.get_attribute(&ns!(), &atom!("value"))
|
||||||
|
.map_or(DOMString::from("on"),
|
||||||
|
|a| DOMString::from(a.summarize().value))
|
||||||
|
}
|
||||||
|
ValueMode::Filename => {
|
||||||
|
// TODO: return C:\fakepath\<first of selected files> when a file is selected
|
||||||
|
DOMString::from("")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-input-value
|
// https://html.spec.whatwg.org/multipage/#dom-input-value
|
||||||
fn SetValue(&self, value: DOMString) {
|
fn SetValue(&self, value: DOMString) -> ErrorResult {
|
||||||
|
match self.get_value_mode() {
|
||||||
|
ValueMode::Value => {
|
||||||
self.textinput.borrow_mut().set_content(value);
|
self.textinput.borrow_mut().set_content(value);
|
||||||
|
self.value_dirty.set(true);
|
||||||
|
}
|
||||||
|
ValueMode::Default |
|
||||||
|
ValueMode::DefaultOn => {
|
||||||
|
self.upcast::<Element>().set_string_attribute(&atom!("value"), value);
|
||||||
|
}
|
||||||
|
ValueMode::Filename => {
|
||||||
|
if value.is_empty() {
|
||||||
|
// TODO: empty list of selected files
|
||||||
|
} else {
|
||||||
|
return Err(Error::InvalidState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.value_changed.set(true);
|
self.value_changed.set(true);
|
||||||
self.force_relayout();
|
self.force_relayout();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-input-defaultvalue
|
// https://html.spec.whatwg.org/multipage/#dom-input-defaultvalue
|
||||||
|
@ -465,17 +531,11 @@ impl HTMLInputElement {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut value = self.Value();
|
|
||||||
// Step 3.6
|
// Step 3.6
|
||||||
if ty == atom!("radio") || ty == atom!("checkbox") {
|
|
||||||
if value.is_empty() {
|
|
||||||
value = DOMString::from("on");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(FormDatum {
|
Some(FormDatum {
|
||||||
ty: DOMString::from(&*ty), // FIXME(ajeffrey): Convert directly from Atoms to DOMStrings
|
ty: DOMString::from(&*ty), // FIXME(ajeffrey): Convert directly from Atoms to DOMStrings
|
||||||
name: name,
|
name: name,
|
||||||
value: value
|
value: self.Value()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,7 +585,9 @@ impl HTMLInputElement {
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.SetValue(self.DefaultValue());
|
self.SetValue(self.DefaultValue())
|
||||||
|
.expect("Failed to reset input value to default.");
|
||||||
|
self.value_dirty.set(false);
|
||||||
self.value_changed.set(false);
|
self.value_changed.set(false);
|
||||||
self.force_relayout();
|
self.force_relayout();
|
||||||
}
|
}
|
||||||
|
@ -574,7 +636,7 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
&atom!("type") => {
|
&atom!("type") => {
|
||||||
match mutation {
|
match mutation {
|
||||||
AttributeMutation::Set(_) => {
|
AttributeMutation::Set(_) => {
|
||||||
let value = match attr.value().as_atom() {
|
let new_type = match attr.value().as_atom() {
|
||||||
&atom!("button") => InputType::InputButton,
|
&atom!("button") => InputType::InputButton,
|
||||||
&atom!("submit") => InputType::InputSubmit,
|
&atom!("submit") => InputType::InputSubmit,
|
||||||
&atom!("reset") => InputType::InputReset,
|
&atom!("reset") => InputType::InputReset,
|
||||||
|
@ -584,11 +646,46 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
&atom!("password") => InputType::InputPassword,
|
&atom!("password") => InputType::InputPassword,
|
||||||
_ => InputType::InputText,
|
_ => InputType::InputText,
|
||||||
};
|
};
|
||||||
self.input_type.set(value);
|
|
||||||
if value == InputType::InputRadio {
|
// https://html.spec.whatwg.org/multipage/#input-type-change
|
||||||
|
let (old_value_mode, old_idl_value) = (self.get_value_mode(), self.Value());
|
||||||
|
self.input_type.set(new_type);
|
||||||
|
let new_value_mode = self.get_value_mode();
|
||||||
|
|
||||||
|
match (&old_value_mode, old_idl_value.is_empty(), new_value_mode) {
|
||||||
|
|
||||||
|
// Step 1
|
||||||
|
(&ValueMode::Value, false, ValueMode::Default) |
|
||||||
|
(&ValueMode::Value, false, ValueMode::DefaultOn) => {
|
||||||
|
self.SetValue(old_idl_value)
|
||||||
|
.expect("Failed to set input value on type change to a default ValueMode.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
(_, _, ValueMode::Value) if old_value_mode != ValueMode::Value => {
|
||||||
|
self.SetValue(self.upcast::<Element>()
|
||||||
|
.get_attribute(&ns!(), &atom!("value"))
|
||||||
|
.map_or(DOMString::from(""),
|
||||||
|
|a| DOMString::from(a.summarize().value)))
|
||||||
|
.expect("Failed to set input value on type change to ValueMode::Value.");
|
||||||
|
self.value_dirty.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3
|
||||||
|
(_, _, ValueMode::Filename) if old_value_mode != ValueMode::Filename => {
|
||||||
|
self.SetValue(DOMString::from(""))
|
||||||
|
.expect("Failed to set input value on type change to ValueMode::Filename.");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5
|
||||||
|
if new_type == InputType::InputRadio {
|
||||||
self.radio_group_updated(
|
self.radio_group_updated(
|
||||||
self.get_radio_group_name().as_ref());
|
self.get_radio_group_name().as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Step 6 - value sanitization
|
||||||
},
|
},
|
||||||
AttributeMutation::Removed => {
|
AttributeMutation::Removed => {
|
||||||
if self.input_type.get() == InputType::InputRadio {
|
if self.input_type.get() == InputType::InputRadio {
|
||||||
|
|
|
@ -41,7 +41,8 @@ interface HTMLInputElement : HTMLElement {
|
||||||
// attribute DOMString step;
|
// attribute DOMString step;
|
||||||
attribute DOMString type;
|
attribute DOMString type;
|
||||||
attribute DOMString defaultValue;
|
attribute DOMString defaultValue;
|
||||||
[TreatNullAs=EmptyString] attribute DOMString value;
|
[TreatNullAs=EmptyString, SetterThrows]
|
||||||
|
attribute DOMString value;
|
||||||
// attribute Date? valueAsDate;
|
// attribute Date? valueAsDate;
|
||||||
// attribute unrestricted double valueAsNumber;
|
// attribute unrestricted double valueAsNumber;
|
||||||
// attribute double valueLow;
|
// attribute double valueLow;
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[radionodelist.html]
|
|
||||||
type: testharness
|
|
||||||
[Check the RadioNodeList.value on setting to 'on']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[input-type-checkbox.html]
|
|
||||||
type: testharness
|
|
||||||
[default/on: on getting, if the element has a value attribute, it must return that attribute's value; otherwise, it must return the string 'on']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -672,69 +672,6 @@
|
||||||
[change state from radio to color]
|
[change state from radio to color]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[change state from file to hidden]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to text]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to search]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to tel]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to url]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to email]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to password]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to datetime]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to date]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to month]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to week]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to time]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to number]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to range]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to color]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to checkbox]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to radio]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to submit]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to image]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to reset]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from file to button]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from submit to text]
|
[change state from submit to text]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -903,66 +840,3 @@
|
||||||
[change state from button to color]
|
[change state from button to color]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[change state from hidden to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from text to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from search to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from tel to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from url to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from email to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from password to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from datetime to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from date to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from month to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from week to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from time to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from number to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from range to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from color to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from checkbox to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from radio to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from submit to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from image to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from reset to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[change state from button to file]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue