| 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 |