blob: 15737128d485e60b8f34ec2fa05ab19eead37584 [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/MouseEvent.h"
32#include "core/dom/Text.h"
33#include "core/html/DateTimeFieldsState.h"
34#include "core/html/shadow/DateTimeFieldElements.h"
Torne (Richard Coles)5267f702013-06-11 10:57:24 +010035#include "core/html/shadow/ShadowElementNames.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010036#include "core/platform/DateComponents.h"
37#include "core/platform/graphics/FontCache.h"
38#include "core/platform/text/DateTimeFormat.h"
39#include "core/platform/text/PlatformLocale.h"
40#include "core/rendering/style/RenderStyle.h"
Torne (Richard Coles)5267f702013-06-11 10:57:24 +010041#include "core/rendering/style/StyleInheritedData.h"
Ben Murdoch591b9582013-07-10 11:41:44 +010042#include "wtf/DateMath.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010043
44namespace WebCore {
45
46using namespace HTMLNames;
47using namespace WTF::Unicode;
48
49class DateTimeEditBuilder : private DateTimeFormat::TokenHandler {
50 WTF_MAKE_NONCOPYABLE(DateTimeEditBuilder);
51
52public:
53 // The argument objects must be alive until this object dies.
54 DateTimeEditBuilder(DateTimeEditElement&, const DateTimeEditElement::LayoutParameters&, const DateComponents&);
55
56 bool build(const String&);
57
58private:
59 bool needMillisecondField() const;
60 bool shouldAMPMFieldDisabled() const;
61 bool shouldDayOfMonthFieldDisabled() const;
62 bool shouldHourFieldDisabled() const;
63 bool shouldMillisecondFieldDisabled() const;
64 bool shouldMinuteFieldDisabled() const;
65 bool shouldSecondFieldDisabled() const;
66 bool shouldYearFieldDisabled() const;
67 inline const StepRange& stepRange() const { return m_parameters.stepRange; }
68 DateTimeNumericFieldElement::Step createStep(double msPerFieldUnit, double msPerFieldSize) const;
69
70 // DateTimeFormat::TokenHandler functions.
71 virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
72 virtual void visitLiteral(const String&) OVERRIDE FINAL;
73
74 DateTimeEditElement& m_editElement;
75 const DateComponents m_dateValue;
76 const DateTimeEditElement::LayoutParameters& m_parameters;
77 DateTimeNumericFieldElement::Range m_dayRange;
78 DateTimeNumericFieldElement::Range m_hour23Range;
79 DateTimeNumericFieldElement::Range m_minuteRange;
80 DateTimeNumericFieldElement::Range m_secondRange;
81 DateTimeNumericFieldElement::Range m_millisecondRange;
82};
83
84DateTimeEditBuilder::DateTimeEditBuilder(DateTimeEditElement& elemnt, const DateTimeEditElement::LayoutParameters& layoutParameters, const DateComponents& dateValue)
85 : m_editElement(elemnt)
86 , m_dateValue(dateValue)
87 , m_parameters(layoutParameters)
88 , m_dayRange(1, 31)
89 , m_hour23Range(0, 23)
90 , m_minuteRange(0, 59)
91 , m_secondRange(0, 59)
92 , m_millisecondRange(0, 999)
93{
Torne (Richard Coles)e5249552013-05-15 11:35:13 +010094 if (m_dateValue.type() == DateComponents::Date || m_dateValue.type() == DateComponents::DateTimeLocal) {
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010095 if (m_parameters.minimum.type() != DateComponents::Invalid
96 && m_parameters.maximum.type() != DateComponents::Invalid
97 && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
98 && m_parameters.minimum.month() == m_parameters.maximum.month()
99 && m_parameters.minimum.monthDay() <= m_parameters.maximum.monthDay()) {
100 m_dayRange.minimum = m_parameters.minimum.monthDay();
101 m_dayRange.maximum = m_parameters.maximum.monthDay();
102 }
103 }
104
105 if (m_dateValue.type() == DateComponents::Time || m_dayRange.isSingleton()) {
106 if (m_parameters.minimum.type() != DateComponents::Invalid
107 && m_parameters.maximum.type() != DateComponents::Invalid
108 && m_parameters.minimum.hour() <= m_parameters.maximum.hour()) {
109 m_hour23Range.minimum = m_parameters.minimum.hour();
110 m_hour23Range.maximum = m_parameters.maximum.hour();
111 }
112 }
113
114 if (m_hour23Range.isSingleton() && m_parameters.minimum.minute() <= m_parameters.maximum.minute()) {
115 m_minuteRange.minimum = m_parameters.minimum.minute();
116 m_minuteRange.maximum = m_parameters.maximum.minute();
117 }
118 if (m_minuteRange.isSingleton() && m_parameters.minimum.second() <= m_parameters.maximum.second()) {
119 m_secondRange.minimum = m_parameters.minimum.second();
120 m_secondRange.maximum = m_parameters.maximum.second();
121 }
122 if (m_secondRange.isSingleton() && m_parameters.minimum.millisecond() <= m_parameters.maximum.millisecond()) {
123 m_millisecondRange.minimum = m_parameters.minimum.millisecond();
124 m_millisecondRange.maximum = m_parameters.maximum.millisecond();
125 }
126}
127
128bool DateTimeEditBuilder::build(const String& formatString)
129{
130 m_editElement.resetFields();
131 return DateTimeFormat::parse(formatString, *this);
132}
133
134bool DateTimeEditBuilder::needMillisecondField() const
135{
136 return m_dateValue.millisecond()
137 || !stepRange().minimum().remainder(static_cast<int>(msPerSecond)).isZero()
138 || !stepRange().step().remainder(static_cast<int>(msPerSecond)).isZero();
139}
140
141void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int count)
142{
143 const int countForAbbreviatedMonth = 3;
144 const int countForFullMonth = 4;
145 const int countForNarrowMonth = 5;
146 Document* const document = m_editElement.document();
147
148 switch (fieldType) {
149 case DateTimeFormat::FieldTypeDayOfMonth: {
150 RefPtr<DateTimeFieldElement> field = DateTimeDayFieldElement::create(document, m_editElement, m_parameters.placeholderForDay, m_dayRange);
151 m_editElement.addField(field);
152 if (shouldDayOfMonthFieldDisabled()) {
153 field->setValueAsDate(m_dateValue);
154 field->setDisabled();
155 }
156 return;
157 }
158
159 case DateTimeFormat::FieldTypeHour11: {
160 DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerHour * 12);
161 RefPtr<DateTimeFieldElement> field = DateTimeHour11FieldElement::create(document, m_editElement, m_hour23Range, step);
162 m_editElement.addField(field);
163 if (shouldHourFieldDisabled()) {
164 field->setValueAsDate(m_dateValue);
165 field->setDisabled();
166 }
167 return;
168 }
169
170 case DateTimeFormat::FieldTypeHour12: {
171 DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerHour * 12);
172 RefPtr<DateTimeFieldElement> field = DateTimeHour12FieldElement::create(document, m_editElement, m_hour23Range, step);
173 m_editElement.addField(field);
174 if (shouldHourFieldDisabled()) {
175 field->setValueAsDate(m_dateValue);
176 field->setDisabled();
177 }
178 return;
179 }
180
181 case DateTimeFormat::FieldTypeHour23: {
182 DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerDay);
183 RefPtr<DateTimeFieldElement> field = DateTimeHour23FieldElement::create(document, m_editElement, m_hour23Range, step);
184 m_editElement.addField(field);
185 if (shouldHourFieldDisabled()) {
186 field->setValueAsDate(m_dateValue);
187 field->setDisabled();
188 }
189 return;
190 }
191
192 case DateTimeFormat::FieldTypeHour24: {
193 DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerDay);
194 RefPtr<DateTimeFieldElement> field = DateTimeHour24FieldElement::create(document, m_editElement, m_hour23Range, step);
195 m_editElement.addField(field);
196 if (shouldHourFieldDisabled()) {
197 field->setValueAsDate(m_dateValue);
198 field->setDisabled();
199 }
200 return;
201 }
202
203 case DateTimeFormat::FieldTypeMinute: {
204 DateTimeNumericFieldElement::Step step = createStep(msPerMinute, msPerHour);
205 RefPtr<DateTimeNumericFieldElement> field = DateTimeMinuteFieldElement::create(document, m_editElement, m_minuteRange, step);
206 m_editElement.addField(field);
207 if (shouldMinuteFieldDisabled()) {
208 field->setValueAsDate(m_dateValue);
209 field->setDisabled();
210 }
211 return;
212 }
213
214 case DateTimeFormat::FieldTypeMonth: // Fallthrough.
215 case DateTimeFormat::FieldTypeMonthStandAlone: {
216 int minMonth = 0, maxMonth = 11;
217 if (m_parameters.minimum.type() != DateComponents::Invalid
218 && m_parameters.maximum.type() != DateComponents::Invalid
219 && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
220 && m_parameters.minimum.month() <= m_parameters.maximum.month()) {
221 minMonth = m_parameters.minimum.month();
222 maxMonth = m_parameters.maximum.month();
223 }
224 RefPtr<DateTimeFieldElement> field;
225 switch (count) {
226 case countForNarrowMonth: // Fallthrough.
227 case countForAbbreviatedMonth:
228 field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.shortMonthLabels() : m_parameters.locale.shortStandAloneMonthLabels(), minMonth, maxMonth);
229 break;
230 case countForFullMonth:
231 field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.monthLabels() : m_parameters.locale.standAloneMonthLabels(), minMonth, maxMonth);
232 break;
233 default:
234 field = DateTimeMonthFieldElement::create(document, m_editElement, m_parameters.placeholderForMonth, DateTimeNumericFieldElement::Range(minMonth + 1, maxMonth + 1));
235 break;
236 }
237 m_editElement.addField(field);
238 if (minMonth == maxMonth && minMonth == m_dateValue.month() && m_dateValue.type() != DateComponents::Month) {
239 field->setValueAsDate(m_dateValue);
240 field->setDisabled();
241 }
242 return;
243 }
244
245 case DateTimeFormat::FieldTypePeriod: {
246 RefPtr<DateTimeFieldElement> field = DateTimeAMPMFieldElement::create(document, m_editElement, m_parameters.locale.timeAMPMLabels());
247 m_editElement.addField(field);
248 if (shouldAMPMFieldDisabled()) {
249 field->setValueAsDate(m_dateValue);
250 field->setDisabled();
251 }
252 return;
253 }
254
255 case DateTimeFormat::FieldTypeSecond: {
256 DateTimeNumericFieldElement::Step step = createStep(msPerSecond, msPerMinute);
257 RefPtr<DateTimeNumericFieldElement> field = DateTimeSecondFieldElement::create(document, m_editElement, m_secondRange, step);
258 m_editElement.addField(field);
259 if (shouldSecondFieldDisabled()) {
260 field->setValueAsDate(m_dateValue);
261 field->setDisabled();
262 }
263
264 if (needMillisecondField()) {
265 visitLiteral(m_parameters.locale.localizedDecimalSeparator());
266 visitField(DateTimeFormat::FieldTypeFractionalSecond, 3);
267 }
268 return;
269 }
270
271 case DateTimeFormat::FieldTypeFractionalSecond: {
272 DateTimeNumericFieldElement::Step step = createStep(1, msPerSecond);
273 RefPtr<DateTimeNumericFieldElement> field = DateTimeMillisecondFieldElement::create(document, m_editElement, m_millisecondRange, step);
274 m_editElement.addField(field);
275 if (shouldMillisecondFieldDisabled()) {
276 field->setValueAsDate(m_dateValue);
277 field->setDisabled();
278 }
279 return;
280 }
281
282 case DateTimeFormat::FieldTypeWeekOfYear: {
283 DateTimeNumericFieldElement::Range range(DateComponents::minimumWeekNumber, DateComponents::maximumWeekNumber);
284 if (m_parameters.minimum.type() != DateComponents::Invalid
285 && m_parameters.maximum.type() != DateComponents::Invalid
286 && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
287 && m_parameters.minimum.week() <= m_parameters.maximum.week()) {
288 range.minimum = m_parameters.minimum.week();
289 range.maximum = m_parameters.maximum.week();
290 }
291 m_editElement.addField(DateTimeWeekFieldElement::create(document, m_editElement, range));
292 return;
293 }
294
295 case DateTimeFormat::FieldTypeYear: {
296 DateTimeYearFieldElement::Parameters yearParams;
297 if (m_parameters.minimum.type() == DateComponents::Invalid) {
298 yearParams.minimumYear = DateComponents::minimumYear();
299 yearParams.minIsSpecified = false;
300 } else {
301 yearParams.minimumYear = m_parameters.minimum.fullYear();
302 yearParams.minIsSpecified = true;
303 }
304 if (m_parameters.maximum.type() == DateComponents::Invalid) {
305 yearParams.maximumYear = DateComponents::maximumYear();
306 yearParams.maxIsSpecified = false;
307 } else {
308 yearParams.maximumYear = m_parameters.maximum.fullYear();
309 yearParams.maxIsSpecified = true;
310 }
311 if (yearParams.minimumYear > yearParams.maximumYear) {
312 std::swap(yearParams.minimumYear, yearParams.maximumYear);
313 std::swap(yearParams.minIsSpecified, yearParams.maxIsSpecified);
314 }
315 yearParams.placeholder = m_parameters.placeholderForYear;
316 RefPtr<DateTimeFieldElement> field = DateTimeYearFieldElement::create(document, m_editElement, yearParams);
317 m_editElement.addField(field);
318 if (shouldYearFieldDisabled()) {
319 field->setValueAsDate(m_dateValue);
320 field->setDisabled();
321 }
322 return;
323 }
324
325 default:
326 return;
327 }
328}
329
330bool DateTimeEditBuilder::shouldAMPMFieldDisabled() const
331{
332 return shouldHourFieldDisabled()
333 || (m_hour23Range.minimum < 12 && m_hour23Range.maximum < 12 && m_dateValue.hour() < 12)
334 || (m_hour23Range.minimum >= 12 && m_hour23Range.maximum >= 12 && m_dateValue.hour() >= 12);
335}
336
337bool DateTimeEditBuilder::shouldDayOfMonthFieldDisabled() const
338{
339 return m_dayRange.isSingleton() && m_dayRange.minimum == m_dateValue.monthDay() && m_dateValue.type() != DateComponents::Date;
340}
341
342bool DateTimeEditBuilder::shouldHourFieldDisabled() const
343{
344 if (m_hour23Range.isSingleton() && m_hour23Range.minimum == m_dateValue.hour()
345 && !(shouldMinuteFieldDisabled() && shouldSecondFieldDisabled() && shouldMillisecondFieldDisabled()))
346 return true;
347
348 if (m_dateValue.type() == DateComponents::Time)
349 return false;
Torne (Richard Coles)e5249552013-05-15 11:35:13 +0100350 ASSERT(m_dateValue.type() == DateComponents::DateTimeLocal);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100351
352 if (shouldDayOfMonthFieldDisabled()) {
353 ASSERT(m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear());
354 ASSERT(m_parameters.minimum.month() == m_parameters.maximum.month());
355 return false;
356 }
357
358 const Decimal decimalMsPerDay(static_cast<int>(msPerDay));
359 Decimal hourPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerDay) / static_cast<int>(msPerHour)).floor();
360 return hourPartOfMinimum == m_dateValue.hour() && stepRange().step().remainder(decimalMsPerDay).isZero();
361}
362
363bool DateTimeEditBuilder::shouldMillisecondFieldDisabled() const
364{
365 if (m_millisecondRange.isSingleton() && m_millisecondRange.minimum == m_dateValue.millisecond())
366 return true;
367
368 const Decimal decimalMsPerSecond(static_cast<int>(msPerSecond));
369 return stepRange().minimum().abs().remainder(decimalMsPerSecond) == m_dateValue.millisecond() && stepRange().step().remainder(decimalMsPerSecond).isZero();
370}
371
372bool DateTimeEditBuilder::shouldMinuteFieldDisabled() const
373{
374 if (m_minuteRange.isSingleton() && m_minuteRange.minimum == m_dateValue.minute())
375 return true;
376
377 const Decimal decimalMsPerHour(static_cast<int>(msPerHour));
378 Decimal minutePartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerHour) / static_cast<int>(msPerMinute)).floor();
379 return minutePartOfMinimum == m_dateValue.minute() && stepRange().step().remainder(decimalMsPerHour).isZero();
380}
381
382bool DateTimeEditBuilder::shouldSecondFieldDisabled() const
383{
384 if (m_secondRange.isSingleton() && m_secondRange.minimum == m_dateValue.second())
385 return true;
386
387 const Decimal decimalMsPerMinute(static_cast<int>(msPerMinute));
388 Decimal secondPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerMinute) / static_cast<int>(msPerSecond)).floor();
389 return secondPartOfMinimum == m_dateValue.second() && stepRange().step().remainder(decimalMsPerMinute).isZero();
390}
391
392bool DateTimeEditBuilder::shouldYearFieldDisabled() const
393{
394 return m_parameters.minimum.type() != DateComponents::Invalid
395 && m_parameters.maximum.type() != DateComponents::Invalid
396 && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
397 && m_parameters.minimum.fullYear() == m_dateValue.fullYear();
398}
399
400void DateTimeEditBuilder::visitLiteral(const String& text)
401{
402 DEFINE_STATIC_LOCAL(AtomicString, textPseudoId, ("-webkit-datetime-edit-text", AtomicString::ConstructFromLiteral));
403 ASSERT(text.length());
404 RefPtr<HTMLDivElement> element = HTMLDivElement::create(m_editElement.document());
405 element->setPseudo(textPseudoId);
406 if (m_parameters.locale.isRTL() && text.length()) {
407 Direction dir = direction(text[0]);
408 if (dir == SegmentSeparator || dir == WhiteSpaceNeutral || dir == OtherNeutral)
409 element->appendChild(Text::create(m_editElement.document(), String(&rightToLeftMark, 1)));
410 }
411 element->appendChild(Text::create(m_editElement.document(), text));
Ben Murdoch83750172013-07-24 10:36:59 +0100412 m_editElement.fieldsWrapperElement()->appendChild(element, ASSERT_NO_EXCEPTION, AttachLazily);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100413}
414
415DateTimeNumericFieldElement::Step DateTimeEditBuilder::createStep(double msPerFieldUnit, double msPerFieldSize) const
416{
417 const Decimal msPerFieldUnitDecimal(static_cast<int>(msPerFieldUnit));
418 const Decimal msPerFieldSizeDecimal(static_cast<int>(msPerFieldSize));
419 Decimal stepMilliseconds = stepRange().step();
420 ASSERT(!msPerFieldUnitDecimal.isZero());
421 ASSERT(!msPerFieldSizeDecimal.isZero());
422 ASSERT(!stepMilliseconds.isZero());
423
424 DateTimeNumericFieldElement::Step step(1, 0);
425
426 if (stepMilliseconds.remainder(msPerFieldSizeDecimal).isZero())
427 stepMilliseconds = msPerFieldSizeDecimal;
428
429 if (msPerFieldSizeDecimal.remainder(stepMilliseconds).isZero() && stepMilliseconds.remainder(msPerFieldUnitDecimal).isZero()) {
430 step.step = static_cast<int>((stepMilliseconds / msPerFieldUnitDecimal).toDouble());
431 step.stepBase = static_cast<int>((stepRange().stepBase() / msPerFieldUnitDecimal).floor().remainder(msPerFieldSizeDecimal / msPerFieldUnitDecimal).toDouble());
432 }
433 return step;
434}
435
436// ----------------------------
437
438DateTimeEditElement::EditControlOwner::~EditControlOwner()
439{
440}
441
442DateTimeEditElement::DateTimeEditElement(Document* document, EditControlOwner& editControlOwner)
443 : HTMLDivElement(divTag, document)
444 , m_editControlOwner(&editControlOwner)
445{
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100446 setHasCustomStyleCallbacks();
447}
448
449DateTimeEditElement::~DateTimeEditElement()
450{
451 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
452 m_fields[fieldIndex]->removeEventHandler();
453}
454
455inline Element* DateTimeEditElement::fieldsWrapperElement() const
456{
457 ASSERT(firstChild());
458 return toElement(firstChild());
459}
460
461void DateTimeEditElement::addField(PassRefPtr<DateTimeFieldElement> field)
462{
463 if (m_fields.size() == m_fields.capacity())
464 return;
465 m_fields.append(field.get());
Ben Murdoch83750172013-07-24 10:36:59 +0100466 fieldsWrapperElement()->appendChild(field, ASSERT_NO_EXCEPTION, AttachLazily);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100467}
468
469bool DateTimeEditElement::anyEditableFieldsHaveValues() const
470{
471 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
472 if (!m_fields[fieldIndex]->isDisabled() && m_fields[fieldIndex]->hasValue())
473 return true;
474 }
475 return false;
476}
477
478void DateTimeEditElement::blurByOwner()
479{
480 if (DateTimeFieldElement* field = focusedField())
481 field->blur();
482}
483
484PassRefPtr<DateTimeEditElement> DateTimeEditElement::create(Document* document, EditControlOwner& editControlOwner)
485{
486 RefPtr<DateTimeEditElement> container = adoptRef(new DateTimeEditElement(document, editControlOwner));
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +0100487 container->setPseudo(AtomicString("-webkit-datetime-edit", AtomicString::ConstructFromLiteral));
488 container->setAttribute(idAttr, ShadowElementNames::dateTimeEdit());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100489 return container.release();
490}
491
492PassRefPtr<RenderStyle> DateTimeEditElement::customStyleForRenderer()
493{
494 // FIXME: This is a kind of layout. We might want to introduce new renderer.
495 FontCachePurgePreventer fontCachePurgePreventer;
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100496 RefPtr<RenderStyle> originalStyle = originalStyleForRenderer();
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100497 RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
498 float width = 0;
499 for (Node* child = fieldsWrapperElement()->firstChild(); child; child = child->nextSibling()) {
500 if (!child->isElementNode())
501 continue;
502 Element* childElement = toElement(child);
503 if (childElement->isDateTimeFieldElement()) {
504 // We need to pass the Font of this element because child elements
505 // can't resolve inherited style at this timing.
506 width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(style->font());
507 } else {
508 // ::-webkit-datetime-edit-text case. It has no
509 // border/padding/margin in html.css.
510 width += style->font().width(childElement->textContent());
511 }
512 }
513 style->setWidth(Length(ceilf(width), Fixed));
514 return style.release();
515}
516
517void DateTimeEditElement::didBlurFromField()
518{
519 if (m_editControlOwner)
520 m_editControlOwner->didBlurFromControl();
521}
522
523void DateTimeEditElement::didFocusOnField()
524{
525 if (m_editControlOwner)
526 m_editControlOwner->didFocusOnControl();
527}
528
529void DateTimeEditElement::disabledStateChanged()
530{
531 updateUIState();
532}
533
534DateTimeFieldElement* DateTimeEditElement::fieldAt(size_t fieldIndex) const
535{
536 return fieldIndex < m_fields.size() ? m_fields[fieldIndex] : 0;
537}
538
539size_t DateTimeEditElement::fieldIndexOf(const DateTimeFieldElement& field) const
540{
541 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
542 if (m_fields[fieldIndex] == &field)
543 return fieldIndex;
544 }
545 return invalidFieldIndex;
546}
547
548void DateTimeEditElement::focusIfNoFocus()
549{
550 if (focusedFieldIndex() != invalidFieldIndex)
551 return;
552 focusOnNextFocusableField(0);
553}
554
555void DateTimeEditElement::focusByOwner(Node* oldFocusedNode)
556{
557 if (oldFocusedNode && oldFocusedNode->isElementNode() && toElement(oldFocusedNode)->isDateTimeFieldElement()) {
558 DateTimeFieldElement* oldFocusedField = static_cast<DateTimeFieldElement*>(oldFocusedNode);
559 size_t index = fieldIndexOf(*oldFocusedField);
560 if (index != invalidFieldIndex && oldFocusedField->isFocusable()) {
561 oldFocusedField->focus();
562 return;
563 }
564 }
565 focusOnNextFocusableField(0);
566}
567
568DateTimeFieldElement* DateTimeEditElement::focusedField() const
569{
570 return fieldAt(focusedFieldIndex());
571}
572
573size_t DateTimeEditElement::focusedFieldIndex() const
574{
Ben Murdoch7757ec22013-07-23 11:17:36 +0100575 Element* const focusedFieldElement = document()->focusedElement();
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100576 for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
Ben Murdoch7757ec22013-07-23 11:17:36 +0100577 if (m_fields[fieldIndex] == focusedFieldElement)
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100578 return fieldIndex;
579 }
580 return invalidFieldIndex;
581}
582
583void DateTimeEditElement::fieldValueChanged()
584{
585 if (m_editControlOwner)
586 m_editControlOwner->editControlValueChanged();
587}
588
589bool DateTimeEditElement::focusOnNextFocusableField(size_t startIndex)
590{
591 for (size_t fieldIndex = startIndex; fieldIndex < m_fields.size(); ++fieldIndex) {
592 if (m_fields[fieldIndex]->isFocusable()) {
593 m_fields[fieldIndex]->focus();
594 return true;
595 }
596 }
597 return false;
598}
599
600bool DateTimeEditElement::focusOnNextField(const DateTimeFieldElement& field)
601{
602 const size_t startFieldIndex = fieldIndexOf(field);
603 if (startFieldIndex == invalidFieldIndex)
604 return false;
605 return focusOnNextFocusableField(startFieldIndex + 1);
606}
607
608bool DateTimeEditElement::focusOnPreviousField(const DateTimeFieldElement& field)
609{
610 const size_t startFieldIndex = fieldIndexOf(field);
611 if (startFieldIndex == invalidFieldIndex)
612 return false;
613 size_t fieldIndex = startFieldIndex;
614 while (fieldIndex > 0) {
615 --fieldIndex;
616 if (m_fields[fieldIndex]->isFocusable()) {
617 m_fields[fieldIndex]->focus();
618 return true;
619 }
620 }
621 return false;
622}
623
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100624bool DateTimeEditElement::isDateTimeEditElement() const
625{
626 return true;
627}
628
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100629bool 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);
Ben Murdoch83750172013-07-24 10:36:59 +0100655 appendChild(element.get(), ASSERT_NO_EXCEPTION, AttachLazily);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100656 }
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