1 | /* |
2 | * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 | * (C) 1999 Antti Koivisto (koivisto@kde.org) |
4 | * (C) 2001 Dirk Mueller (mueller@kde.org) |
5 | * Copyright (C) 2004-2018 Apple Inc. All rights reserved. |
6 | * (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
7 | * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) |
8 | * Copyright (C) 2009, 2010, 2011, 2012 Google Inc. All rights reserved. |
9 | * Copyright (C) 2012 Samsung Electronics. All rights reserved. |
10 | * |
11 | * This library is free software; you can redistribute it and/or |
12 | * modify it under the terms of the GNU Library General Public |
13 | * License as published by the Free Software Foundation; either |
14 | * version 2 of the License, or (at your option) any later version. |
15 | * |
16 | * This library is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
19 | * Library General Public License for more details. |
20 | * |
21 | * You should have received a copy of the GNU Library General Public License |
22 | * along with this library; see the file COPYING.LIB. If not, write to |
23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
24 | * Boston, MA 02110-1301, USA. |
25 | * |
26 | */ |
27 | |
28 | #include "config.h" |
29 | #include "InputType.h" |
30 | |
31 | #include "AXObjectCache.h" |
32 | #include "BeforeTextInsertedEvent.h" |
33 | #include "ButtonInputType.h" |
34 | #include "CheckboxInputType.h" |
35 | #include "ColorInputType.h" |
36 | #include "DOMFormData.h" |
37 | #include "DateComponents.h" |
38 | #include "DateInputType.h" |
39 | #include "DateTimeInputType.h" |
40 | #include "DateTimeLocalInputType.h" |
41 | #include "EmailInputType.h" |
42 | #include "EventNames.h" |
43 | #include "FileInputType.h" |
44 | #include "FileList.h" |
45 | #include "FormController.h" |
46 | #include "HTMLFormElement.h" |
47 | #include "HTMLInputElement.h" |
48 | #include "HTMLNames.h" |
49 | #include "HTMLParserIdioms.h" |
50 | #include "HiddenInputType.h" |
51 | #include "ImageInputType.h" |
52 | #include "InputTypeNames.h" |
53 | #include "KeyboardEvent.h" |
54 | #include "LocalizedStrings.h" |
55 | #include "MonthInputType.h" |
56 | #include "NodeRenderStyle.h" |
57 | #include "NumberInputType.h" |
58 | #include "Page.h" |
59 | #include "PasswordInputType.h" |
60 | #include "RadioInputType.h" |
61 | #include "RangeInputType.h" |
62 | #include "RenderElement.h" |
63 | #include "RenderTheme.h" |
64 | #include "ResetInputType.h" |
65 | #include "RuntimeEnabledFeatures.h" |
66 | #include "ScopedEventQueue.h" |
67 | #include "SearchInputType.h" |
68 | #include "ShadowRoot.h" |
69 | #include "SubmitInputType.h" |
70 | #include "TelephoneInputType.h" |
71 | #include "TextControlInnerElements.h" |
72 | #include "TextInputType.h" |
73 | #include "TimeInputType.h" |
74 | #include "URLInputType.h" |
75 | #include "WeekInputType.h" |
76 | #include <limits> |
77 | #include <wtf/Assertions.h> |
78 | #include <wtf/HashMap.h> |
79 | #include <wtf/text/StringHash.h> |
80 | #include <wtf/text/TextBreakIterator.h> |
81 | |
82 | namespace WebCore { |
83 | |
84 | using namespace HTMLNames; |
85 | |
86 | typedef bool (RuntimeEnabledFeatures::*InputTypeConditionalFunction)() const; |
87 | typedef const AtomicString& (*InputTypeNameFunction)(); |
88 | typedef Ref<InputType> (*InputTypeFactoryFunction)(HTMLInputElement&); |
89 | typedef HashMap<AtomicString, InputTypeFactoryFunction, ASCIICaseInsensitiveHash> InputTypeFactoryMap; |
90 | |
91 | template<class T> |
92 | static Ref<InputType> createInputType(HTMLInputElement& element) |
93 | { |
94 | return adoptRef(*new T(element)); |
95 | } |
96 | |
97 | static InputTypeFactoryMap createInputTypeFactoryMap() |
98 | { |
99 | static const struct InputTypes { |
100 | InputTypeConditionalFunction conditionalFunction; |
101 | InputTypeNameFunction nameFunction; |
102 | InputTypeFactoryFunction factoryFunction; |
103 | } inputTypes[] = { |
104 | { nullptr, &InputTypeNames::button, &createInputType<ButtonInputType> }, |
105 | { nullptr, &InputTypeNames::checkbox, &createInputType<CheckboxInputType> }, |
106 | #if ENABLE(INPUT_TYPE_COLOR) |
107 | { &RuntimeEnabledFeatures::inputTypeColorEnabled, &InputTypeNames::color, &createInputType<ColorInputType> }, |
108 | #endif |
109 | #if ENABLE(INPUT_TYPE_DATE) |
110 | { &RuntimeEnabledFeatures::inputTypeDateEnabled, &InputTypeNames::date, &createInputType<DateInputType> }, |
111 | #endif |
112 | #if ENABLE(INPUT_TYPE_DATETIME_INCOMPLETE) |
113 | { &RuntimeEnabledFeatures::inputTypeDateTimeEnabled, &InputTypeNames::datetime, &createInputType<DateTimeInputType> }, |
114 | #endif |
115 | #if ENABLE(INPUT_TYPE_DATETIMELOCAL) |
116 | { &RuntimeEnabledFeatures::inputTypeDateTimeLocalEnabled, &InputTypeNames::datetimelocal, &createInputType<DateTimeLocalInputType> }, |
117 | #endif |
118 | { nullptr, &InputTypeNames::email, &createInputType<EmailInputType> }, |
119 | { nullptr, &InputTypeNames::file, &createInputType<FileInputType> }, |
120 | { nullptr, &InputTypeNames::hidden, &createInputType<HiddenInputType> }, |
121 | { nullptr, &InputTypeNames::image, &createInputType<ImageInputType> }, |
122 | #if ENABLE(INPUT_TYPE_MONTH) |
123 | { &RuntimeEnabledFeatures::inputTypeMonthEnabled, &InputTypeNames::month, &createInputType<MonthInputType> }, |
124 | #endif |
125 | { nullptr, &InputTypeNames::number, &createInputType<NumberInputType> }, |
126 | { nullptr, &InputTypeNames::password, &createInputType<PasswordInputType> }, |
127 | { nullptr, &InputTypeNames::radio, &createInputType<RadioInputType> }, |
128 | { nullptr, &InputTypeNames::range, &createInputType<RangeInputType> }, |
129 | { nullptr, &InputTypeNames::reset, &createInputType<ResetInputType> }, |
130 | { nullptr, &InputTypeNames::search, &createInputType<SearchInputType> }, |
131 | { nullptr, &InputTypeNames::submit, &createInputType<SubmitInputType> }, |
132 | { nullptr, &InputTypeNames::telephone, &createInputType<TelephoneInputType> }, |
133 | #if ENABLE(INPUT_TYPE_TIME) |
134 | { &RuntimeEnabledFeatures::inputTypeTimeEnabled, &InputTypeNames::time, &createInputType<TimeInputType> }, |
135 | #endif |
136 | { nullptr, &InputTypeNames::url, &createInputType<URLInputType> }, |
137 | #if ENABLE(INPUT_TYPE_WEEK) |
138 | { &RuntimeEnabledFeatures::inputTypeWeekEnabled, &InputTypeNames::week, &createInputType<WeekInputType> }, |
139 | #endif |
140 | // No need to register "text" because it is the default type. |
141 | }; |
142 | |
143 | InputTypeFactoryMap map; |
144 | for (auto& inputType : inputTypes) { |
145 | auto conditionalFunction = inputType.conditionalFunction; |
146 | if (!conditionalFunction || (RuntimeEnabledFeatures::sharedFeatures().*conditionalFunction)()) |
147 | map.add(inputType.nameFunction(), inputType.factoryFunction); |
148 | } |
149 | return map; |
150 | } |
151 | |
152 | Ref<InputType> InputType::create(HTMLInputElement& element, const AtomicString& typeName) |
153 | { |
154 | if (!typeName.isEmpty()) { |
155 | static const auto factoryMap = makeNeverDestroyed(createInputTypeFactoryMap()); |
156 | if (auto factory = factoryMap.get().get(typeName)) |
157 | return factory(element); |
158 | } |
159 | return adoptRef(*new TextInputType(element)); |
160 | } |
161 | |
162 | Ref<InputType> InputType::createText(HTMLInputElement& element) |
163 | { |
164 | return adoptRef(*new TextInputType(element)); |
165 | } |
166 | |
167 | InputType::~InputType() = default; |
168 | |
169 | bool InputType::themeSupportsDataListUI(InputType* type) |
170 | { |
171 | return RenderTheme::singleton().supportsDataListUI(type->formControlType()); |
172 | } |
173 | |
174 | bool InputType::isTextField() const |
175 | { |
176 | return false; |
177 | } |
178 | |
179 | bool InputType::isTextType() const |
180 | { |
181 | return false; |
182 | } |
183 | |
184 | bool InputType::isRangeControl() const |
185 | { |
186 | return false; |
187 | } |
188 | |
189 | bool InputType::shouldSaveAndRestoreFormControlState() const |
190 | { |
191 | return true; |
192 | } |
193 | |
194 | FormControlState InputType::saveFormControlState() const |
195 | { |
196 | ASSERT(element()); |
197 | auto currentValue = element()->value(); |
198 | if (currentValue == element()->defaultValue()) |
199 | return { }; |
200 | return { { currentValue } }; |
201 | } |
202 | |
203 | void InputType::restoreFormControlState(const FormControlState& state) |
204 | { |
205 | ASSERT(element()); |
206 | element()->setValue(state[0]); |
207 | } |
208 | |
209 | bool InputType::isFormDataAppendable() const |
210 | { |
211 | ASSERT(element()); |
212 | // There is no form data unless there's a name for non-image types. |
213 | return !element()->name().isEmpty(); |
214 | } |
215 | |
216 | bool InputType::appendFormData(DOMFormData& formData, bool) const |
217 | { |
218 | ASSERT(element()); |
219 | // Always successful. |
220 | formData.append(element()->name(), element()->value()); |
221 | return true; |
222 | } |
223 | |
224 | double InputType::valueAsDate() const |
225 | { |
226 | return DateComponents::invalidMilliseconds(); |
227 | } |
228 | |
229 | ExceptionOr<void> InputType::setValueAsDate(double) const |
230 | { |
231 | return Exception { InvalidStateError }; |
232 | } |
233 | |
234 | double InputType::valueAsDouble() const |
235 | { |
236 | return std::numeric_limits<double>::quiet_NaN(); |
237 | } |
238 | |
239 | ExceptionOr<void> InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior) const |
240 | { |
241 | return setValueAsDecimal(Decimal::fromDouble(doubleValue), eventBehavior); |
242 | } |
243 | |
244 | ExceptionOr<void> InputType::setValueAsDecimal(const Decimal&, TextFieldEventBehavior) const |
245 | { |
246 | return Exception { InvalidStateError }; |
247 | } |
248 | |
249 | bool InputType::supportsValidation() const |
250 | { |
251 | return true; |
252 | } |
253 | |
254 | bool InputType::typeMismatchFor(const String&) const |
255 | { |
256 | return false; |
257 | } |
258 | |
259 | bool InputType::typeMismatch() const |
260 | { |
261 | return false; |
262 | } |
263 | |
264 | bool InputType::supportsRequired() const |
265 | { |
266 | // Almost all validatable types support @required. |
267 | return supportsValidation(); |
268 | } |
269 | |
270 | bool InputType::valueMissing(const String&) const |
271 | { |
272 | return false; |
273 | } |
274 | |
275 | bool InputType::hasBadInput() const |
276 | { |
277 | return false; |
278 | } |
279 | |
280 | bool InputType::patternMismatch(const String&) const |
281 | { |
282 | return false; |
283 | } |
284 | |
285 | bool InputType::rangeUnderflow(const String& value) const |
286 | { |
287 | if (!isSteppable()) |
288 | return false; |
289 | |
290 | const Decimal numericValue = parseToNumberOrNaN(value); |
291 | if (!numericValue.isFinite()) |
292 | return false; |
293 | |
294 | return numericValue < createStepRange(RejectAny).minimum(); |
295 | } |
296 | |
297 | bool InputType::rangeOverflow(const String& value) const |
298 | { |
299 | if (!isSteppable()) |
300 | return false; |
301 | |
302 | const Decimal numericValue = parseToNumberOrNaN(value); |
303 | if (!numericValue.isFinite()) |
304 | return false; |
305 | |
306 | return numericValue > createStepRange(RejectAny).maximum(); |
307 | } |
308 | |
309 | Decimal InputType::defaultValueForStepUp() const |
310 | { |
311 | return 0; |
312 | } |
313 | |
314 | double InputType::minimum() const |
315 | { |
316 | return createStepRange(RejectAny).minimum().toDouble(); |
317 | } |
318 | |
319 | double InputType::maximum() const |
320 | { |
321 | return createStepRange(RejectAny).maximum().toDouble(); |
322 | } |
323 | |
324 | bool InputType::sizeShouldIncludeDecoration(int, int& preferredSize) const |
325 | { |
326 | ASSERT(element()); |
327 | preferredSize = element()->size(); |
328 | return false; |
329 | } |
330 | |
331 | float InputType::decorationWidth() const |
332 | { |
333 | return 0; |
334 | } |
335 | |
336 | bool InputType::isInRange(const String& value) const |
337 | { |
338 | if (!isSteppable()) |
339 | return false; |
340 | |
341 | StepRange stepRange(createStepRange(RejectAny)); |
342 | if (!stepRange.hasRangeLimitations()) |
343 | return false; |
344 | |
345 | const Decimal numericValue = parseToNumberOrNaN(value); |
346 | if (!numericValue.isFinite()) |
347 | return true; |
348 | |
349 | return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum(); |
350 | } |
351 | |
352 | bool InputType::isOutOfRange(const String& value) const |
353 | { |
354 | if (!isSteppable() || value.isEmpty()) |
355 | return false; |
356 | |
357 | StepRange stepRange(createStepRange(RejectAny)); |
358 | if (!stepRange.hasRangeLimitations()) |
359 | return false; |
360 | |
361 | const Decimal numericValue = parseToNumberOrNaN(value); |
362 | if (!numericValue.isFinite()) |
363 | return true; |
364 | |
365 | return numericValue < stepRange.minimum() || numericValue > stepRange.maximum(); |
366 | } |
367 | |
368 | bool InputType::stepMismatch(const String& value) const |
369 | { |
370 | if (!isSteppable()) |
371 | return false; |
372 | |
373 | const Decimal numericValue = parseToNumberOrNaN(value); |
374 | if (!numericValue.isFinite()) |
375 | return false; |
376 | |
377 | return createStepRange(RejectAny).stepMismatch(numericValue); |
378 | } |
379 | |
380 | String InputType::badInputText() const |
381 | { |
382 | ASSERT_NOT_REACHED(); |
383 | return validationMessageTypeMismatchText(); |
384 | } |
385 | |
386 | String InputType::typeMismatchText() const |
387 | { |
388 | return validationMessageTypeMismatchText(); |
389 | } |
390 | |
391 | String InputType::valueMissingText() const |
392 | { |
393 | return validationMessageValueMissingText(); |
394 | } |
395 | |
396 | String InputType::validationMessage() const |
397 | { |
398 | ASSERT(element()); |
399 | String value = element()->value(); |
400 | |
401 | // The order of the following checks is meaningful. e.g. We'd like to show the |
402 | // badInput message even if the control has other validation errors. |
403 | if (hasBadInput()) |
404 | return badInputText(); |
405 | |
406 | if (valueMissing(value)) |
407 | return valueMissingText(); |
408 | |
409 | if (typeMismatch()) |
410 | return typeMismatchText(); |
411 | |
412 | if (patternMismatch(value)) |
413 | return validationMessagePatternMismatchText(); |
414 | |
415 | if (element()->tooShort()) |
416 | return validationMessageTooShortText(numGraphemeClusters(value), element()->minLength()); |
417 | |
418 | if (element()->tooLong()) |
419 | return validationMessageTooLongText(numGraphemeClusters(value), element()->effectiveMaxLength()); |
420 | |
421 | if (!isSteppable()) |
422 | return emptyString(); |
423 | |
424 | const Decimal numericValue = parseToNumberOrNaN(value); |
425 | if (!numericValue.isFinite()) |
426 | return emptyString(); |
427 | |
428 | StepRange stepRange(createStepRange(RejectAny)); |
429 | |
430 | if (numericValue < stepRange.minimum()) |
431 | return validationMessageRangeUnderflowText(serialize(stepRange.minimum())); |
432 | |
433 | if (numericValue > stepRange.maximum()) |
434 | return validationMessageRangeOverflowText(serialize(stepRange.maximum())); |
435 | |
436 | if (stepRange.stepMismatch(numericValue)) { |
437 | const String stepString = stepRange.hasStep() ? serializeForNumberType(stepRange.step() / stepRange.stepScaleFactor()) : emptyString(); |
438 | return validationMessageStepMismatchText(serialize(stepRange.stepBase()), stepString); |
439 | } |
440 | |
441 | return emptyString(); |
442 | } |
443 | |
444 | void InputType::handleClickEvent(MouseEvent&) |
445 | { |
446 | } |
447 | |
448 | void InputType::handleMouseDownEvent(MouseEvent&) |
449 | { |
450 | } |
451 | |
452 | void InputType::handleDOMActivateEvent(Event&) |
453 | { |
454 | } |
455 | |
456 | auto InputType::handleKeydownEvent(KeyboardEvent&) -> ShouldCallBaseEventHandler |
457 | { |
458 | return ShouldCallBaseEventHandler::Yes; |
459 | } |
460 | |
461 | void InputType::handleKeypressEvent(KeyboardEvent&) |
462 | { |
463 | } |
464 | |
465 | void InputType::handleKeyupEvent(KeyboardEvent&) |
466 | { |
467 | } |
468 | |
469 | void InputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent&) |
470 | { |
471 | } |
472 | |
473 | #if ENABLE(TOUCH_EVENTS) |
474 | void InputType::handleTouchEvent(TouchEvent&) |
475 | { |
476 | } |
477 | #endif |
478 | |
479 | void InputType::forwardEvent(Event&) |
480 | { |
481 | } |
482 | |
483 | bool InputType::shouldSubmitImplicitly(Event& event) |
484 | { |
485 | return is<KeyboardEvent>(event) && event.type() == eventNames().keypressEvent && downcast<KeyboardEvent>(event).charCode() == '\r'; |
486 | } |
487 | |
488 | RenderPtr<RenderElement> InputType::createInputRenderer(RenderStyle&& style) |
489 | { |
490 | ASSERT(element()); |
491 | return RenderPtr<RenderElement>(RenderElement::createFor(*element(), WTFMove(style))); |
492 | } |
493 | |
494 | void InputType::blur() |
495 | { |
496 | ASSERT(element()); |
497 | element()->defaultBlur(); |
498 | } |
499 | |
500 | void InputType::createShadowSubtree() |
501 | { |
502 | } |
503 | |
504 | void InputType::destroyShadowSubtree() |
505 | { |
506 | ASSERT(element()); |
507 | RefPtr<ShadowRoot> root = element()->userAgentShadowRoot(); |
508 | if (!root) |
509 | return; |
510 | |
511 | root->removeChildren(); |
512 | } |
513 | |
514 | Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const |
515 | { |
516 | ASSERT_NOT_REACHED(); |
517 | return defaultValue; |
518 | } |
519 | |
520 | Decimal InputType::parseToNumberOrNaN(const String& string) const |
521 | { |
522 | return parseToNumber(string, Decimal::nan()); |
523 | } |
524 | |
525 | bool InputType::parseToDateComponents(const String&, DateComponents*) const |
526 | { |
527 | ASSERT_NOT_REACHED(); |
528 | return false; |
529 | } |
530 | |
531 | String InputType::serialize(const Decimal&) const |
532 | { |
533 | ASSERT_NOT_REACHED(); |
534 | return String(); |
535 | } |
536 | |
537 | #if PLATFORM(IOS_FAMILY) |
538 | DateComponents::Type InputType::dateType() const |
539 | { |
540 | return DateComponents::Invalid; |
541 | } |
542 | #endif |
543 | |
544 | void InputType::dispatchSimulatedClickIfActive(KeyboardEvent& event) const |
545 | { |
546 | ASSERT(element()); |
547 | if (element()->active()) |
548 | element()->dispatchSimulatedClick(&event); |
549 | event.setDefaultHandled(); |
550 | } |
551 | |
552 | Chrome* InputType::chrome() const |
553 | { |
554 | ASSERT(element()); |
555 | if (Page* page = element()->document().page()) |
556 | return &page->chrome(); |
557 | return nullptr; |
558 | } |
559 | |
560 | bool InputType::canSetStringValue() const |
561 | { |
562 | return true; |
563 | } |
564 | |
565 | bool InputType::hasCustomFocusLogic() const |
566 | { |
567 | return true; |
568 | } |
569 | |
570 | bool InputType::isKeyboardFocusable(KeyboardEvent* event) const |
571 | { |
572 | ASSERT(element()); |
573 | return !element()->isReadOnly() && element()->isTextFormControlKeyboardFocusable(event); |
574 | } |
575 | |
576 | bool InputType::isMouseFocusable() const |
577 | { |
578 | ASSERT(element()); |
579 | return element()->isTextFormControlMouseFocusable(); |
580 | } |
581 | |
582 | bool InputType::shouldUseInputMethod() const |
583 | { |
584 | return false; |
585 | } |
586 | |
587 | void InputType::handleFocusEvent(Node*, FocusDirection) |
588 | { |
589 | } |
590 | |
591 | void InputType::handleBlurEvent() |
592 | { |
593 | } |
594 | |
595 | void InputType::accessKeyAction(bool) |
596 | { |
597 | ASSERT(element()); |
598 | element()->focus(false); |
599 | } |
600 | |
601 | void InputType::addSearchResult() |
602 | { |
603 | } |
604 | |
605 | void InputType::attach() |
606 | { |
607 | } |
608 | |
609 | void InputType::detach() |
610 | { |
611 | } |
612 | |
613 | bool InputType::shouldRespectAlignAttribute() |
614 | { |
615 | return false; |
616 | } |
617 | |
618 | bool InputType::canBeSuccessfulSubmitButton() |
619 | { |
620 | return false; |
621 | } |
622 | |
623 | HTMLElement* InputType::placeholderElement() const |
624 | { |
625 | return nullptr; |
626 | } |
627 | |
628 | bool InputType::rendererIsNeeded() |
629 | { |
630 | return true; |
631 | } |
632 | |
633 | FileList* InputType::files() |
634 | { |
635 | return nullptr; |
636 | } |
637 | |
638 | void InputType::setFiles(RefPtr<FileList>&&) |
639 | { |
640 | } |
641 | |
642 | bool InputType::getTypeSpecificValue(String&) |
643 | { |
644 | return false; |
645 | } |
646 | |
647 | String InputType::fallbackValue() const |
648 | { |
649 | return String(); |
650 | } |
651 | |
652 | String InputType::defaultValue() const |
653 | { |
654 | return String(); |
655 | } |
656 | |
657 | bool InputType::shouldSendChangeEventAfterCheckedChanged() |
658 | { |
659 | return true; |
660 | } |
661 | |
662 | bool InputType::storesValueSeparateFromAttribute() |
663 | { |
664 | return true; |
665 | } |
666 | |
667 | void InputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior) |
668 | { |
669 | ASSERT(element()); |
670 | element()->setValueInternal(sanitizedValue, eventBehavior); |
671 | element()->invalidateStyleForSubtree(); |
672 | if (!valueChanged) |
673 | return; |
674 | |
675 | switch (eventBehavior) { |
676 | case DispatchChangeEvent: |
677 | element()->dispatchFormControlChangeEvent(); |
678 | break; |
679 | case DispatchInputAndChangeEvent: |
680 | element()->dispatchFormControlInputEvent(); |
681 | if (auto element = this->element()) |
682 | element->dispatchFormControlChangeEvent(); |
683 | break; |
684 | case DispatchNoEvent: |
685 | break; |
686 | } |
687 | } |
688 | |
689 | bool InputType::canSetValue(const String&) |
690 | { |
691 | return true; |
692 | } |
693 | |
694 | void InputType::willDispatchClick(InputElementClickState&) |
695 | { |
696 | } |
697 | |
698 | void InputType::didDispatchClick(Event&, const InputElementClickState&) |
699 | { |
700 | } |
701 | |
702 | String InputType::localizeValue(const String& proposedValue) const |
703 | { |
704 | return proposedValue; |
705 | } |
706 | |
707 | String InputType::visibleValue() const |
708 | { |
709 | ASSERT(element()); |
710 | return element()->value(); |
711 | } |
712 | |
713 | bool InputType::isEmptyValue() const |
714 | { |
715 | return true; |
716 | } |
717 | |
718 | String InputType::sanitizeValue(const String& proposedValue) const |
719 | { |
720 | return proposedValue; |
721 | } |
722 | |
723 | #if ENABLE(DRAG_SUPPORT) |
724 | |
725 | bool InputType::receiveDroppedFiles(const DragData&) |
726 | { |
727 | ASSERT_NOT_REACHED(); |
728 | return false; |
729 | } |
730 | |
731 | #endif |
732 | |
733 | Icon* InputType::icon() const |
734 | { |
735 | ASSERT_NOT_REACHED(); |
736 | return nullptr; |
737 | } |
738 | |
739 | String InputType::displayString() const |
740 | { |
741 | ASSERT_NOT_REACHED(); |
742 | return String(); |
743 | } |
744 | |
745 | bool InputType::shouldResetOnDocumentActivation() |
746 | { |
747 | return false; |
748 | } |
749 | |
750 | bool InputType::shouldRespectListAttribute() |
751 | { |
752 | return false; |
753 | } |
754 | |
755 | bool InputType::isTextButton() const |
756 | { |
757 | return false; |
758 | } |
759 | |
760 | bool InputType::isRadioButton() const |
761 | { |
762 | return false; |
763 | } |
764 | |
765 | bool InputType::isSearchField() const |
766 | { |
767 | return false; |
768 | } |
769 | |
770 | bool InputType::isHiddenType() const |
771 | { |
772 | return false; |
773 | } |
774 | |
775 | bool InputType::isPasswordField() const |
776 | { |
777 | return false; |
778 | } |
779 | |
780 | bool InputType::isCheckbox() const |
781 | { |
782 | return false; |
783 | } |
784 | |
785 | bool InputType::isEmailField() const |
786 | { |
787 | return false; |
788 | } |
789 | |
790 | bool InputType::isFileUpload() const |
791 | { |
792 | return false; |
793 | } |
794 | |
795 | bool InputType::isImageButton() const |
796 | { |
797 | return false; |
798 | } |
799 | |
800 | bool InputType::supportLabels() const |
801 | { |
802 | return true; |
803 | } |
804 | |
805 | bool InputType::isNumberField() const |
806 | { |
807 | return false; |
808 | } |
809 | |
810 | bool InputType::isSubmitButton() const |
811 | { |
812 | return false; |
813 | } |
814 | |
815 | bool InputType::isTelephoneField() const |
816 | { |
817 | return false; |
818 | } |
819 | |
820 | bool InputType::isURLField() const |
821 | { |
822 | return false; |
823 | } |
824 | |
825 | bool InputType::isDateField() const |
826 | { |
827 | return false; |
828 | } |
829 | |
830 | bool InputType::isDateTimeField() const |
831 | { |
832 | return false; |
833 | } |
834 | |
835 | bool InputType::isDateTimeLocalField() const |
836 | { |
837 | return false; |
838 | } |
839 | |
840 | bool InputType::isMonthField() const |
841 | { |
842 | return false; |
843 | } |
844 | |
845 | bool InputType::isTimeField() const |
846 | { |
847 | return false; |
848 | } |
849 | |
850 | bool InputType::isWeekField() const |
851 | { |
852 | return false; |
853 | } |
854 | |
855 | bool InputType::isEnumeratable() |
856 | { |
857 | return true; |
858 | } |
859 | |
860 | bool InputType::isCheckable() |
861 | { |
862 | return false; |
863 | } |
864 | |
865 | bool InputType::isSteppable() const |
866 | { |
867 | return false; |
868 | } |
869 | |
870 | bool InputType::isColorControl() const |
871 | { |
872 | return false; |
873 | } |
874 | |
875 | bool InputType::shouldRespectHeightAndWidthAttributes() |
876 | { |
877 | return false; |
878 | } |
879 | |
880 | bool InputType::supportsPlaceholder() const |
881 | { |
882 | return false; |
883 | } |
884 | |
885 | bool InputType::supportsReadOnly() const |
886 | { |
887 | return false; |
888 | } |
889 | |
890 | void InputType::updateInnerTextValue() |
891 | { |
892 | } |
893 | |
894 | void InputType::updatePlaceholderText() |
895 | { |
896 | } |
897 | |
898 | void InputType::capsLockStateMayHaveChanged() |
899 | { |
900 | } |
901 | |
902 | void InputType::updateAutoFillButton() |
903 | { |
904 | } |
905 | |
906 | void InputType::subtreeHasChanged() |
907 | { |
908 | ASSERT_NOT_REACHED(); |
909 | } |
910 | |
911 | #if ENABLE(TOUCH_EVENTS) |
912 | bool InputType::hasTouchEventHandler() const |
913 | { |
914 | return false; |
915 | } |
916 | #endif |
917 | |
918 | String InputType::defaultToolTip() const |
919 | { |
920 | return String(); |
921 | } |
922 | |
923 | #if ENABLE(DATALIST_ELEMENT) |
924 | void InputType::listAttributeTargetChanged() |
925 | { |
926 | } |
927 | |
928 | Optional<Decimal> InputType::findClosestTickMarkValue(const Decimal&) |
929 | { |
930 | ASSERT_NOT_REACHED(); |
931 | return WTF::nullopt; |
932 | } |
933 | #endif |
934 | |
935 | bool InputType::matchesIndeterminatePseudoClass() const |
936 | { |
937 | return false; |
938 | } |
939 | |
940 | bool InputType::shouldAppearIndeterminate() const |
941 | { |
942 | return false; |
943 | } |
944 | |
945 | bool InputType::isPresentingAttachedView() const |
946 | { |
947 | return false; |
948 | } |
949 | |
950 | bool InputType::supportsSelectionAPI() const |
951 | { |
952 | return false; |
953 | } |
954 | |
955 | unsigned InputType::height() const |
956 | { |
957 | return 0; |
958 | } |
959 | |
960 | unsigned InputType::width() const |
961 | { |
962 | return 0; |
963 | } |
964 | |
965 | ExceptionOr<void> InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior) |
966 | { |
967 | StepRange stepRange(createStepRange(anyStepHandling)); |
968 | if (!stepRange.hasStep()) |
969 | return Exception { InvalidStateError }; |
970 | |
971 | ASSERT(element()); |
972 | const Decimal current = parseToNumberOrNaN(element()->value()); |
973 | if (!current.isFinite()) |
974 | return Exception { InvalidStateError }; |
975 | Decimal newValue = current + stepRange.step() * count; |
976 | if (!newValue.isFinite()) |
977 | return Exception { InvalidStateError }; |
978 | |
979 | const Decimal acceptableErrorValue = stepRange.acceptableError(); |
980 | if (newValue - stepRange.minimum() < -acceptableErrorValue) |
981 | return Exception { InvalidStateError }; |
982 | if (newValue < stepRange.minimum()) |
983 | newValue = stepRange.minimum(); |
984 | |
985 | if (!equalLettersIgnoringASCIICase(element()->attributeWithoutSynchronization(stepAttr), "any" )) |
986 | newValue = stepRange.alignValueForStep(current, newValue); |
987 | |
988 | if (newValue - stepRange.maximum() > acceptableErrorValue) |
989 | return Exception { InvalidStateError }; |
990 | if (newValue > stepRange.maximum()) |
991 | newValue = stepRange.maximum(); |
992 | |
993 | auto result = setValueAsDecimal(newValue, eventBehavior); |
994 | if (result.hasException()) |
995 | return result; |
996 | |
997 | if (AXObjectCache* cache = element()->document().existingAXObjectCache()) |
998 | cache->postNotification(element(), AXObjectCache::AXValueChanged); |
999 | |
1000 | return result; |
1001 | } |
1002 | |
1003 | bool InputType::getAllowedValueStep(Decimal* step) const |
1004 | { |
1005 | StepRange stepRange(createStepRange(RejectAny)); |
1006 | *step = stepRange.step(); |
1007 | return stepRange.hasStep(); |
1008 | } |
1009 | |
1010 | StepRange InputType::createStepRange(AnyStepHandling) const |
1011 | { |
1012 | ASSERT_NOT_REACHED(); |
1013 | return StepRange(); |
1014 | } |
1015 | |
1016 | ExceptionOr<void> InputType::stepUp(int n) |
1017 | { |
1018 | if (!isSteppable()) |
1019 | return Exception { InvalidStateError }; |
1020 | return applyStep(n, RejectAny, DispatchNoEvent); |
1021 | } |
1022 | |
1023 | void InputType::stepUpFromRenderer(int n) |
1024 | { |
1025 | // The differences from stepUp()/stepDown(): |
1026 | // |
1027 | // Difference 1: the current value |
1028 | // If the current value is not a number, including empty, the current value is assumed as 0. |
1029 | // * If 0 is in-range, and matches to step value |
1030 | // - The value should be the +step if n > 0 |
1031 | // - The value should be the -step if n < 0 |
1032 | // If -step or +step is out of range, new value should be 0. |
1033 | // * If 0 is smaller than the minimum value |
1034 | // - The value should be the minimum value for any n |
1035 | // * If 0 is larger than the maximum value |
1036 | // - The value should be the maximum value for any n |
1037 | // * If 0 is in-range, but not matched to step value |
1038 | // - The value should be the larger matched value nearest to 0 if n > 0 |
1039 | // e.g. <input type=number min=-100 step=3> -> 2 |
1040 | // - The value should be the smaller matched value nearest to 0 if n < 0 |
1041 | // e.g. <input type=number min=-100 step=3> -> -1 |
1042 | // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time". |
1043 | // As for datetime type, the current value is assumed as "the current date/time in UTC". |
1044 | // If the current value is smaller than the minimum value: |
1045 | // - The value should be the minimum value if n > 0 |
1046 | // - Nothing should happen if n < 0 |
1047 | // If the current value is larger than the maximum value: |
1048 | // - The value should be the maximum value if n < 0 |
1049 | // - Nothing should happen if n > 0 |
1050 | // |
1051 | // Difference 2: clamping steps |
1052 | // If the current value is not matched to step value: |
1053 | // - The value should be the larger matched value nearest to 0 if n > 0 |
1054 | // e.g. <input type=number value=3 min=-100 step=3> -> 5 |
1055 | // - The value should be the smaller matched value nearest to 0 if n < 0 |
1056 | // e.g. <input type=number value=3 min=-100 step=3> -> 2 |
1057 | // |
1058 | // n is assumed as -n if step < 0. |
1059 | |
1060 | ASSERT(isSteppable()); |
1061 | if (!isSteppable()) |
1062 | return; |
1063 | ASSERT(n); |
1064 | if (!n) |
1065 | return; |
1066 | |
1067 | StepRange stepRange(createStepRange(AnyIsDefaultStep)); |
1068 | |
1069 | // FIXME: Not any changes after stepping, even if it is an invalid value, may be better. |
1070 | // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo") |
1071 | if (!stepRange.hasStep()) |
1072 | return; |
1073 | |
1074 | EventQueueScope scope; |
1075 | const Decimal step = stepRange.step(); |
1076 | |
1077 | int sign; |
1078 | if (step > 0) |
1079 | sign = n; |
1080 | else if (step < 0) |
1081 | sign = -n; |
1082 | else |
1083 | sign = 0; |
1084 | |
1085 | ASSERT(element()); |
1086 | String currentStringValue = element()->value(); |
1087 | Decimal current = parseToNumberOrNaN(currentStringValue); |
1088 | if (!current.isFinite()) { |
1089 | current = defaultValueForStepUp(); |
1090 | const Decimal nextDiff = step * n; |
1091 | if (current < stepRange.minimum() - nextDiff) |
1092 | current = stepRange.minimum() - nextDiff; |
1093 | if (current > stepRange.maximum() - nextDiff) |
1094 | current = stepRange.maximum() - nextDiff; |
1095 | setValueAsDecimal(current, DispatchNoEvent); |
1096 | } |
1097 | if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) |
1098 | setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchInputAndChangeEvent); |
1099 | else { |
1100 | if (stepMismatch(element()->value())) { |
1101 | ASSERT(!step.isZero()); |
1102 | const Decimal base = stepRange.stepBase(); |
1103 | Decimal newValue; |
1104 | if (sign < 0) |
1105 | newValue = base + ((current - base) / step).floor() * step; |
1106 | else if (sign > 0) |
1107 | newValue = base + ((current - base) / step).ceiling() * step; |
1108 | else |
1109 | newValue = current; |
1110 | |
1111 | if (newValue < stepRange.minimum()) |
1112 | newValue = stepRange.minimum(); |
1113 | if (newValue > stepRange.maximum()) |
1114 | newValue = stepRange.maximum(); |
1115 | |
1116 | setValueAsDecimal(newValue, n == 1 || n == -1 ? DispatchInputAndChangeEvent : DispatchNoEvent); |
1117 | if (n > 1) |
1118 | applyStep(n - 1, AnyIsDefaultStep, DispatchInputAndChangeEvent); |
1119 | else if (n < -1) |
1120 | applyStep(n + 1, AnyIsDefaultStep, DispatchInputAndChangeEvent); |
1121 | } else |
1122 | applyStep(n, AnyIsDefaultStep, DispatchInputAndChangeEvent); |
1123 | } |
1124 | } |
1125 | |
1126 | Color InputType::valueAsColor() const |
1127 | { |
1128 | return Color::transparent; |
1129 | } |
1130 | |
1131 | void InputType::selectColor(StringView) |
1132 | { |
1133 | } |
1134 | |
1135 | Vector<Color> InputType::suggestedColors() const |
1136 | { |
1137 | return { }; |
1138 | } |
1139 | |
1140 | RefPtr<TextControlInnerTextElement> InputType::innerTextElement() const |
1141 | { |
1142 | return nullptr; |
1143 | } |
1144 | |
1145 | } // namespace WebCore |
1146 | |