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