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
54namespace blink {
55
56class DateTimeFormatValidator : public DateTimeFormat::TokenHandler {
57public:
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
73private:
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
84void 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
123bool 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
130DateTimeEditElement* BaseMultipleFieldsDateAndTimeInputType::dateTimeEditElement() const
131{
132    return toDateTimeEditElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::dateTimeEdit()));
133}
134
135SpinButtonElement* BaseMultipleFieldsDateAndTimeInputType::spinButtonElement() const
136{
137    return toSpinButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
138}
139
140ClearButtonElement* BaseMultipleFieldsDateAndTimeInputType::clearButtonElement() const
141{
142    return toClearButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::clearButton()));
143}
144
145PickerIndicatorElement* BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorElement() const
146{
147    return toPickerIndicatorElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::pickerIndicator()));
148}
149
150inline bool BaseMultipleFieldsDateAndTimeInputType::containsFocusedShadowElement() const
151{
152    return element().userAgentShadowRoot()->contains(element().document().focusedElement());
153}
154
155void 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
170void 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
182void 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
199bool BaseMultipleFieldsDateAndTimeInputType::hasCustomFocusLogic() const
200{
201    return false;
202}
203
204bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerDisabled() const
205{
206    return element().isDisabledFormControl();
207}
208
209bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerReadOnly() const
210{
211    return element().isReadOnly();
212}
213
214void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectSpinButtonOwner()
215{
216    if (DateTimeEditElement* edit = dateTimeEditElement())
217        edit->focusIfNoFocus();
218}
219
220bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToMouseEvents()
221{
222    return !element().isDisabledOrReadOnly();
223}
224
225bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToWheelEvents()
226{
227    if (!shouldSpinButtonRespondToMouseEvents())
228        return false;
229    if (DateTimeEditElement* edit = dateTimeEditElement())
230        return edit->hasFocusedField();
231    return false;
232}
233
234void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepDown()
235{
236    if (DateTimeEditElement* edit = dateTimeEditElement())
237        edit->stepDown();
238}
239
240void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepUp()
241{
242    if (DateTimeEditElement* edit = dateTimeEditElement())
243        edit->stepUp();
244}
245
246void BaseMultipleFieldsDateAndTimeInputType::spinButtonDidReleaseMouseCapture(SpinButtonElement::EventDispatch eventDispatch)
247{
248    if (eventDispatch == SpinButtonElement::EventDispatchAllowed)
249        element().dispatchFormControlChangeEvent();
250}
251
252bool BaseMultipleFieldsDateAndTimeInputType::isPickerIndicatorOwnerDisabledOrReadOnly() const
253{
254    return element().isDisabledOrReadOnly();
255}
256
257void 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
275void 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
284Element& BaseMultipleFieldsDateAndTimeInputType::pickerOwnerElement() const
285{
286    return element();
287}
288
289bool BaseMultipleFieldsDateAndTimeInputType::setupDateTimeChooserParameters(DateTimeChooserParameters& parameters)
290{
291    return element().setupDateTimeChooserParameters(parameters);
292}
293
294BaseMultipleFieldsDateAndTimeInputType::BaseMultipleFieldsDateAndTimeInputType(HTMLInputElement& element)
295    : BaseDateAndTimeInputType(element)
296    , m_isDestroyingShadowSubtree(false)
297    , m_pickerIndicatorIsVisible(false)
298    , m_pickerIndicatorIsAlwaysVisible(false)
299{
300}
301
302BaseMultipleFieldsDateAndTimeInputType::~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
316String BaseMultipleFieldsDateAndTimeInputType::badInputText() const
317{
318    return locale().queryString(blink::WebLocalizedString::ValidationBadInputForDateTime);
319}
320
321void BaseMultipleFieldsDateAndTimeInputType::blur()
322{
323    if (DateTimeEditElement* edit = dateTimeEditElement())
324        edit->blurByOwner();
325}
326
327PassRefPtr<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
346void 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
371void 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
393void 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
408void 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
420void BaseMultipleFieldsDateAndTimeInputType::disabledAttributeChanged()
421{
422    spinButtonElement()->releaseCapture();
423    clearButtonElement()->releaseCapture();
424    if (DateTimeEditElement* edit = dateTimeEditElement())
425        edit->disabledStateChanged();
426}
427
428void BaseMultipleFieldsDateAndTimeInputType::requiredAttributeChanged()
429{
430    clearButtonElement()->releaseCapture();
431    updateClearButtonVisibility();
432}
433
434void 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
446bool BaseMultipleFieldsDateAndTimeInputType::hasBadInput() const
447{
448    DateTimeEditElement* edit = dateTimeEditElement();
449    return element().value().isEmpty() && edit && edit->anyEditableFieldsHaveValues();
450}
451
452AtomicString BaseMultipleFieldsDateAndTimeInputType::localeIdentifier() const
453{
454    return element().computeInheritedLanguage();
455}
456
457void BaseMultipleFieldsDateAndTimeInputType::editControlDidChangeValueByKeyboard()
458{
459    element().dispatchFormControlChangeEvent();
460}
461
462void BaseMultipleFieldsDateAndTimeInputType::minOrMaxAttributeChanged()
463{
464    updateView();
465}
466
467void BaseMultipleFieldsDateAndTimeInputType::readonlyAttributeChanged()
468{
469    spinButtonElement()->releaseCapture();
470    clearButtonElement()->releaseCapture();
471    if (DateTimeEditElement* edit = dateTimeEditElement())
472        edit->readOnlyStateChanged();
473}
474
475void 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
486FormControlState BaseMultipleFieldsDateAndTimeInputType::saveFormControlState() const
487{
488    if (DateTimeEditElement* edit = dateTimeEditElement())
489        return edit->valueAsDateTimeFieldsState().saveFormControlState();
490    return FormControlState();
491}
492
493void 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
503void BaseMultipleFieldsDateAndTimeInputType::stepAttributeChanged()
504{
505    updateView();
506}
507
508void 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
543void BaseMultipleFieldsDateAndTimeInputType::valueAttributeChanged()
544{
545    if (!element().hasDirtyValue())
546        updateView();
547}
548
549void BaseMultipleFieldsDateAndTimeInputType::listAttributeTargetChanged()
550{
551    updatePickerIndicatorVisibility();
552}
553
554void BaseMultipleFieldsDateAndTimeInputType::updatePickerIndicatorVisibility()
555{
556    if (m_pickerIndicatorIsAlwaysVisible) {
557        showPickerIndicator();
558        return;
559    }
560    if (element().hasValidDataListOptions())
561        showPickerIndicator();
562    else
563        hidePickerIndicator();
564}
565
566void BaseMultipleFieldsDateAndTimeInputType::hidePickerIndicator()
567{
568    if (!m_pickerIndicatorIsVisible)
569        return;
570    m_pickerIndicatorIsVisible = false;
571    ASSERT(pickerIndicatorElement());
572    pickerIndicatorElement()->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
573}
574
575void BaseMultipleFieldsDateAndTimeInputType::showPickerIndicator()
576{
577    if (m_pickerIndicatorIsVisible)
578        return;
579    m_pickerIndicatorIsVisible = true;
580    ASSERT(pickerIndicatorElement());
581    pickerIndicatorElement()->removeInlineStyleProperty(CSSPropertyDisplay);
582}
583
584bool 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
592void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectClearButtonOwner()
593{
594    element().focus();
595}
596
597bool BaseMultipleFieldsDateAndTimeInputType::shouldClearButtonRespondToMouseEvents()
598{
599    return !element().isDisabledOrReadOnly() && !element().isRequired();
600}
601
602void BaseMultipleFieldsDateAndTimeInputType::clearValue()
603{
604    RefPtrWillBeRawPtr<HTMLInputElement> input(element());
605    input->setValue("", DispatchInputAndChangeEvent);
606    input->updateClearButtonVisibility();
607}
608
609void 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
624TextDirection BaseMultipleFieldsDateAndTimeInputType::computedTextDirection()
625{
626    return element().locale().isRTL() ? RTL : LTR;
627}
628
629AXObject* BaseMultipleFieldsDateAndTimeInputType::popupRootAXObject()
630{
631    if (PickerIndicatorElement* picker = pickerIndicatorElement())
632        return picker->popupRootAXObject();
633    return 0;
634}
635
636} // namespace blink
637
638#endif
639