1use embedder_traits::InputMethodType;
5use js::context::JSContext;
6use script_bindings::codegen::GenericBindings::HTMLInputElementBinding::HTMLInputElementMethods;
7use script_bindings::domstring::DOMString;
8use script_bindings::root::DomRoot;
9use script_bindings::script_runtime::CanGc;
10use stylo_atoms::Atom;
11use time::OffsetDateTime;
12
13use crate::dom::attr::Attr;
14use crate::dom::element::AttributeMutation;
15use crate::dom::event::Event;
16use crate::dom::eventtarget::EventTarget;
17use crate::dom::filelist::FileList;
18use crate::dom::htmlformelement::HTMLFormElement;
19use crate::dom::input_element::button_input_type::ButtonInputType;
20use crate::dom::input_element::checkbox_input_type::CheckboxInputType;
21use crate::dom::input_element::color_input_type::ColorInputType;
22use crate::dom::input_element::date_input_type::DateInputType;
23use crate::dom::input_element::datetime_local_input_type::DatetimeLocalInputType;
24use crate::dom::input_element::email_input_type::EmailInputType;
25use crate::dom::input_element::file_input_type::FileInputType;
26use crate::dom::input_element::hidden_input_type::HiddenInputType;
27use crate::dom::input_element::image_input_type::ImageInputType;
28use crate::dom::input_element::month_input_type::MonthInputType;
29use crate::dom::input_element::number_input_type::NumberInputType;
30use crate::dom::input_element::password_input_type::PasswordInputType;
31use crate::dom::input_element::radio_input_type::RadioInputType;
32use crate::dom::input_element::range_input_type::RangeInputType;
33use crate::dom::input_element::reset_input_type::ResetInputType;
34use crate::dom::input_element::search_input_type::SearchInputType;
35use crate::dom::input_element::submit_input_type::SubmitInputType;
36use crate::dom::input_element::tel_input_type::TelInputType;
37use crate::dom::input_element::text_input_type::TextInputType;
38use crate::dom::input_element::time_input_type::TimeInputType;
39use crate::dom::input_element::url_input_type::UrlInputType;
40use crate::dom::input_element::week_input_type::WeekInputType;
41use crate::dom::input_element::{HTMLInputElement, InputActivationState, ValueMode};
42use crate::dom::node::{BindContext, UnbindContext};
43
44#[derive(JSTraceable, MallocSizeOf, PartialEq)]
46#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
47pub(crate) enum InputType {
48 Button(ButtonInputType),
50
51 Checkbox(CheckboxInputType),
53
54 Color(ColorInputType),
56
57 Date(DateInputType),
59
60 DatetimeLocal(DatetimeLocalInputType),
62
63 Email(EmailInputType),
65
66 File(FileInputType),
68
69 Hidden(HiddenInputType),
71
72 Image(ImageInputType),
74
75 Month(MonthInputType),
77
78 Number(NumberInputType),
80
81 Password(PasswordInputType),
83
84 Radio(RadioInputType),
86
87 Range(RangeInputType),
89
90 Reset(ResetInputType),
92
93 Search(SearchInputType),
95
96 Submit(SubmitInputType),
98
99 Tel(TelInputType),
101
102 Text(TextInputType),
104
105 Time(TimeInputType),
107
108 Url(UrlInputType),
110
111 Week(WeekInputType),
113}
114
115impl InputType {
116 pub(crate) fn new_from_atom(value: &Atom) -> Self {
117 match value.to_ascii_lowercase() {
118 atom!("button") => InputType::Button(Default::default()),
119 atom!("checkbox") => InputType::Checkbox(Default::default()),
120 atom!("color") => InputType::Color(Default::default()),
121 atom!("date") => InputType::Date(Default::default()),
122 atom!("datetime-local") => InputType::DatetimeLocal(Default::default()),
123 atom!("email") => InputType::Email(Default::default()),
124 atom!("file") => InputType::File(Default::default()),
125 atom!("hidden") => InputType::Hidden(Default::default()),
126 atom!("image") => InputType::Image(Default::default()),
127 atom!("month") => InputType::Month(Default::default()),
128 atom!("number") => InputType::Number(Default::default()),
129 atom!("password") => InputType::Password(Default::default()),
130 atom!("radio") => InputType::Radio(Default::default()),
131 atom!("range") => InputType::Range(Default::default()),
132 atom!("reset") => InputType::Reset(Default::default()),
133 atom!("search") => InputType::Search(Default::default()),
134 atom!("submit") => InputType::Submit(Default::default()),
135 atom!("tel") => InputType::Tel(Default::default()),
136 atom!("text") => InputType::Text(Default::default()),
137 atom!("time") => InputType::Time(Default::default()),
138 atom!("url") => InputType::Url(Default::default()),
139 atom!("week") => InputType::Week(Default::default()),
140 _ => InputType::Text(Default::default()),
141 }
142 }
143
144 pub(crate) fn new_text() -> Self {
145 Self::Text(TextInputType::default())
146 }
147
148 pub(crate) fn as_specific(&self) -> &dyn SpecificInputType {
149 match self {
150 Self::Button(input_type) => input_type as &dyn SpecificInputType,
151 Self::Checkbox(input_type) => input_type as &dyn SpecificInputType,
152 Self::Color(input_type) => input_type as &dyn SpecificInputType,
153 Self::Date(input_type) => input_type as &dyn SpecificInputType,
154 Self::DatetimeLocal(input_type) => input_type as &dyn SpecificInputType,
155 Self::Email(input_type) => input_type as &dyn SpecificInputType,
156 Self::File(input_type) => input_type as &dyn SpecificInputType,
157 Self::Hidden(input_type) => input_type as &dyn SpecificInputType,
158 Self::Image(input_type) => input_type as &dyn SpecificInputType,
159 Self::Month(input_type) => input_type as &dyn SpecificInputType,
160 Self::Number(input_type) => input_type as &dyn SpecificInputType,
161 Self::Password(input_type) => input_type as &dyn SpecificInputType,
162 Self::Radio(input_type) => input_type as &dyn SpecificInputType,
163 Self::Range(input_type) => input_type as &dyn SpecificInputType,
164 Self::Reset(input_type) => input_type as &dyn SpecificInputType,
165 Self::Search(input_type) => input_type as &dyn SpecificInputType,
166 Self::Submit(input_type) => input_type as &dyn SpecificInputType,
167 Self::Tel(input_type) => input_type as &dyn SpecificInputType,
168 Self::Text(input_type) => input_type as &dyn SpecificInputType,
169 Self::Time(input_type) => input_type as &dyn SpecificInputType,
170 Self::Url(input_type) => input_type as &dyn SpecificInputType,
171 Self::Week(input_type) => input_type as &dyn SpecificInputType,
172 }
173 }
174
175 pub(crate) fn is_textual(&self) -> bool {
180 matches!(
181 *self,
182 Self::Date(_) |
183 Self::DatetimeLocal(_) |
184 Self::Email(_) |
185 Self::Hidden(_) |
186 Self::Month(_) |
187 Self::Number(_) |
188 Self::Range(_) |
189 Self::Search(_) |
190 Self::Tel(_) |
191 Self::Text(_) |
192 Self::Time(_) |
193 Self::Url(_) |
194 Self::Week(_)
195 )
196 }
197
198 pub(crate) fn is_textual_or_password(&self) -> bool {
199 self.is_textual() || matches!(self, Self::Password(_))
200 }
201
202 pub(crate) fn has_periodic_domain(&self) -> bool {
204 matches!(self, Self::Time(_))
205 }
206
207 pub(crate) fn as_str(&self) -> &str {
208 match *self {
209 InputType::Button(_) => "button",
210 InputType::Checkbox(_) => "checkbox",
211 InputType::Color(_) => "color",
212 InputType::Date(_) => "date",
213 InputType::DatetimeLocal(_) => "datetime-local",
214 InputType::Email(_) => "email",
215 InputType::File(_) => "file",
216 InputType::Hidden(_) => "hidden",
217 InputType::Image(_) => "image",
218 InputType::Month(_) => "month",
219 InputType::Number(_) => "number",
220 InputType::Password(_) => "password",
221 InputType::Radio(_) => "radio",
222 InputType::Range(_) => "range",
223 InputType::Reset(_) => "reset",
224 InputType::Search(_) => "search",
225 InputType::Submit(_) => "submit",
226 InputType::Tel(_) => "tel",
227 InputType::Text(_) => "text",
228 InputType::Time(_) => "time",
229 InputType::Url(_) => "url",
230 InputType::Week(_) => "week",
231 }
232 }
233}
234
235impl TryFrom<&InputType> for InputMethodType {
236 type Error = &'static str;
237
238 fn try_from(input_type: &InputType) -> Result<Self, Self::Error> {
239 match input_type {
240 InputType::Color(_) => Ok(InputMethodType::Color),
241 InputType::Date(_) => Ok(InputMethodType::Date),
242 InputType::DatetimeLocal(_) => Ok(InputMethodType::DatetimeLocal),
243 InputType::Email(_) => Ok(InputMethodType::Email),
244 InputType::Month(_) => Ok(InputMethodType::Month),
245 InputType::Number(_) => Ok(InputMethodType::Number),
246 InputType::Password(_) => Ok(InputMethodType::Password),
247 InputType::Search(_) => Ok(InputMethodType::Search),
248 InputType::Tel(_) => Ok(InputMethodType::Tel),
249 InputType::Text(_) => Ok(InputMethodType::Text),
250 InputType::Time(_) => Ok(InputMethodType::Time),
251 InputType::Url(_) => Ok(InputMethodType::Url),
252 InputType::Week(_) => Ok(InputMethodType::Week),
253 _ => Err("Input does not support IME."),
254 }
255 }
256}
257
258pub(crate) trait SpecificInputType {
259 fn sanitize_value(&self, _input: &HTMLInputElement, _value: &mut DOMString) {}
260
261 fn convert_string_to_number(&self, _value: &str) -> Option<f64> {
262 None
263 }
264
265 fn convert_number_to_string(&self, _value: f64) -> Option<DOMString> {
266 unreachable!("Should not have called convert_number_to_string for non-Date types")
267 }
268
269 fn convert_string_to_naive_datetime(&self, _value: DOMString) -> Option<OffsetDateTime> {
273 None
274 }
275
276 fn convert_datetime_to_dom_string(&self, _value: OffsetDateTime) -> DOMString {
280 unreachable!("Should not have called convert_datetime_to_string for non-Date types")
281 }
282
283 fn suffers_from_being_missing(&self, input: &HTMLInputElement, value: &DOMString) -> bool {
285 input.Required() &&
286 input.value_mode() == ValueMode::Value &&
287 input.is_mutable() &&
288 value.is_empty()
289 }
290
291 fn suffers_from_bad_input(&self, _value: &DOMString) -> bool {
292 false
293 }
294
295 fn suffers_from_type_mismatch(&self, _input: &HTMLInputElement, _value: &DOMString) -> bool {
296 false
297 }
298
299 fn value_for_shadow_dom(&self, _input: &HTMLInputElement) -> DOMString {
300 "".into()
301 }
302
303 fn signal_type_change(&self, _input: &HTMLInputElement, _can_gc: CanGc) {}
305
306 fn activation_behavior(
307 &self,
308 _input: &HTMLInputElement,
309 _event: &Event,
310 _target: &EventTarget,
311 _can_gc: CanGc,
312 ) {
313 }
314
315 fn legacy_pre_activation_behavior(
316 &self,
317 _input: &HTMLInputElement,
318 _can_gc: CanGc,
319 ) -> Option<InputActivationState> {
320 None
321 }
322
323 fn legacy_canceled_activation_behavior(
324 &self,
325 _input: &HTMLInputElement,
326 _cache: InputActivationState,
327 _can_gc: CanGc,
328 ) {
329 }
330
331 fn show_the_picker_if_applicable(&self, _input: &HTMLInputElement) {}
332
333 fn select_files(&self, _input: &HTMLInputElement, _test_paths: Option<Vec<DOMString>>) {}
334
335 fn get_files(&self) -> Option<DomRoot<FileList>> {
336 None
337 }
338
339 fn set_files(&self, _filelist: &FileList) {}
340
341 fn update_shadow_tree(&self, _cx: &mut JSContext, _input: &HTMLInputElement) {}
342
343 fn update_placeholder_contents(&self, _cx: &mut JSContext, _input: &HTMLInputElement) {}
344
345 fn attribute_mutated(
346 &self,
347 _cx: &mut JSContext,
348 _input: &HTMLInputElement,
349 _attr: &Attr,
350 _mutation: AttributeMutation,
351 ) {
352 }
353
354 fn bind_to_tree(&self, _cx: &mut JSContext, _input: &HTMLInputElement, _context: &BindContext) {
355 }
356
357 fn unbind_from_tree(
358 &self,
359 _input: &HTMLInputElement,
360 _form_owner: Option<DomRoot<HTMLFormElement>>,
361 _context: &UnbindContext,
362 _can_gc: CanGc,
363 ) {
364 }
365}