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