Upstream version 10.39.225.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 "bindings/core/v8/ExceptionStatePlaceholder.h"
36 #include "core/HTMLNames.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/FrameHost.h"
45 #include "core/frame/LocalFrame.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 blink {
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 PassRefPtrWillBeRawPtr<DataListIndicatorElement> create(Document& document)
102     {
103         RefPtrWillBeRawPtr<DataListIndicatorElement> element = adoptRefWillBeNoop(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 !ENABLE(OILPAN)
119     if (SpinButtonElement* spinButton = spinButtonElement())
120         spinButton->removeSpinButtonOwner();
121 #endif
122 }
123
124 SpinButtonElement* TextFieldInputType::spinButtonElement() const
125 {
126     return toSpinButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
127 }
128
129 bool TextFieldInputType::shouldShowFocusRingOnMouseFocus() const
130 {
131     return true;
132 }
133
134 bool TextFieldInputType::isTextField() const
135 {
136     return true;
137 }
138
139 bool TextFieldInputType::valueMissing(const String& value) const
140 {
141     return element().isRequired() && value.isEmpty();
142 }
143
144 bool TextFieldInputType::canSetSuggestedValue()
145 {
146     return true;
147 }
148
149 void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
150 {
151     // Grab this input element to keep reference even if JS event handler
152     // changes input type.
153     RefPtrWillBeRawPtr<HTMLInputElement> input(element());
154
155     // We don't ask InputType::setValue to dispatch events because
156     // TextFieldInputType dispatches events different way from InputType.
157     InputType::setValue(sanitizedValue, valueChanged, DispatchNoEvent);
158
159     if (valueChanged)
160         input->updateView();
161
162     unsigned max = visibleValue().length();
163     if (input->focused())
164         input->setSelectionRange(max, max);
165     else
166         input->cacheSelectionInResponseToSetValue(max);
167
168     if (!valueChanged)
169         return;
170
171     switch (eventBehavior) {
172     case DispatchChangeEvent:
173         // If the user is still editing this field, dispatch an input event rather than a change event.
174         // The change event will be dispatched when editing finishes.
175         if (input->focused())
176             input->dispatchFormControlInputEvent();
177         else
178             input->dispatchFormControlChangeEvent();
179         break;
180
181     case DispatchInputAndChangeEvent: {
182         input->dispatchFormControlInputEvent();
183         input->dispatchFormControlChangeEvent();
184         break;
185     }
186
187     case DispatchNoEvent:
188         break;
189     }
190
191     if (!input->focused())
192         input->setTextAsOfLastFormControlChangeEvent(sanitizedValue);
193 }
194
195 void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
196 {
197     if (!element().focused())
198         return;
199     if (Chrome* chrome = this->chrome()) {
200         chrome->client().handleKeyboardEventOnTextField(element(), *event);
201         return;
202     }
203     event->setDefaultHandled();
204 }
205
206 void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
207 {
208     if (element().isDisabledOrReadOnly())
209         return;
210     const String& key = event->keyIdentifier();
211     if (key == "Up")
212         spinButtonStepUp();
213     else if (key == "Down" && !event->altKey())
214         spinButtonStepDown();
215     else
216         return;
217     element().dispatchFormControlChangeEvent();
218     event->setDefaultHandled();
219 }
220
221 void TextFieldInputType::forwardEvent(Event* event)
222 {
223     if (SpinButtonElement* spinButton = spinButtonElement()) {
224         spinButton->forwardEvent(event);
225         if (event->defaultHandled())
226             return;
227     }
228
229     if (element().renderer() && (event->isMouseEvent() || event->isDragEvent() || event->hasInterface(EventNames::WheelEvent) || event->type() == EventTypeNames::blur || event->type() == EventTypeNames::focus)) {
230         RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element().renderer());
231         if (event->type() == EventTypeNames::blur) {
232             if (RenderBox* innerEditorRenderer = element().innerEditorElement()->renderBox()) {
233                 // FIXME: This class has no need to know about RenderLayer!
234                 if (RenderLayer* innerLayer = innerEditorRenderer->layer()) {
235                     if (RenderLayerScrollableArea* innerScrollableArea = innerLayer->scrollableArea()) {
236                         IntSize scrollOffset(!renderTextControl->style()->isLeftToRightDirection() ? innerScrollableArea->scrollWidth().toInt() : 0, 0);
237                         innerScrollableArea->scrollToOffset(scrollOffset, ScrollOffsetClamped);
238                     }
239                 }
240             }
241
242             renderTextControl->capsLockStateMayHaveChanged();
243         } else if (event->type() == EventTypeNames::focus) {
244             renderTextControl->capsLockStateMayHaveChanged();
245         }
246
247         element().forwardEvent(event);
248     }
249 }
250
251 void TextFieldInputType::handleFocusEvent(Element* oldFocusedNode, FocusType focusType)
252 {
253     InputType::handleFocusEvent(oldFocusedNode, focusType);
254     element().beginEditing();
255 }
256
257 void TextFieldInputType::handleBlurEvent()
258 {
259     InputType::handleBlurEvent();
260     element().endEditing();
261     if (SpinButtonElement *spinButton = spinButtonElement())
262         spinButton->releaseCapture();
263 }
264
265 bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
266 {
267     return (event->type() == EventTypeNames::textInput && event->hasInterface(EventNames::TextEvent) && toTextEvent(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
268 }
269
270 RenderObject* TextFieldInputType::createRenderer(RenderStyle*) const
271 {
272     return new RenderTextControlSingleLine(&element());
273 }
274
275 bool TextFieldInputType::shouldHaveSpinButton() const
276 {
277     return RenderTheme::theme().shouldHaveSpinButton(&element());
278 }
279
280 void TextFieldInputType::createShadowSubtree()
281 {
282     ASSERT(element().shadow());
283     ShadowRoot* shadowRoot = element().userAgentShadowRoot();
284     ASSERT(!shadowRoot->hasChildren());
285
286     Document& document = element().document();
287     bool shouldHaveSpinButton = this->shouldHaveSpinButton();
288     bool shouldHaveDataListIndicator = element().hasValidDataListOptions();
289     bool createsContainer = shouldHaveSpinButton || shouldHaveDataListIndicator || needsContainer();
290
291     RefPtrWillBeRawPtr<TextControlInnerEditorElement> innerEditor = TextControlInnerEditorElement::create(document);
292     if (!createsContainer) {
293         shadowRoot->appendChild(innerEditor.release());
294         return;
295     }
296
297     RefPtrWillBeRawPtr<TextControlInnerContainer> container = TextControlInnerContainer::create(document);
298     container->setShadowPseudoId(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
299     shadowRoot->appendChild(container);
300
301     RefPtrWillBeRawPtr<EditingViewPortElement> editingViewPort = EditingViewPortElement::create(document);
302     editingViewPort->appendChild(innerEditor.release());
303     container->appendChild(editingViewPort.release());
304
305     if (shouldHaveDataListIndicator)
306         container->appendChild(DataListIndicatorElement::create(document));
307     // FIXME: Because of a special handling for a spin button in
308     // RenderTextControlSingleLine, we need to put it to the last position. It's
309     // inconsistent with multiple-fields date/time types.
310     if (shouldHaveSpinButton)
311         container->appendChild(SpinButtonElement::create(document, *this));
312
313     // See listAttributeTargetChanged too.
314 }
315
316 Element* TextFieldInputType::containerElement() const
317 {
318     return element().userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
319 }
320
321 void TextFieldInputType::destroyShadowSubtree()
322 {
323     InputType::destroyShadowSubtree();
324     if (SpinButtonElement* spinButton = spinButtonElement())
325         spinButton->removeSpinButtonOwner();
326 }
327
328 void TextFieldInputType::listAttributeTargetChanged()
329 {
330     Element* picker = element().userAgentShadowRoot()->getElementById(ShadowElementNames::pickerIndicator());
331     bool didHavePickerIndicator = picker;
332     bool willHavePickerIndicator = element().hasValidDataListOptions();
333     if (didHavePickerIndicator == willHavePickerIndicator)
334         return;
335     if (willHavePickerIndicator) {
336         Document& document = element().document();
337         if (Element* container = containerElement()) {
338             container->insertBefore(DataListIndicatorElement::create(document), spinButtonElement());
339         } else {
340             // FIXME: The following code is similar to createShadowSubtree(),
341             // but they are different. We should simplify the code by making
342             // containerElement mandatory.
343             RefPtrWillBeRawPtr<Element> rpContainer = TextControlInnerContainer::create(document);
344             rpContainer->setShadowPseudoId(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
345             RefPtrWillBeRawPtr<Element> innerEditor = element().innerEditorElement();
346             innerEditor->parentNode()->replaceChild(rpContainer.get(), innerEditor.get());
347             RefPtrWillBeRawPtr<Element> editingViewPort = EditingViewPortElement::create(document);
348             editingViewPort->appendChild(innerEditor.release());
349             rpContainer->appendChild(editingViewPort.release());
350             rpContainer->appendChild(DataListIndicatorElement::create(document));
351             if (element().document().focusedElement() == element())
352                 element().updateFocusAppearance(true /* restore selection */);
353         }
354     } else {
355         picker->remove(ASSERT_NO_EXCEPTION);
356     }
357 }
358
359 void TextFieldInputType::attributeChanged()
360 {
361     // FIXME: Updating on any attribute update should be unnecessary. We should
362     // figure out what attributes affect.
363     updateView();
364 }
365
366 void TextFieldInputType::disabledAttributeChanged()
367 {
368     if (SpinButtonElement* spinButton = spinButtonElement())
369         spinButton->releaseCapture();
370 }
371
372 void TextFieldInputType::readonlyAttributeChanged()
373 {
374     if (SpinButtonElement* spinButton = spinButtonElement())
375         spinButton->releaseCapture();
376 }
377
378 bool TextFieldInputType::supportsReadOnly() const
379 {
380     return true;
381 }
382
383 static bool isASCIILineBreak(UChar c)
384 {
385     return c == '\r' || c == '\n';
386 }
387
388 static String limitLength(const String& string, unsigned maxLength)
389 {
390     unsigned newLength = std::min(maxLength, string.length());
391     if (newLength == string.length())
392         return string;
393     if (newLength > 0 && U16_IS_LEAD(string[newLength - 1]))
394         --newLength;
395     return string.left(newLength);
396 }
397
398 String TextFieldInputType::sanitizeValue(const String& proposedValue) const
399 {
400     return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
401 }
402
403 void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
404 {
405     // Make sure that the text to be inserted will not violate the maxLength.
406
407     // We use HTMLInputElement::innerEditorValue() instead of
408     // HTMLInputElement::value() because they can be mismatched by
409     // sanitizeValue() in HTMLInputElement::subtreeHasChanged() in some cases.
410     unsigned oldLength = element().innerEditorValue().length();
411
412     // selectionLength represents the selection length of this text field to be
413     // removed by this insertion.
414     // If the text field has no focus, we don't need to take account of the
415     // selection length. The selection is the source of text drag-and-drop in
416     // that case, and nothing in the text field will be removed.
417     unsigned selectionLength = element().focused() ? plainText(element().document().frame()->selection().selection().toNormalizedRange().get()).length() : 0;
418     ASSERT(oldLength >= selectionLength);
419
420     // Selected characters will be removed by the next text event.
421     unsigned baseLength = oldLength - selectionLength;
422     unsigned maxLength = static_cast<unsigned>(this->maxLength()); // maxLength can never be negative.
423     unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
424
425     // Truncate the inserted text to avoid violating the maxLength and other constraints.
426     String eventText = event->text();
427     unsigned textLength = eventText.length();
428     while (textLength > 0 && isASCIILineBreak(eventText[textLength - 1]))
429         textLength--;
430     eventText.truncate(textLength);
431     eventText.replace("\r\n", " ");
432     eventText.replace('\r', ' ');
433     eventText.replace('\n', ' ');
434
435     event->setText(limitLength(eventText, appendableLength));
436 }
437
438 bool TextFieldInputType::shouldRespectListAttribute()
439 {
440     return true;
441 }
442
443 void TextFieldInputType::updatePlaceholderText()
444 {
445     if (!supportsPlaceholder())
446         return;
447     HTMLElement* placeholder = element().placeholderElement();
448     String placeholderText = element().strippedPlaceholder();
449     if (placeholderText.isEmpty()) {
450         if (placeholder)
451             placeholder->remove(ASSERT_NO_EXCEPTION);
452         return;
453     }
454     if (!placeholder) {
455         RefPtrWillBeRawPtr<HTMLElement> newElement = HTMLDivElement::create(element().document());
456         placeholder = newElement.get();
457         placeholder->setShadowPseudoId(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral));
458         placeholder->setAttribute(idAttr, ShadowElementNames::placeholder());
459         Element* container = containerElement();
460         Node* previous = container ? container : element().innerEditorElement();
461         previous->parentNode()->insertBefore(placeholder, previous->nextSibling());
462         ASSERT_WITH_SECURITY_IMPLICATION(placeholder->parentNode() == previous->parentNode());
463     }
464     placeholder->setTextContent(placeholderText);
465 }
466
467 bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const
468 {
469     InputType::appendFormData(list, multipart);
470     const AtomicString& dirnameAttrValue = element().fastGetAttribute(dirnameAttr);
471     if (!dirnameAttrValue.isNull())
472         list.appendData(dirnameAttrValue, element().directionForFormData());
473     return true;
474 }
475
476 String TextFieldInputType::convertFromVisibleValue(const String& visibleValue) const
477 {
478     return visibleValue;
479 }
480
481 void TextFieldInputType::subtreeHasChanged()
482 {
483     ASSERT(element().renderer());
484
485     bool wasChanged = element().wasChangedSinceLastFormControlChangeEvent();
486     element().setChangedSinceLastFormControlChangeEvent(true);
487
488     // We don't need to call sanitizeUserInputValue() function here because
489     // HTMLInputElement::handleBeforeTextInsertedEvent() has already called
490     // sanitizeUserInputValue().
491     // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
492     element().setValueFromRenderer(sanitizeValue(convertFromVisibleValue(element().innerEditorValue())));
493     element().updatePlaceholderVisibility(false);
494     // Recalc for :invalid change.
495     element().setNeedsStyleRecalc(SubtreeStyleChange);
496
497     didSetValueByUserEdit(wasChanged ? ValueChangeStateChanged : ValueChangeStateNone);
498 }
499
500 void TextFieldInputType::didSetValueByUserEdit(ValueChangeState state)
501 {
502     if (!element().focused())
503         return;
504     if (Chrome* chrome = this->chrome())
505         chrome->client().didChangeValueInTextField(element());
506 }
507
508 void TextFieldInputType::spinButtonStepDown()
509 {
510     stepUpFromRenderer(-1);
511 }
512
513 void TextFieldInputType::spinButtonStepUp()
514 {
515     stepUpFromRenderer(1);
516 }
517
518 void TextFieldInputType::updateView()
519 {
520     if (!element().suggestedValue().isNull()) {
521         element().setInnerEditorValue(element().suggestedValue());
522         element().updatePlaceholderVisibility(false);
523     } else if (element().needsToUpdateViewValue()) {
524         // Update the view only if needsToUpdateViewValue is true. It protects
525         // an unacceptable view value from being overwritten with the DOM value.
526         //
527         // e.g. <input type=number> has a view value "abc", and input.max is
528         // updated. In this case, updateView() is called but we should not
529         // update the view value.
530         element().setInnerEditorValue(visibleValue());
531         element().updatePlaceholderVisibility(false);
532     }
533 }
534
535 void TextFieldInputType::focusAndSelectSpinButtonOwner()
536 {
537     RefPtrWillBeRawPtr<HTMLInputElement> input(element());
538     input->focus();
539     input->select();
540 }
541
542 bool TextFieldInputType::shouldSpinButtonRespondToMouseEvents()
543 {
544     return !element().isDisabledOrReadOnly();
545 }
546
547 bool TextFieldInputType::shouldSpinButtonRespondToWheelEvents()
548 {
549     return shouldSpinButtonRespondToMouseEvents() && element().focused();
550 }
551
552 void TextFieldInputType::spinButtonDidReleaseMouseCapture(SpinButtonElement::EventDispatch eventDispatch)
553 {
554     if (eventDispatch == SpinButtonElement::EventDispatchAllowed)
555         element().dispatchFormControlChangeEvent();
556 }
557
558 } // namespace blink