Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / forms / TextFieldInputType.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2011 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 "core/html/forms/TextFieldInputType.h"
34
35 #include "HTMLNames.h"
36 #include "bindings/v8/ExceptionStatePlaceholder.h"
37 #include "core/dom/NodeRenderStyle.h"
38 #include "core/dom/shadow/ShadowRoot.h"
39 #include "core/editing/FrameSelection.h"
40 #include "core/editing/TextIterator.h"
41 #include "core/events/BeforeTextInsertedEvent.h"
42 #include "core/events/KeyboardEvent.h"
43 #include "core/events/TextEvent.h"
44 #include "core/frame/Frame.h"
45 #include "core/frame/FrameHost.h"
46 #include "core/html/FormDataList.h"
47 #include "core/html/HTMLInputElement.h"
48 #include "core/html/shadow/ShadowElementNames.h"
49 #include "core/html/shadow/TextControlInnerElements.h"
50 #include "core/page/Chrome.h"
51 #include "core/page/ChromeClient.h"
52 #include "core/rendering/RenderDetailsMarker.h"
53 #include "core/rendering/RenderLayer.h"
54 #include "core/rendering/RenderTextControlSingleLine.h"
55 #include "core/rendering/RenderTheme.h"
56 #include "wtf/text/WTFString.h"
57
58 namespace WebCore {
59
60 using namespace HTMLNames;
61
62 class DataListIndicatorElement FINAL : public HTMLDivElement {
63 private:
64     inline DataListIndicatorElement(Document& document) : HTMLDivElement(document) { }
65     inline HTMLInputElement* hostInput() const { return toHTMLInputElement(shadowHost()); }
66
67     virtual RenderObject* createRenderer(RenderStyle*) OVERRIDE
68     {
69         return new RenderDetailsMarker(this);
70     }
71
72     virtual void* preDispatchEventHandler(Event* event) OVERRIDE
73     {
74         // Chromium opens autofill popup in a mousedown event listener
75         // associated to the document. We don't want to open it in this case
76         // because we opens a datalist chooser later.
77         // FIXME: We should dispatch mousedown events even in such case.
78         if (event->type() == EventTypeNames::mousedown)
79             event->stopPropagation();
80         return 0;
81     }
82
83     virtual void defaultEventHandler(Event* event) OVERRIDE
84     {
85         ASSERT(document().isActive());
86         if (event->type() != EventTypeNames::click)
87             return;
88         HTMLInputElement* host = hostInput();
89         if (host && !host->isDisabledOrReadOnly()) {
90             document().frameHost()->chrome().openTextDataListChooser(*host);
91             event->setDefaultHandled();
92         }
93     }
94
95     virtual bool willRespondToMouseClickEvents() OVERRIDE
96     {
97         return hostInput() && !hostInput()->isDisabledOrReadOnly() && document().isActive();
98     }
99
100 public:
101     static PassRefPtr<DataListIndicatorElement> create(Document& document)
102     {
103         RefPtr<DataListIndicatorElement> element = adoptRef(new DataListIndicatorElement(document));
104         element->setShadowPseudoId(AtomicString("-webkit-calendar-picker-indicator", AtomicString::ConstructFromLiteral));
105         element->setAttribute(idAttr, ShadowElementNames::pickerIndicator());
106         return element.release();
107     }
108
109 };
110
111 TextFieldInputType::TextFieldInputType(HTMLInputElement& element)
112     : InputType(element)
113 {
114 }
115
116 TextFieldInputType::~TextFieldInputType()
117 {
118     if (SpinButtonElement* spinButton = spinButtonElement())
119         spinButton->removeSpinButtonOwner();
120 }
121
122 SpinButtonElement* TextFieldInputType::spinButtonElement() const
123 {
124     return toSpinButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
125 }
126
127 bool TextFieldInputType::shouldShowFocusRingOnMouseFocus() const
128 {
129     return true;
130 }
131
132 bool TextFieldInputType::isTextField() const
133 {
134     return true;
135 }
136
137 bool TextFieldInputType::valueMissing(const String& value) const
138 {
139     return element().isRequired() && value.isEmpty();
140 }
141
142 bool TextFieldInputType::canSetSuggestedValue()
143 {
144     return true;
145 }
146
147 void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
148 {
149     // Grab this input element to keep reference even if JS event handler
150     // changes input type.
151     RefPtr<HTMLInputElement> input(element());
152
153     // We don't ask InputType::setValue to dispatch events because
154     // TextFieldInputType dispatches events different way from InputType.
155     InputType::setValue(sanitizedValue, valueChanged, DispatchNoEvent);
156
157     if (valueChanged)
158         input->updateView();
159
160     unsigned max = visibleValue().length();
161     if (input->focused())
162         input->setSelectionRange(max, max);
163     else
164         input->cacheSelectionInResponseToSetValue(max);
165
166     if (!valueChanged)
167         return;
168
169     switch (eventBehavior) {
170     case DispatchChangeEvent:
171         // If the user is still editing this field, dispatch an input event rather than a change event.
172         // The change event will be dispatched when editing finishes.
173         if (input->focused())
174             input->dispatchFormControlInputEvent();
175         else
176             input->dispatchFormControlChangeEvent();
177         break;
178
179     case DispatchInputAndChangeEvent: {
180         input->dispatchFormControlInputEvent();
181         input->dispatchFormControlChangeEvent();
182         break;
183     }
184
185     case DispatchNoEvent:
186         break;
187     }
188
189     if (!input->focused())
190         input->setTextAsOfLastFormControlChangeEvent(sanitizedValue);
191 }
192
193 void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
194 {
195     if (!element().focused())
196         return;
197     if (Chrome* chrome = this->chrome()) {
198         chrome->client().handleKeyboardEventOnTextField(element(), *event);
199         return;
200     }
201     event->setDefaultHandled();
202 }
203
204 void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
205 {
206     if (element().isDisabledOrReadOnly())
207         return;
208     const String& key = event->keyIdentifier();
209     if (key == "Up")
210         spinButtonStepUp();
211     else if (key == "Down")
212         spinButtonStepDown();
213     else
214         return;
215     event->setDefaultHandled();
216 }
217
218 void TextFieldInputType::forwardEvent(Event* event)
219 {
220     if (SpinButtonElement* spinButton = spinButtonElement()) {
221         spinButton->forwardEvent(event);
222         if (event->defaultHandled())
223             return;
224     }
225
226     if (element().renderer() && (event->isMouseEvent() || event->isDragEvent() || event->hasInterface(EventNames::WheelEvent) || event->type() == EventTypeNames::blur || event->type() == EventTypeNames::focus)) {
227         RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element().renderer());
228         if (event->type() == EventTypeNames::blur) {
229             if (RenderBox* innerTextRenderer = element().innerTextElement()->renderBox()) {
230                 // FIXME: This class has no need to know about RenderLayer!
231                 if (RenderLayer* innerLayer = innerTextRenderer->layer()) {
232                     RenderLayerScrollableArea* innerScrollableArea = innerLayer->scrollableArea();
233                     IntSize scrollOffset(!renderTextControl->style()->isLeftToRightDirection() ? innerScrollableArea->scrollWidth() : 0, 0);
234                     innerScrollableArea->scrollToOffset(scrollOffset, ScrollOffsetClamped);
235                 }
236             }
237
238             renderTextControl->capsLockStateMayHaveChanged();
239         } else if (event->type() == EventTypeNames::focus) {
240             renderTextControl->capsLockStateMayHaveChanged();
241         }
242
243         element().forwardEvent(event);
244     }
245 }
246
247 void TextFieldInputType::handleFocusEvent(Element* oldFocusedNode, FocusType focusType)
248 {
249     InputType::handleFocusEvent(oldFocusedNode, focusType);
250     element().beginEditing();
251 }
252
253 void TextFieldInputType::handleBlurEvent()
254 {
255     InputType::handleBlurEvent();
256     element().endEditing();
257 }
258
259 bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
260 {
261     return (event->type() == EventTypeNames::textInput && event->hasInterface(EventNames::TextEvent) && toTextEvent(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
262 }
263
264 RenderObject* TextFieldInputType::createRenderer(RenderStyle*) const
265 {
266     return new RenderTextControlSingleLine(&element());
267 }
268
269 bool TextFieldInputType::needsContainer() const
270 {
271 #if ENABLE(INPUT_SPEECH)
272     return element().isSpeechEnabled();
273 #else
274     return false;
275 #endif
276 }
277
278 bool TextFieldInputType::shouldHaveSpinButton() const
279 {
280     return RenderTheme::theme().shouldHaveSpinButton(&element());
281 }
282
283 void TextFieldInputType::createShadowSubtree()
284 {
285     ASSERT(element().shadow());
286     ShadowRoot* shadowRoot = element().userAgentShadowRoot();
287     ASSERT(!shadowRoot->hasChildNodes());
288
289     Document& document = element().document();
290     bool shouldHaveSpinButton = this->shouldHaveSpinButton();
291     bool shouldHaveDataListIndicator = element().hasValidDataListOptions();
292     bool createsContainer = shouldHaveSpinButton || shouldHaveDataListIndicator || needsContainer();
293
294     RefPtr<TextControlInnerTextElement> innerEditor = TextControlInnerTextElement::create(document);
295     if (!createsContainer) {
296         shadowRoot->appendChild(innerEditor.release());
297         return;
298     }
299
300     RefPtr<TextControlInnerContainer> container = TextControlInnerContainer::create(document);
301     container->setShadowPseudoId(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
302     shadowRoot->appendChild(container);
303
304     RefPtr<EditingViewPortElement> editingViewPort = EditingViewPortElement::create(document);
305     editingViewPort->appendChild(innerEditor.release());
306     container->appendChild(editingViewPort.release());
307
308 #if ENABLE(INPUT_SPEECH)
309     if (element().isSpeechEnabled())
310         container->appendChild(InputFieldSpeechButtonElement::create(document));
311 #endif
312
313     if (shouldHaveDataListIndicator)
314         container->appendChild(DataListIndicatorElement::create(document));
315     // FIXME: Because of a special handling for a spin button in
316     // RenderTextControlSingleLine, we need to put it to the last position. It's
317     // inconsistent with multiple-fields date/time types.
318     if (shouldHaveSpinButton)
319         container->appendChild(SpinButtonElement::create(document, *this));
320
321     // See listAttributeTargetChanged too.
322 }
323
324 Element* TextFieldInputType::containerElement() const
325 {
326     return element().userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
327 }
328
329 void TextFieldInputType::destroyShadowSubtree()
330 {
331     InputType::destroyShadowSubtree();
332     if (SpinButtonElement* spinButton = spinButtonElement())
333         spinButton->removeSpinButtonOwner();
334 }
335
336 void TextFieldInputType::listAttributeTargetChanged()
337 {
338     Element* picker = element().userAgentShadowRoot()->getElementById(ShadowElementNames::pickerIndicator());
339     bool didHavePickerIndicator = picker;
340     bool willHavePickerIndicator = element().hasValidDataListOptions();
341     if (didHavePickerIndicator == willHavePickerIndicator)
342         return;
343     if (willHavePickerIndicator) {
344         Document& document = element().document();
345         if (Element* container = containerElement()) {
346             container->insertBefore(DataListIndicatorElement::create(document), spinButtonElement());
347         } else {
348             // FIXME: The following code is similar to createShadowSubtree(),
349             // but they are different. We should simplify the code by making
350             // containerElement mandatory.
351             RefPtr<Element> rpContainer = TextControlInnerContainer::create(document);
352             rpContainer->setShadowPseudoId(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
353             RefPtr<Element> innerEditor = element().innerTextElement();
354             innerEditor->parentNode()->replaceChild(rpContainer.get(), innerEditor.get());
355             RefPtr<Element> editingViewPort = EditingViewPortElement::create(document);
356             editingViewPort->appendChild(innerEditor.release());
357             rpContainer->appendChild(editingViewPort.release());
358             rpContainer->appendChild(DataListIndicatorElement::create(document));
359         }
360     } else {
361         picker->remove(ASSERT_NO_EXCEPTION);
362     }
363 }
364
365 void TextFieldInputType::attributeChanged()
366 {
367     // FIXME: Updating on any attribute update should be unnecessary. We should
368     // figure out what attributes affect.
369     updateView();
370 }
371
372 void TextFieldInputType::disabledAttributeChanged()
373 {
374     if (SpinButtonElement* spinButton = spinButtonElement())
375         spinButton->releaseCapture();
376 }
377
378 void TextFieldInputType::readonlyAttributeChanged()
379 {
380     if (SpinButtonElement* spinButton = spinButtonElement())
381         spinButton->releaseCapture();
382 }
383
384 bool TextFieldInputType::supportsReadOnly() const
385 {
386     return true;
387 }
388
389 bool TextFieldInputType::shouldUseInputMethod() const
390 {
391     return true;
392 }
393
394 static bool isASCIILineBreak(UChar c)
395 {
396     return c == '\r' || c == '\n';
397 }
398
399 static String limitLength(const String& string, unsigned maxLength)
400 {
401     unsigned newLength = std::min(maxLength, string.length());
402     // FIXME: We should not truncate the string at a control character. It's not
403     // compatible with IE and Firefox.
404     for (unsigned i = 0; i < newLength; ++i) {
405         const UChar current = string[i];
406         if (current < ' ' && current != '\t') {
407             newLength = i;
408             break;
409         }
410     }
411     if (newLength == string.length())
412         return string;
413     if (newLength > 0 && U16_IS_LEAD(string[newLength - 1]))
414         --newLength;
415     return string.left(newLength);
416 }
417
418 String TextFieldInputType::sanitizeValue(const String& proposedValue) const
419 {
420     return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
421 }
422
423 void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
424 {
425     // Make sure that the text to be inserted will not violate the maxLength.
426
427     // We use HTMLInputElement::innerTextValue() instead of
428     // HTMLInputElement::value() because they can be mismatched by
429     // sanitizeValue() in HTMLInputElement::subtreeHasChanged() in some cases.
430     unsigned oldLength = element().innerTextValue().length();
431
432     // selectionLength represents the selection length of this text field to be
433     // removed by this insertion.
434     // If the text field has no focus, we don't need to take account of the
435     // selection length. The selection is the source of text drag-and-drop in
436     // that case, and nothing in the text field will be removed.
437     unsigned selectionLength = element().focused() ? plainText(element().document().frame()->selection().selection().toNormalizedRange().get()).length() : 0;
438     ASSERT(oldLength >= selectionLength);
439
440     // Selected characters will be removed by the next text event.
441     unsigned baseLength = oldLength - selectionLength;
442     unsigned maxLength = static_cast<unsigned>(isTextType() ? element().maxLength() : HTMLInputElement::maximumLength); // maxLength can never be negative.
443     unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
444
445     // Truncate the inserted text to avoid violating the maxLength and other constraints.
446     String eventText = event->text();
447     unsigned textLength = eventText.length();
448     while (textLength > 0 && isASCIILineBreak(eventText[textLength - 1]))
449         textLength--;
450     eventText.truncate(textLength);
451     eventText.replace("\r\n", " ");
452     eventText.replace('\r', ' ');
453     eventText.replace('\n', ' ');
454
455     event->setText(limitLength(eventText, appendableLength));
456 }
457
458 bool TextFieldInputType::shouldRespectListAttribute()
459 {
460     return InputType::themeSupportsDataListUI(this);
461 }
462
463 void TextFieldInputType::updatePlaceholderText()
464 {
465     if (!supportsPlaceholder())
466         return;
467     HTMLElement* placeholder = element().placeholderElement();
468     String placeholderText = element().strippedPlaceholder();
469     if (placeholderText.isEmpty()) {
470         if (placeholder)
471             placeholder->remove(ASSERT_NO_EXCEPTION);
472         return;
473     }
474     if (!placeholder) {
475         RefPtr<HTMLElement> newElement = HTMLDivElement::create(element().document());
476         placeholder = newElement.get();
477         placeholder->setShadowPseudoId(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral));
478         placeholder->setAttribute(idAttr, ShadowElementNames::placeholder());
479         Element* container = containerElement();
480         Node* previous = container ? container : element().innerTextElement();
481         previous->parentNode()->insertBefore(placeholder, previous->nextSibling());
482         ASSERT_WITH_SECURITY_IMPLICATION(placeholder->parentNode() == previous->parentNode());
483     }
484     placeholder->setTextContent(placeholderText);
485 }
486
487 bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const
488 {
489     InputType::appendFormData(list, multipart);
490     const AtomicString& dirnameAttrValue = element().fastGetAttribute(dirnameAttr);
491     if (!dirnameAttrValue.isNull())
492         list.appendData(dirnameAttrValue, element().directionForFormData());
493     return true;
494 }
495
496 String TextFieldInputType::convertFromVisibleValue(const String& visibleValue) const
497 {
498     return visibleValue;
499 }
500
501 void TextFieldInputType::subtreeHasChanged()
502 {
503     ASSERT(element().renderer());
504
505     bool wasChanged = element().wasChangedSinceLastFormControlChangeEvent();
506     element().setChangedSinceLastFormControlChangeEvent(true);
507
508     // We don't need to call sanitizeUserInputValue() function here because
509     // HTMLInputElement::handleBeforeTextInsertedEvent() has already called
510     // sanitizeUserInputValue().
511     // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
512     element().setValueFromRenderer(sanitizeValue(convertFromVisibleValue(element().innerTextValue())));
513     element().updatePlaceholderVisibility(false);
514     // Recalc for :invalid change.
515     element().setNeedsStyleRecalc(SubtreeStyleChange);
516
517     didSetValueByUserEdit(wasChanged ? ValueChangeStateChanged : ValueChangeStateNone);
518 }
519
520 void TextFieldInputType::didSetValueByUserEdit(ValueChangeState state)
521 {
522     if (!element().focused())
523         return;
524     if (Chrome* chrome = this->chrome())
525         chrome->client().didChangeValueInTextField(element());
526 }
527
528 void TextFieldInputType::spinButtonStepDown()
529 {
530     stepUpFromRenderer(-1);
531 }
532
533 void TextFieldInputType::spinButtonStepUp()
534 {
535     stepUpFromRenderer(1);
536 }
537
538 void TextFieldInputType::updateView()
539 {
540     if (!element().suggestedValue().isNull()) {
541         element().setInnerTextValue(element().suggestedValue());
542         element().updatePlaceholderVisibility(false);
543     } else if (!element().formControlValueMatchesRenderer()) {
544         // Update the renderer value if the formControlValueMatchesRenderer() flag is false.
545         // It protects an unacceptable renderer value from being overwritten with the DOM value.
546         element().setInnerTextValue(visibleValue());
547         element().updatePlaceholderVisibility(false);
548     }
549 }
550
551 void TextFieldInputType::focusAndSelectSpinButtonOwner()
552 {
553     RefPtr<HTMLInputElement> input(element());
554     input->focus();
555     input->select();
556 }
557
558 bool TextFieldInputType::shouldSpinButtonRespondToMouseEvents()
559 {
560     return !element().isDisabledOrReadOnly();
561 }
562
563 bool TextFieldInputType::shouldSpinButtonRespondToWheelEvents()
564 {
565     return shouldSpinButtonRespondToMouseEvents() && element().focused();
566 }
567
568 } // namespace WebCore