Refactoring: Replace Element::disabled and isEnabledFormControl with isDisabledFormCo...
[WebKit-https.git] / Source / WebCore / html / BaseMultipleFieldsDateAndTimeInputType.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
33 #include "BaseMultipleFieldsDateAndTimeInputType.h"
34
35 #include "CSSValueKeywords.h"
36 #include "DateComponents.h"
37 #include "DateTimeFieldsState.h"
38 #include "DateTimeFormat.h"
39 #include "ElementShadow.h"
40 #include "FocusController.h"
41 #include "FormController.h"
42 #include "HTMLDataListElement.h"
43 #include "HTMLInputElement.h"
44 #include "HTMLOptionElement.h"
45 #include "KeyboardEvent.h"
46 #include "LocalizedStrings.h"
47 #include "NodeTraversal.h"
48 #include "Page.h"
49 #include "PickerIndicatorElement.h"
50 #include "PlatformLocale.h"
51 #include "RenderTheme.h"
52 #include "ShadowRoot.h"
53 #include <wtf/DateMath.h>
54
55 namespace WebCore {
56
57 class DateTimeFormatValidator : public DateTimeFormat::TokenHandler {
58 public:
59     DateTimeFormatValidator()
60         : m_hasYear(false)
61         , m_hasMonth(false)
62         , m_hasWeek(false)
63         , m_hasDay(false)
64         , m_hasAMPM(false)
65         , m_hasHour(false)
66         , m_hasMinute(false)
67         , m_hasSecond(false) { }
68
69     virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
70     virtual void visitLiteral(const String&) OVERRIDE FINAL { }
71
72     bool validateFormat(const String& format, const BaseMultipleFieldsDateAndTimeInputType&);
73
74 private:
75     bool m_hasYear;
76     bool m_hasMonth;
77     bool m_hasWeek;
78     bool m_hasDay;
79     bool m_hasAMPM;
80     bool m_hasHour;
81     bool m_hasMinute;
82     bool m_hasSecond;
83 };
84
85 void DateTimeFormatValidator::visitField(DateTimeFormat::FieldType fieldType, int)
86 {
87     switch (fieldType) {
88     case DateTimeFormat::FieldTypeYear:
89         m_hasYear = true;
90         break;
91     case DateTimeFormat::FieldTypeMonth: // Fallthrough.
92     case DateTimeFormat::FieldTypeMonthStandAlone:
93         m_hasMonth = true;
94         break;
95     case DateTimeFormat::FieldTypeWeekOfYear:
96         m_hasWeek = true;
97         break;
98     case DateTimeFormat::FieldTypeDayOfMonth:
99         m_hasDay = true;
100         break;
101     case DateTimeFormat::FieldTypePeriod:
102         m_hasAMPM = true;
103         break;
104     case DateTimeFormat::FieldTypeHour11: // Fallthrough.
105     case DateTimeFormat::FieldTypeHour12:
106         m_hasHour = true;
107         break;
108     case DateTimeFormat::FieldTypeHour23: // Fallthrough.
109     case DateTimeFormat::FieldTypeHour24:
110         m_hasHour = true;
111         m_hasAMPM = true;
112         break;
113     case DateTimeFormat::FieldTypeMinute:
114         m_hasMinute = true;
115         break;
116     case DateTimeFormat::FieldTypeSecond:
117         m_hasSecond = true;
118         break;
119     default:
120         break;
121     }
122 }
123
124 bool DateTimeFormatValidator::validateFormat(const String& format, const BaseMultipleFieldsDateAndTimeInputType& inputType)
125 {
126     if (!DateTimeFormat::parse(format, *this))
127         return false;
128     return inputType.isValidFormat(m_hasYear, m_hasMonth, m_hasWeek, m_hasDay, m_hasAMPM, m_hasHour, m_hasMinute, m_hasSecond);
129 }
130
131 void BaseMultipleFieldsDateAndTimeInputType::didBlurFromControl()
132 {
133     // We don't need to call blur(). This function is called when control
134     // lost focus.
135
136     // Remove focus ring by CSS "focus" pseudo class.
137     element()->setFocus(false);
138 }
139
140 void BaseMultipleFieldsDateAndTimeInputType::didFocusOnControl()
141 {
142     // We don't need to call focus(). This function is called when control
143     // got focus.
144
145     // Add focus ring by CSS "focus" pseudo class.
146     element()->setFocus(true);
147 }
148
149 void BaseMultipleFieldsDateAndTimeInputType::editControlValueChanged()
150 {
151     RefPtr<HTMLInputElement> input(element());
152     String oldValue = input->value();
153     String newValue = sanitizeValue(m_dateTimeEditElement->value());
154     // Even if oldValue is null and newValue is "", we should assume they are same.
155     if ((oldValue.isEmpty() && newValue.isEmpty()) || oldValue == newValue)
156         input->setNeedsValidityCheck();
157     else {
158         input->setValueInternal(newValue, DispatchNoEvent);
159         input->setNeedsStyleRecalc();
160         input->dispatchFormControlInputEvent();
161         input->dispatchFormControlChangeEvent();
162     }
163     input->notifyFormStateChanged();
164     input->updateClearButtonVisibility();
165 }
166
167 bool BaseMultipleFieldsDateAndTimeInputType::hasCustomFocusLogic() const
168 {
169     return false;
170 }
171
172 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerDisabled() const
173 {
174     return element()->isDisabledFormControl();
175 }
176
177 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerReadOnly() const
178 {
179     return element()->isReadOnly();
180 }
181
182 void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectSpinButtonOwner()
183 {
184     if (m_dateTimeEditElement)
185         m_dateTimeEditElement->focusIfNoFocus();
186 }
187
188 bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToMouseEvents()
189 {
190     return !element()->isDisabledOrReadOnly();
191 }
192
193 bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToWheelEvents()
194 {
195     if (!shouldSpinButtonRespondToMouseEvents())
196         return false;
197     return m_dateTimeEditElement && m_dateTimeEditElement->hasFocusedField();
198 }
199
200 void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepDown()
201 {
202     if (m_dateTimeEditElement)
203         m_dateTimeEditElement->stepDown();
204 }
205
206 void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepUp()
207 {
208     if (m_dateTimeEditElement)
209         m_dateTimeEditElement->stepUp();
210 }
211
212 bool BaseMultipleFieldsDateAndTimeInputType::isPickerIndicatorOwnerDisabledOrReadOnly() const
213 {
214     return element()->isDisabledOrReadOnly();
215 }
216
217 void BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorChooseValue(const String& value)
218 {
219     if (element()->isValidValue(value)) {
220         element()->setValue(value, DispatchInputAndChangeEvent);
221         return;
222     }
223
224     if (!m_dateTimeEditElement)
225         return;
226     DateComponents date;
227     unsigned end;
228     if (date.parseDate(value.characters(), value.length(), 0, end) && end == value.length())
229         m_dateTimeEditElement->setOnlyYearMonthDay(date);
230 }
231
232 bool BaseMultipleFieldsDateAndTimeInputType::setupDateTimeChooserParameters(DateTimeChooserParameters& parameters)
233 {
234     return element()->setupDateTimeChooserParameters(parameters);
235 }
236
237 BaseMultipleFieldsDateAndTimeInputType::BaseMultipleFieldsDateAndTimeInputType(HTMLInputElement* element)
238     : BaseDateAndTimeInputType(element)
239     , m_dateTimeEditElement(0)
240     , m_spinButtonElement(0)
241     , m_clearButton(0)
242     , m_pickerIndicatorElement(0)
243     , m_pickerIndicatorIsVisible(false)
244     , m_pickerIndicatorIsAlwaysVisible(false)
245 {
246 }
247
248 BaseMultipleFieldsDateAndTimeInputType::~BaseMultipleFieldsDateAndTimeInputType()
249 {
250     if (m_spinButtonElement)
251         m_spinButtonElement->removeSpinButtonOwner();
252     if (m_clearButton)
253         m_clearButton->removeClearButtonOwner();
254     if (m_dateTimeEditElement)
255         m_dateTimeEditElement->removeEditControlOwner();
256     if (m_pickerIndicatorElement)
257         m_pickerIndicatorElement->removePickerIndicatorOwner();
258 }
259
260 String BaseMultipleFieldsDateAndTimeInputType::badInputText() const
261 {
262     return validationMessageBadInputForDateTimeText();
263 }
264
265 void BaseMultipleFieldsDateAndTimeInputType::blur()
266 {
267     if (m_dateTimeEditElement)
268         m_dateTimeEditElement->blurByOwner();
269 }
270
271 PassRefPtr<RenderStyle> BaseMultipleFieldsDateAndTimeInputType::customStyleForRenderer(PassRefPtr<RenderStyle> originalStyle)
272 {
273     EDisplay originalDisplay = originalStyle->display();
274     EDisplay newDisplay = originalDisplay;
275     if (originalDisplay == INLINE || originalDisplay == INLINE_BLOCK)
276         newDisplay = INLINE_FLEX;
277     else if (originalDisplay == BLOCK)
278         newDisplay = FLEX;
279     TextDirection contentDirection = element()->locale().isRTL() ? RTL : LTR;
280     if (originalStyle->direction() == contentDirection && originalDisplay == newDisplay)
281         return originalStyle;
282
283     RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
284     style->setDirection(contentDirection);
285     style->setDisplay(newDisplay);
286     return style.release();
287 }
288
289 void BaseMultipleFieldsDateAndTimeInputType::createShadowSubtree()
290 {
291     ASSERT(element()->shadow());
292
293     Document* document = element()->document();
294     ContainerNode* container = element()->userAgentShadowRoot();
295
296     RefPtr<DateTimeEditElement> dateTimeEditElement(DateTimeEditElement::create(document, *this));
297     m_dateTimeEditElement = dateTimeEditElement.get();
298     container->appendChild(m_dateTimeEditElement);
299     updateInnerTextValue();
300
301     RefPtr<ClearButtonElement> clearButton = ClearButtonElement::create(document, *this);
302     m_clearButton = clearButton.get();
303     container->appendChild(clearButton);
304
305     RefPtr<SpinButtonElement> spinButton = SpinButtonElement::create(document, *this);
306     m_spinButtonElement = spinButton.get();
307     container->appendChild(spinButton);
308
309     bool shouldAddPickerIndicator = false;
310 #if ENABLE(DATALIST_ELEMENT)
311     if (InputType::themeSupportsDataListUI(this))
312         shouldAddPickerIndicator = true;
313 #endif
314     RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme();
315     if (theme->supportsCalendarPicker(formControlType())) {
316         shouldAddPickerIndicator = true;
317         m_pickerIndicatorIsAlwaysVisible = true;
318     }
319     if (shouldAddPickerIndicator) {
320         RefPtr<PickerIndicatorElement> pickerElement = PickerIndicatorElement::create(document, *this);
321         m_pickerIndicatorElement = pickerElement.get();
322         container->appendChild(m_pickerIndicatorElement);
323         m_pickerIndicatorIsVisible = true;
324         updatePickerIndicatorVisibility();
325     }
326 }
327
328 void BaseMultipleFieldsDateAndTimeInputType::destroyShadowSubtree()
329 {
330     if (m_spinButtonElement) {
331         m_spinButtonElement->removeSpinButtonOwner();
332         m_spinButtonElement = 0;
333     }
334     if (m_clearButton) {
335         m_clearButton->removeClearButtonOwner();
336         m_clearButton = 0;
337     }
338     if (m_dateTimeEditElement) {
339         m_dateTimeEditElement->removeEditControlOwner();
340         m_dateTimeEditElement = 0;
341     }
342     if (m_pickerIndicatorElement) {
343         m_pickerIndicatorElement->removePickerIndicatorOwner();
344         m_pickerIndicatorElement = 0;
345     }
346     BaseDateAndTimeInputType::destroyShadowSubtree();
347 }
348
349 void BaseMultipleFieldsDateAndTimeInputType::handleFocusEvent(Node* oldFocusedNode, FocusDirection direction)
350 {
351     if (!m_dateTimeEditElement)
352         return;
353     if (direction == FocusDirectionBackward) {
354         if (element()->document()->page())
355             element()->document()->page()->focusController()->advanceFocus(direction, 0);
356     } else if (direction == FocusDirectionNone) {
357         m_dateTimeEditElement->focusByOwner(oldFocusedNode);
358     } else
359         m_dateTimeEditElement->focusByOwner();
360 }
361
362 void BaseMultipleFieldsDateAndTimeInputType::forwardEvent(Event* event)
363 {
364     if (m_spinButtonElement) {
365         m_spinButtonElement->forwardEvent(event);
366         if (event->defaultHandled())
367             return;
368     }
369         
370     if (m_dateTimeEditElement)
371         m_dateTimeEditElement->defaultEventHandler(event);
372 }
373
374 void BaseMultipleFieldsDateAndTimeInputType::disabledAttributeChanged()
375 {
376     m_spinButtonElement->releaseCapture();
377     m_clearButton->releaseCapture();
378     if (m_dateTimeEditElement)
379         m_dateTimeEditElement->disabledStateChanged();
380 }
381
382 void BaseMultipleFieldsDateAndTimeInputType::requiredAttributeChanged()
383 {
384     m_clearButton->releaseCapture();
385     updateClearButtonVisibility();
386 }
387
388 void BaseMultipleFieldsDateAndTimeInputType::handleKeydownEvent(KeyboardEvent* event)
389 {
390     Document* document = element()->document();
391     RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme();
392     if (m_pickerIndicatorIsVisible
393         && ((event->keyIdentifier() == "Down" && event->getModifierState("Alt")) || (theme->shouldOpenPickerWithF4Key() && event->keyIdentifier() == "F4"))) {
394         if (m_pickerIndicatorElement)
395             m_pickerIndicatorElement->openPopup();
396         event->setDefaultHandled();
397     } else
398         forwardEvent(event);
399 }
400
401 bool BaseMultipleFieldsDateAndTimeInputType::hasBadInput() const
402 {
403     return element()->value().isEmpty() && m_dateTimeEditElement && m_dateTimeEditElement->anyEditableFieldsHaveValues();
404 }
405
406 bool BaseMultipleFieldsDateAndTimeInputType::isKeyboardFocusable(KeyboardEvent*) const
407 {
408     return element()->isTextFormControlFocusable();
409 }
410
411 bool BaseMultipleFieldsDateAndTimeInputType::isMouseFocusable() const
412 {
413     return element()->isTextFormControlFocusable();
414 }
415
416 AtomicString BaseMultipleFieldsDateAndTimeInputType::localeIdentifier() const
417 {
418     return element()->computeInheritedLanguage();
419 }
420
421 void BaseMultipleFieldsDateAndTimeInputType::minOrMaxAttributeChanged()
422 {
423     updateInnerTextValue();
424 }
425
426 void BaseMultipleFieldsDateAndTimeInputType::readonlyAttributeChanged()
427 {
428     m_spinButtonElement->releaseCapture();
429     m_clearButton->releaseCapture();
430     if (m_dateTimeEditElement)
431         m_dateTimeEditElement->readOnlyStateChanged();
432 }
433
434 void BaseMultipleFieldsDateAndTimeInputType::restoreFormControlState(const FormControlState& state)
435 {
436     if (!m_dateTimeEditElement)
437         return;
438     DateTimeFieldsState dateTimeFieldsState = DateTimeFieldsState::restoreFormControlState(state);
439     m_dateTimeEditElement->setValueAsDateTimeFieldsState(dateTimeFieldsState);
440     element()->setValueInternal(sanitizeValue(m_dateTimeEditElement->value()), DispatchNoEvent);
441     updateClearButtonVisibility();
442 }
443
444 FormControlState BaseMultipleFieldsDateAndTimeInputType::saveFormControlState() const
445 {
446     if (!m_dateTimeEditElement)
447         return FormControlState();
448
449     return m_dateTimeEditElement->valueAsDateTimeFieldsState().saveFormControlState();
450 }
451
452 void BaseMultipleFieldsDateAndTimeInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
453 {
454     InputType::setValue(sanitizedValue, valueChanged, eventBehavior);
455     if (valueChanged || (sanitizedValue.isEmpty() && m_dateTimeEditElement && m_dateTimeEditElement->anyEditableFieldsHaveValues())) {
456         updateInnerTextValue();
457         element()->setNeedsValidityCheck();
458     }
459 }
460
461 bool BaseMultipleFieldsDateAndTimeInputType::shouldUseInputMethod() const
462 {
463     return false;
464 }
465
466 void BaseMultipleFieldsDateAndTimeInputType::stepAttributeChanged()
467 {
468     updateInnerTextValue();
469 }
470
471 void BaseMultipleFieldsDateAndTimeInputType::updateInnerTextValue()
472 {
473     if (!m_dateTimeEditElement)
474         return;
475
476     DateTimeEditElement::LayoutParameters layoutParameters(element()->locale(), createStepRange(AnyIsDefaultStep));
477
478     DateComponents date;
479     const bool hasValue = parseToDateComponents(element()->value(), &date);
480     if (!hasValue)
481         setMillisecondToDateComponents(layoutParameters.stepRange.minimum().toDouble(), &date);
482
483     setupLayoutParameters(layoutParameters, date);
484
485     const AtomicString pattern = m_dateTimeEditElement->fastGetAttribute(HTMLNames::patternAttr);
486     if (!pattern.isEmpty())
487         layoutParameters.dateTimeFormat = pattern;
488
489     if (!DateTimeFormatValidator().validateFormat(layoutParameters.dateTimeFormat, *this))
490         layoutParameters.dateTimeFormat = layoutParameters.fallbackDateTimeFormat;
491
492     if (hasValue)
493         m_dateTimeEditElement->setValueAsDate(layoutParameters, date);
494     else
495         m_dateTimeEditElement->setEmptyValue(layoutParameters, date);
496     updateClearButtonVisibility();
497 }
498
499 void BaseMultipleFieldsDateAndTimeInputType::valueAttributeChanged()
500 {
501     if (!element()->hasDirtyValue())
502         updateInnerTextValue();
503 }
504
505 #if ENABLE(DATALIST_ELEMENT)
506 void BaseMultipleFieldsDateAndTimeInputType::listAttributeTargetChanged()
507 {
508     updatePickerIndicatorVisibility();
509 }
510 #endif
511
512 void BaseMultipleFieldsDateAndTimeInputType::updatePickerIndicatorVisibility()
513 {
514     if (m_pickerIndicatorIsAlwaysVisible) {
515         showPickerIndicator();
516         return;
517     }
518 #if ENABLE(DATALIST_ELEMENT)
519     if (HTMLDataListElement* dataList = element()->dataList()) {
520         RefPtr<HTMLCollection> options = dataList->options();
521         for (unsigned i = 0; HTMLOptionElement* option = toHTMLOptionElement(options->item(i)); ++i) {
522             if (element()->isValidValue(option->value())) {
523                 showPickerIndicator();
524                 return;
525             }
526         }
527     }
528     hidePickerIndicator();
529 #endif
530 }
531
532 void BaseMultipleFieldsDateAndTimeInputType::hidePickerIndicator()
533 {
534     if (!m_pickerIndicatorIsVisible)
535         return;
536     m_pickerIndicatorIsVisible = false;
537     ASSERT(m_pickerIndicatorElement);
538     m_pickerIndicatorElement->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
539 }
540
541 void BaseMultipleFieldsDateAndTimeInputType::showPickerIndicator()
542 {
543     if (m_pickerIndicatorIsVisible)
544         return;
545     m_pickerIndicatorIsVisible = true;
546     ASSERT(m_pickerIndicatorElement);
547     m_pickerIndicatorElement->removeInlineStyleProperty(CSSPropertyDisplay);
548 }
549
550 bool BaseMultipleFieldsDateAndTimeInputType::shouldHaveSecondField(const DateComponents& date) const
551 {
552     StepRange stepRange = createStepRange(AnyIsDefaultStep);
553     return date.second() || date.millisecond()
554         || !stepRange.minimum().remainder(static_cast<int>(msPerMinute)).isZero()
555         || !stepRange.step().remainder(static_cast<int>(msPerMinute)).isZero();
556 }
557
558 void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectClearButtonOwner()
559 {
560     element()->focus();
561 }
562
563 bool BaseMultipleFieldsDateAndTimeInputType::shouldClearButtonRespondToMouseEvents()
564 {
565     return !element()->isDisabledOrReadOnly() && !element()->isRequired();
566 }
567
568 void BaseMultipleFieldsDateAndTimeInputType::clearValue()
569 {
570     RefPtr<HTMLInputElement> input(element());
571     input->setValue("", DispatchInputAndChangeEvent);
572     input->updateClearButtonVisibility();
573 }
574
575 void BaseMultipleFieldsDateAndTimeInputType::updateClearButtonVisibility()
576 {
577     if (!m_clearButton)
578         return;
579
580     if (element()->isRequired() || !m_dateTimeEditElement->anyEditableFieldsHaveValues())
581         m_clearButton->setInlineStyleProperty(CSSPropertyVisibility, CSSValueHidden);
582     else
583         m_clearButton->removeInlineStyleProperty(CSSPropertyVisibility);
584 }
585
586 } // namespace WebCore
587
588 #endif