OLD | NEW |
| (Empty) |
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 "core/html/shadow/DateTimeEditElement.h" | |
27 | |
28 #include "bindings/core/v8/ExceptionState.h" | |
29 #include "core/HTMLNames.h" | |
30 #include "core/dom/Document.h" | |
31 #include "core/dom/StyleChangeReason.h" | |
32 #include "core/dom/Text.h" | |
33 #include "core/events/MouseEvent.h" | |
34 #include "core/html/forms/DateTimeFieldsState.h" | |
35 #include "core/html/shadow/DateTimeFieldElements.h" | |
36 #include "core/html/shadow/ShadowElementNames.h" | |
37 #include "core/style/ComputedStyle.h" | |
38 #include "core/style/StyleInheritedData.h" | |
39 #include "platform/fonts/FontCache.h" | |
40 #include "platform/text/DateTimeFormat.h" | |
41 #include "platform/text/PlatformLocale.h" | |
42 #include "platform/wtf/DateMath.h" | |
43 | |
44 namespace blink { | |
45 | |
46 using namespace HTMLNames; | |
47 using namespace WTF::Unicode; | |
48 | |
49 class DateTimeEditBuilder : private DateTimeFormat::TokenHandler { | |
50 public: | |
51 // The argument objects must be alive until this object dies. | |
52 DateTimeEditBuilder(DateTimeEditElement&, | |
53 const DateTimeEditElement::LayoutParameters&, | |
54 const DateComponents&); | |
55 | |
56 bool Build(const String&); | |
57 | |
58 private: | |
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& GetStepRange() const { | |
68 return parameters_.step_range; | |
69 } | |
70 DateTimeNumericFieldElement::Step CreateStep(double ms_per_field_unit, | |
71 double ms_per_field_size) const; | |
72 | |
73 // DateTimeFormat::TokenHandler functions. | |
74 void VisitField(DateTimeFormat::FieldType, int) final; | |
75 void VisitLiteral(const String&) final; | |
76 | |
77 DateTimeEditElement& EditElement() const; | |
78 | |
79 Member<DateTimeEditElement> edit_element_; | |
80 const DateComponents date_value_; | |
81 const DateTimeEditElement::LayoutParameters& parameters_; | |
82 DateTimeNumericFieldElement::Range day_range_; | |
83 DateTimeNumericFieldElement::Range hour23_range_; | |
84 DateTimeNumericFieldElement::Range minute_range_; | |
85 DateTimeNumericFieldElement::Range second_range_; | |
86 DateTimeNumericFieldElement::Range millisecond_range_; | |
87 }; | |
88 | |
89 DateTimeEditBuilder::DateTimeEditBuilder( | |
90 DateTimeEditElement& element, | |
91 const DateTimeEditElement::LayoutParameters& layout_parameters, | |
92 const DateComponents& date_value) | |
93 : edit_element_(&element), | |
94 date_value_(date_value), | |
95 parameters_(layout_parameters), | |
96 day_range_(1, 31), | |
97 hour23_range_(0, 23), | |
98 minute_range_(0, 59), | |
99 second_range_(0, 59), | |
100 millisecond_range_(0, 999) { | |
101 if (date_value_.GetType() == DateComponents::kDate || | |
102 date_value_.GetType() == DateComponents::kDateTimeLocal) { | |
103 if (parameters_.minimum.GetType() != DateComponents::kInvalid && | |
104 parameters_.maximum.GetType() != DateComponents::kInvalid && | |
105 parameters_.minimum.FullYear() == parameters_.maximum.FullYear() && | |
106 parameters_.minimum.Month() == parameters_.maximum.Month() && | |
107 parameters_.minimum.MonthDay() <= parameters_.maximum.MonthDay()) { | |
108 day_range_.minimum = parameters_.minimum.MonthDay(); | |
109 day_range_.maximum = parameters_.maximum.MonthDay(); | |
110 } | |
111 } | |
112 | |
113 if (date_value_.GetType() == DateComponents::kTime || | |
114 day_range_.IsSingleton()) { | |
115 if (parameters_.minimum.GetType() != DateComponents::kInvalid && | |
116 parameters_.maximum.GetType() != DateComponents::kInvalid && | |
117 parameters_.minimum.Hour() <= parameters_.maximum.Hour()) { | |
118 hour23_range_.minimum = parameters_.minimum.Hour(); | |
119 hour23_range_.maximum = parameters_.maximum.Hour(); | |
120 } | |
121 } | |
122 | |
123 if (hour23_range_.IsSingleton() && | |
124 parameters_.minimum.Minute() <= parameters_.maximum.Minute()) { | |
125 minute_range_.minimum = parameters_.minimum.Minute(); | |
126 minute_range_.maximum = parameters_.maximum.Minute(); | |
127 } | |
128 if (minute_range_.IsSingleton() && | |
129 parameters_.minimum.Second() <= parameters_.maximum.Second()) { | |
130 second_range_.minimum = parameters_.minimum.Second(); | |
131 second_range_.maximum = parameters_.maximum.Second(); | |
132 } | |
133 if (second_range_.IsSingleton() && | |
134 parameters_.minimum.Millisecond() <= parameters_.maximum.Millisecond()) { | |
135 millisecond_range_.minimum = parameters_.minimum.Millisecond(); | |
136 millisecond_range_.maximum = parameters_.maximum.Millisecond(); | |
137 } | |
138 } | |
139 | |
140 bool DateTimeEditBuilder::Build(const String& format_string) { | |
141 EditElement().ResetFields(); | |
142 return DateTimeFormat::Parse(format_string, *this); | |
143 } | |
144 | |
145 bool DateTimeEditBuilder::NeedMillisecondField() const { | |
146 return date_value_.Millisecond() || | |
147 !GetStepRange() | |
148 .Minimum() | |
149 .Remainder(static_cast<int>(kMsPerSecond)) | |
150 .IsZero() || | |
151 !GetStepRange() | |
152 .Step() | |
153 .Remainder(static_cast<int>(kMsPerSecond)) | |
154 .IsZero(); | |
155 } | |
156 | |
157 void DateTimeEditBuilder::VisitField(DateTimeFormat::FieldType field_type, | |
158 int count) { | |
159 const int kCountForAbbreviatedMonth = 3; | |
160 const int kCountForFullMonth = 4; | |
161 const int kCountForNarrowMonth = 5; | |
162 Document& document = EditElement().GetDocument(); | |
163 | |
164 switch (field_type) { | |
165 case DateTimeFormat::kFieldTypeDayOfMonth: { | |
166 DateTimeFieldElement* field = DateTimeDayFieldElement::Create( | |
167 document, EditElement(), parameters_.placeholder_for_day, day_range_); | |
168 EditElement().AddField(field); | |
169 if (ShouldDayOfMonthFieldDisabled()) { | |
170 field->SetValueAsDate(date_value_); | |
171 field->SetDisabled(); | |
172 } | |
173 return; | |
174 } | |
175 | |
176 case DateTimeFormat::kFieldTypeHour11: { | |
177 DateTimeNumericFieldElement::Step step = | |
178 CreateStep(kMsPerHour, kMsPerHour * 12); | |
179 DateTimeFieldElement* field = DateTimeHour11FieldElement::Create( | |
180 document, EditElement(), hour23_range_, step); | |
181 EditElement().AddField(field); | |
182 if (ShouldHourFieldDisabled()) { | |
183 field->SetValueAsDate(date_value_); | |
184 field->SetDisabled(); | |
185 } | |
186 return; | |
187 } | |
188 | |
189 case DateTimeFormat::kFieldTypeHour12: { | |
190 DateTimeNumericFieldElement::Step step = | |
191 CreateStep(kMsPerHour, kMsPerHour * 12); | |
192 DateTimeFieldElement* field = DateTimeHour12FieldElement::Create( | |
193 document, EditElement(), hour23_range_, step); | |
194 EditElement().AddField(field); | |
195 if (ShouldHourFieldDisabled()) { | |
196 field->SetValueAsDate(date_value_); | |
197 field->SetDisabled(); | |
198 } | |
199 return; | |
200 } | |
201 | |
202 case DateTimeFormat::kFieldTypeHour23: { | |
203 DateTimeNumericFieldElement::Step step = | |
204 CreateStep(kMsPerHour, kMsPerDay); | |
205 DateTimeFieldElement* field = DateTimeHour23FieldElement::Create( | |
206 document, EditElement(), hour23_range_, step); | |
207 EditElement().AddField(field); | |
208 if (ShouldHourFieldDisabled()) { | |
209 field->SetValueAsDate(date_value_); | |
210 field->SetDisabled(); | |
211 } | |
212 return; | |
213 } | |
214 | |
215 case DateTimeFormat::kFieldTypeHour24: { | |
216 DateTimeNumericFieldElement::Step step = | |
217 CreateStep(kMsPerHour, kMsPerDay); | |
218 DateTimeFieldElement* field = DateTimeHour24FieldElement::Create( | |
219 document, EditElement(), hour23_range_, step); | |
220 EditElement().AddField(field); | |
221 if (ShouldHourFieldDisabled()) { | |
222 field->SetValueAsDate(date_value_); | |
223 field->SetDisabled(); | |
224 } | |
225 return; | |
226 } | |
227 | |
228 case DateTimeFormat::kFieldTypeMinute: { | |
229 DateTimeNumericFieldElement::Step step = | |
230 CreateStep(kMsPerMinute, kMsPerHour); | |
231 DateTimeNumericFieldElement* field = DateTimeMinuteFieldElement::Create( | |
232 document, EditElement(), minute_range_, step); | |
233 EditElement().AddField(field); | |
234 if (ShouldMinuteFieldDisabled()) { | |
235 field->SetValueAsDate(date_value_); | |
236 field->SetDisabled(); | |
237 } | |
238 return; | |
239 } | |
240 | |
241 case DateTimeFormat::kFieldTypeMonth: // Fallthrough. | |
242 case DateTimeFormat::kFieldTypeMonthStandAlone: { | |
243 int min_month = 0, max_month = 11; | |
244 if (parameters_.minimum.GetType() != DateComponents::kInvalid && | |
245 parameters_.maximum.GetType() != DateComponents::kInvalid && | |
246 parameters_.minimum.FullYear() == parameters_.maximum.FullYear() && | |
247 parameters_.minimum.Month() <= parameters_.maximum.Month()) { | |
248 min_month = parameters_.minimum.Month(); | |
249 max_month = parameters_.maximum.Month(); | |
250 } | |
251 DateTimeFieldElement* field; | |
252 switch (count) { | |
253 case kCountForNarrowMonth: // Fallthrough. | |
254 case kCountForAbbreviatedMonth: | |
255 field = DateTimeSymbolicMonthFieldElement::Create( | |
256 document, EditElement(), | |
257 field_type == DateTimeFormat::kFieldTypeMonth | |
258 ? parameters_.locale.ShortMonthLabels() | |
259 : parameters_.locale.ShortStandAloneMonthLabels(), | |
260 min_month, max_month); | |
261 break; | |
262 case kCountForFullMonth: | |
263 field = DateTimeSymbolicMonthFieldElement::Create( | |
264 document, EditElement(), | |
265 field_type == DateTimeFormat::kFieldTypeMonth | |
266 ? parameters_.locale.MonthLabels() | |
267 : parameters_.locale.StandAloneMonthLabels(), | |
268 min_month, max_month); | |
269 break; | |
270 default: | |
271 field = DateTimeMonthFieldElement::Create( | |
272 document, EditElement(), parameters_.placeholder_for_month, | |
273 DateTimeNumericFieldElement::Range(min_month + 1, max_month + 1)); | |
274 break; | |
275 } | |
276 EditElement().AddField(field); | |
277 if (min_month == max_month && min_month == date_value_.Month() && | |
278 date_value_.GetType() != DateComponents::kMonth) { | |
279 field->SetValueAsDate(date_value_); | |
280 field->SetDisabled(); | |
281 } | |
282 return; | |
283 } | |
284 | |
285 case DateTimeFormat::kFieldTypePeriod: { | |
286 DateTimeFieldElement* field = DateTimeAMPMFieldElement::Create( | |
287 document, EditElement(), parameters_.locale.TimeAMPMLabels()); | |
288 EditElement().AddField(field); | |
289 if (ShouldAMPMFieldDisabled()) { | |
290 field->SetValueAsDate(date_value_); | |
291 field->SetDisabled(); | |
292 } | |
293 return; | |
294 } | |
295 | |
296 case DateTimeFormat::kFieldTypeSecond: { | |
297 DateTimeNumericFieldElement::Step step = | |
298 CreateStep(kMsPerSecond, kMsPerMinute); | |
299 DateTimeNumericFieldElement* field = DateTimeSecondFieldElement::Create( | |
300 document, EditElement(), second_range_, step); | |
301 EditElement().AddField(field); | |
302 if (ShouldSecondFieldDisabled()) { | |
303 field->SetValueAsDate(date_value_); | |
304 field->SetDisabled(); | |
305 } | |
306 | |
307 if (NeedMillisecondField()) { | |
308 VisitLiteral(parameters_.locale.LocalizedDecimalSeparator()); | |
309 VisitField(DateTimeFormat::kFieldTypeFractionalSecond, 3); | |
310 } | |
311 return; | |
312 } | |
313 | |
314 case DateTimeFormat::kFieldTypeFractionalSecond: { | |
315 DateTimeNumericFieldElement::Step step = CreateStep(1, kMsPerSecond); | |
316 DateTimeNumericFieldElement* field = | |
317 DateTimeMillisecondFieldElement::Create(document, EditElement(), | |
318 millisecond_range_, step); | |
319 EditElement().AddField(field); | |
320 if (ShouldMillisecondFieldDisabled()) { | |
321 field->SetValueAsDate(date_value_); | |
322 field->SetDisabled(); | |
323 } | |
324 return; | |
325 } | |
326 | |
327 case DateTimeFormat::kFieldTypeWeekOfYear: { | |
328 DateTimeNumericFieldElement::Range range( | |
329 DateComponents::kMinimumWeekNumber, | |
330 DateComponents::kMaximumWeekNumber); | |
331 if (parameters_.minimum.GetType() != DateComponents::kInvalid && | |
332 parameters_.maximum.GetType() != DateComponents::kInvalid && | |
333 parameters_.minimum.FullYear() == parameters_.maximum.FullYear() && | |
334 parameters_.minimum.Week() <= parameters_.maximum.Week()) { | |
335 range.minimum = parameters_.minimum.Week(); | |
336 range.maximum = parameters_.maximum.Week(); | |
337 } | |
338 EditElement().AddField( | |
339 DateTimeWeekFieldElement::Create(document, EditElement(), range)); | |
340 return; | |
341 } | |
342 | |
343 case DateTimeFormat::kFieldTypeYear: { | |
344 DateTimeYearFieldElement::Parameters year_params; | |
345 if (parameters_.minimum.GetType() == DateComponents::kInvalid) { | |
346 year_params.minimum_year = DateComponents::MinimumYear(); | |
347 year_params.min_is_specified = false; | |
348 } else { | |
349 year_params.minimum_year = parameters_.minimum.FullYear(); | |
350 year_params.min_is_specified = true; | |
351 } | |
352 if (parameters_.maximum.GetType() == DateComponents::kInvalid) { | |
353 year_params.maximum_year = DateComponents::MaximumYear(); | |
354 year_params.max_is_specified = false; | |
355 } else { | |
356 year_params.maximum_year = parameters_.maximum.FullYear(); | |
357 year_params.max_is_specified = true; | |
358 } | |
359 if (year_params.minimum_year > year_params.maximum_year) { | |
360 std::swap(year_params.minimum_year, year_params.maximum_year); | |
361 std::swap(year_params.min_is_specified, year_params.max_is_specified); | |
362 } | |
363 year_params.placeholder = parameters_.placeholder_for_year; | |
364 DateTimeFieldElement* field = DateTimeYearFieldElement::Create( | |
365 document, EditElement(), year_params); | |
366 EditElement().AddField(field); | |
367 if (ShouldYearFieldDisabled()) { | |
368 field->SetValueAsDate(date_value_); | |
369 field->SetDisabled(); | |
370 } | |
371 return; | |
372 } | |
373 | |
374 default: | |
375 return; | |
376 } | |
377 } | |
378 | |
379 bool DateTimeEditBuilder::ShouldAMPMFieldDisabled() const { | |
380 return ShouldHourFieldDisabled() || | |
381 (hour23_range_.minimum < 12 && hour23_range_.maximum < 12 && | |
382 date_value_.Hour() < 12) || | |
383 (hour23_range_.minimum >= 12 && hour23_range_.maximum >= 12 && | |
384 date_value_.Hour() >= 12); | |
385 } | |
386 | |
387 bool DateTimeEditBuilder::ShouldDayOfMonthFieldDisabled() const { | |
388 return day_range_.IsSingleton() && | |
389 day_range_.minimum == date_value_.MonthDay() && | |
390 date_value_.GetType() != DateComponents::kDate; | |
391 } | |
392 | |
393 bool DateTimeEditBuilder::ShouldHourFieldDisabled() const { | |
394 if (hour23_range_.IsSingleton() && | |
395 hour23_range_.minimum == date_value_.Hour() && | |
396 !(ShouldMinuteFieldDisabled() && ShouldSecondFieldDisabled() && | |
397 ShouldMillisecondFieldDisabled())) | |
398 return true; | |
399 | |
400 if (date_value_.GetType() == DateComponents::kTime) | |
401 return false; | |
402 DCHECK_EQ(date_value_.GetType(), DateComponents::kDateTimeLocal); | |
403 | |
404 if (ShouldDayOfMonthFieldDisabled()) { | |
405 DCHECK_EQ(parameters_.minimum.FullYear(), parameters_.maximum.FullYear()); | |
406 DCHECK_EQ(parameters_.minimum.Month(), parameters_.maximum.Month()); | |
407 return false; | |
408 } | |
409 | |
410 const Decimal decimal_ms_per_day(static_cast<int>(kMsPerDay)); | |
411 Decimal hour_part_of_minimum = | |
412 (GetStepRange().StepBase().Abs().Remainder(decimal_ms_per_day) / | |
413 static_cast<int>(kMsPerHour)) | |
414 .Floor(); | |
415 return hour_part_of_minimum == date_value_.Hour() && | |
416 GetStepRange().Step().Remainder(decimal_ms_per_day).IsZero(); | |
417 } | |
418 | |
419 bool DateTimeEditBuilder::ShouldMillisecondFieldDisabled() const { | |
420 if (millisecond_range_.IsSingleton() && | |
421 millisecond_range_.minimum == date_value_.Millisecond()) | |
422 return true; | |
423 | |
424 const Decimal decimal_ms_per_second(static_cast<int>(kMsPerSecond)); | |
425 return GetStepRange().StepBase().Abs().Remainder(decimal_ms_per_second) == | |
426 date_value_.Millisecond() && | |
427 GetStepRange().Step().Remainder(decimal_ms_per_second).IsZero(); | |
428 } | |
429 | |
430 bool DateTimeEditBuilder::ShouldMinuteFieldDisabled() const { | |
431 if (minute_range_.IsSingleton() && | |
432 minute_range_.minimum == date_value_.Minute()) | |
433 return true; | |
434 | |
435 const Decimal decimal_ms_per_hour(static_cast<int>(kMsPerHour)); | |
436 Decimal minute_part_of_minimum = | |
437 (GetStepRange().StepBase().Abs().Remainder(decimal_ms_per_hour) / | |
438 static_cast<int>(kMsPerMinute)) | |
439 .Floor(); | |
440 return minute_part_of_minimum == date_value_.Minute() && | |
441 GetStepRange().Step().Remainder(decimal_ms_per_hour).IsZero(); | |
442 } | |
443 | |
444 bool DateTimeEditBuilder::ShouldSecondFieldDisabled() const { | |
445 if (second_range_.IsSingleton() && | |
446 second_range_.minimum == date_value_.Second()) | |
447 return true; | |
448 | |
449 const Decimal decimal_ms_per_minute(static_cast<int>(kMsPerMinute)); | |
450 Decimal second_part_of_minimum = | |
451 (GetStepRange().StepBase().Abs().Remainder(decimal_ms_per_minute) / | |
452 static_cast<int>(kMsPerSecond)) | |
453 .Floor(); | |
454 return second_part_of_minimum == date_value_.Second() && | |
455 GetStepRange().Step().Remainder(decimal_ms_per_minute).IsZero(); | |
456 } | |
457 | |
458 bool DateTimeEditBuilder::ShouldYearFieldDisabled() const { | |
459 return parameters_.minimum.GetType() != DateComponents::kInvalid && | |
460 parameters_.maximum.GetType() != DateComponents::kInvalid && | |
461 parameters_.minimum.FullYear() == parameters_.maximum.FullYear() && | |
462 parameters_.minimum.FullYear() == date_value_.FullYear(); | |
463 } | |
464 | |
465 void DateTimeEditBuilder::VisitLiteral(const String& text) { | |
466 DEFINE_STATIC_LOCAL(AtomicString, text_pseudo_id, | |
467 ("-webkit-datetime-edit-text")); | |
468 DCHECK_GT(text.length(), 0u); | |
469 HTMLDivElement* element = HTMLDivElement::Create(EditElement().GetDocument()); | |
470 element->SetShadowPseudoId(text_pseudo_id); | |
471 if (parameters_.locale.IsRTL() && text.length()) { | |
472 CharDirection dir = Direction(text[0]); | |
473 if (dir == kSegmentSeparator || dir == kWhiteSpaceNeutral || | |
474 dir == kOtherNeutral) | |
475 element->AppendChild(Text::Create(EditElement().GetDocument(), | |
476 String(&kRightToLeftMarkCharacter, 1))); | |
477 } | |
478 element->AppendChild(Text::Create(EditElement().GetDocument(), text)); | |
479 EditElement().FieldsWrapperElement()->AppendChild(element); | |
480 } | |
481 | |
482 DateTimeEditElement& DateTimeEditBuilder::EditElement() const { | |
483 return *edit_element_; | |
484 } | |
485 | |
486 DateTimeNumericFieldElement::Step DateTimeEditBuilder::CreateStep( | |
487 double ms_per_field_unit, | |
488 double ms_per_field_size) const { | |
489 const Decimal ms_per_field_unit_decimal(static_cast<int>(ms_per_field_unit)); | |
490 const Decimal ms_per_field_size_decimal(static_cast<int>(ms_per_field_size)); | |
491 Decimal step_milliseconds = GetStepRange().Step(); | |
492 DCHECK(!ms_per_field_unit_decimal.IsZero()); | |
493 DCHECK(!ms_per_field_size_decimal.IsZero()); | |
494 DCHECK(!step_milliseconds.IsZero()); | |
495 | |
496 DateTimeNumericFieldElement::Step step(1, 0); | |
497 | |
498 if (step_milliseconds.Remainder(ms_per_field_size_decimal).IsZero()) | |
499 step_milliseconds = ms_per_field_size_decimal; | |
500 | |
501 if (ms_per_field_size_decimal.Remainder(step_milliseconds).IsZero() && | |
502 step_milliseconds.Remainder(ms_per_field_unit_decimal).IsZero()) { | |
503 step.step = static_cast<int>( | |
504 (step_milliseconds / ms_per_field_unit_decimal).ToDouble()); | |
505 step.step_base = static_cast<int>( | |
506 (GetStepRange().StepBase() / ms_per_field_unit_decimal) | |
507 .Floor() | |
508 .Remainder(ms_per_field_size_decimal / ms_per_field_unit_decimal) | |
509 .ToDouble()); | |
510 } | |
511 return step; | |
512 } | |
513 | |
514 // ---------------------------- | |
515 | |
516 DateTimeEditElement::EditControlOwner::~EditControlOwner() {} | |
517 | |
518 DateTimeEditElement::DateTimeEditElement(Document& document, | |
519 EditControlOwner& edit_control_owner) | |
520 : HTMLDivElement(document), edit_control_owner_(&edit_control_owner) { | |
521 SetHasCustomStyleCallbacks(); | |
522 } | |
523 | |
524 DateTimeEditElement::~DateTimeEditElement() {} | |
525 | |
526 DEFINE_TRACE(DateTimeEditElement) { | |
527 visitor->Trace(fields_); | |
528 visitor->Trace(edit_control_owner_); | |
529 HTMLDivElement::Trace(visitor); | |
530 } | |
531 | |
532 inline Element* DateTimeEditElement::FieldsWrapperElement() const { | |
533 DCHECK(FirstChild()); | |
534 return ToElementOrDie(FirstChild()); | |
535 } | |
536 | |
537 void DateTimeEditElement::AddField(DateTimeFieldElement* field) { | |
538 if (fields_.size() >= kMaximumNumberOfFields) | |
539 return; | |
540 fields_.push_back(field); | |
541 FieldsWrapperElement()->AppendChild(field); | |
542 } | |
543 | |
544 bool DateTimeEditElement::AnyEditableFieldsHaveValues() const { | |
545 for (const auto& field : fields_) { | |
546 if (!field->IsDisabled() && field->HasValue()) | |
547 return true; | |
548 } | |
549 return false; | |
550 } | |
551 | |
552 void DateTimeEditElement::BlurByOwner() { | |
553 if (DateTimeFieldElement* field = FocusedField()) | |
554 field->blur(); | |
555 } | |
556 | |
557 DateTimeEditElement* DateTimeEditElement::Create( | |
558 Document& document, | |
559 EditControlOwner& edit_control_owner) { | |
560 DateTimeEditElement* container = | |
561 new DateTimeEditElement(document, edit_control_owner); | |
562 container->SetShadowPseudoId(AtomicString("-webkit-datetime-edit")); | |
563 container->setAttribute(idAttr, ShadowElementNames::DateTimeEdit()); | |
564 return container; | |
565 } | |
566 | |
567 PassRefPtr<ComputedStyle> DateTimeEditElement::CustomStyleForLayoutObject() { | |
568 // FIXME: This is a kind of layout. We might want to introduce new | |
569 // layoutObject. | |
570 RefPtr<ComputedStyle> original_style = OriginalStyleForLayoutObject(); | |
571 RefPtr<ComputedStyle> style = ComputedStyle::Clone(*original_style); | |
572 float width = 0; | |
573 for (Node* child = FieldsWrapperElement()->FirstChild(); child; | |
574 child = child->nextSibling()) { | |
575 if (!child->IsElementNode()) | |
576 continue; | |
577 Element* child_element = ToElement(child); | |
578 if (child_element->IsDateTimeFieldElement()) { | |
579 // We need to pass the ComputedStyle of this element because child | |
580 // elements can't resolve inherited style at this timing. | |
581 width += static_cast<DateTimeFieldElement*>(child_element) | |
582 ->MaximumWidth(*style); | |
583 } else { | |
584 // ::-webkit-datetime-edit-text case. It has no | |
585 // border/padding/margin in html.css. | |
586 width += DateTimeFieldElement::ComputeTextWidth( | |
587 *style, child_element->textContent()); | |
588 } | |
589 } | |
590 style->SetWidth(Length(ceilf(width), kFixed)); | |
591 style->SetUnique(); | |
592 return style.Release(); | |
593 } | |
594 | |
595 void DateTimeEditElement::DidBlurFromField(WebFocusType focus_type) { | |
596 if (edit_control_owner_) | |
597 edit_control_owner_->DidBlurFromControl(focus_type); | |
598 } | |
599 | |
600 void DateTimeEditElement::DidFocusOnField(WebFocusType focus_type) { | |
601 if (edit_control_owner_) | |
602 edit_control_owner_->DidFocusOnControl(focus_type); | |
603 } | |
604 | |
605 void DateTimeEditElement::DisabledStateChanged() { | |
606 UpdateUIState(); | |
607 } | |
608 | |
609 DateTimeFieldElement* DateTimeEditElement::FieldAt(size_t field_index) const { | |
610 return field_index < fields_.size() ? fields_[field_index].Get() : 0; | |
611 } | |
612 | |
613 size_t DateTimeEditElement::FieldIndexOf( | |
614 const DateTimeFieldElement& field) const { | |
615 for (size_t field_index = 0; field_index < fields_.size(); ++field_index) { | |
616 if (fields_[field_index] == &field) | |
617 return field_index; | |
618 } | |
619 return kInvalidFieldIndex; | |
620 } | |
621 | |
622 void DateTimeEditElement::FocusIfNoFocus() { | |
623 if (FocusedFieldIndex() != kInvalidFieldIndex) | |
624 return; | |
625 FocusOnNextFocusableField(0); | |
626 } | |
627 | |
628 void DateTimeEditElement::FocusByOwner(Element* old_focused_element) { | |
629 if (old_focused_element && old_focused_element->IsDateTimeFieldElement()) { | |
630 DateTimeFieldElement* old_focused_field = | |
631 static_cast<DateTimeFieldElement*>(old_focused_element); | |
632 size_t index = FieldIndexOf(*old_focused_field); | |
633 GetDocument().UpdateStyleAndLayoutTreeForNode(old_focused_field); | |
634 if (index != kInvalidFieldIndex && old_focused_field->IsFocusable()) { | |
635 old_focused_field->focus(); | |
636 return; | |
637 } | |
638 } | |
639 FocusOnNextFocusableField(0); | |
640 } | |
641 | |
642 DateTimeFieldElement* DateTimeEditElement::FocusedField() const { | |
643 return FieldAt(FocusedFieldIndex()); | |
644 } | |
645 | |
646 size_t DateTimeEditElement::FocusedFieldIndex() const { | |
647 Element* const focused_field_element = GetDocument().FocusedElement(); | |
648 for (size_t field_index = 0; field_index < fields_.size(); ++field_index) { | |
649 if (fields_[field_index] == focused_field_element) | |
650 return field_index; | |
651 } | |
652 return kInvalidFieldIndex; | |
653 } | |
654 | |
655 void DateTimeEditElement::FieldValueChanged() { | |
656 if (edit_control_owner_) | |
657 edit_control_owner_->EditControlValueChanged(); | |
658 } | |
659 | |
660 bool DateTimeEditElement::FocusOnNextFocusableField(size_t start_index) { | |
661 GetDocument().UpdateStyleAndLayoutTreeIgnorePendingStylesheets(); | |
662 for (size_t field_index = start_index; field_index < fields_.size(); | |
663 ++field_index) { | |
664 if (fields_[field_index]->IsFocusable()) { | |
665 fields_[field_index]->focus(); | |
666 return true; | |
667 } | |
668 } | |
669 return false; | |
670 } | |
671 | |
672 bool DateTimeEditElement::FocusOnNextField(const DateTimeFieldElement& field) { | |
673 const size_t start_field_index = FieldIndexOf(field); | |
674 if (start_field_index == kInvalidFieldIndex) | |
675 return false; | |
676 return FocusOnNextFocusableField(start_field_index + 1); | |
677 } | |
678 | |
679 bool DateTimeEditElement::FocusOnPreviousField( | |
680 const DateTimeFieldElement& field) { | |
681 const size_t start_field_index = FieldIndexOf(field); | |
682 if (start_field_index == kInvalidFieldIndex) | |
683 return false; | |
684 GetDocument().UpdateStyleAndLayoutTreeIgnorePendingStylesheets(); | |
685 size_t field_index = start_field_index; | |
686 while (field_index > 0) { | |
687 --field_index; | |
688 if (fields_[field_index]->IsFocusable()) { | |
689 fields_[field_index]->focus(); | |
690 return true; | |
691 } | |
692 } | |
693 return false; | |
694 } | |
695 | |
696 bool DateTimeEditElement::IsDateTimeEditElement() const { | |
697 return true; | |
698 } | |
699 | |
700 bool DateTimeEditElement::IsDisabled() const { | |
701 return edit_control_owner_ && | |
702 edit_control_owner_->IsEditControlOwnerDisabled(); | |
703 } | |
704 | |
705 bool DateTimeEditElement::IsFieldOwnerDisabled() const { | |
706 return IsDisabled(); | |
707 } | |
708 | |
709 bool DateTimeEditElement::IsFieldOwnerReadOnly() const { | |
710 return IsReadOnly(); | |
711 } | |
712 | |
713 bool DateTimeEditElement::IsReadOnly() const { | |
714 return edit_control_owner_ && | |
715 edit_control_owner_->IsEditControlOwnerReadOnly(); | |
716 } | |
717 | |
718 void DateTimeEditElement::GetLayout(const LayoutParameters& layout_parameters, | |
719 const DateComponents& date_value) { | |
720 // TODO(tkent): We assume this function never dispatches events. However this | |
721 // can dispatch 'blur' event in Node::removeChild(). | |
722 | |
723 DEFINE_STATIC_LOCAL(AtomicString, fields_wrapper_pseudo_id, | |
724 ("-webkit-datetime-edit-fields-wrapper")); | |
725 if (!HasChildren()) { | |
726 HTMLDivElement* element = HTMLDivElement::Create(GetDocument()); | |
727 element->SetShadowPseudoId(fields_wrapper_pseudo_id); | |
728 AppendChild(element); | |
729 } | |
730 Element* fields_wrapper = FieldsWrapperElement(); | |
731 | |
732 size_t focused_field_index = this->FocusedFieldIndex(); | |
733 DateTimeFieldElement* const focused_field = FieldAt(focused_field_index); | |
734 const AtomicString focused_field_id = | |
735 focused_field ? focused_field->ShadowPseudoId() : g_null_atom; | |
736 | |
737 DateTimeEditBuilder builder(*this, layout_parameters, date_value); | |
738 Node* last_child_to_be_removed = fields_wrapper->LastChild(); | |
739 if (!builder.Build(layout_parameters.date_time_format) || fields_.IsEmpty()) { | |
740 last_child_to_be_removed = fields_wrapper->LastChild(); | |
741 builder.Build(layout_parameters.fallback_date_time_format); | |
742 } | |
743 | |
744 if (focused_field_index != kInvalidFieldIndex) { | |
745 for (size_t field_index = 0; field_index < fields_.size(); ++field_index) { | |
746 if (fields_[field_index]->ShadowPseudoId() == focused_field_id) { | |
747 focused_field_index = field_index; | |
748 break; | |
749 } | |
750 } | |
751 if (DateTimeFieldElement* field = | |
752 FieldAt(std::min(focused_field_index, fields_.size() - 1))) | |
753 field->focus(); | |
754 } | |
755 | |
756 if (last_child_to_be_removed) { | |
757 for (Node* child_node = fields_wrapper->FirstChild(); child_node; | |
758 child_node = fields_wrapper->FirstChild()) { | |
759 fields_wrapper->RemoveChild(child_node); | |
760 if (child_node == last_child_to_be_removed) | |
761 break; | |
762 } | |
763 SetNeedsStyleRecalc( | |
764 kSubtreeStyleChange, | |
765 StyleChangeReasonForTracing::Create(StyleChangeReason::kControl)); | |
766 } | |
767 } | |
768 | |
769 AtomicString DateTimeEditElement::LocaleIdentifier() const { | |
770 return edit_control_owner_ ? edit_control_owner_->LocaleIdentifier() | |
771 : g_null_atom; | |
772 } | |
773 | |
774 void DateTimeEditElement::FieldDidChangeValueByKeyboard() { | |
775 if (edit_control_owner_) | |
776 edit_control_owner_->EditControlDidChangeValueByKeyboard(); | |
777 } | |
778 | |
779 void DateTimeEditElement::ReadOnlyStateChanged() { | |
780 UpdateUIState(); | |
781 } | |
782 | |
783 void DateTimeEditElement::ResetFields() { | |
784 for (const auto& field : fields_) | |
785 field->RemoveEventHandler(); | |
786 fields_.Shrink(0); | |
787 } | |
788 | |
789 void DateTimeEditElement::DefaultEventHandler(Event* event) { | |
790 // In case of control owner forward event to control, e.g. DOM | |
791 // dispatchEvent method. | |
792 if (DateTimeFieldElement* field = FocusedField()) { | |
793 field->DefaultEventHandler(event); | |
794 if (event->DefaultHandled()) | |
795 return; | |
796 } | |
797 | |
798 HTMLDivElement::DefaultEventHandler(event); | |
799 } | |
800 | |
801 void DateTimeEditElement::SetValueAsDate( | |
802 const LayoutParameters& layout_parameters, | |
803 const DateComponents& date) { | |
804 GetLayout(layout_parameters, date); | |
805 for (const auto& field : fields_) | |
806 field->SetValueAsDate(date); | |
807 } | |
808 | |
809 void DateTimeEditElement::SetValueAsDateTimeFieldsState( | |
810 const DateTimeFieldsState& date_time_fields_state) { | |
811 for (const auto& field : fields_) | |
812 field->SetValueAsDateTimeFieldsState(date_time_fields_state); | |
813 } | |
814 | |
815 void DateTimeEditElement::SetEmptyValue( | |
816 const LayoutParameters& layout_parameters, | |
817 const DateComponents& date_for_read_only_field) { | |
818 GetLayout(layout_parameters, date_for_read_only_field); | |
819 for (const auto& field : fields_) | |
820 field->SetEmptyValue(DateTimeFieldElement::kDispatchNoEvent); | |
821 } | |
822 | |
823 bool DateTimeEditElement::HasFocusedField() { | |
824 return FocusedFieldIndex() != kInvalidFieldIndex; | |
825 } | |
826 | |
827 void DateTimeEditElement::SetOnlyYearMonthDay(const DateComponents& date) { | |
828 DCHECK_EQ(date.GetType(), DateComponents::kDate); | |
829 | |
830 if (!edit_control_owner_) | |
831 return; | |
832 | |
833 DateTimeFieldsState date_time_fields_state = ValueAsDateTimeFieldsState(); | |
834 date_time_fields_state.SetYear(date.FullYear()); | |
835 date_time_fields_state.SetMonth(date.Month() + 1); | |
836 date_time_fields_state.SetDayOfMonth(date.MonthDay()); | |
837 SetValueAsDateTimeFieldsState(date_time_fields_state); | |
838 edit_control_owner_->EditControlValueChanged(); | |
839 } | |
840 | |
841 void DateTimeEditElement::StepDown() { | |
842 if (DateTimeFieldElement* const field = FocusedField()) | |
843 field->StepDown(); | |
844 } | |
845 | |
846 void DateTimeEditElement::StepUp() { | |
847 if (DateTimeFieldElement* const field = FocusedField()) | |
848 field->StepUp(); | |
849 } | |
850 | |
851 void DateTimeEditElement::UpdateUIState() { | |
852 if (IsDisabled()) { | |
853 if (DateTimeFieldElement* field = FocusedField()) | |
854 field->blur(); | |
855 } | |
856 } | |
857 | |
858 String DateTimeEditElement::Value() const { | |
859 if (!edit_control_owner_) | |
860 return g_empty_string; | |
861 return edit_control_owner_->FormatDateTimeFieldsState( | |
862 ValueAsDateTimeFieldsState()); | |
863 } | |
864 | |
865 DateTimeFieldsState DateTimeEditElement::ValueAsDateTimeFieldsState() const { | |
866 DateTimeFieldsState date_time_fields_state; | |
867 for (const auto& field : fields_) | |
868 field->PopulateDateTimeFieldsState(date_time_fields_state); | |
869 return date_time_fields_state; | |
870 } | |
871 | |
872 } // namespace blink | |
OLD | NEW |