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