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