F4 key should open the picker popup on Windows and Linux
[WebKit-https.git] / Source / WebCore / html / BaseMultipleFieldsDateAndTimeInputType.cpp
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 "config.h"
32 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
33 #include "BaseMultipleFieldsDateAndTimeInputType.h"
34
35 #include "CSSValueKeywords.h"
36 #include "DateComponents.h"
37 #include "DateTimeFieldsState.h"
38 #include "ElementShadow.h"
39 #include "FormController.h"
40 #include "HTMLDataListElement.h"
41 #include "HTMLInputElement.h"
42 #include "HTMLOptionElement.h"
43 #include "KeyboardEvent.h"
44 #include "Localizer.h"
45 #include "Page.h"
46 #include "PickerIndicatorElement.h"
47 #include "RenderTheme.h"
48 #include "ShadowRoot.h"
49 #include <wtf/DateMath.h>
50
51 namespace WebCore {
52
53 void BaseMultipleFieldsDateAndTimeInputType::didBlurFromControl()
54 {
55     // We don't need to call blur(). This function is called when control
56     // lost focus.
57
58     // Remove focus ring by CSS "focus" pseudo class.
59     element()->setFocus(false);
60 }
61
62 void BaseMultipleFieldsDateAndTimeInputType::didFocusOnControl()
63 {
64     // We don't need to call focus(). This function is called when control
65     // got focus.
66
67     // Add focus ring by CSS "focus" pseudo class.
68     element()->setFocus(true);
69 }
70
71 void BaseMultipleFieldsDateAndTimeInputType::editControlValueChanged()
72 {
73     RefPtr<HTMLInputElement> input(element());
74     input->setValueInternal(sanitizeValue(m_dateTimeEditElement->value()), DispatchNoEvent);
75     input->setNeedsStyleRecalc();
76     input->dispatchFormControlInputEvent();
77     input->dispatchFormControlChangeEvent();
78     input->notifyFormStateChanged();
79 }
80
81 bool BaseMultipleFieldsDateAndTimeInputType::hasCustomFocusLogic() const
82 {
83     return false;
84 }
85
86 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerDisabled() const
87 {
88     return element()->readOnly();
89 }
90
91 bool BaseMultipleFieldsDateAndTimeInputType::isEditControlOwnerReadOnly() const
92 {
93     return element()->disabled();
94 }
95
96 BaseMultipleFieldsDateAndTimeInputType::BaseMultipleFieldsDateAndTimeInputType(HTMLInputElement* element)
97     : BaseDateAndTimeInputType(element)
98     , m_dateTimeEditElement(0)
99     , m_pickerIndicatorElement(0)
100     , m_pickerIndicatorIsVisible(false)
101     , m_pickerIndicatorIsAlwaysVisible(false)
102 {
103 }
104
105 BaseMultipleFieldsDateAndTimeInputType::~BaseMultipleFieldsDateAndTimeInputType()
106 {
107     if (m_dateTimeEditElement)
108         m_dateTimeEditElement->removeEditControlOwner();
109 }
110
111 void BaseMultipleFieldsDateAndTimeInputType::blur()
112 {
113     if (m_dateTimeEditElement)
114         m_dateTimeEditElement->blurByOwner();
115 }
116
117 RenderObject* BaseMultipleFieldsDateAndTimeInputType::createRenderer(RenderArena* arena, RenderStyle* style) const
118 {
119     return InputType::createRenderer(arena, style);
120 }
121
122 void BaseMultipleFieldsDateAndTimeInputType::createShadowSubtree()
123 {
124     DEFINE_STATIC_LOCAL(AtomicString, dateAndTimeInputContainerPseudoId, ("-webkit-date-and-time-container", AtomicString::ConstructFromLiteral));
125
126     ASSERT(element()->shadow());
127
128     Document* document = element()->document();
129     RefPtr<HTMLDivElement> container = HTMLDivElement::create(document);
130     element()->userAgentShadowRoot()->appendChild(container);
131     container->setShadowPseudoId(dateAndTimeInputContainerPseudoId);
132
133     RefPtr<DateTimeEditElement> dateTimeEditElement(DateTimeEditElement::create(document, *this));
134     m_dateTimeEditElement = dateTimeEditElement.get();
135     container->appendChild(m_dateTimeEditElement);
136     updateInnerTextValue();
137
138 #if ENABLE(DATALIST_ELEMENT) || ENABLE(CALENDAR_PICKER)
139     bool shouldAddPickerIndicator = false;
140 #if ENABLE(DATALIST_ELEMENT)
141     if (InputType::themeSupportsDataListUI(this))
142         shouldAddPickerIndicator = true;
143 #endif
144 #if ENABLE(CALENDAR_PICKER)
145     RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme();
146     if (theme->supportsCalendarPicker(formControlType())) {
147         shouldAddPickerIndicator = true;
148         m_pickerIndicatorIsAlwaysVisible = true;
149     }
150 #endif
151     if (shouldAddPickerIndicator) {
152         RefPtr<PickerIndicatorElement> pickerElement = PickerIndicatorElement::create(document);
153         m_pickerIndicatorElement = pickerElement.get();
154         container->appendChild(m_pickerIndicatorElement);
155         m_pickerIndicatorIsVisible = true;
156         updatePickerIndicatorVisibility();
157     }
158 #endif // ENABLE(DATALIST_ELEMENT) || ENABLE(CALENDAR_PICKER)
159 }
160
161 void BaseMultipleFieldsDateAndTimeInputType::destroyShadowSubtree()
162 {
163     if (m_dateTimeEditElement) {
164         m_dateTimeEditElement->removeEditControlOwner();
165         m_dateTimeEditElement = 0;
166     }
167     BaseDateAndTimeInputType::destroyShadowSubtree();
168 }
169
170 void BaseMultipleFieldsDateAndTimeInputType::focus(bool)
171 {
172     if (m_dateTimeEditElement)
173         m_dateTimeEditElement->focusByOwner();
174 }
175
176 void BaseMultipleFieldsDateAndTimeInputType::forwardEvent(Event* event)
177 {
178     if (m_dateTimeEditElement)
179         m_dateTimeEditElement->defaultEventHandler(event);
180 }
181
182 void BaseMultipleFieldsDateAndTimeInputType::disabledAttributeChanged()
183 {
184     if (m_dateTimeEditElement)
185         m_dateTimeEditElement->disabledStateChanged();
186 }
187
188 void BaseMultipleFieldsDateAndTimeInputType::handleKeydownEvent(KeyboardEvent* event)
189 {
190     Document* document = element()->document();
191     RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme();
192     if (theme->shouldOpenPickerWithF4Key() && event->keyIdentifier() == "F4") {
193         if (m_pickerIndicatorElement)
194             m_pickerIndicatorElement->openPopup();
195         event->setDefaultHandled();
196     } else if (m_pickerIndicatorIsVisible && event->keyIdentifier() == "Down" && event->getModifierState("Alt")) {
197         if (m_pickerIndicatorElement)
198             m_pickerIndicatorElement->openPopup();
199         event->setDefaultHandled();
200     } else
201         forwardEvent(event);
202 }
203
204 bool BaseMultipleFieldsDateAndTimeInputType::isKeyboardFocusable(KeyboardEvent*) const
205 {
206     return false;
207 }
208
209 bool BaseMultipleFieldsDateAndTimeInputType::isMouseFocusable() const
210 {
211     return false;
212 }
213
214 AtomicString BaseMultipleFieldsDateAndTimeInputType::localeIdentifier() const
215 {
216     return element()->computeInheritedLanguage();
217 }
218
219 void BaseMultipleFieldsDateAndTimeInputType::minOrMaxAttributeChanged()
220 {
221     updateInnerTextValue();
222 }
223
224 void BaseMultipleFieldsDateAndTimeInputType::readonlyAttributeChanged()
225 {
226     if (m_dateTimeEditElement)
227         m_dateTimeEditElement->readOnlyStateChanged();
228 }
229
230 bool BaseMultipleFieldsDateAndTimeInputType::isTextField() const
231 {
232     return false;
233 }
234
235 void BaseMultipleFieldsDateAndTimeInputType::restoreFormControlState(const FormControlState& state)
236 {
237     if (!m_dateTimeEditElement)
238         return;
239     DateComponents date;
240     setMillisecondToDateComponents(createStepRange(AnyIsDefaultStep).minimum().toDouble(), &date);
241     DateTimeFieldsState dateTimeFieldsState = DateTimeFieldsState::restoreFormControlState(state);
242     m_dateTimeEditElement->setValueAsDateTimeFieldsState(dateTimeFieldsState, date);
243     element()->setValueInternal(sanitizeValue(m_dateTimeEditElement->value()), DispatchNoEvent);
244 }
245
246 FormControlState BaseMultipleFieldsDateAndTimeInputType::saveFormControlState() const
247 {
248     if (!m_dateTimeEditElement)
249         return FormControlState();
250
251     return m_dateTimeEditElement->valueAsDateTimeFieldsState().saveFormControlState();
252 }
253
254 void BaseMultipleFieldsDateAndTimeInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
255 {
256     InputType::setValue(sanitizedValue, valueChanged, eventBehavior);
257     if (valueChanged)
258         updateInnerTextValue();
259 }
260
261 bool BaseMultipleFieldsDateAndTimeInputType::shouldUseInputMethod() const
262 {
263     return false;
264 }
265
266 void BaseMultipleFieldsDateAndTimeInputType::stepAttributeChanged()
267 {
268     updateInnerTextValue();
269 }
270
271 void BaseMultipleFieldsDateAndTimeInputType::updateInnerTextValue()
272 {
273     if (!m_dateTimeEditElement)
274         return;
275
276     DateTimeEditElement::LayoutParameters layoutParameters(element()->localizer(), createStepRange(AnyIsDefaultStep));
277
278     DateComponents date;
279     const bool hasValue = parseToDateComponents(element()->value(), &date);
280     if (!hasValue)
281         setMillisecondToDateComponents(layoutParameters.stepRange.minimum().toDouble(), &date);
282
283     setupLayoutParameters(layoutParameters, date);
284
285     if (hasValue)
286         m_dateTimeEditElement->setValueAsDate(layoutParameters, date);
287     else
288         m_dateTimeEditElement->setEmptyValue(layoutParameters, date);
289 }
290
291 #if ENABLE(DATALIST_ELEMENT)
292 void BaseMultipleFieldsDateAndTimeInputType::listAttributeTargetChanged()
293 {
294     updatePickerIndicatorVisibility();
295 }
296 #endif
297
298 #if ENABLE(DATALIST_ELEMENT) || ENABLE(CALENDAR_PICKER)
299 void BaseMultipleFieldsDateAndTimeInputType::updatePickerIndicatorVisibility()
300 {
301 #if ENABLE(CALENDAR_PICKER)
302     if (m_pickerIndicatorIsAlwaysVisible) {
303         showPickerIndicator();
304         return;
305     }
306 #endif
307 #if ENABLE(DATALIST_ELEMENT)
308     if (HTMLDataListElement* dataList = element()->dataList()) {
309         RefPtr<HTMLCollection> options = dataList->options();
310         for (unsigned i = 0; HTMLOptionElement* option = toHTMLOptionElement(options->item(i)); ++i) {
311             if (element()->isValidValue(option->value())) {
312                 showPickerIndicator();
313                 return;
314             }
315         }
316     }
317     hidePickerIndicator();
318 #endif
319 }
320
321 void BaseMultipleFieldsDateAndTimeInputType::hidePickerIndicator()
322 {
323     if (!m_pickerIndicatorIsVisible)
324         return;
325     m_pickerIndicatorIsVisible = false;
326     ASSERT(m_pickerIndicatorElement);
327     m_pickerIndicatorElement->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
328 }
329
330 void BaseMultipleFieldsDateAndTimeInputType::showPickerIndicator()
331 {
332     if (m_pickerIndicatorIsVisible)
333         return;
334     m_pickerIndicatorIsVisible = true;
335     ASSERT(m_pickerIndicatorElement);
336     m_pickerIndicatorElement->removeInlineStyleProperty(CSSPropertyDisplay);
337 }
338 #endif // ENABLE(DATALIST_ELEMENT) || ENABLE(CALENDAR_PICKER)
339
340 int BaseMultipleFieldsDateAndTimeInputType::fullYear(const String& source) const
341 {
342     DateComponents date;
343     if (!parseToDateComponents(source, &date))
344         return DateTimeEditElement::LayoutParameters::undefinedYear();
345     return date.fullYear();
346 }
347
348 bool BaseMultipleFieldsDateAndTimeInputType::shouldHaveSecondField(const DateComponents& date) const
349 {
350     StepRange stepRange = createStepRange(AnyIsDefaultStep);
351     return date.second()
352         || !stepRange.minimum().remainder(static_cast<int>(msPerMinute)).isZero()
353         || !stepRange.step().remainder(static_cast<int>(msPerMinute)).isZero();
354 }
355
356 } // namespace WebCore
357
358 #endif