Remove HTMLInputElement's suggestedValue
[WebKit-https.git] / Source / WebCore / html / 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 "TextFieldInputType.h"
34
35 #include "BeforeTextInsertedEvent.h"
36 #include "Chrome.h"
37 #include "Editor.h"
38 #include "FormDataList.h"
39 #include "Frame.h"
40 #include "FrameSelection.h"
41 #include "HTMLInputElement.h"
42 #include "HTMLNames.h"
43 #include "KeyboardEvent.h"
44 #include "NodeRenderStyle.h"
45 #include "Page.h"
46 #include "RenderLayer.h"
47 #include "RenderTextControlSingleLine.h"
48 #include "RenderTheme.h"
49 #include "ShadowRoot.h"
50 #include "TextBreakIterator.h"
51 #include "TextControlInnerElements.h"
52 #include "TextEvent.h"
53 #include "TextIterator.h"
54 #include "WheelEvent.h"
55
56 namespace WebCore {
57
58 using namespace HTMLNames;
59
60 TextFieldInputType::TextFieldInputType(HTMLInputElement& element)
61     : InputType(element)
62 {
63 }
64
65 TextFieldInputType::~TextFieldInputType()
66 {
67     if (m_innerSpinButton)
68         m_innerSpinButton->removeSpinButtonOwner();
69 }
70
71 bool TextFieldInputType::isKeyboardFocusable(KeyboardEvent*) const
72 {
73 #if PLATFORM(IOS)
74     if (element().isReadOnly())
75         return false;
76 #endif
77     return element().isTextFormControlFocusable();
78 }
79
80 bool TextFieldInputType::isMouseFocusable() const
81 {
82     return element().isTextFormControlFocusable();
83 }
84
85 bool TextFieldInputType::isTextField() const
86 {
87     return true;
88 }
89
90 bool TextFieldInputType::valueMissing(const String& value) const
91 {
92     return element().isRequired() && value.isEmpty();
93 }
94
95 void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
96 {
97     // Grab this input element to keep reference even if JS event handler
98     // changes input type.
99     Ref<HTMLInputElement> input(element());
100
101     // We don't ask InputType::setValue to dispatch events because
102     // TextFieldInputType dispatches events different way from InputType.
103     InputType::setValue(sanitizedValue, valueChanged, DispatchNoEvent);
104
105     if (valueChanged)
106         updateInnerTextValue();
107
108     unsigned max = visibleValue().length();
109     if (input->focused())
110         input->setSelectionRange(max, max);
111     else
112         input->cacheSelectionInResponseToSetValue(max);
113
114     if (!valueChanged)
115         return;
116
117     switch (eventBehavior) {
118     case DispatchChangeEvent:
119         // If the user is still editing this field, dispatch an input event rather than a change event.
120         // The change event will be dispatched when editing finishes.
121         if (input->focused())
122             input->dispatchFormControlInputEvent();
123         else
124             input->dispatchFormControlChangeEvent();
125         break;
126
127     case DispatchInputAndChangeEvent: {
128         input->dispatchFormControlInputEvent();
129         input->dispatchFormControlChangeEvent();
130         break;
131     }
132
133     case DispatchNoEvent:
134         break;
135     }
136
137     // FIXME: Why do we do this when eventBehavior == DispatchNoEvent
138     if (!input->focused() || eventBehavior == DispatchNoEvent)
139         input->setTextAsOfLastFormControlChangeEvent(sanitizedValue);
140 }
141
142 void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
143 {
144     if (!element().focused())
145         return;
146     Frame* frame = element().document().frame();
147     if (!frame || !frame->editor().doTextFieldCommandFromEvent(&element(), event))
148         return;
149     event->setDefaultHandled();
150 }
151
152 void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
153 {
154     if (element().isDisabledOrReadOnly())
155         return;
156     const String& key = event->keyIdentifier();
157     if (key == "Up")
158         spinButtonStepUp();
159     else if (key == "Down")
160         spinButtonStepDown();
161     else
162         return;
163     event->setDefaultHandled();
164 }
165
166 void TextFieldInputType::forwardEvent(Event* event)
167 {
168     if (m_innerSpinButton) {
169         m_innerSpinButton->forwardEvent(event);
170         if (event->defaultHandled())
171             return;
172     }
173
174     if (event->isMouseEvent()
175         || event->isDragEvent()
176         || event->eventInterface() == WheelEventInterfaceType
177         || event->type() == eventNames().blurEvent
178         || event->type() == eventNames().focusEvent)
179     {
180         element().document().updateStyleIfNeededForNode(element());
181
182         if (element().renderer()) {
183             RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element().renderer());
184             if (event->type() == eventNames().blurEvent) {
185                 if (RenderTextControlInnerBlock* innerTextRenderer = innerTextElement()->renderer()) {
186                     if (RenderLayer* innerLayer = innerTextRenderer->layer()) {
187                         IntSize scrollOffset(!renderTextControl->style().isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0);
188                         innerLayer->scrollToOffset(scrollOffset, RenderLayer::ScrollOffsetClamped);
189                     }
190                 }
191
192                 renderTextControl->capsLockStateMayHaveChanged();
193             } else if (event->type() == eventNames().focusEvent)
194                 renderTextControl->capsLockStateMayHaveChanged();
195
196             element().forwardEvent(event);
197         }
198     }
199 }
200
201 void TextFieldInputType::handleBlurEvent()
202 {
203     InputType::handleBlurEvent();
204     element().endEditing();
205 }
206
207 bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
208 {
209     return (event->type() == eventNames().textInputEvent && event->eventInterface() == TextEventInterfaceType && toTextEvent(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
210 }
211
212 RenderPtr<RenderElement> TextFieldInputType::createInputRenderer(PassRef<RenderStyle> style)
213 {
214     return createRenderer<RenderTextControlSingleLine>(element(), WTF::move(style));
215 }
216
217 bool TextFieldInputType::needsContainer() const
218 {
219     return false;
220 }
221
222 bool TextFieldInputType::shouldHaveSpinButton() const
223 {
224     Document& document = element().document();
225     RefPtr<RenderTheme> theme = document.page() ? &document.page()->theme() : RenderTheme::defaultTheme();
226     return theme->shouldHaveSpinButton(element());
227 }
228
229 void TextFieldInputType::createShadowSubtree()
230 {
231     ASSERT(element().shadowRoot());
232
233     ASSERT(!m_innerText);
234     ASSERT(!m_innerBlock);
235     ASSERT(!m_innerSpinButton);
236
237     Document& document = element().document();
238     bool shouldHaveSpinButton = this->shouldHaveSpinButton();
239     bool createsContainer = shouldHaveSpinButton || needsContainer();
240
241     m_innerText = TextControlInnerTextElement::create(document);
242     if (!createsContainer) {
243         element().userAgentShadowRoot()->appendChild(m_innerText, IGNORE_EXCEPTION);
244         return;
245     }
246
247     ShadowRoot* shadowRoot = element().userAgentShadowRoot();
248     m_container = TextControlInnerContainer::create(document);
249     m_container->setPseudo(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
250     shadowRoot->appendChild(m_container, IGNORE_EXCEPTION);
251
252     m_innerBlock = TextControlInnerElement::create(document);
253     m_innerBlock->appendChild(m_innerText, IGNORE_EXCEPTION);
254     m_container->appendChild(m_innerBlock, IGNORE_EXCEPTION);
255
256     if (shouldHaveSpinButton) {
257         m_innerSpinButton = SpinButtonElement::create(document, *this);
258         m_container->appendChild(m_innerSpinButton, IGNORE_EXCEPTION);
259     }
260 }
261
262 HTMLElement* TextFieldInputType::containerElement() const
263 {
264     return m_container.get();
265 }
266
267 HTMLElement* TextFieldInputType::innerBlockElement() const
268 {
269     return m_innerBlock.get();
270 }
271
272 TextControlInnerTextElement* TextFieldInputType::innerTextElement() const
273 {
274     ASSERT(m_innerText);
275     return m_innerText.get();
276 }
277
278 HTMLElement* TextFieldInputType::innerSpinButtonElement() const
279 {
280     return m_innerSpinButton.get();
281 }
282
283 HTMLElement* TextFieldInputType::placeholderElement() const
284 {
285     return m_placeholder.get();
286 }
287
288 void TextFieldInputType::destroyShadowSubtree()
289 {
290     InputType::destroyShadowSubtree();
291     m_innerText.clear();
292     m_placeholder.clear();
293     m_innerBlock.clear();
294     if (m_innerSpinButton)
295         m_innerSpinButton->removeSpinButtonOwner();
296     m_innerSpinButton.clear();
297     m_container.clear();
298 }
299
300 void TextFieldInputType::attributeChanged()
301 {
302     // FIXME: Updating the inner text on any attribute update should
303     // be unnecessary. We should figure out what attributes affect.
304     updateInnerTextValue();
305 }
306
307 void TextFieldInputType::disabledAttributeChanged()
308 {
309     if (m_innerSpinButton)
310         m_innerSpinButton->releaseCapture();
311 }
312
313 void TextFieldInputType::readonlyAttributeChanged()
314 {
315     if (m_innerSpinButton)
316         m_innerSpinButton->releaseCapture();
317 }
318
319 bool TextFieldInputType::supportsReadOnly() const
320 {
321     return true;
322 }
323
324 bool TextFieldInputType::shouldUseInputMethod() const
325 {
326     return true;
327 }
328
329 static bool isASCIILineBreak(UChar c)
330 {
331     return c == '\r' || c == '\n';
332 }
333
334 static String limitLength(const String& string, int maxLength)
335 {
336     unsigned newLength = numCharactersInGraphemeClusters(string, maxLength);
337     for (unsigned i = 0; i < newLength; ++i) {
338         const UChar current = string[i];
339         if (current < ' ' && current != '\t') {
340             newLength = i;
341             break;
342         }
343     }
344     return string.left(newLength);
345 }
346
347 String TextFieldInputType::sanitizeValue(const String& proposedValue) const
348 {
349     return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
350 }
351
352 void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
353 {
354     // Make sure that the text to be inserted will not violate the maxLength.
355
356     // We use RenderTextControlSingleLine::text() instead of InputElement::value()
357     // because they can be mismatched by sanitizeValue() in
358     // HTMLInputElement::subtreeHasChanged() in some cases.
359     String innerText = element().innerTextValue();
360     unsigned oldLength = numGraphemeClusters(innerText);
361
362     // selectionLength represents the selection length of this text field to be
363     // removed by this insertion.
364     // If the text field has no focus, we don't need to take account of the
365     // selection length. The selection is the source of text drag-and-drop in
366     // that case, and nothing in the text field will be removed.
367     unsigned selectionLength = 0;
368     if (element().focused()) {
369         ASSERT(enclosingTextFormControl(element().document().frame()->selection().selection().start()) == &element());
370         selectionLength = numGraphemeClusters(innerText.substring(element().selectionStart(), element().selectionEnd()));
371     }
372     ASSERT(oldLength >= selectionLength);
373
374     // Selected characters will be removed by the next text event.
375     unsigned baseLength = oldLength - selectionLength;
376     unsigned maxLength = static_cast<unsigned>(isTextType() ? element().maxLength() : HTMLInputElement::maximumLength); // maxLength can never be negative.
377     unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
378
379     // Truncate the inserted text to avoid violating the maxLength and other constraints.
380     String eventText = event->text();
381     unsigned textLength = eventText.length();
382     while (textLength > 0 && isASCIILineBreak(eventText[textLength - 1]))
383         textLength--;
384     eventText.truncate(textLength);
385     eventText.replace("\r\n", " ");
386     eventText.replace('\r', ' ');
387     eventText.replace('\n', ' ');
388
389     event->setText(limitLength(eventText, appendableLength));
390 }
391
392 bool TextFieldInputType::shouldRespectListAttribute()
393 {
394     return InputType::themeSupportsDataListUI(this);
395 }
396
397 void TextFieldInputType::updatePlaceholderText()
398 {
399     if (!supportsPlaceholder())
400         return;
401     String placeholderText = element().strippedPlaceholder();
402     if (placeholderText.isEmpty()) {
403         if (m_placeholder) {
404             m_placeholder->parentNode()->removeChild(m_placeholder.get(), ASSERT_NO_EXCEPTION);
405             m_placeholder.clear();
406         }
407         return;
408     }
409     if (!m_placeholder) {
410         m_placeholder = HTMLDivElement::create(element().document());
411         m_placeholder->setPseudo(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral));
412         element().userAgentShadowRoot()->insertBefore(m_placeholder, m_container ? m_container.get() : innerTextElement(), ASSERT_NO_EXCEPTION);
413     }
414     m_placeholder->setInnerText(placeholderText, ASSERT_NO_EXCEPTION);
415 }
416
417 bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const
418 {
419     InputType::appendFormData(list, multipart);
420     const AtomicString& dirnameAttrValue = element().fastGetAttribute(dirnameAttr);
421     if (!dirnameAttrValue.isNull())
422         list.appendData(dirnameAttrValue, element().directionForFormData());
423     return true;
424 }
425
426 String TextFieldInputType::convertFromVisibleValue(const String& visibleValue) const
427 {
428     return visibleValue;
429 }
430
431 void TextFieldInputType::subtreeHasChanged()
432 {
433     bool wasChanged = element().wasChangedSinceLastFormControlChangeEvent();
434     element().setChangedSinceLastFormControlChangeEvent(true);
435
436     // We don't need to call sanitizeUserInputValue() function here because
437     // HTMLInputElement::handleBeforeTextInsertedEvent() has already called
438     // sanitizeUserInputValue().
439     // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
440     element().setValueFromRenderer(sanitizeValue(convertFromVisibleValue(element().innerTextValue())));
441     element().updatePlaceholderVisibility(false);
442     // Recalc for :invalid change.
443     element().setNeedsStyleRecalc();
444
445     didSetValueByUserEdit(wasChanged ? ValueChangeStateChanged : ValueChangeStateNone);
446 }
447
448 void TextFieldInputType::didSetValueByUserEdit(ValueChangeState state)
449 {
450     if (!element().focused())
451         return;
452     if (Frame* frame = element().document().frame()) {
453         if (state == ValueChangeStateNone)
454             frame->editor().textFieldDidBeginEditing(&element());
455         frame->editor().textDidChangeInTextField(&element());
456     }
457 }
458
459 void TextFieldInputType::spinButtonStepDown()
460 {
461     stepUpFromRenderer(-1);
462 }
463
464 void TextFieldInputType::spinButtonStepUp()
465 {
466     stepUpFromRenderer(1);
467 }
468
469 void TextFieldInputType::updateInnerTextValue()
470 {
471     if (!element().formControlValueMatchesRenderer()) {
472         // Update the renderer value if the formControlValueMatchesRenderer() flag is false.
473         // It protects an unacceptable renderer value from being overwritten with the DOM value.
474         element().setInnerTextValue(visibleValue());
475         element().updatePlaceholderVisibility(false);
476     }
477 }
478
479 void TextFieldInputType::focusAndSelectSpinButtonOwner()
480 {
481     Ref<HTMLInputElement> input(element());
482     input->focus();
483     input->select();
484 }
485
486 bool TextFieldInputType::shouldSpinButtonRespondToMouseEvents()
487 {
488     return !element().isDisabledOrReadOnly();
489 }
490
491 bool TextFieldInputType::shouldSpinButtonRespondToWheelEvents()
492 {
493     return shouldSpinButtonRespondToMouseEvents() && element().focused();
494 }
495
496 } // namespace WebCore