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 stylo_atoms::Atom;
10use time::OffsetDateTime;
11
12use crate::dom::element::AttributeMutation;
13use crate::dom::element::attributes::storage::AttrRef;
14use crate::dom::event::Event;
15use crate::dom::eventtarget::EventTarget;
16use crate::dom::filelist::FileList;
17use crate::dom::htmlformelement::HTMLFormElement;
18use crate::dom::input_element::button_input_type::{ButtonInputActivation, ButtonInputType};
19use crate::dom::input_element::checkbox_input_type::{CheckboxInputActivation, CheckboxInputType};
20use crate::dom::input_element::color_input_type::{ColorInputActivation, ColorInputType};
21use crate::dom::input_element::date_input_type::DateInputType;
22use crate::dom::input_element::datetime_local_input_type::DatetimeLocalInputType;
23use crate::dom::input_element::email_input_type::EmailInputType;
24use crate::dom::input_element::file_input_type::{FileInputActivation, FileInputType};
25use crate::dom::input_element::hidden_input_type::HiddenInputType;
26use crate::dom::input_element::image_input_type::{ImageInputActivation, ImageInputType};
27use crate::dom::input_element::month_input_type::MonthInputType;
28use crate::dom::input_element::number_input_type::NumberInputType;
29use crate::dom::input_element::password_input_type::PasswordInputType;
30use crate::dom::input_element::radio_input_type::{RadioInputActivation, RadioInputType};
31use crate::dom::input_element::range_input_type::RangeInputType;
32use crate::dom::input_element::reset_input_type::{ResetInputActivation, ResetInputType};
33use crate::dom::input_element::search_input_type::SearchInputType;
34use crate::dom::input_element::submit_input_type::{SubmitInputActivation, SubmitInputType};
35use crate::dom::input_element::tel_input_type::TelInputType;
36use crate::dom::input_element::text_input_type::TextInputType;
37use crate::dom::input_element::time_input_type::TimeInputType;
38use crate::dom::input_element::url_input_type::UrlInputType;
39use crate::dom::input_element::week_input_type::WeekInputType;
40use crate::dom::input_element::{HTMLInputElement, InputActivationState, ValueMode};
41use crate::dom::node::{BindContext, UnbindContext};
42
43#[derive(JSTraceable, MallocSizeOf, PartialEq)]
45#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
46pub(crate) enum InputType {
47 Button(ButtonInputType),
49
50 Checkbox(CheckboxInputType),
52
53 Color(ColorInputType),
55
56 Date(DateInputType),
58
59 DatetimeLocal(DatetimeLocalInputType),
61
62 Email(EmailInputType),
64
65 File(FileInputType),
67
68 Hidden(HiddenInputType),
70
71 Image(ImageInputType),
73
74 Month(MonthInputType),
76
77 Number(NumberInputType),
79
80 Password(PasswordInputType),
82
83 Radio(RadioInputType),
85
86 Range(RangeInputType),
88
89 Reset(ResetInputType),
91
92 Search(SearchInputType),
94
95 Submit(SubmitInputType),
97
98 Tel(TelInputType),
100
101 Text(TextInputType),
103
104 Time(TimeInputType),
106
107 Url(UrlInputType),
109
110 Week(WeekInputType),
112}
113
114#[derive(Clone, Copy)]
115pub(crate) enum InputActivationType {
116 Button(ButtonInputActivation),
117 Checkbox(CheckboxInputActivation),
118 Color(ColorInputActivation),
119 File(FileInputActivation),
120 Image(ImageInputActivation),
121 Radio(RadioInputActivation),
122 Reset(ResetInputActivation),
123 Submit(SubmitInputActivation),
124}
125
126impl InputActivationType {
127 pub(crate) fn new_from_input_type(input_type: &InputType) -> Option<Self> {
128 match input_type {
129 InputType::Button(_) => Some(Self::Button(ButtonInputActivation)),
130 InputType::Checkbox(_) => Some(Self::Checkbox(CheckboxInputActivation)),
131 InputType::Color(_) => Some(Self::Color(ColorInputActivation)),
132 InputType::File(_) => Some(Self::File(FileInputActivation)),
133 InputType::Image(_) => Some(Self::Image(ImageInputActivation)),
134 InputType::Radio(_) => Some(Self::Radio(RadioInputActivation)),
135 InputType::Reset(_) => Some(Self::Reset(ResetInputActivation)),
136 InputType::Submit(_) => Some(Self::Submit(SubmitInputActivation)),
137 _ => None,
138 }
139 }
140
141 pub(crate) fn as_specific(&self) -> &dyn SpecificInputActivationType {
142 match self {
143 Self::Button(input_type) => input_type as &dyn SpecificInputActivationType,
144 Self::Checkbox(input_type) => input_type as &dyn SpecificInputActivationType,
145 Self::Color(input_type) => input_type as &dyn SpecificInputActivationType,
146 Self::File(input_type) => input_type as &dyn SpecificInputActivationType,
147 Self::Image(input_type) => input_type as &dyn SpecificInputActivationType,
148 Self::Radio(input_type) => input_type as &dyn SpecificInputActivationType,
149 Self::Reset(input_type) => input_type as &dyn SpecificInputActivationType,
150 Self::Submit(input_type) => input_type as &dyn SpecificInputActivationType,
151 }
152 }
153}
154
155impl InputType {
156 pub(crate) fn new_from_atom(value: &Atom) -> Self {
157 match value.to_ascii_lowercase() {
158 atom!("button") => InputType::Button(Default::default()),
159 atom!("checkbox") => InputType::Checkbox(Default::default()),
160 atom!("color") => InputType::Color(Default::default()),
161 atom!("date") => InputType::Date(Default::default()),
162 atom!("datetime-local") => InputType::DatetimeLocal(Default::default()),
163 atom!("email") => InputType::Email(Default::default()),
164 atom!("file") => InputType::File(Default::default()),
165 atom!("hidden") => InputType::Hidden(Default::default()),
166 atom!("image") => InputType::Image(Default::default()),
167 atom!("month") => InputType::Month(Default::default()),
168 atom!("number") => InputType::Number(Default::default()),
169 atom!("password") => InputType::Password(Default::default()),
170 atom!("radio") => InputType::Radio(Default::default()),
171 atom!("range") => InputType::Range(Default::default()),
172 atom!("reset") => InputType::Reset(Default::default()),
173 atom!("search") => InputType::Search(Default::default()),
174 atom!("submit") => InputType::Submit(Default::default()),
175 atom!("tel") => InputType::Tel(Default::default()),
176 atom!("text") => InputType::Text(Default::default()),
177 atom!("time") => InputType::Time(Default::default()),
178 atom!("url") => InputType::Url(Default::default()),
179 atom!("week") => InputType::Week(Default::default()),
180 _ => InputType::Text(Default::default()),
181 }
182 }
183
184 pub(crate) fn new_text() -> Self {
185 Self::Text(TextInputType::default())
186 }
187
188 pub(crate) fn as_specific(&self) -> &dyn SpecificInputType {
189 match self {
190 Self::Button(input_type) => input_type as &dyn SpecificInputType,
191 Self::Checkbox(input_type) => input_type as &dyn SpecificInputType,
192 Self::Color(input_type) => input_type as &dyn SpecificInputType,
193 Self::Date(input_type) => input_type as &dyn SpecificInputType,
194 Self::DatetimeLocal(input_type) => input_type as &dyn SpecificInputType,
195 Self::Email(input_type) => input_type as &dyn SpecificInputType,
196 Self::File(input_type) => input_type as &dyn SpecificInputType,
197 Self::Hidden(input_type) => input_type as &dyn SpecificInputType,
198 Self::Image(input_type) => input_type as &dyn SpecificInputType,
199 Self::Month(input_type) => input_type as &dyn SpecificInputType,
200 Self::Number(input_type) => input_type as &dyn SpecificInputType,
201 Self::Password(input_type) => input_type as &dyn SpecificInputType,
202 Self::Radio(input_type) => input_type as &dyn SpecificInputType,
203 Self::Range(input_type) => input_type as &dyn SpecificInputType,
204 Self::Reset(input_type) => input_type as &dyn SpecificInputType,
205 Self::Search(input_type) => input_type as &dyn SpecificInputType,
206 Self::Submit(input_type) => input_type as &dyn SpecificInputType,
207 Self::Tel(input_type) => input_type as &dyn SpecificInputType,
208 Self::Text(input_type) => input_type as &dyn SpecificInputType,
209 Self::Time(input_type) => input_type as &dyn SpecificInputType,
210 Self::Url(input_type) => input_type as &dyn SpecificInputType,
211 Self::Week(input_type) => input_type as &dyn SpecificInputType,
212 }
213 }
214
215 pub(crate) fn is_textual(&self) -> bool {
220 matches!(
221 *self,
222 Self::Date(_) |
223 Self::DatetimeLocal(_) |
224 Self::Email(_) |
225 Self::Hidden(_) |
226 Self::Month(_) |
227 Self::Number(_) |
228 Self::Search(_) |
229 Self::Tel(_) |
230 Self::Text(_) |
231 Self::Time(_) |
232 Self::Url(_) |
233 Self::Week(_)
234 )
235 }
236
237 pub(crate) fn is_textual_or_password(&self) -> bool {
238 self.is_textual() || matches!(self, Self::Password(_))
239 }
240
241 pub(crate) fn has_periodic_domain(&self) -> bool {
243 matches!(self, Self::Time(_))
244 }
245
246 pub(crate) fn as_str(&self) -> &str {
247 match *self {
248 InputType::Button(_) => "button",
249 InputType::Checkbox(_) => "checkbox",
250 InputType::Color(_) => "color",
251 InputType::Date(_) => "date",
252 InputType::DatetimeLocal(_) => "datetime-local",
253 InputType::Email(_) => "email",
254 InputType::File(_) => "file",
255 InputType::Hidden(_) => "hidden",
256 InputType::Image(_) => "image",
257 InputType::Month(_) => "month",
258 InputType::Number(_) => "number",
259 InputType::Password(_) => "password",
260 InputType::Radio(_) => "radio",
261 InputType::Range(_) => "range",
262 InputType::Reset(_) => "reset",
263 InputType::Search(_) => "search",
264 InputType::Submit(_) => "submit",
265 InputType::Tel(_) => "tel",
266 InputType::Text(_) => "text",
267 InputType::Time(_) => "time",
268 InputType::Url(_) => "url",
269 InputType::Week(_) => "week",
270 }
271 }
272}
273
274impl TryFrom<&InputType> for InputMethodType {
275 type Error = &'static str;
276
277 fn try_from(input_type: &InputType) -> Result<Self, Self::Error> {
278 match input_type {
279 InputType::Color(_) => Ok(InputMethodType::Color),
280 InputType::Date(_) => Ok(InputMethodType::Date),
281 InputType::DatetimeLocal(_) => Ok(InputMethodType::DatetimeLocal),
282 InputType::Email(_) => Ok(InputMethodType::Email),
283 InputType::Month(_) => Ok(InputMethodType::Month),
284 InputType::Number(_) => Ok(InputMethodType::Number),
285 InputType::Password(_) => Ok(InputMethodType::Password),
286 InputType::Search(_) => Ok(InputMethodType::Search),
287 InputType::Tel(_) => Ok(InputMethodType::Tel),
288 InputType::Text(_) => Ok(InputMethodType::Text),
289 InputType::Time(_) => Ok(InputMethodType::Time),
290 InputType::Url(_) => Ok(InputMethodType::Url),
291 InputType::Week(_) => Ok(InputMethodType::Week),
292 _ => Err("Input does not support IME."),
293 }
294 }
295}
296
297pub(crate) trait SpecificInputType {
298 fn sanitize_value(&self, _input: &HTMLInputElement, _value: &mut DOMString) {}
299
300 fn convert_string_to_number(&self, _value: &str) -> Option<f64> {
301 None
302 }
303
304 fn convert_number_to_string(&self, _value: f64) -> Option<DOMString> {
305 unreachable!("Should not have called convert_number_to_string for non-Date types")
306 }
307
308 fn convert_string_to_naive_datetime(&self, _value: DOMString) -> Option<OffsetDateTime> {
312 None
313 }
314
315 fn convert_datetime_to_dom_string(&self, _value: OffsetDateTime) -> DOMString {
319 unreachable!("Should not have called convert_datetime_to_string for non-Date types")
320 }
321
322 fn suffers_from_being_missing(&self, input: &HTMLInputElement, value: &DOMString) -> bool {
324 input.Required() &&
325 input.value_mode() == ValueMode::Value &&
326 input.is_mutable() &&
327 value.is_empty()
328 }
329
330 fn suffers_from_bad_input(&self, _value: &DOMString) -> bool {
331 false
332 }
333
334 fn suffers_from_type_mismatch(&self, _input: &HTMLInputElement, _value: &DOMString) -> bool {
335 false
336 }
337
338 fn value_for_shadow_dom(&self, _input: &HTMLInputElement) -> DOMString {
339 "".into()
340 }
341
342 fn signal_type_change(&self, _cx: &mut js::context::JSContext, _input: &HTMLInputElement) {}
344
345 fn legacy_pre_activation_behavior(
346 &self,
347 _cx: &mut JSContext,
348 _input: &HTMLInputElement,
349 ) -> Option<InputActivationState> {
350 None
351 }
352
353 fn legacy_canceled_activation_behavior(
354 &self,
355 _cx: &mut JSContext,
356 _input: &HTMLInputElement,
357 _cache: InputActivationState,
358 ) {
359 }
360
361 fn show_the_picker_if_applicable(&self, _input: &HTMLInputElement) {}
362
363 fn select_files(&self, _input: &HTMLInputElement, _test_paths: Option<Vec<DOMString>>) {}
364
365 fn get_files(&self) -> Option<DomRoot<FileList>> {
366 None
367 }
368
369 fn set_files(&self, _filelist: &FileList) {}
370
371 fn update_shadow_tree(&self, _cx: &mut JSContext, _input: &HTMLInputElement) {}
372
373 fn update_placeholder_contents(&self, _cx: &mut JSContext, _input: &HTMLInputElement) {}
374
375 fn attribute_mutated(
376 &self,
377 _cx: &mut JSContext,
378 _input: &HTMLInputElement,
379 _attr: AttrRef<'_>,
380 _mutation: AttributeMutation,
381 ) {
382 }
383
384 fn bind_to_tree(&self, _cx: &mut JSContext, _input: &HTMLInputElement, _context: &BindContext) {
385 }
386
387 fn unbind_from_tree(
388 &self,
389 _cx: &mut JSContext,
390 _input: &HTMLInputElement,
391 _form_owner: Option<DomRoot<HTMLFormElement>>,
392 _context: &UnbindContext,
393 ) {
394 }
395}
396
397pub(crate) trait SpecificInputActivationType {
398 fn activation_behavior(
399 &self,
400 _cx: &mut js::context::JSContext,
401 _input: &HTMLInputElement,
402 _event: &Event,
403 _target: &EventTarget,
404 ) {
405 }
406}