Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / forms / 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 "core/html/forms/BaseMultipleFieldsDateAndTimeInputType.h"
34
35 #include "core/CSSValueKeywords.h"
36 #include "core/dom/shadow/ShadowRoot.h"
37 #include "core/events/KeyboardEvent.h"
38 #include "core/events/ScopedEventQueue.h"
39 #include "core/html/HTMLDataListElement.h"
40 #include "core/html/HTMLInputElement.h"
41 #include "core/html/HTMLOptionElement.h"
42 #include "core/html/forms/DateTimeFieldsState.h"
43 #include "core/html/forms/FormController.h"
44 #include "core/html/shadow/ShadowElementNames.h"
45 #include "core/page/FocusController.h"
46 #include "core/page/Page.h"
47 #include "core/rendering/RenderTheme.h"
48 #include "platform/DateComponents.h"
49 #include "platform/RuntimeEnabledFeatures.h"
50 #include "platform/text/DateTimeFormat.h"
51 #include "platform/text/PlatformLocale.h"
52 #include "wtf/DateMath.h"
53
54 namespace blink {
55
56 class DateTimeFormatValidator : public DateTimeFormat::TokenHandler {
57 public:
58     DateTimeFormatValidator()
59         : m_hasYear(false)
60         , m_hasMonth(false)
61         , m_hasWeek(false)
62         , m_hasDay(false)
63         , m_hasAMPM(false)
64         , m_hasHour(false)
65         , m_hasMinute(false)
66         , m_hasSecond(false) { }
67
68     virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
69     virtual void visitLiteral(const String&) OVERRIDE FINAL { }
70
71     bool validateFormat(const String& format, const BaseMultipleFieldsDateAndTimeInputType&);
72
73 private:
74     bool m_hasYear;
75     bool m_hasMonth;
76     bool m_hasWeek;
77     bool m_hasDay;
78     bool m_hasAMPM;
79     bool m_hasHour;
80     bool m_hasMinute;
81     bool m_hasSecond;
82 };
83
84 void DateTimeFormatValidator::visitField(DateTimeFormat::FieldType fieldType, int)
85 {
86     switch (fieldType) {
87     case DateTimeFormat::FieldTypeYear:
88         m_hasYear = true;
89         break;
90     case DateTimeFormat::FieldTypeMonth: // Fallthrough.
91     case DateTimeFormat::FieldTypeMonthStandAlone:
92         m_hasMonth = true;
93         break;
94     case DateTimeFormat::FieldTypeWeekOfYear:
95         m_hasWeek = true;
96         break;
97     case DateTimeFormat::FieldTypeDayOfMonth:
98         m_hasDay = true;
99         break;
100     case DateTimeFormat::FieldTypePeriod:
101         m_hasAMPM = true;
102         break;
103     case DateTimeFormat::FieldTypeHour11: // Fallthrough.
104     case DateTimeFormat::FieldTypeHour12:
105         m_hasHour = true;
106         break;
107     case DateTimeFormat::FieldTypeHour23: // Fallthrough.
108     case DateTimeFormat::FieldTypeHour24:
109         m_hasHour = true;
110         m_hasAMPM = true;
111         break;
112     case DateTimeFormat::FieldTypeMinute:
113         m_hasMinute = true;
114         break;
115     case DateTimeFormat::FieldTypeSecond:
116         m_hasSecond = true;
117         break;
118     default:
119         break;
120     }
121 }
122
123 bool DateTimeFormatValidator::validateFormat(const String& format, const BaseMultipleFieldsDateAndTimeInputType& inputType)
124 {
125     if (!DateTimeFormat::parse(format, *this))
126         return false;
127     return inputType.isValidFormat(m_hasYear, m_hasMonth, m_hasWeek, m_hasDay, m_hasAMPM, m_hasHour, m_hasMinute, m_hasSecond);
128 }
129
130 DateTimeEditElement* BaseMultipleFieldsDateAndTimeInputType::dateTimeEditElement() const
131 {
132     return toDateTimeEditElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::dateTimeEdit()));
133 }
134
135 SpinButtonElement* BaseMultipleFieldsDateAndTimeInputType::spinButtonElement() const
136 {
137     return toSpinButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
138 }
139
140 ClearButtonElement* BaseMultipleFieldsDateAndTimeInputType::clearButtonElement() const
141 {
142     return toClearButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::clearButton()));
143 }
144
145 PickerIndicatorElement* BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorElement() const
146 {
147     return toPickerIndicatorElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::pickerIndicator()));
148 }
149
150 inline bool BaseMultipleFieldsDateAndTimeInputType::containsFocusedShadowElement() const
151 {
152     return element().userAgentShadowRoot()->contains(element().document().focusedElement());
153 }
154
155 void BaseMultipleFieldsDateAndTimeInputType::didBlurFromControl()
156 {
157     // We don't need to call blur(). This function is called when control
158     // lost focus.
159
160     if (containsFocusedShadowElement())
161         return;
162     EventQueueScope scope;
163     RefPtrWillBeRawPtr<HTMLInputElement> protector(element());
164     // Remove focus ring by CSS "focus" pseudo class.
165     element().setFocus(false);
166     if (SpinButtonElement *spinButton = spinButtonElement())
167         spinButton->releaseCapture();
168 }
169
170 void BaseMultipleFieldsDateAndTimeInputType::didFocusOnControl()
171 {
172     // We don't need to call focus(). This function is called when control
173     // got focus.
174
175     if (!containsFocusedShadowElement())
176         return;
177     // Add focus ring by CSS "focus" pseudo class.
178     // FIXME: Setting the focus flag to non-focused element is too tricky.
179     element().setFocus(true);
180 }
181
182 void BaseMultipleFieldsDateAndTimeInputType::editControlValueChanged()
183 {
184     RefPtrWillBeRawPtr<HTMLInputElement> input(element());
185     String oldValue = input->value();
186     String newValue = sanitizeValue(dateTimeEditElement()->value());
187     // Even if oldValue is null and newValue is "", we should assume they are same.
188     if ((oldValue.isEmpty() && newValue.isEmpty()) || oldValue == newValue) {
189         input->setNeedsValidityCheck();
190     } else {
191         input->setValueInternal(newValue, DispatchNoEvent);
192         input->setNeedsStyleRecalc(SubtreeStyleChange);
193         input->dispatchFormControlInputEvent();
194     }
195     input->notifyFormStateChanged();
196     input->updateClearButtonVisibility();
197 }
198
199 bool BaseMultipleFieldsDateAndTimeInputType::hasCustomFocusLogic() const
200 {
201     return false;
202 }
203
204 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerDisabled() const
205 {
206     return element().isDisabledFormControl();
207 }
208
209 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerReadOnly() const
210 {
211     return element().isReadOnly();
212 }
213
214 void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectSpinButtonOwner()
215 {
216     if (DateTimeEditElement* edit = dateTimeEditElement())
217         edit->focusIfNoFocus();
218 }
219
220 bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToMouseEvents()
221 {
222     return !element().isDisabledOrReadOnly();
223 }
224
225 bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToWheelEvents()
226 {
227     if (!shouldSpinButtonRespondToMouseEvents())
228         return false;
229     if (DateTimeEditElement* edit = dateTimeEditElement())
230         return edit->hasFocusedField();
231     return false;
232 }
233
234 void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepDown()
235 {
236     if (DateTimeEditElement* edit = dateTimeEditElement())
237         edit->stepDown();
238 }
239
240 void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepUp()
241 {
242     if (DateTimeEditElement* edit = dateTimeEditElement())
243         edit->stepUp();
244 }
245
246 void BaseMultipleFieldsDateAndTimeInputType::spinButtonDidReleaseMouseCapture(SpinButtonElement::EventDispatch eventDispatch)
247 {
248     if (eventDispatch == SpinButtonElement::EventDispatchAllowed)
249         element().dispatchFormControlChangeEvent();
250 }
251
252 bool BaseMultipleFieldsDateAndTimeInputType::isPickerIndicatorOwnerDisabledOrReadOnly() const
253 {
254     return element().isDisabledOrReadOnly();
255 }
256
257 void BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorChooseValue(const String& value)
258 {
259     if (element().isValidValue(value)) {
260         element().setValue(value, DispatchInputAndChangeEvent);
261         return;
262     }
263
264     DateTimeEditElement* edit = this->dateTimeEditElement();
265     if (!edit)
266         return;
267     EventQueueScope scope;
268     DateComponents date;
269     unsigned end;
270     if (date.parseDate(value, 0, end) && end == value.length())
271         edit->setOnlyYearMonthDay(date);
272     element().dispatchFormControlChangeEvent();
273 }
274
275 void BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorChooseValue(double value)
276 {
277     ASSERT(std::isfinite(value) || std::isnan(value));
278     if (std::isnan(value))
279         element().setValue(emptyString(), DispatchInputAndChangeEvent);
280     else
281         element().setValueAsNumber(value, ASSERT_NO_EXCEPTION, DispatchInputAndChangeEvent);
282 }
283
284 Element& BaseMultipleFieldsDateAndTimeInputType::pickerOwnerElement() const
285 {
286     return element();
287 }
288
289 bool BaseMultipleFieldsDateAndTimeInputType::setupDateTimeChooserParameters(DateTimeChooserParameters& parameters)
290 {
291     return element().setupDateTimeChooserParameters(parameters);
292 }
293
294 BaseMultipleFieldsDateAndTimeInputType::BaseMultipleFieldsDateAndTimeInputType(HTMLInputElement& element)
295     : BaseDateAndTimeInputType(element)
296     , m_isDestroyingShadowSubtree(false)
297     , m_pickerIndicatorIsVisible(false)
298     , m_pickerIndicatorIsAlwaysVisible(false)
299 {
300 }
301
302 BaseMultipleFieldsDateAndTimeInputType::~BaseMultipleFieldsDateAndTimeInputType()
303 {
304 #if !ENABLE(OILPAN)
305     if (SpinButtonElement* element = spinButtonElement())
306         element->removeSpinButtonOwner();
307     if (ClearButtonElement* element = clearButtonElement())
308         element->removeClearButtonOwner();
309     if (DateTimeEditElement* element = dateTimeEditElement())
310         element->removeEditControlOwner();
311     if (PickerIndicatorElement* element = pickerIndicatorElement())
312         element->removePickerIndicatorOwner();
313 #endif
314 }
315
316 String BaseMultipleFieldsDateAndTimeInputType::badInputText() const
317 {
318     return locale().queryString(blink::WebLocalizedString::ValidationBadInputForDateTime);
319 }
320
321 void BaseMultipleFieldsDateAndTimeInputType::blur()
322 {
323     if (DateTimeEditElement* edit = dateTimeEditElement())
324         edit->blurByOwner();
325 }
326
327 PassRefPtr<RenderStyle> BaseMultipleFieldsDateAndTimeInputType::customStyleForRenderer(PassRefPtr<RenderStyle> originalStyle)
328 {
329     EDisplay originalDisplay = originalStyle->display();
330     EDisplay newDisplay = originalDisplay;
331     if (originalDisplay == INLINE || originalDisplay == INLINE_BLOCK)
332         newDisplay = INLINE_FLEX;
333     else if (originalDisplay == BLOCK)
334         newDisplay = FLEX;
335     TextDirection contentDirection = computedTextDirection();
336     if (originalStyle->direction() == contentDirection && originalDisplay == newDisplay)
337         return originalStyle;
338
339     RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
340     style->setDirection(contentDirection);
341     style->setDisplay(newDisplay);
342     style->setUnique();
343     return style.release();
344 }
345
346 void BaseMultipleFieldsDateAndTimeInputType::createShadowSubtree()
347 {
348     ASSERT(element().shadow());
349
350     // Element must not have a renderer here, because if it did
351     // DateTimeEditElement::customStyleForRenderer() is called in appendChild()
352     // before the field wrapper element is created.
353     // FIXME: This code should not depend on such craziness.
354     ASSERT(!element().renderer());
355
356     Document& document = element().document();
357     ContainerNode* container = element().userAgentShadowRoot();
358
359     container->appendChild(DateTimeEditElement::create(document, *this));
360     element().updateView();
361     container->appendChild(ClearButtonElement::create(document, *this));
362     container->appendChild(SpinButtonElement::create(document, *this));
363
364     if (RenderTheme::theme().supportsCalendarPicker(formControlType()))
365         m_pickerIndicatorIsAlwaysVisible = true;
366     container->appendChild(PickerIndicatorElement::create(document, *this));
367     m_pickerIndicatorIsVisible = true;
368     updatePickerIndicatorVisibility();
369 }
370
371 void BaseMultipleFieldsDateAndTimeInputType::destroyShadowSubtree()
372 {
373     ASSERT(!m_isDestroyingShadowSubtree);
374     m_isDestroyingShadowSubtree = true;
375     if (SpinButtonElement* element = spinButtonElement())
376         element->removeSpinButtonOwner();
377     if (ClearButtonElement* element = clearButtonElement())
378         element->removeClearButtonOwner();
379     if (DateTimeEditElement* element = dateTimeEditElement())
380         element->removeEditControlOwner();
381     if (PickerIndicatorElement* element = pickerIndicatorElement())
382         element->removePickerIndicatorOwner();
383
384     // If a field element has focus, set focus back to the <input> itself before
385     // deleting the field. This prevents unnecessary focusout/blur events.
386     if (containsFocusedShadowElement())
387         element().focus();
388
389     BaseDateAndTimeInputType::destroyShadowSubtree();
390     m_isDestroyingShadowSubtree = false;
391 }
392
393 void BaseMultipleFieldsDateAndTimeInputType::handleFocusInEvent(Element* oldFocusedElement, FocusType type)
394 {
395     DateTimeEditElement* edit = dateTimeEditElement();
396     if (!edit || m_isDestroyingShadowSubtree)
397         return;
398     if (type == FocusTypeBackward) {
399         if (element().document().page())
400             element().document().page()->focusController().advanceFocus(type);
401     } else if (type == FocusTypeNone || type == FocusTypeMouse || type == FocusTypePage) {
402         edit->focusByOwner(oldFocusedElement);
403     } else {
404         edit->focusByOwner();
405     }
406 }
407
408 void BaseMultipleFieldsDateAndTimeInputType::forwardEvent(Event* event)
409 {
410     if (SpinButtonElement* element = spinButtonElement()) {
411         element->forwardEvent(event);
412         if (event->defaultHandled())
413             return;
414     }
415
416     if (DateTimeEditElement* edit = dateTimeEditElement())
417         edit->defaultEventHandler(event);
418 }
419
420 void BaseMultipleFieldsDateAndTimeInputType::disabledAttributeChanged()
421 {
422     spinButtonElement()->releaseCapture();
423     clearButtonElement()->releaseCapture();
424     if (DateTimeEditElement* edit = dateTimeEditElement())
425         edit->disabledStateChanged();
426 }
427
428 void BaseMultipleFieldsDateAndTimeInputType::requiredAttributeChanged()
429 {
430     clearButtonElement()->releaseCapture();
431     updateClearButtonVisibility();
432 }
433
434 void BaseMultipleFieldsDateAndTimeInputType::handleKeydownEvent(KeyboardEvent* event)
435 {
436     if (m_pickerIndicatorIsVisible
437         && ((event->keyIdentifier() == "Down" && event->getModifierState("Alt")) || (RenderTheme::theme().shouldOpenPickerWithF4Key() && event->keyIdentifier() == "F4"))) {
438         if (PickerIndicatorElement* element = pickerIndicatorElement())
439             element->openPopup();
440         event->setDefaultHandled();
441     } else {
442         forwardEvent(event);
443     }
444 }
445
446 bool BaseMultipleFieldsDateAndTimeInputType::hasBadInput() const
447 {
448     DateTimeEditElement* edit = dateTimeEditElement();
449     return element().value().isEmpty() && edit && edit->anyEditableFieldsHaveValues();
450 }
451
452 AtomicString BaseMultipleFieldsDateAndTimeInputType::localeIdentifier() const
453 {
454     return element().computeInheritedLanguage();
455 }
456
457 void BaseMultipleFieldsDateAndTimeInputType::editControlDidChangeValueByKeyboard()
458 {
459     element().dispatchFormControlChangeEvent();
460 }
461
462 void BaseMultipleFieldsDateAndTimeInputType::minOrMaxAttributeChanged()
463 {
464     updateView();
465 }
466
467 void BaseMultipleFieldsDateAndTimeInputType::readonlyAttributeChanged()
468 {
469     spinButtonElement()->releaseCapture();
470     clearButtonElement()->releaseCapture();
471     if (DateTimeEditElement* edit = dateTimeEditElement())
472         edit->readOnlyStateChanged();
473 }
474
475 void BaseMultipleFieldsDateAndTimeInputType::restoreFormControlState(const FormControlState& state)
476 {
477     DateTimeEditElement* edit = dateTimeEditElement();
478     if (!edit)
479         return;
480     DateTimeFieldsState dateTimeFieldsState = DateTimeFieldsState::restoreFormControlState(state);
481     edit->setValueAsDateTimeFieldsState(dateTimeFieldsState);
482     element().setValueInternal(sanitizeValue(edit->value()), DispatchNoEvent);
483     updateClearButtonVisibility();
484 }
485
486 FormControlState BaseMultipleFieldsDateAndTimeInputType::saveFormControlState() const
487 {
488     if (DateTimeEditElement* edit = dateTimeEditElement())
489         return edit->valueAsDateTimeFieldsState().saveFormControlState();
490     return FormControlState();
491 }
492
493 void BaseMultipleFieldsDateAndTimeInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
494 {
495     InputType::setValue(sanitizedValue, valueChanged, eventBehavior);
496     DateTimeEditElement* edit = dateTimeEditElement();
497     if (valueChanged || (sanitizedValue.isEmpty() && edit && edit->anyEditableFieldsHaveValues())) {
498         element().updateView();
499         element().setNeedsValidityCheck();
500     }
501 }
502
503 void BaseMultipleFieldsDateAndTimeInputType::stepAttributeChanged()
504 {
505     updateView();
506 }
507
508 void BaseMultipleFieldsDateAndTimeInputType::updateView()
509 {
510     DateTimeEditElement* edit = dateTimeEditElement();
511     if (!edit)
512         return;
513
514     DateTimeEditElement::LayoutParameters layoutParameters(element().locale(), createStepRange(AnyIsDefaultStep));
515
516     DateComponents date;
517     bool hasValue = false;
518     if (!element().suggestedValue().isNull())
519         hasValue = parseToDateComponents(element().suggestedValue(), &date);
520     else
521         hasValue = parseToDateComponents(element().value(), &date);
522     if (!hasValue)
523         setMillisecondToDateComponents(layoutParameters.stepRange.minimum().toDouble(), &date);
524
525     setupLayoutParameters(layoutParameters, date);
526
527     DEFINE_STATIC_LOCAL(AtomicString, datetimeformatAttr, ("datetimeformat", AtomicString::ConstructFromLiteral));
528     edit->setAttribute(datetimeformatAttr, AtomicString(layoutParameters.dateTimeFormat), ASSERT_NO_EXCEPTION);
529     const AtomicString pattern = edit->fastGetAttribute(HTMLNames::patternAttr);
530     if (!pattern.isEmpty())
531         layoutParameters.dateTimeFormat = pattern;
532
533     if (!DateTimeFormatValidator().validateFormat(layoutParameters.dateTimeFormat, *this))
534         layoutParameters.dateTimeFormat = layoutParameters.fallbackDateTimeFormat;
535
536     if (hasValue)
537         edit->setValueAsDate(layoutParameters, date);
538     else
539         edit->setEmptyValue(layoutParameters, date);
540     updateClearButtonVisibility();
541 }
542
543 void BaseMultipleFieldsDateAndTimeInputType::valueAttributeChanged()
544 {
545     if (!element().hasDirtyValue())
546         updateView();
547 }
548
549 void BaseMultipleFieldsDateAndTimeInputType::listAttributeTargetChanged()
550 {
551     updatePickerIndicatorVisibility();
552 }
553
554 void BaseMultipleFieldsDateAndTimeInputType::updatePickerIndicatorVisibility()
555 {
556     if (m_pickerIndicatorIsAlwaysVisible) {
557         showPickerIndicator();
558         return;
559     }
560     if (element().hasValidDataListOptions())
561         showPickerIndicator();
562     else
563         hidePickerIndicator();
564 }
565
566 void BaseMultipleFieldsDateAndTimeInputType::hidePickerIndicator()
567 {
568     if (!m_pickerIndicatorIsVisible)
569         return;
570     m_pickerIndicatorIsVisible = false;
571     ASSERT(pickerIndicatorElement());
572     pickerIndicatorElement()->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
573 }
574
575 void BaseMultipleFieldsDateAndTimeInputType::showPickerIndicator()
576 {
577     if (m_pickerIndicatorIsVisible)
578         return;
579     m_pickerIndicatorIsVisible = true;
580     ASSERT(pickerIndicatorElement());
581     pickerIndicatorElement()->removeInlineStyleProperty(CSSPropertyDisplay);
582 }
583
584 bool BaseMultipleFieldsDateAndTimeInputType::shouldHaveSecondField(const DateComponents& date) const
585 {
586     StepRange stepRange = createStepRange(AnyIsDefaultStep);
587     return date.second() || date.millisecond()
588         || !stepRange.minimum().remainder(static_cast<int>(msPerMinute)).isZero()
589         || !stepRange.step().remainder(static_cast<int>(msPerMinute)).isZero();
590 }
591
592 void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectClearButtonOwner()
593 {
594     element().focus();
595 }
596
597 bool BaseMultipleFieldsDateAndTimeInputType::shouldClearButtonRespondToMouseEvents()
598 {
599     return !element().isDisabledOrReadOnly() && !element().isRequired();
600 }
601
602 void BaseMultipleFieldsDateAndTimeInputType::clearValue()
603 {
604     RefPtrWillBeRawPtr<HTMLInputElement> input(element());
605     input->setValue("", DispatchInputAndChangeEvent);
606     input->updateClearButtonVisibility();
607 }
608
609 void BaseMultipleFieldsDateAndTimeInputType::updateClearButtonVisibility()
610 {
611     ClearButtonElement* clearButton = clearButtonElement();
612     if (!clearButton)
613         return;
614
615     if (element().isRequired() || !dateTimeEditElement()->anyEditableFieldsHaveValues()) {
616         clearButton->setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
617         clearButton->setInlineStyleProperty(CSSPropertyPointerEvents, CSSValueNone);
618     } else {
619         clearButton->removeInlineStyleProperty(CSSPropertyOpacity);
620         clearButton->removeInlineStyleProperty(CSSPropertyPointerEvents);
621     }
622 }
623
624 TextDirection BaseMultipleFieldsDateAndTimeInputType::computedTextDirection()
625 {
626     return element().locale().isRTL() ? RTL : LTR;
627 }
628
629 AXObject* BaseMultipleFieldsDateAndTimeInputType::popupRootAXObject()
630 {
631     if (PickerIndicatorElement* picker = pickerIndicatorElement())
632         return picker->popupRootAXObject();
633     return 0;
634 }
635
636 } // namespace blink
637
638 #endif