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