1 /*
2 * Copyright (C) 2010 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 are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "third_party/blink/renderer/core/html/forms/time_input_type.h"
32
33 #include "third_party/blink/public/strings/grit/blink_strings.h"
34 #include "third_party/blink/renderer/core/dom/document.h"
35 #include "third_party/blink/renderer/core/frame/web_feature.h"
36 #include "third_party/blink/renderer/core/html/forms/date_time_fields_state.h"
37 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
38 #include "third_party/blink/renderer/core/html_names.h"
39 #include "third_party/blink/renderer/core/input_type_names.h"
40 #include "third_party/blink/renderer/core/inspector/console_message.h"
41 #include "third_party/blink/renderer/platform/text/date_components.h"
42 #include "third_party/blink/renderer/platform/text/platform_locale.h"
43 #include "third_party/blink/renderer/platform/wtf/date_math.h"
44 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
45 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
46
47 namespace blink {
48
49 static const int kTimeDefaultStep = 60;
50 static const int kTimeDefaultStepBase = 0;
51 static const int kTimeStepScaleFactor = 1000;
52
TimeInputType(HTMLInputElement & element)53 TimeInputType::TimeInputType(HTMLInputElement& element)
54 : BaseTemporalInputType(element) {}
55
CountUsage()56 void TimeInputType::CountUsage() {
57 CountUsageIfVisible(WebFeature::kInputTypeTime);
58 }
59
FormControlType() const60 const AtomicString& TimeInputType::FormControlType() const {
61 return input_type_names::kTime;
62 }
63
DefaultValueForStepUp() const64 Decimal TimeInputType::DefaultValueForStepUp() const {
65 DateComponents date;
66 date.SetMillisecondsSinceMidnight(
67 ConvertToLocalTime(base::Time::Now()).InMillisecondsF());
68 double milliseconds = date.MillisecondsSinceEpoch();
69 DCHECK(std::isfinite(milliseconds));
70 return Decimal::FromDouble(milliseconds);
71 }
72
CreateStepRange(AnyStepHandling any_step_handling) const73 StepRange TimeInputType::CreateStepRange(
74 AnyStepHandling any_step_handling) const {
75 DEFINE_STATIC_LOCAL(
76 const StepRange::StepDescription, step_description,
77 (kTimeDefaultStep, kTimeDefaultStepBase, kTimeStepScaleFactor,
78 StepRange::kScaledStepValueShouldBeInteger));
79
80 return InputType::CreateReversibleStepRange(
81 any_step_handling, kTimeDefaultStepBase,
82 Decimal::FromDouble(DateComponents::MinimumTime()),
83 Decimal::FromDouble(DateComponents::MaximumTime()), step_description);
84 }
85
ParseToDateComponentsInternal(const String & string,DateComponents * out) const86 bool TimeInputType::ParseToDateComponentsInternal(const String& string,
87 DateComponents* out) const {
88 DCHECK(out);
89 unsigned end;
90 return out->ParseTime(string, 0, end) && end == string.length();
91 }
92
SetMillisecondToDateComponents(double value,DateComponents * date) const93 bool TimeInputType::SetMillisecondToDateComponents(double value,
94 DateComponents* date) const {
95 DCHECK(date);
96 return date->SetMillisecondsSinceMidnight(value);
97 }
98
WarnIfValueIsInvalid(const String & value) const99 void TimeInputType::WarnIfValueIsInvalid(const String& value) const {
100 if (value != GetElement().SanitizeValue(value)) {
101 AddWarningToConsole(
102 "The specified value %s does not conform to the required format. The "
103 "format is \"HH:mm\", \"HH:mm:ss\" or \"HH:mm:ss.SSS\" where HH is "
104 "00-23, mm is 00-59, ss is 00-59, and SSS is 000-999.",
105 value);
106 }
107 }
108
LocalizeValue(const String & proposed_value) const109 String TimeInputType::LocalizeValue(const String& proposed_value) const {
110 DateComponents date;
111 if (!ParseToDateComponents(proposed_value, &date))
112 return proposed_value;
113
114 Locale::FormatType format_type = ShouldHaveSecondField(date)
115 ? Locale::kFormatTypeMedium
116 : Locale::kFormatTypeShort;
117
118 String localized = GetElement().GetLocale().FormatDateTime(date, format_type);
119 return localized.IsEmpty() ? proposed_value : localized;
120 }
121
FormatDateTimeFieldsState(const DateTimeFieldsState & date_time_fields_state) const122 String TimeInputType::FormatDateTimeFieldsState(
123 const DateTimeFieldsState& date_time_fields_state) const {
124 if (!date_time_fields_state.HasHour() ||
125 !date_time_fields_state.HasMinute() || !date_time_fields_state.HasAMPM())
126 return g_empty_string;
127 if (date_time_fields_state.HasMillisecond() &&
128 date_time_fields_state.Millisecond()) {
129 return String::Format(
130 "%02u:%02u:%02u.%03u", date_time_fields_state.Hour23(),
131 date_time_fields_state.Minute(),
132 date_time_fields_state.HasSecond() ? date_time_fields_state.Second()
133 : 0,
134 date_time_fields_state.Millisecond());
135 }
136 if (date_time_fields_state.HasSecond() && date_time_fields_state.Second()) {
137 return String::Format("%02u:%02u:%02u", date_time_fields_state.Hour23(),
138 date_time_fields_state.Minute(),
139 date_time_fields_state.Second());
140 }
141 return String::Format("%02u:%02u", date_time_fields_state.Hour23(),
142 date_time_fields_state.Minute());
143 }
144
SetupLayoutParameters(DateTimeEditElement::LayoutParameters & layout_parameters,const DateComponents & date) const145 void TimeInputType::SetupLayoutParameters(
146 DateTimeEditElement::LayoutParameters& layout_parameters,
147 const DateComponents& date) const {
148 if (ShouldHaveSecondField(date)) {
149 layout_parameters.date_time_format = layout_parameters.locale.TimeFormat();
150 layout_parameters.fallback_date_time_format = "HH:mm:ss";
151 } else {
152 layout_parameters.date_time_format =
153 layout_parameters.locale.ShortTimeFormat();
154 layout_parameters.fallback_date_time_format = "HH:mm";
155 }
156 if (!ParseToDateComponents(
157 GetElement().FastGetAttribute(html_names::kMinAttr),
158 &layout_parameters.minimum))
159 layout_parameters.minimum = DateComponents();
160 if (!ParseToDateComponents(
161 GetElement().FastGetAttribute(html_names::kMaxAttr),
162 &layout_parameters.maximum))
163 layout_parameters.maximum = DateComponents();
164 }
165
IsValidFormat(bool has_year,bool has_month,bool has_week,bool has_day,bool has_ampm,bool has_hour,bool has_minute,bool has_second) const166 bool TimeInputType::IsValidFormat(bool has_year,
167 bool has_month,
168 bool has_week,
169 bool has_day,
170 bool has_ampm,
171 bool has_hour,
172 bool has_minute,
173 bool has_second) const {
174 return has_hour && has_minute && has_ampm;
175 }
176
AriaRoleForPickerIndicator() const177 String TimeInputType::AriaRoleForPickerIndicator() const {
178 return GetLocale().QueryString(IDS_AX_CALENDAR_SHOW_TIME_PICKER);
179 }
180
ReversedRangeOutOfRangeText(const Decimal & minimum,const Decimal & maximum) const181 String TimeInputType::ReversedRangeOutOfRangeText(
182 const Decimal& minimum,
183 const Decimal& maximum) const {
184 return GetLocale().QueryString(
185 IDS_FORM_VALIDATION_REVERSED_RANGE_OUT_OF_RANGE_TIME,
186 LocalizeValue(Serialize(minimum)), LocalizeValue(Serialize(maximum)));
187 }
188
189 } // namespace blink
190