1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "BaseDateAndTimeInputType.h"
34
35#if ENABLE(DATE_AND_TIME_INPUT_TYPES)
36
37#include "HTMLInputElement.h"
38#include "HTMLNames.h"
39#include "KeyboardEvent.h"
40#include "PlatformLocale.h"
41#include <limits>
42#include <wtf/DateMath.h>
43#include <wtf/MathExtras.h>
44#include <wtf/text/StringView.h>
45
46namespace WebCore {
47
48using namespace HTMLNames;
49
50static const int msecPerMinute = 60 * 1000;
51static const int msecPerSecond = 1000;
52
53double BaseDateAndTimeInputType::valueAsDate() const
54{
55 return valueAsDouble();
56}
57
58ExceptionOr<void> BaseDateAndTimeInputType::setValueAsDate(double value) const
59{
60 ASSERT(element());
61 element()->setValue(serializeWithMilliseconds(value));
62 return { };
63}
64
65double BaseDateAndTimeInputType::valueAsDouble() const
66{
67 ASSERT(element());
68 const Decimal value = parseToNumber(element()->value(), Decimal::nan());
69 return value.isFinite() ? value.toDouble() : DateComponents::invalidMilliseconds();
70}
71
72ExceptionOr<void> BaseDateAndTimeInputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior) const
73{
74 ASSERT(element());
75 element()->setValue(serialize(newValue), eventBehavior);
76 return { };
77}
78
79bool BaseDateAndTimeInputType::typeMismatchFor(const String& value) const
80{
81 return !value.isEmpty() && !parseToDateComponents(value, 0);
82}
83
84bool BaseDateAndTimeInputType::typeMismatch() const
85{
86 ASSERT(element());
87 return typeMismatchFor(element()->value());
88}
89
90Decimal BaseDateAndTimeInputType::defaultValueForStepUp() const
91{
92 double ms = WallTime::now().secondsSinceEpoch().milliseconds();
93 int offset = calculateLocalTimeOffset(ms).offset / msPerMinute;
94 return Decimal::fromDouble(ms + (offset * msPerMinute));
95}
96
97bool BaseDateAndTimeInputType::isSteppable() const
98{
99 return true;
100}
101
102void BaseDateAndTimeInputType::attributeChanged(const QualifiedName& name)
103{
104 if (name == maxAttr || name == minAttr) {
105 if (auto* element = this->element())
106 element->invalidateStyleForSubtree();
107 }
108 InputType::attributeChanged(name);
109}
110
111Decimal BaseDateAndTimeInputType::parseToNumber(const String& source, const Decimal& defaultValue) const
112{
113 DateComponents date;
114 if (!parseToDateComponents(source, &date))
115 return defaultValue;
116 double msec = date.millisecondsSinceEpoch();
117 ASSERT(std::isfinite(msec));
118 return Decimal::fromDouble(msec);
119}
120
121bool BaseDateAndTimeInputType::parseToDateComponents(const String& source, DateComponents* out) const
122{
123 if (source.isEmpty())
124 return false;
125 DateComponents ignoredResult;
126 if (!out)
127 out = &ignoredResult;
128 return parseToDateComponentsInternal(StringView(source).upconvertedCharacters(), source.length(), out);
129}
130
131String BaseDateAndTimeInputType::serialize(const Decimal& value) const
132{
133 if (!value.isFinite())
134 return String();
135 DateComponents date;
136 if (!setMillisecondToDateComponents(value.toDouble(), &date))
137 return String();
138 return serializeWithComponents(date);
139}
140
141String BaseDateAndTimeInputType::serializeWithComponents(const DateComponents& date) const
142{
143 ASSERT(element());
144 Decimal step;
145 if (!element()->getAllowedValueStep(&step))
146 return date.toString();
147 if (step.remainder(msecPerMinute).isZero())
148 return date.toString(DateComponents::None);
149 if (step.remainder(msecPerSecond).isZero())
150 return date.toString(DateComponents::Second);
151 return date.toString(DateComponents::Millisecond);
152}
153
154String BaseDateAndTimeInputType::serializeWithMilliseconds(double value) const
155{
156 return serialize(Decimal::fromDouble(value));
157}
158
159String BaseDateAndTimeInputType::localizeValue(const String& proposedValue) const
160{
161 DateComponents date;
162 if (!parseToDateComponents(proposedValue, &date))
163 return proposedValue;
164
165 ASSERT(element());
166 String localized = element()->locale().formatDateTime(date);
167 return localized.isEmpty() ? proposedValue : localized;
168}
169
170String BaseDateAndTimeInputType::visibleValue() const
171{
172 ASSERT(element());
173 return localizeValue(element()->value());
174}
175
176String BaseDateAndTimeInputType::sanitizeValue(const String& proposedValue) const
177{
178 return typeMismatchFor(proposedValue) ? String() : proposedValue;
179}
180
181bool BaseDateAndTimeInputType::supportsReadOnly() const
182{
183 return true;
184}
185
186bool BaseDateAndTimeInputType::shouldRespectListAttribute()
187{
188 return InputType::themeSupportsDataListUI(this);
189}
190
191bool BaseDateAndTimeInputType::valueMissing(const String& value) const
192{
193 ASSERT(element());
194 return element()->isRequired() && value.isEmpty();
195}
196
197#if PLATFORM(IOS_FAMILY)
198bool BaseDateAndTimeInputType::isKeyboardFocusable(KeyboardEvent*) const
199{
200 ASSERT(element());
201 return !element()->isReadOnly() && element()->isTextFormControlFocusable();
202}
203#endif
204
205} // namespace WebCore
206#endif
207