2 * Copyright (C) 2012 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
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
28 #include "core/html/shadow/DateTimeEditElement.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "core/HTMLNames.h"
32 #include "core/dom/Document.h"
33 #include "core/dom/Text.h"
34 #include "core/events/MouseEvent.h"
35 #include "core/html/forms/DateTimeFieldsState.h"
36 #include "core/html/shadow/DateTimeFieldElements.h"
37 #include "core/html/shadow/ShadowElementNames.h"
38 #include "core/rendering/style/RenderStyle.h"
39 #include "core/rendering/style/StyleInheritedData.h"
40 #include "platform/fonts/FontCache.h"
41 #include "platform/text/DateTimeFormat.h"
42 #include "platform/text/PlatformLocale.h"
43 #include "wtf/DateMath.h"
47 using namespace HTMLNames;
48 using namespace WTF::Unicode;
50 class DateTimeEditBuilder : private DateTimeFormat::TokenHandler {
51 WTF_MAKE_NONCOPYABLE(DateTimeEditBuilder);
54 // The argument objects must be alive until this object dies.
55 DateTimeEditBuilder(DateTimeEditElement&, const DateTimeEditElement::LayoutParameters&, const DateComponents&);
57 bool build(const String&);
60 bool needMillisecondField() const;
61 bool shouldAMPMFieldDisabled() const;
62 bool shouldDayOfMonthFieldDisabled() const;
63 bool shouldHourFieldDisabled() const;
64 bool shouldMillisecondFieldDisabled() const;
65 bool shouldMinuteFieldDisabled() const;
66 bool shouldSecondFieldDisabled() const;
67 bool shouldYearFieldDisabled() const;
68 inline const StepRange& stepRange() const { return m_parameters.stepRange; }
69 DateTimeNumericFieldElement::Step createStep(double msPerFieldUnit, double msPerFieldSize) const;
71 // DateTimeFormat::TokenHandler functions.
72 virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
73 virtual void visitLiteral(const String&) OVERRIDE FINAL;
75 DateTimeEditElement& m_editElement;
76 const DateComponents m_dateValue;
77 const DateTimeEditElement::LayoutParameters& m_parameters;
78 DateTimeNumericFieldElement::Range m_dayRange;
79 DateTimeNumericFieldElement::Range m_hour23Range;
80 DateTimeNumericFieldElement::Range m_minuteRange;
81 DateTimeNumericFieldElement::Range m_secondRange;
82 DateTimeNumericFieldElement::Range m_millisecondRange;
85 DateTimeEditBuilder::DateTimeEditBuilder(DateTimeEditElement& elemnt, const DateTimeEditElement::LayoutParameters& layoutParameters, const DateComponents& dateValue)
86 : m_editElement(elemnt)
87 , m_dateValue(dateValue)
88 , m_parameters(layoutParameters)
90 , m_hour23Range(0, 23)
91 , m_minuteRange(0, 59)
92 , m_secondRange(0, 59)
93 , m_millisecondRange(0, 999)
95 if (m_dateValue.type() == DateComponents::Date || m_dateValue.type() == DateComponents::DateTimeLocal) {
96 if (m_parameters.minimum.type() != DateComponents::Invalid
97 && m_parameters.maximum.type() != DateComponents::Invalid
98 && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
99 && m_parameters.minimum.month() == m_parameters.maximum.month()
100 && m_parameters.minimum.monthDay() <= m_parameters.maximum.monthDay()) {
101 m_dayRange.minimum = m_parameters.minimum.monthDay();
102 m_dayRange.maximum = m_parameters.maximum.monthDay();
106 if (m_dateValue.type() == DateComponents::Time || m_dayRange.isSingleton()) {
107 if (m_parameters.minimum.type() != DateComponents::Invalid
108 && m_parameters.maximum.type() != DateComponents::Invalid
109 && m_parameters.minimum.hour() <= m_parameters.maximum.hour()) {
110 m_hour23Range.minimum = m_parameters.minimum.hour();
111 m_hour23Range.maximum = m_parameters.maximum.hour();
115 if (m_hour23Range.isSingleton() && m_parameters.minimum.minute() <= m_parameters.maximum.minute()) {
116 m_minuteRange.minimum = m_parameters.minimum.minute();
117 m_minuteRange.maximum = m_parameters.maximum.minute();
119 if (m_minuteRange.isSingleton() && m_parameters.minimum.second() <= m_parameters.maximum.second()) {
120 m_secondRange.minimum = m_parameters.minimum.second();
121 m_secondRange.maximum = m_parameters.maximum.second();
123 if (m_secondRange.isSingleton() && m_parameters.minimum.millisecond() <= m_parameters.maximum.millisecond()) {
124 m_millisecondRange.minimum = m_parameters.minimum.millisecond();
125 m_millisecondRange.maximum = m_parameters.maximum.millisecond();
129 bool DateTimeEditBuilder::build(const String& formatString)
131 m_editElement.resetFields();
132 return DateTimeFormat::parse(formatString, *this);
135 bool DateTimeEditBuilder::needMillisecondField() const
137 return m_dateValue.millisecond()
138 || !stepRange().minimum().remainder(static_cast<int>(msPerSecond)).isZero()
139 || !stepRange().step().remainder(static_cast<int>(msPerSecond)).isZero();
142 void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int count)
144 const int countForAbbreviatedMonth = 3;
145 const int countForFullMonth = 4;
146 const int countForNarrowMonth = 5;
147 Document& document = m_editElement.document();
150 case DateTimeFormat::FieldTypeDayOfMonth: {
151 RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeDayFieldElement::create(document, m_editElement, m_parameters.placeholderForDay, m_dayRange);
152 m_editElement.addField(field);
153 if (shouldDayOfMonthFieldDisabled()) {
154 field->setValueAsDate(m_dateValue);
155 field->setDisabled();
160 case DateTimeFormat::FieldTypeHour11: {
161 DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerHour * 12);
162 RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeHour11FieldElement::create(document, m_editElement, m_hour23Range, step);
163 m_editElement.addField(field);
164 if (shouldHourFieldDisabled()) {
165 field->setValueAsDate(m_dateValue);
166 field->setDisabled();
171 case DateTimeFormat::FieldTypeHour12: {
172 DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerHour * 12);
173 RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeHour12FieldElement::create(document, m_editElement, m_hour23Range, step);
174 m_editElement.addField(field);
175 if (shouldHourFieldDisabled()) {
176 field->setValueAsDate(m_dateValue);
177 field->setDisabled();
182 case DateTimeFormat::FieldTypeHour23: {
183 DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerDay);
184 RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeHour23FieldElement::create(document, m_editElement, m_hour23Range, step);
185 m_editElement.addField(field);
186 if (shouldHourFieldDisabled()) {
187 field->setValueAsDate(m_dateValue);
188 field->setDisabled();
193 case DateTimeFormat::FieldTypeHour24: {
194 DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerDay);
195 RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeHour24FieldElement::create(document, m_editElement, m_hour23Range, step);
196 m_editElement.addField(field);
197 if (shouldHourFieldDisabled()) {
198 field->setValueAsDate(m_dateValue);
199 field->setDisabled();
204 case DateTimeFormat::FieldTypeMinute: {
205 DateTimeNumericFieldElement::Step step = createStep(msPerMinute, msPerHour);
206 RefPtrWillBeRawPtr<DateTimeNumericFieldElement> field = DateTimeMinuteFieldElement::create(document, m_editElement, m_minuteRange, step);
207 m_editElement.addField(field);
208 if (shouldMinuteFieldDisabled()) {
209 field->setValueAsDate(m_dateValue);
210 field->setDisabled();
215 case DateTimeFormat::FieldTypeMonth: // Fallthrough.
216 case DateTimeFormat::FieldTypeMonthStandAlone: {
217 int minMonth = 0, maxMonth = 11;
218 if (m_parameters.minimum.type() != DateComponents::Invalid
219 && m_parameters.maximum.type() != DateComponents::Invalid
220 && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
221 && m_parameters.minimum.month() <= m_parameters.maximum.month()) {
222 minMonth = m_parameters.minimum.month();
223 maxMonth = m_parameters.maximum.month();
225 RefPtrWillBeRawPtr<DateTimeFieldElement> field;
227 case countForNarrowMonth: // Fallthrough.
228 case countForAbbreviatedMonth:
229 field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.shortMonthLabels() : m_parameters.locale.shortStandAloneMonthLabels(), minMonth, maxMonth);
231 case countForFullMonth:
232 field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.monthLabels() : m_parameters.locale.standAloneMonthLabels(), minMonth, maxMonth);
235 field = DateTimeMonthFieldElement::create(document, m_editElement, m_parameters.placeholderForMonth, DateTimeNumericFieldElement::Range(minMonth + 1, maxMonth + 1));
238 m_editElement.addField(field);
239 if (minMonth == maxMonth && minMonth == m_dateValue.month() && m_dateValue.type() != DateComponents::Month) {
240 field->setValueAsDate(m_dateValue);
241 field->setDisabled();
246 case DateTimeFormat::FieldTypePeriod: {
247 RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeAMPMFieldElement::create(document, m_editElement, m_parameters.locale.timeAMPMLabels());
248 m_editElement.addField(field);
249 if (shouldAMPMFieldDisabled()) {
250 field->setValueAsDate(m_dateValue);
251 field->setDisabled();
256 case DateTimeFormat::FieldTypeSecond: {
257 DateTimeNumericFieldElement::Step step = createStep(msPerSecond, msPerMinute);
258 RefPtrWillBeRawPtr<DateTimeNumericFieldElement> field = DateTimeSecondFieldElement::create(document, m_editElement, m_secondRange, step);
259 m_editElement.addField(field);
260 if (shouldSecondFieldDisabled()) {
261 field->setValueAsDate(m_dateValue);
262 field->setDisabled();
265 if (needMillisecondField()) {
266 visitLiteral(m_parameters.locale.localizedDecimalSeparator());
267 visitField(DateTimeFormat::FieldTypeFractionalSecond, 3);
272 case DateTimeFormat::FieldTypeFractionalSecond: {
273 DateTimeNumericFieldElement::Step step = createStep(1, msPerSecond);
274 RefPtrWillBeRawPtr<DateTimeNumericFieldElement> field = DateTimeMillisecondFieldElement::create(document, m_editElement, m_millisecondRange, step);
275 m_editElement.addField(field);
276 if (shouldMillisecondFieldDisabled()) {
277 field->setValueAsDate(m_dateValue);
278 field->setDisabled();
283 case DateTimeFormat::FieldTypeWeekOfYear: {
284 DateTimeNumericFieldElement::Range range(DateComponents::minimumWeekNumber, DateComponents::maximumWeekNumber);
285 if (m_parameters.minimum.type() != DateComponents::Invalid
286 && m_parameters.maximum.type() != DateComponents::Invalid
287 && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
288 && m_parameters.minimum.week() <= m_parameters.maximum.week()) {
289 range.minimum = m_parameters.minimum.week();
290 range.maximum = m_parameters.maximum.week();
292 m_editElement.addField(DateTimeWeekFieldElement::create(document, m_editElement, range));
296 case DateTimeFormat::FieldTypeYear: {
297 DateTimeYearFieldElement::Parameters yearParams;
298 if (m_parameters.minimum.type() == DateComponents::Invalid) {
299 yearParams.minimumYear = DateComponents::minimumYear();
300 yearParams.minIsSpecified = false;
302 yearParams.minimumYear = m_parameters.minimum.fullYear();
303 yearParams.minIsSpecified = true;
305 if (m_parameters.maximum.type() == DateComponents::Invalid) {
306 yearParams.maximumYear = DateComponents::maximumYear();
307 yearParams.maxIsSpecified = false;
309 yearParams.maximumYear = m_parameters.maximum.fullYear();
310 yearParams.maxIsSpecified = true;
312 if (yearParams.minimumYear > yearParams.maximumYear) {
313 std::swap(yearParams.minimumYear, yearParams.maximumYear);
314 std::swap(yearParams.minIsSpecified, yearParams.maxIsSpecified);
316 yearParams.placeholder = m_parameters.placeholderForYear;
317 RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeYearFieldElement::create(document, m_editElement, yearParams);
318 m_editElement.addField(field);
319 if (shouldYearFieldDisabled()) {
320 field->setValueAsDate(m_dateValue);
321 field->setDisabled();
331 bool DateTimeEditBuilder::shouldAMPMFieldDisabled() const
333 return shouldHourFieldDisabled()
334 || (m_hour23Range.minimum < 12 && m_hour23Range.maximum < 12 && m_dateValue.hour() < 12)
335 || (m_hour23Range.minimum >= 12 && m_hour23Range.maximum >= 12 && m_dateValue.hour() >= 12);
338 bool DateTimeEditBuilder::shouldDayOfMonthFieldDisabled() const
340 return m_dayRange.isSingleton() && m_dayRange.minimum == m_dateValue.monthDay() && m_dateValue.type() != DateComponents::Date;
343 bool DateTimeEditBuilder::shouldHourFieldDisabled() const
345 if (m_hour23Range.isSingleton() && m_hour23Range.minimum == m_dateValue.hour()
346 && !(shouldMinuteFieldDisabled() && shouldSecondFieldDisabled() && shouldMillisecondFieldDisabled()))
349 if (m_dateValue.type() == DateComponents::Time)
351 ASSERT(m_dateValue.type() == DateComponents::DateTimeLocal);
353 if (shouldDayOfMonthFieldDisabled()) {
354 ASSERT(m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear());
355 ASSERT(m_parameters.minimum.month() == m_parameters.maximum.month());
359 const Decimal decimalMsPerDay(static_cast<int>(msPerDay));
360 Decimal hourPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerDay) / static_cast<int>(msPerHour)).floor();
361 return hourPartOfMinimum == m_dateValue.hour() && stepRange().step().remainder(decimalMsPerDay).isZero();
364 bool DateTimeEditBuilder::shouldMillisecondFieldDisabled() const
366 if (m_millisecondRange.isSingleton() && m_millisecondRange.minimum == m_dateValue.millisecond())
369 const Decimal decimalMsPerSecond(static_cast<int>(msPerSecond));
370 return stepRange().minimum().abs().remainder(decimalMsPerSecond) == m_dateValue.millisecond() && stepRange().step().remainder(decimalMsPerSecond).isZero();
373 bool DateTimeEditBuilder::shouldMinuteFieldDisabled() const
375 if (m_minuteRange.isSingleton() && m_minuteRange.minimum == m_dateValue.minute())
378 const Decimal decimalMsPerHour(static_cast<int>(msPerHour));
379 Decimal minutePartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerHour) / static_cast<int>(msPerMinute)).floor();
380 return minutePartOfMinimum == m_dateValue.minute() && stepRange().step().remainder(decimalMsPerHour).isZero();
383 bool DateTimeEditBuilder::shouldSecondFieldDisabled() const
385 if (m_secondRange.isSingleton() && m_secondRange.minimum == m_dateValue.second())
388 const Decimal decimalMsPerMinute(static_cast<int>(msPerMinute));
389 Decimal secondPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerMinute) / static_cast<int>(msPerSecond)).floor();
390 return secondPartOfMinimum == m_dateValue.second() && stepRange().step().remainder(decimalMsPerMinute).isZero();
393 bool DateTimeEditBuilder::shouldYearFieldDisabled() const
395 return m_parameters.minimum.type() != DateComponents::Invalid
396 && m_parameters.maximum.type() != DateComponents::Invalid
397 && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
398 && m_parameters.minimum.fullYear() == m_dateValue.fullYear();
401 void DateTimeEditBuilder::visitLiteral(const String& text)
403 DEFINE_STATIC_LOCAL(AtomicString, textPseudoId, ("-webkit-datetime-edit-text", AtomicString::ConstructFromLiteral));
404 ASSERT(text.length());
405 RefPtrWillBeRawPtr<HTMLDivElement> element = HTMLDivElement::create(m_editElement.document());
406 element->setShadowPseudoId(textPseudoId);
407 if (m_parameters.locale.isRTL() && text.length()) {
408 Direction dir = direction(text[0]);
409 if (dir == SegmentSeparator || dir == WhiteSpaceNeutral || dir == OtherNeutral)
410 element->appendChild(Text::create(m_editElement.document(), String(&rightToLeftMark, 1)));
412 element->appendChild(Text::create(m_editElement.document(), text));
413 m_editElement.fieldsWrapperElement()->appendChild(element);
416 DateTimeNumericFieldElement::Step DateTimeEditBuilder::createStep(double msPerFieldUnit, double msPerFieldSize) const
418 const Decimal msPerFieldUnitDecimal(static_cast<int>(msPerFieldUnit));
419 const Decimal msPerFieldSizeDecimal(static_cast<int>(msPerFieldSize));
420 Decimal stepMilliseconds = stepRange().step();
421 ASSERT(!msPerFieldUnitDecimal.isZero());
422 ASSERT(!msPerFieldSizeDecimal.isZero());
423 ASSERT(!stepMilliseconds.isZero());
425 DateTimeNumericFieldElement::Step step(1, 0);
427 if (stepMilliseconds.remainder(msPerFieldSizeDecimal).isZero())
428 stepMilliseconds = msPerFieldSizeDecimal;
430 if (msPerFieldSizeDecimal.remainder(stepMilliseconds).isZero() && stepMilliseconds.remainder(msPerFieldUnitDecimal).isZero()) {
431 step.step = static_cast<int>((stepMilliseconds / msPerFieldUnitDecimal).toDouble());
432 step.stepBase = static_cast<int>((stepRange().stepBase() / msPerFieldUnitDecimal).floor().remainder(msPerFieldSizeDecimal / msPerFieldUnitDecimal).toDouble());
437 // ----------------------------
439 DateTimeEditElement::EditControlOwner::~EditControlOwner()
443 DateTimeEditElement::DateTimeEditElement(Document& document, EditControlOwner& editControlOwner)
444 : HTMLDivElement(document)
445 , m_editControlOwner(&editControlOwner)
447 setHasCustomStyleCallbacks();
450 DateTimeEditElement::~DateTimeEditElement()
453 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
454 m_fields[fieldIndex]->removeEventHandler();
458 void DateTimeEditElement::trace(Visitor* visitor)
461 visitor->trace(m_fields);
463 visitor->trace(m_editControlOwner);
464 HTMLDivElement::trace(visitor);
467 inline Element* DateTimeEditElement::fieldsWrapperElement() const
469 ASSERT(firstChild());
470 return toElement(firstChild());
473 void DateTimeEditElement::addField(PassRefPtrWillBeRawPtr<DateTimeFieldElement> field)
475 if (m_fields.size() == m_fields.capacity())
477 m_fields.append(field.get());
478 fieldsWrapperElement()->appendChild(field);
481 bool DateTimeEditElement::anyEditableFieldsHaveValues() const
483 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
484 if (!m_fields[fieldIndex]->isDisabled() && m_fields[fieldIndex]->hasValue())
490 void DateTimeEditElement::blurByOwner()
492 if (DateTimeFieldElement* field = focusedField())
496 PassRefPtrWillBeRawPtr<DateTimeEditElement> DateTimeEditElement::create(Document& document, EditControlOwner& editControlOwner)
498 RefPtrWillBeRawPtr<DateTimeEditElement> container = adoptRefWillBeNoop(new DateTimeEditElement(document, editControlOwner));
499 container->setShadowPseudoId(AtomicString("-webkit-datetime-edit", AtomicString::ConstructFromLiteral));
500 container->setAttribute(idAttr, ShadowElementNames::dateTimeEdit());
501 return container.release();
504 PassRefPtr<RenderStyle> DateTimeEditElement::customStyleForRenderer()
506 // FIXME: This is a kind of layout. We might want to introduce new renderer.
507 FontCachePurgePreventer fontCachePurgePreventer;
508 RefPtr<RenderStyle> originalStyle = originalStyleForRenderer();
509 RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
511 for (Node* child = fieldsWrapperElement()->firstChild(); child; child = child->nextSibling()) {
512 if (!child->isElementNode())
514 Element* childElement = toElement(child);
515 if (childElement->isDateTimeFieldElement()) {
516 // We need to pass the Font of this element because child elements
517 // can't resolve inherited style at this timing.
518 width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(style->font());
520 // ::-webkit-datetime-edit-text case. It has no
521 // border/padding/margin in html.css.
522 width += style->font().width(childElement->textContent());
525 style->setWidth(Length(ceilf(width), Fixed));
527 return style.release();
530 void DateTimeEditElement::didBlurFromField()
532 if (m_editControlOwner)
533 m_editControlOwner->didBlurFromControl();
536 void DateTimeEditElement::didFocusOnField()
538 if (m_editControlOwner)
539 m_editControlOwner->didFocusOnControl();
542 void DateTimeEditElement::disabledStateChanged()
547 DateTimeFieldElement* DateTimeEditElement::fieldAt(size_t fieldIndex) const
549 return fieldIndex < m_fields.size() ? m_fields[fieldIndex].get() : 0;
552 size_t DateTimeEditElement::fieldIndexOf(const DateTimeFieldElement& field) const
554 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
555 if (m_fields[fieldIndex] == &field)
558 return invalidFieldIndex;
561 void DateTimeEditElement::focusIfNoFocus()
563 if (focusedFieldIndex() != invalidFieldIndex)
565 focusOnNextFocusableField(0);
568 void DateTimeEditElement::focusByOwner(Element* oldFocusedElement)
570 if (oldFocusedElement && oldFocusedElement->isDateTimeFieldElement()) {
571 DateTimeFieldElement* oldFocusedField = static_cast<DateTimeFieldElement*>(oldFocusedElement);
572 size_t index = fieldIndexOf(*oldFocusedField);
573 if (index != invalidFieldIndex && oldFocusedField->isFocusable()) {
574 oldFocusedField->focus();
578 focusOnNextFocusableField(0);
581 DateTimeFieldElement* DateTimeEditElement::focusedField() const
583 return fieldAt(focusedFieldIndex());
586 size_t DateTimeEditElement::focusedFieldIndex() const
588 Element* const focusedFieldElement = document().focusedElement();
589 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
590 if (m_fields[fieldIndex] == focusedFieldElement)
593 return invalidFieldIndex;
596 void DateTimeEditElement::fieldValueChanged()
598 if (m_editControlOwner)
599 m_editControlOwner->editControlValueChanged();
602 bool DateTimeEditElement::focusOnNextFocusableField(size_t startIndex)
604 for (size_t fieldIndex = startIndex; fieldIndex < m_fields.size(); ++fieldIndex) {
605 if (m_fields[fieldIndex]->isFocusable()) {
606 m_fields[fieldIndex]->focus();
613 bool DateTimeEditElement::focusOnNextField(const DateTimeFieldElement& field)
615 const size_t startFieldIndex = fieldIndexOf(field);
616 if (startFieldIndex == invalidFieldIndex)
618 return focusOnNextFocusableField(startFieldIndex + 1);
621 bool DateTimeEditElement::focusOnPreviousField(const DateTimeFieldElement& field)
623 const size_t startFieldIndex = fieldIndexOf(field);
624 if (startFieldIndex == invalidFieldIndex)
626 size_t fieldIndex = startFieldIndex;
627 while (fieldIndex > 0) {
629 if (m_fields[fieldIndex]->isFocusable()) {
630 m_fields[fieldIndex]->focus();
637 bool DateTimeEditElement::isDateTimeEditElement() const
642 bool DateTimeEditElement::isDisabled() const
644 return m_editControlOwner && m_editControlOwner->isEditControlOwnerDisabled();
647 bool DateTimeEditElement::isFieldOwnerDisabled() const
652 bool DateTimeEditElement::isFieldOwnerReadOnly() const
657 bool DateTimeEditElement::isReadOnly() const
659 return m_editControlOwner && m_editControlOwner->isEditControlOwnerReadOnly();
662 void DateTimeEditElement::layout(const LayoutParameters& layoutParameters, const DateComponents& dateValue)
664 DEFINE_STATIC_LOCAL(AtomicString, fieldsWrapperPseudoId, ("-webkit-datetime-edit-fields-wrapper", AtomicString::ConstructFromLiteral));
666 RefPtrWillBeRawPtr<HTMLDivElement> element = HTMLDivElement::create(document());
667 element->setShadowPseudoId(fieldsWrapperPseudoId);
668 appendChild(element.get());
670 Element* fieldsWrapper = fieldsWrapperElement();
672 size_t focusedFieldIndex = this->focusedFieldIndex();
673 DateTimeFieldElement* const focusedField = fieldAt(focusedFieldIndex);
674 const AtomicString focusedFieldId = focusedField ? focusedField->shadowPseudoId() : nullAtom;
676 DateTimeEditBuilder builder(*this, layoutParameters, dateValue);
677 Node* lastChildToBeRemoved = fieldsWrapper->lastChild();
678 if (!builder.build(layoutParameters.dateTimeFormat) || m_fields.isEmpty()) {
679 lastChildToBeRemoved = fieldsWrapper->lastChild();
680 builder.build(layoutParameters.fallbackDateTimeFormat);
683 if (focusedFieldIndex != invalidFieldIndex) {
684 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
685 if (m_fields[fieldIndex]->shadowPseudoId() == focusedFieldId) {
686 focusedFieldIndex = fieldIndex;
690 if (DateTimeFieldElement* field = fieldAt(std::min(focusedFieldIndex, m_fields.size() - 1)))
694 if (lastChildToBeRemoved) {
695 for (Node* childNode = fieldsWrapper->firstChild(); childNode; childNode = fieldsWrapper->firstChild()) {
696 fieldsWrapper->removeChild(childNode);
697 if (childNode == lastChildToBeRemoved)
700 setNeedsStyleRecalc(SubtreeStyleChange);
704 AtomicString DateTimeEditElement::localeIdentifier() const
706 return m_editControlOwner ? m_editControlOwner->localeIdentifier() : nullAtom;
709 void DateTimeEditElement::fieldDidChangeValueByKeyboard()
711 if (m_editControlOwner)
712 m_editControlOwner->editControlDidChangeValueByKeyboard();
715 void DateTimeEditElement::readOnlyStateChanged()
720 void DateTimeEditElement::resetFields()
722 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
723 m_fields[fieldIndex]->removeEventHandler();
727 void DateTimeEditElement::defaultEventHandler(Event* event)
729 // In case of control owner forward event to control, e.g. DOM
730 // dispatchEvent method.
731 if (DateTimeFieldElement* field = focusedField()) {
732 field->defaultEventHandler(event);
733 if (event->defaultHandled())
737 HTMLDivElement::defaultEventHandler(event);
740 void DateTimeEditElement::setValueAsDate(const LayoutParameters& layoutParameters, const DateComponents& date)
742 layout(layoutParameters, date);
743 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
744 m_fields[fieldIndex]->setValueAsDate(date);
747 void DateTimeEditElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
749 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
750 m_fields[fieldIndex]->setValueAsDateTimeFieldsState(dateTimeFieldsState);
753 void DateTimeEditElement::setEmptyValue(const LayoutParameters& layoutParameters, const DateComponents& dateForReadOnlyField)
755 layout(layoutParameters, dateForReadOnlyField);
756 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
757 m_fields[fieldIndex]->setEmptyValue(DateTimeFieldElement::DispatchNoEvent);
760 bool DateTimeEditElement::hasFocusedField()
762 return focusedFieldIndex() != invalidFieldIndex;
765 void DateTimeEditElement::setOnlyYearMonthDay(const DateComponents& date)
767 ASSERT(date.type() == DateComponents::Date);
769 if (!m_editControlOwner)
772 DateTimeFieldsState dateTimeFieldsState = valueAsDateTimeFieldsState();
773 dateTimeFieldsState.setYear(date.fullYear());
774 dateTimeFieldsState.setMonth(date.month() + 1);
775 dateTimeFieldsState.setDayOfMonth(date.monthDay());
776 setValueAsDateTimeFieldsState(dateTimeFieldsState);
777 m_editControlOwner->editControlValueChanged();
780 void DateTimeEditElement::stepDown()
782 if (DateTimeFieldElement* const field = focusedField())
786 void DateTimeEditElement::stepUp()
788 if (DateTimeFieldElement* const field = focusedField())
792 void DateTimeEditElement::updateUIState()
795 if (DateTimeFieldElement* field = focusedField())
800 String DateTimeEditElement::value() const
802 if (!m_editControlOwner)
803 return emptyString();
804 return m_editControlOwner->formatDateTimeFieldsState(valueAsDateTimeFieldsState());
807 DateTimeFieldsState DateTimeEditElement::valueAsDateTimeFieldsState() const
809 DateTimeFieldsState dateTimeFieldsState;
810 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
811 m_fields[fieldIndex]->populateDateTimeFieldsState(dateTimeFieldsState);
812 return dateTimeFieldsState;
815 } // namespace WebCore