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