INPUT_MULTIPLE_FIELDS_UI: Step-up/-down of hour field should respect min/max attributes
[WebKit-https.git] / Source / WebCore / html / shadow / DateTimeEditElement.cpp
1 /*
2  * Copyright (C) 2012 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
6  * are met:
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.
12  *
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
23  * SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
28 #include "DateTimeEditElement.h"
29
30 #include "DateComponents.h"
31 #include "DateTimeFieldElements.h"
32 #include "DateTimeFieldsState.h"
33 #include "DateTimeFormat.h"
34 #include "DateTimeSymbolicFieldElement.h"
35 #include "EventHandler.h"
36 #include "FontCache.h"
37 #include "HTMLNames.h"
38 #include "KeyboardEvent.h"
39 #include "MouseEvent.h"
40 #include "PlatformLocale.h"
41 #include "RenderStyle.h"
42 #include "StyleResolver.h"
43 #include "Text.h"
44 #include <wtf/DateMath.h>
45 #include <wtf/text/StringBuilder.h>
46
47 namespace WebCore {
48
49 using namespace HTMLNames;
50 using namespace WTF::Unicode;
51
52 class DateTimeEditBuilder : private DateTimeFormat::TokenHandler {
53     WTF_MAKE_NONCOPYABLE(DateTimeEditBuilder);
54
55 public:
56     // The argument objects must be alive until this object dies.
57     DateTimeEditBuilder(DateTimeEditElement&, const DateTimeEditElement::LayoutParameters&, const DateComponents&);
58
59     bool build(const String&);
60
61 private:
62     bool needMillisecondField() const;
63     bool shouldAMPMFieldDisabled() const;
64     bool shouldDayOfMonthFieldDisabled() const;
65     bool shouldHourFieldDisabled() const;
66     bool shouldMillisecondFieldDisabled() const;
67     bool shouldMinuteFieldDisabled() const;
68     bool shouldSecondFieldDisabled() const;
69     bool shouldYearFieldDisabled() const;
70     inline const StepRange& stepRange() const { return m_parameters.stepRange; }
71     DateTimeNumericFieldElement::Parameters createNumericFieldParameters(const Decimal& msPerFieldUnit, const Decimal& msPerFieldSize) const;
72
73     // DateTimeFormat::TokenHandler functions.
74     virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
75     virtual void visitLiteral(const String&) OVERRIDE FINAL;
76
77     DateTimeEditElement& m_editElement;
78     const DateComponents m_dateValue;
79     const DateTimeEditElement::LayoutParameters& m_parameters;
80     int m_minDay;
81     int m_maxDay;
82     int m_minHour23;
83     int m_maxHour23;
84 };
85
86 DateTimeEditBuilder::DateTimeEditBuilder(DateTimeEditElement& elemnt, const DateTimeEditElement::LayoutParameters& layoutParameters, const DateComponents& dateValue)
87     : m_editElement(elemnt)
88     , m_dateValue(dateValue)
89     , m_parameters(layoutParameters)
90     , m_minDay(1)
91     , m_maxDay(31)
92     , m_minHour23(0)
93     , m_maxHour23(23)
94 {
95     if (m_dateValue.type() == DateComponents::Date
96         || m_dateValue.type() == DateComponents::DateTimeLocal
97         || m_dateValue.type() == DateComponents::DateTime) {
98         if (m_parameters.minimum.type() != DateComponents::Invalid
99             && m_parameters.maximum.type() != DateComponents::Invalid
100             && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
101             && m_parameters.minimum.month() == m_parameters.maximum.month()
102             && m_parameters.minimum.monthDay() <= m_parameters.maximum.monthDay()) {
103             m_minDay = m_parameters.minimum.monthDay();
104             m_maxDay = m_parameters.maximum.monthDay();
105         }
106     }
107
108     if (m_dateValue.type() == DateComponents::Time || m_minDay == m_maxDay) {
109         if (m_parameters.minimum.type() != DateComponents::Invalid
110             && m_parameters.maximum.type() != DateComponents::Invalid
111             && m_parameters.minimum.hour() <= m_parameters.maximum.hour()) {
112             m_minHour23 = m_parameters.minimum.hour();
113             m_maxHour23 = m_parameters.maximum.hour();
114         }
115     }
116 }
117
118 bool DateTimeEditBuilder::build(const String& formatString)
119 {
120     m_editElement.resetFields();
121     return DateTimeFormat::parse(formatString, *this);
122 }
123
124 bool DateTimeEditBuilder::needMillisecondField() const
125 {
126     return m_dateValue.millisecond()
127         || !stepRange().minimum().remainder(static_cast<int>(msPerSecond)).isZero()
128         || !stepRange().step().remainder(static_cast<int>(msPerSecond)).isZero();
129 }
130
131 void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int count)
132 {
133     const int countForAbbreviatedMonth = 3;
134     const int countForFullMonth = 4;
135     const int countForNarrowMonth = 5;
136     Document* const document = m_editElement.document();
137
138     switch (fieldType) {
139     case DateTimeFormat::FieldTypeDayOfMonth: {
140         RefPtr<DateTimeFieldElement> field = DateTimeDayFieldElement::create(document, m_editElement, m_parameters.placeholderForDay, m_minDay, m_maxDay);
141         m_editElement.addField(field);
142         if (shouldDayOfMonthFieldDisabled()) {
143             field->setValueAsDate(m_dateValue);
144             field->setDisabled();
145         }
146         return;
147     }
148
149     case DateTimeFormat::FieldTypeHour11: {
150         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerHour * 12));
151         RefPtr<DateTimeFieldElement> field = DateTimeHour11FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
152         m_editElement.addField(field);
153         if (shouldHourFieldDisabled()) {
154             field->setValueAsDate(m_dateValue);
155             field->setDisabled();
156         }
157         return;
158     }
159
160     case DateTimeFormat::FieldTypeHour12: {
161         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerHour * 12));
162         RefPtr<DateTimeFieldElement> field = DateTimeHour12FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
163         m_editElement.addField(field);
164         if (shouldHourFieldDisabled()) {
165             field->setValueAsDate(m_dateValue);
166             field->setDisabled();
167         }
168         return;
169     }
170
171     case DateTimeFormat::FieldTypeHour23: {
172         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerDay));
173         RefPtr<DateTimeFieldElement> field = DateTimeHour23FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
174         m_editElement.addField(field);
175         if (shouldHourFieldDisabled()) {
176             field->setValueAsDate(m_dateValue);
177             field->setDisabled();
178         }
179         return;
180     }
181
182     case DateTimeFormat::FieldTypeHour24: {
183         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerHour), static_cast<int>(msPerDay));
184         RefPtr<DateTimeFieldElement> field = DateTimeHour24FieldElement::create(document, m_editElement, m_minHour23, m_maxHour23, parameters);
185         m_editElement.addField(field);
186         if (shouldHourFieldDisabled()) {
187             field->setValueAsDate(m_dateValue);
188             field->setDisabled();
189         }
190         return;
191     }
192
193     case DateTimeFormat::FieldTypeMinute: {
194         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerMinute), static_cast<int>(msPerHour));
195         RefPtr<DateTimeNumericFieldElement> field = DateTimeMinuteFieldElement::create(document, m_editElement, parameters);
196         m_editElement.addField(field);
197         if (shouldMinuteFieldDisabled()) {
198             field->setValueAsDate(m_dateValue);
199             field->setDisabled();
200         }
201         return;
202     }
203
204     case DateTimeFormat::FieldTypeMonth: // Fallthrough.
205     case DateTimeFormat::FieldTypeMonthStandAlone: {
206         int minMonth = 0, maxMonth = 11;
207         if (m_parameters.minimum.type() != DateComponents::Invalid
208             && m_parameters.maximum.type() != DateComponents::Invalid
209             && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
210             && m_parameters.minimum.month() <= m_parameters.maximum.month()) {
211             minMonth = m_parameters.minimum.month();
212             maxMonth = m_parameters.maximum.month();
213         }
214         RefPtr<DateTimeFieldElement> field;
215         switch (count) {
216         case countForNarrowMonth: // Fallthrough.
217         case countForAbbreviatedMonth:
218             field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.shortMonthLabels() : m_parameters.locale.shortStandAloneMonthLabels(), minMonth, maxMonth);
219             break;
220         case countForFullMonth:
221             field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.monthLabels() : m_parameters.locale.standAloneMonthLabels(), minMonth, maxMonth);
222             break;
223         default:
224             field = DateTimeMonthFieldElement::create(document, m_editElement, m_parameters.placeholderForMonth, minMonth + 1, maxMonth + 1);
225             break;
226         }
227         m_editElement.addField(field);
228         if (minMonth == maxMonth && minMonth == m_dateValue.month() && m_dateValue.type() != DateComponents::Month) {
229             field->setValueAsDate(m_dateValue);
230             field->setDisabled();
231         }
232         return;
233     }
234
235     case DateTimeFormat::FieldTypePeriod: {
236         RefPtr<DateTimeFieldElement> field = DateTimeAMPMFieldElement::create(document, m_editElement, m_parameters.locale.timeAMPMLabels());
237         m_editElement.addField(field);
238         if (shouldAMPMFieldDisabled()) {
239             field->setValueAsDate(m_dateValue);
240             field->setDisabled();
241         }
242         return;
243     }
244
245     case DateTimeFormat::FieldTypeSecond: {
246         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(static_cast<int>(msPerSecond), static_cast<int>(msPerMinute));
247         RefPtr<DateTimeNumericFieldElement> field = DateTimeSecondFieldElement::create(document, m_editElement, parameters);
248         m_editElement.addField(field);
249         if (shouldSecondFieldDisabled()) {
250             field->setValueAsDate(m_dateValue);
251             field->setDisabled();
252         }
253
254         if (needMillisecondField()) {
255             visitLiteral(m_parameters.locale.localizedDecimalSeparator());
256             visitField(DateTimeFormat::FieldTypeFractionalSecond, 3);
257         }
258         return;
259     }
260
261     case DateTimeFormat::FieldTypeFractionalSecond: {
262         DateTimeNumericFieldElement::Parameters parameters = createNumericFieldParameters(1, static_cast<int>(msPerSecond));
263         RefPtr<DateTimeNumericFieldElement> field = DateTimeMillisecondFieldElement::create(document, m_editElement, parameters);
264         m_editElement.addField(field);
265         if (shouldMillisecondFieldDisabled()) {
266             field->setValueAsDate(m_dateValue);
267             field->setDisabled();
268         }
269         return;
270     }
271
272     case DateTimeFormat::FieldTypeWeekOfYear: {
273         int minWeek = DateComponents::minimumWeekNumber;
274         int maxWeek = DateComponents::maximumWeekNumber;
275         if (m_parameters.minimum.type() != DateComponents::Invalid
276             && m_parameters.maximum.type() != DateComponents::Invalid
277             && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
278             && m_parameters.minimum.week() <= m_parameters.maximum.week()) {
279             minWeek = m_parameters.minimum.week();
280             maxWeek = m_parameters.maximum.week();
281         }
282         m_editElement.addField(DateTimeWeekFieldElement::create(document, m_editElement, minWeek, maxWeek));
283         return;
284     }
285
286     case DateTimeFormat::FieldTypeYear: {
287         DateTimeYearFieldElement::Parameters yearParams;
288         if (m_parameters.minimum.type() == DateComponents::Invalid) {
289             yearParams.minimumYear = DateComponents::minimumYear();
290             yearParams.minIsSpecified = false;
291         } else {
292             yearParams.minimumYear = m_parameters.minimum.fullYear();
293             yearParams.minIsSpecified = true;
294         }
295         if (m_parameters.maximum.type() == DateComponents::Invalid) {
296             yearParams.maximumYear = DateComponents::maximumYear();
297             yearParams.maxIsSpecified = false;
298         } else {
299             yearParams.maximumYear = m_parameters.maximum.fullYear();
300             yearParams.maxIsSpecified = true;
301         }
302         if (yearParams.minimumYear > yearParams.maximumYear) {
303             std::swap(yearParams.minimumYear, yearParams.maximumYear);
304             std::swap(yearParams.minIsSpecified, yearParams.maxIsSpecified);
305         }
306         yearParams.placeholder = m_parameters.placeholderForYear;
307         RefPtr<DateTimeFieldElement> field = DateTimeYearFieldElement::create(document, m_editElement, yearParams);
308         m_editElement.addField(field);
309         if (shouldYearFieldDisabled()) {
310             field->setValueAsDate(m_dateValue);
311             field->setDisabled();
312         }
313         return;
314     }
315
316     default:
317         return;
318     }
319 }
320
321 bool DateTimeEditBuilder::shouldAMPMFieldDisabled() const
322 {
323     return shouldHourFieldDisabled()
324         || (m_minHour23 < 12 && m_maxHour23 < 12 && m_dateValue.hour() < 12)
325         || (m_minHour23 >= 12 && m_maxHour23 >= 12 && m_dateValue.hour() >= 12);
326 }
327
328 bool DateTimeEditBuilder::shouldDayOfMonthFieldDisabled() const
329 {
330     return m_minDay == m_maxDay && m_minDay == m_dateValue.monthDay() && m_dateValue.type() != DateComponents::Date;
331 }
332
333 bool DateTimeEditBuilder::shouldHourFieldDisabled() const
334 {
335     if (m_minHour23 == m_maxHour23 && m_minHour23 == m_dateValue.hour() && !shouldMinuteFieldDisabled())
336         return true;
337
338     if (m_dateValue.type() == DateComponents::Time)
339         return false;
340     ASSERT(m_dateValue.type() == DateComponents::DateTimeLocal || m_dateValue.type() == DateComponents::DateTime);
341
342     if (shouldDayOfMonthFieldDisabled()) {
343         ASSERT(m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear());
344         ASSERT(m_parameters.minimum.month() == m_parameters.maximum.month());
345         return false;
346     }
347
348     const Decimal decimalMsPerDay(static_cast<int>(msPerDay));
349     Decimal hourPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerDay) / static_cast<int>(msPerHour)).floor();
350     return hourPartOfMinimum == m_dateValue.hour() && stepRange().step().remainder(decimalMsPerDay).isZero();
351 }
352
353 bool DateTimeEditBuilder::shouldMillisecondFieldDisabled() const
354 {
355     const Decimal decimalMsPerSecond(static_cast<int>(msPerSecond));
356     return stepRange().minimum().abs().remainder(decimalMsPerSecond) == m_dateValue.millisecond() && stepRange().step().remainder(decimalMsPerSecond).isZero();
357 }
358
359 bool DateTimeEditBuilder::shouldMinuteFieldDisabled() const
360 {
361     const Decimal decimalMsPerHour(static_cast<int>(msPerHour));
362     Decimal minutePartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerHour) / static_cast<int>(msPerMinute)).floor();
363     return minutePartOfMinimum == m_dateValue.minute() && stepRange().step().remainder(decimalMsPerHour).isZero();
364 }
365
366 bool DateTimeEditBuilder::shouldSecondFieldDisabled() const
367 {
368     const Decimal decimalMsPerMinute(static_cast<int>(msPerMinute));
369     Decimal secondPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerMinute) / static_cast<int>(msPerSecond)).floor();
370     return secondPartOfMinimum == m_dateValue.second() && stepRange().step().remainder(decimalMsPerMinute).isZero();
371 }
372
373 bool DateTimeEditBuilder::shouldYearFieldDisabled() const
374 {
375     return m_parameters.minimum.type() != DateComponents::Invalid
376         && m_parameters.maximum.type() != DateComponents::Invalid
377         && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
378         && m_parameters.minimum.fullYear() == m_dateValue.fullYear();
379 }
380
381 void DateTimeEditBuilder::visitLiteral(const String& text)
382 {
383     DEFINE_STATIC_LOCAL(AtomicString, textPseudoId, ("-webkit-datetime-edit-text", AtomicString::ConstructFromLiteral));
384     ASSERT(text.length());
385     RefPtr<HTMLDivElement> element = HTMLDivElement::create(m_editElement.document());
386     element->setPseudo(textPseudoId);
387     if (m_parameters.locale.isRTL() && text.length()) {
388         Direction dir = direction(text[0]);
389         if (dir == SegmentSeparator || dir == WhiteSpaceNeutral || dir == OtherNeutral)
390             element->appendChild(Text::create(m_editElement.document(), String(&rightToLeftMark, 1)));
391     }
392     element->appendChild(Text::create(m_editElement.document(), text));
393     m_editElement.fieldsWrapperElement()->appendChild(element);
394 }
395
396 DateTimeNumericFieldElement::Parameters DateTimeEditBuilder::createNumericFieldParameters(const Decimal& msPerFieldUnit, const Decimal& msPerFieldSize) const
397 {
398     ASSERT(!msPerFieldUnit.isZero());
399     ASSERT(!msPerFieldSize.isZero());
400     Decimal stepMilliseconds = stepRange().step();
401     ASSERT(!stepMilliseconds.isZero());
402
403     DateTimeNumericFieldElement::Parameters parameters(1, 0);
404
405     if (stepMilliseconds.remainder(msPerFieldSize).isZero())
406         stepMilliseconds = msPerFieldSize;
407
408     if (msPerFieldSize.remainder(stepMilliseconds).isZero() && stepMilliseconds.remainder(msPerFieldUnit).isZero()) {
409         parameters.step = static_cast<int>((stepMilliseconds / msPerFieldUnit).toDouble());
410         parameters.stepBase = static_cast<int>((stepRange().stepBase() / msPerFieldUnit).floor().remainder(msPerFieldSize / msPerFieldUnit).toDouble());
411     }
412     return parameters;
413 }
414
415 // ----------------------------
416
417 DateTimeEditElement::EditControlOwner::~EditControlOwner()
418 {
419 }
420
421 DateTimeEditElement::DateTimeEditElement(Document* document, EditControlOwner& editControlOwner)
422     : HTMLDivElement(divTag, document)
423     , m_editControlOwner(&editControlOwner)
424 {
425     DEFINE_STATIC_LOCAL(AtomicString, dateTimeEditPseudoId, ("-webkit-datetime-edit", AtomicString::ConstructFromLiteral));
426     setPseudo(dateTimeEditPseudoId);
427     setHasCustomStyleCallbacks();
428 }
429
430 DateTimeEditElement::~DateTimeEditElement()
431 {
432     for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
433         m_fields[fieldIndex]->removeEventHandler();
434 }
435
436 inline Element* DateTimeEditElement::fieldsWrapperElement() const
437 {
438     ASSERT(firstChild());
439     return toElement(firstChild());
440 }
441
442 void DateTimeEditElement::addField(PassRefPtr<DateTimeFieldElement> field)
443 {
444     if (m_fields.size() == m_fields.capacity())
445         return;
446     m_fields.append(field.get());
447     fieldsWrapperElement()->appendChild(field);
448 }
449
450 bool DateTimeEditElement::anyEditableFieldsHaveValues() const
451 {
452     for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
453         if (!m_fields[fieldIndex]->isDisabled() && m_fields[fieldIndex]->hasValue())
454             return true;
455     }
456     return false;
457 }
458
459 void DateTimeEditElement::blurByOwner()
460 {
461     if (DateTimeFieldElement* field = focusedField())
462         field->blur();
463 }
464
465 PassRefPtr<DateTimeEditElement> DateTimeEditElement::create(Document* document, EditControlOwner& editControlOwner)
466 {
467     RefPtr<DateTimeEditElement> container = adoptRef(new DateTimeEditElement(document, editControlOwner));
468     return container.release();
469 }
470
471 PassRefPtr<RenderStyle> DateTimeEditElement::customStyleForRenderer()
472 {
473     // FIXME: This is a kind of layout. We might want to introduce new renderer.
474     FontCachePurgePreventer fontCachePurgePreventer;
475     RefPtr<RenderStyle> originalStyle = document()->styleResolver()->styleForElement(this);
476     RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
477     float width = 0;
478     for (Node* child = fieldsWrapperElement()->firstChild(); child; child = child->nextSibling()) {
479         if (!child->isElementNode())
480             continue;
481         Element* childElement = toElement(child);
482         if (childElement->isDateTimeFieldElement()) {
483             // We need to pass the Font of this element because child elements
484             // can't resolve inherited style at this timing.
485             width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(style->font());
486         } else {
487             // ::-webkit-datetime-edit-text case. It has no
488             // border/padding/margin in html.css.
489             width += style->font().width(childElement->textContent());
490         }
491     }
492     style->setWidth(Length(ceilf(width), Fixed));
493     return style.release();
494 }
495
496 void DateTimeEditElement::didBlurFromField()
497 {
498     if (m_editControlOwner)
499         m_editControlOwner->didBlurFromControl();
500 }
501
502 void DateTimeEditElement::didFocusOnField()
503 {
504     if (m_editControlOwner)
505         m_editControlOwner->didFocusOnControl();
506 }
507
508 void DateTimeEditElement::disabledStateChanged()
509 {
510     updateUIState();
511 }
512
513 DateTimeFieldElement* DateTimeEditElement::fieldAt(size_t fieldIndex) const
514 {
515     return fieldIndex < m_fields.size() ? m_fields[fieldIndex] : 0;
516 }
517
518 size_t DateTimeEditElement::fieldIndexOf(const DateTimeFieldElement& field) const
519 {
520     for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
521         if (m_fields[fieldIndex] == &field)
522             return fieldIndex;
523     }
524     return invalidFieldIndex;
525 }
526
527 void DateTimeEditElement::focusIfNoFocus()
528 {
529     if (focusedFieldIndex() != invalidFieldIndex)
530         return;
531     focusOnNextFocusableField(0);
532 }
533
534 void DateTimeEditElement::focusByOwner(Node* oldFocusedNode)
535 {
536     if (oldFocusedNode && oldFocusedNode->isElementNode() && toElement(oldFocusedNode)->isDateTimeFieldElement()) {
537         DateTimeFieldElement* oldFocusedField = static_cast<DateTimeFieldElement*>(oldFocusedNode);
538         size_t index = fieldIndexOf(*oldFocusedField);
539         if (index != invalidFieldIndex && oldFocusedField->isFocusable()) {
540             oldFocusedField->focus();
541             return;
542         }
543     }
544     focusOnNextFocusableField(0);
545 }
546
547 DateTimeFieldElement* DateTimeEditElement::focusedField() const
548 {
549     return fieldAt(focusedFieldIndex());
550 }
551
552 size_t DateTimeEditElement::focusedFieldIndex() const
553 {
554     Node* const focusedFieldNode = document()->focusedNode();
555     for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
556         if (m_fields[fieldIndex] == focusedFieldNode)
557             return fieldIndex;
558     }
559     return invalidFieldIndex;
560 }
561
562 void DateTimeEditElement::fieldValueChanged()
563 {
564     if (m_editControlOwner)
565         m_editControlOwner->editControlValueChanged();
566 }
567
568 bool DateTimeEditElement::focusOnNextFocusableField(size_t startIndex)
569 {
570     for (size_t fieldIndex = startIndex; fieldIndex < m_fields.size(); ++fieldIndex) {
571         if (m_fields[fieldIndex]->isFocusable()) {
572             m_fields[fieldIndex]->focus();
573             return true;
574         }
575     }
576     return false;
577 }
578
579 bool DateTimeEditElement::focusOnNextField(const DateTimeFieldElement& field)
580 {
581     const size_t startFieldIndex = fieldIndexOf(field);
582     if (startFieldIndex == invalidFieldIndex)
583         return false;
584     return focusOnNextFocusableField(startFieldIndex + 1);
585 }
586
587 bool DateTimeEditElement::focusOnPreviousField(const DateTimeFieldElement& field)
588 {
589     const size_t startFieldIndex = fieldIndexOf(field);
590     if (startFieldIndex == invalidFieldIndex)
591         return false;
592     size_t fieldIndex = startFieldIndex;
593     while (fieldIndex > 0) {
594         --fieldIndex;
595         if (m_fields[fieldIndex]->isFocusable()) {
596             m_fields[fieldIndex]->focus();
597             return true;
598         }
599     }
600     return false;
601 }
602
603 bool DateTimeEditElement::isDisabled() const
604 {
605     return m_editControlOwner && m_editControlOwner->isEditControlOwnerDisabled();
606 }
607
608 bool DateTimeEditElement::isFieldOwnerDisabled() const
609 {
610     return isDisabled();
611 }
612
613 bool DateTimeEditElement::isFieldOwnerReadOnly() const
614 {
615     return isReadOnly();
616 }
617
618 bool DateTimeEditElement::isReadOnly() const
619 {
620     return m_editControlOwner && m_editControlOwner->isEditControlOwnerReadOnly();
621 }
622
623 void DateTimeEditElement::layout(const LayoutParameters& layoutParameters, const DateComponents& dateValue)
624 {
625     DEFINE_STATIC_LOCAL(AtomicString, fieldsWrapperPseudoId, ("-webkit-datetime-edit-fields-wrapper", AtomicString::ConstructFromLiteral));
626     if (!firstChild()) {
627         RefPtr<HTMLDivElement> element = HTMLDivElement::create(document());
628         element->setPseudo(fieldsWrapperPseudoId);
629         appendChild(element.get());
630     }
631     Element* fieldsWrapper = fieldsWrapperElement();
632
633     size_t focusedFieldIndex = this->focusedFieldIndex();
634     DateTimeFieldElement* const focusedField = fieldAt(focusedFieldIndex);
635     const AtomicString focusedFieldId = focusedField ? focusedField->shadowPseudoId() : nullAtom;
636
637     DateTimeEditBuilder builder(*this, layoutParameters, dateValue);
638     Node* lastChildToBeRemoved = fieldsWrapper->lastChild();
639     if (!builder.build(layoutParameters.dateTimeFormat) || m_fields.isEmpty()) {
640         lastChildToBeRemoved = fieldsWrapper->lastChild();
641         builder.build(layoutParameters.fallbackDateTimeFormat);
642     }
643
644     if (focusedFieldIndex != invalidFieldIndex) {
645         for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
646             if (m_fields[fieldIndex]->shadowPseudoId() == focusedFieldId) {
647                 focusedFieldIndex = fieldIndex;
648                 break;
649             }
650         }
651         if (DateTimeFieldElement* field = fieldAt(std::min(focusedFieldIndex, m_fields.size() - 1)))
652             field->focus();
653     }
654
655     if (lastChildToBeRemoved) {
656         for (Node* childNode = fieldsWrapper->firstChild(); childNode; childNode = fieldsWrapper->firstChild()) {
657             fieldsWrapper->removeChild(childNode);
658             if (childNode == lastChildToBeRemoved)
659                 break;
660         }
661         setNeedsStyleRecalc();
662     }
663 }
664
665 AtomicString DateTimeEditElement::localeIdentifier() const
666 {
667     return m_editControlOwner ? m_editControlOwner->localeIdentifier() : nullAtom;
668 }
669
670 void DateTimeEditElement::readOnlyStateChanged()
671 {
672     updateUIState();
673 }
674
675 void DateTimeEditElement::resetFields()
676 {
677     for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
678         m_fields[fieldIndex]->removeEventHandler();
679     m_fields.shrink(0);
680 }
681
682 void DateTimeEditElement::defaultEventHandler(Event* event)
683 {
684     // In case of control owner forward event to control, e.g. DOM
685     // dispatchEvent method.
686     if (DateTimeFieldElement* field = focusedField()) {
687         field->defaultEventHandler(event);
688         if (event->defaultHandled())
689             return;
690     }
691
692     HTMLDivElement::defaultEventHandler(event);
693 }
694
695 void DateTimeEditElement::setValueAsDate(const LayoutParameters& layoutParameters, const DateComponents& date)
696 {
697     layout(layoutParameters, date);
698     for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
699         m_fields[fieldIndex]->setValueAsDate(date);
700 }
701
702 void DateTimeEditElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
703 {
704     for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
705         m_fields[fieldIndex]->setValueAsDateTimeFieldsState(dateTimeFieldsState);
706 }
707
708 void DateTimeEditElement::setEmptyValue(const LayoutParameters& layoutParameters, const DateComponents& dateForReadOnlyField)
709 {
710     layout(layoutParameters, dateForReadOnlyField);
711     for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
712         m_fields[fieldIndex]->setEmptyValue(DateTimeFieldElement::DispatchNoEvent);
713 }
714
715 bool DateTimeEditElement::hasFocusedField()
716 {
717     return focusedFieldIndex() != invalidFieldIndex;
718 }
719
720 void DateTimeEditElement::setOnlyYearMonthDay(const DateComponents& date)
721 {
722     ASSERT(date.type() == DateComponents::Date);
723
724     if (!m_editControlOwner)
725         return;
726
727     DateTimeFieldsState dateTimeFieldsState = valueAsDateTimeFieldsState();
728     dateTimeFieldsState.setYear(date.fullYear());
729     dateTimeFieldsState.setMonth(date.month() + 1);
730     dateTimeFieldsState.setDayOfMonth(date.monthDay());
731     setValueAsDateTimeFieldsState(dateTimeFieldsState);
732     m_editControlOwner->editControlValueChanged();
733 }
734
735 void DateTimeEditElement::stepDown()
736 {
737     if (DateTimeFieldElement* const field = focusedField())
738         field->stepDown();
739 }
740
741 void DateTimeEditElement::stepUp()
742 {
743     if (DateTimeFieldElement* const field = focusedField())
744         field->stepUp();
745 }
746
747 void DateTimeEditElement::updateUIState()
748 {
749     if (isDisabled()) {
750         if (DateTimeFieldElement* field = focusedField())
751             field->blur();
752     }
753 }
754
755 String DateTimeEditElement::value() const
756 {
757     if (!m_editControlOwner)
758         return emptyString();
759     return m_editControlOwner->formatDateTimeFieldsState(valueAsDateTimeFieldsState());
760 }
761
762 DateTimeFieldsState DateTimeEditElement::valueAsDateTimeFieldsState() const
763 {
764     DateTimeFieldsState dateTimeFieldsState;
765     for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
766         m_fields[fieldIndex]->populateDateTimeFieldsState(dateTimeFieldsState);
767     return dateTimeFieldsState;
768 }
769
770 } // namespace WebCore
771
772 #endif