2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
33 #include "core/html/forms/BaseMultipleFieldsDateAndTimeInputType.h"
35 #include "CSSValueKeywords.h"
36 #include "RuntimeEnabledFeatures.h"
37 #include "core/dom/shadow/ShadowRoot.h"
38 #include "core/events/KeyboardEvent.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/text/DateTimeFormat.h"
50 #include "platform/text/PlatformLocale.h"
51 #include "wtf/DateMath.h"
55 class DateTimeFormatValidator : public DateTimeFormat::TokenHandler {
57 DateTimeFormatValidator()
65 , m_hasSecond(false) { }
67 virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
68 virtual void visitLiteral(const String&) OVERRIDE FINAL { }
70 bool validateFormat(const String& format, const BaseMultipleFieldsDateAndTimeInputType&);
83 void DateTimeFormatValidator::visitField(DateTimeFormat::FieldType fieldType, int)
86 case DateTimeFormat::FieldTypeYear:
89 case DateTimeFormat::FieldTypeMonth: // Fallthrough.
90 case DateTimeFormat::FieldTypeMonthStandAlone:
93 case DateTimeFormat::FieldTypeWeekOfYear:
96 case DateTimeFormat::FieldTypeDayOfMonth:
99 case DateTimeFormat::FieldTypePeriod:
102 case DateTimeFormat::FieldTypeHour11: // Fallthrough.
103 case DateTimeFormat::FieldTypeHour12:
106 case DateTimeFormat::FieldTypeHour23: // Fallthrough.
107 case DateTimeFormat::FieldTypeHour24:
111 case DateTimeFormat::FieldTypeMinute:
114 case DateTimeFormat::FieldTypeSecond:
122 bool DateTimeFormatValidator::validateFormat(const String& format, const BaseMultipleFieldsDateAndTimeInputType& inputType)
124 if (!DateTimeFormat::parse(format, *this))
126 return inputType.isValidFormat(m_hasYear, m_hasMonth, m_hasWeek, m_hasDay, m_hasAMPM, m_hasHour, m_hasMinute, m_hasSecond);
129 DateTimeEditElement* BaseMultipleFieldsDateAndTimeInputType::dateTimeEditElement() const
131 return toDateTimeEditElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::dateTimeEdit()));
134 SpinButtonElement* BaseMultipleFieldsDateAndTimeInputType::spinButtonElement() const
136 return toSpinButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
139 ClearButtonElement* BaseMultipleFieldsDateAndTimeInputType::clearButtonElement() const
141 return toClearButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::clearButton()));
144 PickerIndicatorElement* BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorElement() const
146 return toPickerIndicatorElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::pickerIndicator()));
149 inline bool BaseMultipleFieldsDateAndTimeInputType::containsFocusedShadowElement() const
151 return element().userAgentShadowRoot()->contains(element().document().focusedElement());
154 void BaseMultipleFieldsDateAndTimeInputType::didBlurFromControl()
156 // We don't need to call blur(). This function is called when control
159 if (containsFocusedShadowElement())
161 RefPtr<HTMLInputElement> protector(element());
162 // Remove focus ring by CSS "focus" pseudo class.
163 element().setFocus(false);
166 void BaseMultipleFieldsDateAndTimeInputType::didFocusOnControl()
168 // We don't need to call focus(). This function is called when control
171 if (!containsFocusedShadowElement())
173 // Add focus ring by CSS "focus" pseudo class.
174 // FIXME: Setting the focus flag to non-focused element is too tricky.
175 element().setFocus(true);
178 void BaseMultipleFieldsDateAndTimeInputType::editControlValueChanged()
180 RefPtr<HTMLInputElement> input(element());
181 String oldValue = input->value();
182 String newValue = sanitizeValue(dateTimeEditElement()->value());
183 // Even if oldValue is null and newValue is "", we should assume they are same.
184 if ((oldValue.isEmpty() && newValue.isEmpty()) || oldValue == newValue) {
185 input->setNeedsValidityCheck();
187 input->setValueInternal(newValue, DispatchNoEvent);
188 input->setNeedsStyleRecalc();
189 input->dispatchFormControlInputEvent();
191 input->notifyFormStateChanged();
192 input->updateClearButtonVisibility();
195 bool BaseMultipleFieldsDateAndTimeInputType::hasCustomFocusLogic() const
200 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerDisabled() const
202 return element().isDisabledFormControl();
205 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerReadOnly() const
207 return element().isReadOnly();
210 void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectSpinButtonOwner()
212 if (DateTimeEditElement* edit = dateTimeEditElement())
213 edit->focusIfNoFocus();
216 bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToMouseEvents()
218 return !element().isDisabledOrReadOnly();
221 bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToWheelEvents()
223 if (!shouldSpinButtonRespondToMouseEvents())
225 if (DateTimeEditElement* edit = dateTimeEditElement())
226 return edit->hasFocusedField();
230 void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepDown()
232 if (DateTimeEditElement* edit = dateTimeEditElement())
236 void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepUp()
238 if (DateTimeEditElement* edit = dateTimeEditElement())
242 void BaseMultipleFieldsDateAndTimeInputType::spinButtonDidReleaseMouseCapture()
244 element().dispatchFormControlChangeEvent();
247 bool BaseMultipleFieldsDateAndTimeInputType::isPickerIndicatorOwnerDisabledOrReadOnly() const
249 return element().isDisabledOrReadOnly();
252 void BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorChooseValue(const String& value)
254 if (element().isValidValue(value)) {
255 element().setValue(value, DispatchInputAndChangeEvent);
259 DateTimeEditElement* edit = this->dateTimeEditElement();
264 if (date.parseDate(value, 0, end) && end == value.length())
265 edit->setOnlyYearMonthDay(date);
266 element().dispatchFormControlChangeEvent();
269 void BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorChooseValue(double value)
271 ASSERT(std::isfinite(value) || std::isnan(value));
272 if (std::isnan(value))
273 element().setValue(emptyString(), DispatchInputAndChangeEvent);
275 element().setValueAsNumber(value, ASSERT_NO_EXCEPTION, DispatchInputAndChangeEvent);
278 bool BaseMultipleFieldsDateAndTimeInputType::setupDateTimeChooserParameters(DateTimeChooserParameters& parameters)
280 return element().setupDateTimeChooserParameters(parameters);
283 BaseMultipleFieldsDateAndTimeInputType::BaseMultipleFieldsDateAndTimeInputType(HTMLInputElement& element)
284 : BaseDateAndTimeInputType(element)
285 , m_isDestroyingShadowSubtree(false)
286 , m_pickerIndicatorIsVisible(false)
287 , m_pickerIndicatorIsAlwaysVisible(false)
291 BaseMultipleFieldsDateAndTimeInputType::~BaseMultipleFieldsDateAndTimeInputType()
293 if (SpinButtonElement* element = spinButtonElement())
294 element->removeSpinButtonOwner();
295 if (ClearButtonElement* element = clearButtonElement())
296 element->removeClearButtonOwner();
297 if (DateTimeEditElement* element = dateTimeEditElement())
298 element->removeEditControlOwner();
299 if (PickerIndicatorElement* element = pickerIndicatorElement())
300 element->removePickerIndicatorOwner();
303 String BaseMultipleFieldsDateAndTimeInputType::badInputText() const
305 return locale().queryString(blink::WebLocalizedString::ValidationBadInputForDateTime);
308 void BaseMultipleFieldsDateAndTimeInputType::blur()
310 if (DateTimeEditElement* edit = dateTimeEditElement())
314 PassRefPtr<RenderStyle> BaseMultipleFieldsDateAndTimeInputType::customStyleForRenderer(PassRefPtr<RenderStyle> originalStyle)
316 EDisplay originalDisplay = originalStyle->display();
317 EDisplay newDisplay = originalDisplay;
318 if (originalDisplay == INLINE || originalDisplay == INLINE_BLOCK)
319 newDisplay = INLINE_FLEX;
320 else if (originalDisplay == BLOCK)
322 TextDirection contentDirection = element().locale().isRTL() ? RTL : LTR;
323 if (originalStyle->direction() == contentDirection && originalDisplay == newDisplay)
324 return originalStyle;
326 RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
327 style->setDirection(contentDirection);
328 style->setDisplay(newDisplay);
330 return style.release();
333 void BaseMultipleFieldsDateAndTimeInputType::createShadowSubtree()
335 ASSERT(element().shadow());
337 // Element must not have a renderer here, because if it did
338 // DateTimeEditElement::customStyleForRenderer() is called in appendChild()
339 // before the field wrapper element is created.
340 // FIXME: This code should not depend on such craziness.
341 ASSERT(!element().renderer());
343 Document& document = element().document();
344 ContainerNode* container = element().userAgentShadowRoot();
346 container->appendChild(DateTimeEditElement::create(document, *this));
348 container->appendChild(ClearButtonElement::create(document, *this));
349 container->appendChild(SpinButtonElement::create(document, *this));
351 bool shouldAddPickerIndicator = false;
352 if (InputType::themeSupportsDataListUI(this))
353 shouldAddPickerIndicator = true;
354 if (RenderTheme::theme().supportsCalendarPicker(formControlType())) {
355 shouldAddPickerIndicator = true;
356 m_pickerIndicatorIsAlwaysVisible = true;
358 if (shouldAddPickerIndicator) {
359 container->appendChild(PickerIndicatorElement::create(document, *this));
360 m_pickerIndicatorIsVisible = true;
361 updatePickerIndicatorVisibility();
365 void BaseMultipleFieldsDateAndTimeInputType::destroyShadowSubtree()
367 ASSERT(!m_isDestroyingShadowSubtree);
368 m_isDestroyingShadowSubtree = true;
369 if (SpinButtonElement* element = spinButtonElement())
370 element->removeSpinButtonOwner();
371 if (ClearButtonElement* element = clearButtonElement())
372 element->removeClearButtonOwner();
373 if (DateTimeEditElement* element = dateTimeEditElement())
374 element->removeEditControlOwner();
375 if (PickerIndicatorElement* element = pickerIndicatorElement())
376 element->removePickerIndicatorOwner();
378 // If a field element has focus, set focus back to the <input> itself before
379 // deleting the field. This prevents unnecessary focusout/blur events.
380 if (containsFocusedShadowElement())
383 BaseDateAndTimeInputType::destroyShadowSubtree();
384 m_isDestroyingShadowSubtree = false;
387 void BaseMultipleFieldsDateAndTimeInputType::handleFocusEvent(Element* oldFocusedElement, FocusDirection direction)
389 DateTimeEditElement* edit = dateTimeEditElement();
390 if (!edit || m_isDestroyingShadowSubtree)
392 if (direction == FocusDirectionBackward) {
393 if (element().document().page())
394 element().document().page()->focusController().advanceFocus(direction);
395 } else if (direction == FocusDirectionNone || direction == FocusDirectionMouse || direction == FocusDirectionPage) {
396 edit->focusByOwner(oldFocusedElement);
398 edit->focusByOwner();
402 void BaseMultipleFieldsDateAndTimeInputType::forwardEvent(Event* event)
404 if (SpinButtonElement* element = spinButtonElement()) {
405 element->forwardEvent(event);
406 if (event->defaultHandled())
410 if (DateTimeEditElement* edit = dateTimeEditElement())
411 edit->defaultEventHandler(event);
414 void BaseMultipleFieldsDateAndTimeInputType::disabledAttributeChanged()
416 spinButtonElement()->releaseCapture();
417 clearButtonElement()->releaseCapture();
418 if (DateTimeEditElement* edit = dateTimeEditElement())
419 edit->disabledStateChanged();
422 void BaseMultipleFieldsDateAndTimeInputType::requiredAttributeChanged()
424 clearButtonElement()->releaseCapture();
425 updateClearButtonVisibility();
428 void BaseMultipleFieldsDateAndTimeInputType::handleKeydownEvent(KeyboardEvent* event)
430 if (m_pickerIndicatorIsVisible
431 && ((event->keyIdentifier() == "Down" && event->getModifierState("Alt")) || (RenderTheme::theme().shouldOpenPickerWithF4Key() && event->keyIdentifier() == "F4"))) {
432 if (PickerIndicatorElement* element = pickerIndicatorElement())
433 element->openPopup();
434 event->setDefaultHandled();
440 bool BaseMultipleFieldsDateAndTimeInputType::hasBadInput() const
442 DateTimeEditElement* edit = dateTimeEditElement();
443 return element().value().isEmpty() && edit && edit->anyEditableFieldsHaveValues();
446 AtomicString BaseMultipleFieldsDateAndTimeInputType::localeIdentifier() const
448 return element().computeInheritedLanguage();
451 void BaseMultipleFieldsDateAndTimeInputType::editControlDidChangeValueByKeyboard()
453 element().dispatchFormControlChangeEvent();
456 void BaseMultipleFieldsDateAndTimeInputType::minOrMaxAttributeChanged()
461 void BaseMultipleFieldsDateAndTimeInputType::readonlyAttributeChanged()
463 spinButtonElement()->releaseCapture();
464 clearButtonElement()->releaseCapture();
465 if (DateTimeEditElement* edit = dateTimeEditElement())
466 edit->readOnlyStateChanged();
469 void BaseMultipleFieldsDateAndTimeInputType::restoreFormControlState(const FormControlState& state)
471 DateTimeEditElement* edit = dateTimeEditElement();
474 DateTimeFieldsState dateTimeFieldsState = DateTimeFieldsState::restoreFormControlState(state);
475 edit->setValueAsDateTimeFieldsState(dateTimeFieldsState);
476 element().setValueInternal(sanitizeValue(edit->value()), DispatchNoEvent);
477 updateClearButtonVisibility();
480 FormControlState BaseMultipleFieldsDateAndTimeInputType::saveFormControlState() const
482 if (DateTimeEditElement* edit = dateTimeEditElement())
483 return edit->valueAsDateTimeFieldsState().saveFormControlState();
484 return FormControlState();
487 void BaseMultipleFieldsDateAndTimeInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
489 InputType::setValue(sanitizedValue, valueChanged, eventBehavior);
490 DateTimeEditElement* edit = dateTimeEditElement();
491 if (valueChanged || (sanitizedValue.isEmpty() && edit && edit->anyEditableFieldsHaveValues())) {
493 element().setNeedsValidityCheck();
497 bool BaseMultipleFieldsDateAndTimeInputType::shouldUseInputMethod() const
502 void BaseMultipleFieldsDateAndTimeInputType::stepAttributeChanged()
507 void BaseMultipleFieldsDateAndTimeInputType::updateView()
509 DateTimeEditElement* edit = dateTimeEditElement();
513 DateTimeEditElement::LayoutParameters layoutParameters(element().locale(), createStepRange(AnyIsDefaultStep));
516 const bool hasValue = parseToDateComponents(element().value(), &date);
518 setMillisecondToDateComponents(layoutParameters.stepRange.minimum().toDouble(), &date);
520 setupLayoutParameters(layoutParameters, date);
522 const AtomicString pattern = edit->fastGetAttribute(HTMLNames::patternAttr);
523 if (!pattern.isEmpty())
524 layoutParameters.dateTimeFormat = pattern;
526 if (!DateTimeFormatValidator().validateFormat(layoutParameters.dateTimeFormat, *this))
527 layoutParameters.dateTimeFormat = layoutParameters.fallbackDateTimeFormat;
530 edit->setValueAsDate(layoutParameters, date);
532 edit->setEmptyValue(layoutParameters, date);
533 updateClearButtonVisibility();
536 void BaseMultipleFieldsDateAndTimeInputType::valueAttributeChanged()
538 if (!element().hasDirtyValue())
542 void BaseMultipleFieldsDateAndTimeInputType::listAttributeTargetChanged()
544 updatePickerIndicatorVisibility();
547 void BaseMultipleFieldsDateAndTimeInputType::updatePickerIndicatorVisibility()
549 if (m_pickerIndicatorIsAlwaysVisible) {
550 showPickerIndicator();
553 if (RuntimeEnabledFeatures::dataListElementEnabled()) {
554 if (element().hasValidDataListOptions())
555 showPickerIndicator();
557 hidePickerIndicator();
561 void BaseMultipleFieldsDateAndTimeInputType::hidePickerIndicator()
563 if (!m_pickerIndicatorIsVisible)
565 m_pickerIndicatorIsVisible = false;
566 ASSERT(pickerIndicatorElement());
567 pickerIndicatorElement()->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
570 void BaseMultipleFieldsDateAndTimeInputType::showPickerIndicator()
572 if (m_pickerIndicatorIsVisible)
574 m_pickerIndicatorIsVisible = true;
575 ASSERT(pickerIndicatorElement());
576 pickerIndicatorElement()->removeInlineStyleProperty(CSSPropertyDisplay);
579 bool BaseMultipleFieldsDateAndTimeInputType::shouldHaveSecondField(const DateComponents& date) const
581 StepRange stepRange = createStepRange(AnyIsDefaultStep);
582 return date.second() || date.millisecond()
583 || !stepRange.minimum().remainder(static_cast<int>(msPerMinute)).isZero()
584 || !stepRange.step().remainder(static_cast<int>(msPerMinute)).isZero();
587 void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectClearButtonOwner()
592 bool BaseMultipleFieldsDateAndTimeInputType::shouldClearButtonRespondToMouseEvents()
594 return !element().isDisabledOrReadOnly() && !element().isRequired();
597 void BaseMultipleFieldsDateAndTimeInputType::clearValue()
599 RefPtr<HTMLInputElement> input(element());
600 input->setValue("", DispatchInputAndChangeEvent);
601 input->updateClearButtonVisibility();
604 void BaseMultipleFieldsDateAndTimeInputType::updateClearButtonVisibility()
606 ClearButtonElement* clearButton = clearButtonElement();
610 if (element().isRequired() || !dateTimeEditElement()->anyEditableFieldsHaveValues()) {
611 clearButton->setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
612 clearButton->setInlineStyleProperty(CSSPropertyPointerEvents, CSSValueNone);
614 clearButton->removeInlineStyleProperty(CSSPropertyOpacity);
615 clearButton->removeInlineStyleProperty(CSSPropertyPointerEvents);
619 } // namespace WebCore