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 "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"
56 class DateTimeFormatValidator : public DateTimeFormat::TokenHandler {
58 DateTimeFormatValidator()
66 , m_hasSecond(false) { }
68 virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
69 virtual void visitLiteral(const String&) OVERRIDE FINAL { }
71 bool validateFormat(const String& format, const BaseMultipleFieldsDateAndTimeInputType&);
84 void DateTimeFormatValidator::visitField(DateTimeFormat::FieldType fieldType, int)
87 case DateTimeFormat::FieldTypeYear:
90 case DateTimeFormat::FieldTypeMonth: // Fallthrough.
91 case DateTimeFormat::FieldTypeMonthStandAlone:
94 case DateTimeFormat::FieldTypeWeekOfYear:
97 case DateTimeFormat::FieldTypeDayOfMonth:
100 case DateTimeFormat::FieldTypePeriod:
103 case DateTimeFormat::FieldTypeHour11: // Fallthrough.
104 case DateTimeFormat::FieldTypeHour12:
107 case DateTimeFormat::FieldTypeHour23: // Fallthrough.
108 case DateTimeFormat::FieldTypeHour24:
112 case DateTimeFormat::FieldTypeMinute:
115 case DateTimeFormat::FieldTypeSecond:
123 bool DateTimeFormatValidator::validateFormat(const String& format, const BaseMultipleFieldsDateAndTimeInputType& inputType)
125 if (!DateTimeFormat::parse(format, *this))
127 return inputType.isValidFormat(m_hasYear, m_hasMonth, m_hasWeek, m_hasDay, m_hasAMPM, m_hasHour, m_hasMinute, m_hasSecond);
130 DateTimeEditElement* BaseMultipleFieldsDateAndTimeInputType::dateTimeEditElement() const
132 return toDateTimeEditElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::dateTimeEdit()));
135 SpinButtonElement* BaseMultipleFieldsDateAndTimeInputType::spinButtonElement() const
137 return toSpinButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
140 ClearButtonElement* BaseMultipleFieldsDateAndTimeInputType::clearButtonElement() const
142 return toClearButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::clearButton()));
145 PickerIndicatorElement* BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorElement() const
147 return toPickerIndicatorElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::pickerIndicator()));
150 inline bool BaseMultipleFieldsDateAndTimeInputType::containsFocusedShadowElement() const
152 return element().userAgentShadowRoot()->contains(element().document().focusedElement());
155 void BaseMultipleFieldsDateAndTimeInputType::didBlurFromControl()
157 // We don't need to call blur(). This function is called when control
160 if (containsFocusedShadowElement())
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();
170 void BaseMultipleFieldsDateAndTimeInputType::didFocusOnControl()
172 // We don't need to call focus(). This function is called when control
175 if (!containsFocusedShadowElement())
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);
182 void BaseMultipleFieldsDateAndTimeInputType::editControlValueChanged()
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();
191 input->setValueInternal(newValue, DispatchNoEvent);
192 input->setNeedsStyleRecalc(SubtreeStyleChange);
193 input->dispatchFormControlInputEvent();
195 input->notifyFormStateChanged();
196 input->updateClearButtonVisibility();
199 bool BaseMultipleFieldsDateAndTimeInputType::hasCustomFocusLogic() const
204 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerDisabled() const
206 return element().isDisabledFormControl();
209 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerReadOnly() const
211 return element().isReadOnly();
214 void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectSpinButtonOwner()
216 if (DateTimeEditElement* edit = dateTimeEditElement())
217 edit->focusIfNoFocus();
220 bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToMouseEvents()
222 return !element().isDisabledOrReadOnly();
225 bool BaseMultipleFieldsDateAndTimeInputType::shouldSpinButtonRespondToWheelEvents()
227 if (!shouldSpinButtonRespondToMouseEvents())
229 if (DateTimeEditElement* edit = dateTimeEditElement())
230 return edit->hasFocusedField();
234 void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepDown()
236 if (DateTimeEditElement* edit = dateTimeEditElement())
240 void BaseMultipleFieldsDateAndTimeInputType::spinButtonStepUp()
242 if (DateTimeEditElement* edit = dateTimeEditElement())
246 void BaseMultipleFieldsDateAndTimeInputType::spinButtonDidReleaseMouseCapture(SpinButtonElement::EventDispatch eventDispatch)
248 if (eventDispatch == SpinButtonElement::EventDispatchAllowed)
249 element().dispatchFormControlChangeEvent();
252 bool BaseMultipleFieldsDateAndTimeInputType::isPickerIndicatorOwnerDisabledOrReadOnly() const
254 return element().isDisabledOrReadOnly();
257 void BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorChooseValue(const String& value)
259 if (element().isValidValue(value)) {
260 element().setValue(value, DispatchInputAndChangeEvent);
264 DateTimeEditElement* edit = this->dateTimeEditElement();
267 EventQueueScope scope;
270 if (date.parseDate(value, 0, end) && end == value.length())
271 edit->setOnlyYearMonthDay(date);
272 element().dispatchFormControlChangeEvent();
275 void BaseMultipleFieldsDateAndTimeInputType::pickerIndicatorChooseValue(double value)
277 ASSERT(std::isfinite(value) || std::isnan(value));
278 if (std::isnan(value))
279 element().setValue(emptyString(), DispatchInputAndChangeEvent);
281 element().setValueAsNumber(value, ASSERT_NO_EXCEPTION, DispatchInputAndChangeEvent);
284 Element& BaseMultipleFieldsDateAndTimeInputType::pickerOwnerElement() const
289 bool BaseMultipleFieldsDateAndTimeInputType::setupDateTimeChooserParameters(DateTimeChooserParameters& parameters)
291 return element().setupDateTimeChooserParameters(parameters);
294 BaseMultipleFieldsDateAndTimeInputType::BaseMultipleFieldsDateAndTimeInputType(HTMLInputElement& element)
295 : BaseDateAndTimeInputType(element)
296 , m_isDestroyingShadowSubtree(false)
297 , m_pickerIndicatorIsVisible(false)
298 , m_pickerIndicatorIsAlwaysVisible(false)
302 BaseMultipleFieldsDateAndTimeInputType::~BaseMultipleFieldsDateAndTimeInputType()
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();
316 String BaseMultipleFieldsDateAndTimeInputType::badInputText() const
318 return locale().queryString(blink::WebLocalizedString::ValidationBadInputForDateTime);
321 void BaseMultipleFieldsDateAndTimeInputType::blur()
323 if (DateTimeEditElement* edit = dateTimeEditElement())
327 PassRefPtr<RenderStyle> BaseMultipleFieldsDateAndTimeInputType::customStyleForRenderer(PassRefPtr<RenderStyle> originalStyle)
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)
335 TextDirection contentDirection = computedTextDirection();
336 if (originalStyle->direction() == contentDirection && originalDisplay == newDisplay)
337 return originalStyle;
339 RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
340 style->setDirection(contentDirection);
341 style->setDisplay(newDisplay);
343 return style.release();
346 void BaseMultipleFieldsDateAndTimeInputType::createShadowSubtree()
348 ASSERT(element().shadow());
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());
356 Document& document = element().document();
357 ContainerNode* container = element().userAgentShadowRoot();
359 container->appendChild(DateTimeEditElement::create(document, *this));
360 element().updateView();
361 container->appendChild(ClearButtonElement::create(document, *this));
362 container->appendChild(SpinButtonElement::create(document, *this));
364 if (RenderTheme::theme().supportsCalendarPicker(formControlType()))
365 m_pickerIndicatorIsAlwaysVisible = true;
366 container->appendChild(PickerIndicatorElement::create(document, *this));
367 m_pickerIndicatorIsVisible = true;
368 updatePickerIndicatorVisibility();
371 void BaseMultipleFieldsDateAndTimeInputType::destroyShadowSubtree()
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();
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())
389 BaseDateAndTimeInputType::destroyShadowSubtree();
390 m_isDestroyingShadowSubtree = false;
393 void BaseMultipleFieldsDateAndTimeInputType::handleFocusInEvent(Element* oldFocusedElement, FocusType type)
395 DateTimeEditElement* edit = dateTimeEditElement();
396 if (!edit || m_isDestroyingShadowSubtree)
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);
404 edit->focusByOwner();
408 void BaseMultipleFieldsDateAndTimeInputType::forwardEvent(Event* event)
410 if (SpinButtonElement* element = spinButtonElement()) {
411 element->forwardEvent(event);
412 if (event->defaultHandled())
416 if (DateTimeEditElement* edit = dateTimeEditElement())
417 edit->defaultEventHandler(event);
420 void BaseMultipleFieldsDateAndTimeInputType::disabledAttributeChanged()
422 spinButtonElement()->releaseCapture();
423 clearButtonElement()->releaseCapture();
424 if (DateTimeEditElement* edit = dateTimeEditElement())
425 edit->disabledStateChanged();
428 void BaseMultipleFieldsDateAndTimeInputType::requiredAttributeChanged()
430 clearButtonElement()->releaseCapture();
431 updateClearButtonVisibility();
434 void BaseMultipleFieldsDateAndTimeInputType::handleKeydownEvent(KeyboardEvent* event)
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();
446 bool BaseMultipleFieldsDateAndTimeInputType::hasBadInput() const
448 DateTimeEditElement* edit = dateTimeEditElement();
449 return element().value().isEmpty() && edit && edit->anyEditableFieldsHaveValues();
452 AtomicString BaseMultipleFieldsDateAndTimeInputType::localeIdentifier() const
454 return element().computeInheritedLanguage();
457 void BaseMultipleFieldsDateAndTimeInputType::editControlDidChangeValueByKeyboard()
459 element().dispatchFormControlChangeEvent();
462 void BaseMultipleFieldsDateAndTimeInputType::minOrMaxAttributeChanged()
467 void BaseMultipleFieldsDateAndTimeInputType::readonlyAttributeChanged()
469 spinButtonElement()->releaseCapture();
470 clearButtonElement()->releaseCapture();
471 if (DateTimeEditElement* edit = dateTimeEditElement())
472 edit->readOnlyStateChanged();
475 void BaseMultipleFieldsDateAndTimeInputType::restoreFormControlState(const FormControlState& state)
477 DateTimeEditElement* edit = dateTimeEditElement();
480 DateTimeFieldsState dateTimeFieldsState = DateTimeFieldsState::restoreFormControlState(state);
481 edit->setValueAsDateTimeFieldsState(dateTimeFieldsState);
482 element().setValueInternal(sanitizeValue(edit->value()), DispatchNoEvent);
483 updateClearButtonVisibility();
486 FormControlState BaseMultipleFieldsDateAndTimeInputType::saveFormControlState() const
488 if (DateTimeEditElement* edit = dateTimeEditElement())
489 return edit->valueAsDateTimeFieldsState().saveFormControlState();
490 return FormControlState();
493 void BaseMultipleFieldsDateAndTimeInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
495 InputType::setValue(sanitizedValue, valueChanged, eventBehavior);
496 DateTimeEditElement* edit = dateTimeEditElement();
497 if (valueChanged || (sanitizedValue.isEmpty() && edit && edit->anyEditableFieldsHaveValues())) {
498 element().updateView();
499 element().setNeedsValidityCheck();
503 void BaseMultipleFieldsDateAndTimeInputType::stepAttributeChanged()
508 void BaseMultipleFieldsDateAndTimeInputType::updateView()
510 DateTimeEditElement* edit = dateTimeEditElement();
514 DateTimeEditElement::LayoutParameters layoutParameters(element().locale(), createStepRange(AnyIsDefaultStep));
517 bool hasValue = false;
518 if (!element().suggestedValue().isNull())
519 hasValue = parseToDateComponents(element().suggestedValue(), &date);
521 hasValue = parseToDateComponents(element().value(), &date);
523 setMillisecondToDateComponents(layoutParameters.stepRange.minimum().toDouble(), &date);
525 setupLayoutParameters(layoutParameters, date);
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;
533 if (!DateTimeFormatValidator().validateFormat(layoutParameters.dateTimeFormat, *this))
534 layoutParameters.dateTimeFormat = layoutParameters.fallbackDateTimeFormat;
537 edit->setValueAsDate(layoutParameters, date);
539 edit->setEmptyValue(layoutParameters, date);
540 updateClearButtonVisibility();
543 void BaseMultipleFieldsDateAndTimeInputType::valueAttributeChanged()
545 if (!element().hasDirtyValue())
549 void BaseMultipleFieldsDateAndTimeInputType::listAttributeTargetChanged()
551 updatePickerIndicatorVisibility();
554 void BaseMultipleFieldsDateAndTimeInputType::updatePickerIndicatorVisibility()
556 if (m_pickerIndicatorIsAlwaysVisible) {
557 showPickerIndicator();
560 if (element().hasValidDataListOptions())
561 showPickerIndicator();
563 hidePickerIndicator();
566 void BaseMultipleFieldsDateAndTimeInputType::hidePickerIndicator()
568 if (!m_pickerIndicatorIsVisible)
570 m_pickerIndicatorIsVisible = false;
571 ASSERT(pickerIndicatorElement());
572 pickerIndicatorElement()->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
575 void BaseMultipleFieldsDateAndTimeInputType::showPickerIndicator()
577 if (m_pickerIndicatorIsVisible)
579 m_pickerIndicatorIsVisible = true;
580 ASSERT(pickerIndicatorElement());
581 pickerIndicatorElement()->removeInlineStyleProperty(CSSPropertyDisplay);
584 bool BaseMultipleFieldsDateAndTimeInputType::shouldHaveSecondField(const DateComponents& date) const
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();
592 void BaseMultipleFieldsDateAndTimeInputType::focusAndSelectClearButtonOwner()
597 bool BaseMultipleFieldsDateAndTimeInputType::shouldClearButtonRespondToMouseEvents()
599 return !element().isDisabledOrReadOnly() && !element().isRequired();
602 void BaseMultipleFieldsDateAndTimeInputType::clearValue()
604 RefPtrWillBeRawPtr<HTMLInputElement> input(element());
605 input->setValue("", DispatchInputAndChangeEvent);
606 input->updateClearButtonVisibility();
609 void BaseMultipleFieldsDateAndTimeInputType::updateClearButtonVisibility()
611 ClearButtonElement* clearButton = clearButtonElement();
615 if (element().isRequired() || !dateTimeEditElement()->anyEditableFieldsHaveValues()) {
616 clearButton->setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
617 clearButton->setInlineStyleProperty(CSSPropertyPointerEvents, CSSValueNone);
619 clearButton->removeInlineStyleProperty(CSSPropertyOpacity);
620 clearButton->removeInlineStyleProperty(CSSPropertyPointerEvents);
624 TextDirection BaseMultipleFieldsDateAndTimeInputType::computedTextDirection()
626 return element().locale().isRTL() ? RTL : LTR;
629 AXObject* BaseMultipleFieldsDateAndTimeInputType::popupRootAXObject()
631 if (PickerIndicatorElement* picker = pickerIndicatorElement())
632 return picker->popupRootAXObject();