REGRESSION (r90971): the cursor is painted “behind” the placeholder text
[WebKit.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 "ChromeClient.h"
38 #include "ElementShadow.h"
39 #include "FormDataList.h"
40 #include "Frame.h"
41 #include "HTMLInputElement.h"
42 #include "HTMLNames.h"
43 #include "KeyboardEvent.h"
44 #include "Page.h"
45 #include "RenderLayer.h"
46 #include "RenderTextControlSingleLine.h"
47 #include "RenderTheme.h"
48 #include "ShadowRoot.h"
49 #include "TextControlInnerElements.h"
50 #include "TextEvent.h"
51 #include "TextIterator.h"
52 #include "WheelEvent.h"
53 #include <wtf/text/WTFString.h>
54
55 namespace WebCore {
56
57 using namespace HTMLNames;
58
59 TextFieldInputType::TextFieldInputType(HTMLInputElement* element)
60     : InputType(element)
61 {
62 }
63
64 TextFieldInputType::~TextFieldInputType()
65 {
66 }
67
68 bool TextFieldInputType::isTextField() const
69 {
70     return true;
71 }
72
73 bool TextFieldInputType::valueMissing(const String& value) const
74 {
75     return element()->required() && value.isEmpty();
76 }
77
78 bool TextFieldInputType::canSetSuggestedValue()
79 {
80     return true;
81 }
82
83 void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
84 {
85     // Grab this input element to keep reference even if JS event handler
86     // changes input type.
87     RefPtr<HTMLInputElement> input(element());
88
89     // We don't ask InputType::setValue to dispatch events because
90     // TextFieldInputType dispatches events different way from InputType.
91     InputType::setValue(sanitizedValue, valueChanged, DispatchNoEvent);
92
93     if (valueChanged)
94         input->updateInnerTextValue();
95
96     unsigned max = visibleValue().length();
97     if (input->focused())
98         input->setSelectionRange(max, max);
99     else
100         input->cacheSelectionInResponseToSetValue(max);
101
102     if (!valueChanged)
103         return;
104
105     switch (eventBehavior) {
106     case DispatchChangeEvent:
107         // If the user is still editing this field, dispatch an input event rather than a change event.
108         // The change event will be dispatched when editing finishes.
109         if (input->focused())
110             input->dispatchFormControlInputEvent();
111         else
112             input->dispatchFormControlChangeEvent();
113         break;
114
115     case DispatchInputAndChangeEvent: {
116         input->dispatchFormControlInputEvent();
117         input->dispatchFormControlChangeEvent();
118         break;
119     }
120
121     case DispatchNoEvent:
122         break;
123     }
124
125     // FIXME: Why do we do this when eventBehavior == DispatchNoEvent
126     if (!input->focused() || eventBehavior == DispatchNoEvent)
127         input->setTextAsOfLastFormControlChangeEvent(sanitizedValue);
128 }
129
130 void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
131 {
132     if (!element()->focused())
133         return;
134     Frame* frame = element()->document()->frame();
135     if (!frame || !frame->editor()->doTextFieldCommandFromEvent(element(), event))
136         return;
137     event->setDefaultHandled();
138 }
139
140 void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
141 {
142     if (element()->disabled() || element()->readOnly())
143         return;
144     const String& key = event->keyIdentifier();
145     int step = 0;
146     if (key == "Up")
147         step = 1;
148     else if (key == "Down")
149         step = -1;
150     else
151         return;
152     element()->stepUpFromRenderer(step);
153     event->setDefaultHandled();
154 }
155
156 void TextFieldInputType::handleWheelEventForSpinButton(WheelEvent* event)
157 {
158     if (element()->disabled() || element()->readOnly() || !element()->focused())
159         return;
160     int step = 0;
161     if (event->wheelDeltaY() > 0)
162         step = 1;
163     else if (event->wheelDeltaY() < 0)
164         step = -1;
165     else
166         return;
167     element()->stepUpFromRenderer(step);
168     event->setDefaultHandled();
169 }
170
171 void TextFieldInputType::forwardEvent(Event* event)
172 {
173     if (element()->renderer() && (event->isMouseEvent() || event->isDragEvent() || event->hasInterface(eventNames().interfaceForWheelEvent) || event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)) {
174         RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element()->renderer());
175         if (event->type() == eventNames().blurEvent) {
176             if (RenderBox* innerTextRenderer = innerTextElement()->renderBox()) {
177                 if (RenderLayer* innerLayer = innerTextRenderer->layer())
178                     innerLayer->scrollToOffset(!renderTextControl->style()->isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0, RenderLayer::ScrollOffsetClamped);
179             }
180
181             renderTextControl->capsLockStateMayHaveChanged();
182         } else if (event->type() == eventNames().focusEvent)
183             renderTextControl->capsLockStateMayHaveChanged();
184
185         element()->forwardEvent(event);
186     }
187 }
188
189 void TextFieldInputType::handleBlurEvent()
190 {
191     InputType::handleBlurEvent();
192     if (Frame* frame = element()->document()->frame())
193         frame->editor()->textFieldDidEndEditing(element());
194 }
195
196 bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
197 {
198     return (event->type() == eventNames().textInputEvent && event->hasInterface(eventNames().interfaceForTextEvent) && static_cast<TextEvent*>(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
199 }
200
201 RenderObject* TextFieldInputType::createRenderer(RenderArena* arena, RenderStyle*) const
202 {
203     return new (arena) RenderTextControlSingleLine(element());
204 }
205
206 bool TextFieldInputType::needsContainer() const
207 {
208 #if ENABLE(INPUT_SPEECH)
209     return element()->isSpeechEnabled();
210 #else
211     return false;
212 #endif
213 }
214
215 bool TextFieldInputType::shouldHaveSpinButton() const
216 {
217     Document* document = element()->document();
218     RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme();
219     return theme->shouldHaveSpinButton(element());
220 }
221
222 void TextFieldInputType::createShadowSubtree()
223 {
224     ASSERT(element()->shadow());
225
226     ASSERT(!m_innerText);
227     ASSERT(!m_innerBlock);
228     ASSERT(!m_innerSpinButton);
229
230     Document* document = element()->document();
231     ChromeClient* chromeClient = document->page() ? document->page()->chrome()->client() : 0;
232     bool shouldAddDecorations = chromeClient && chromeClient->willAddTextFieldDecorationsTo(element());
233     bool shouldHaveSpinButton = this->shouldHaveSpinButton();
234     bool createsContainer = shouldHaveSpinButton || needsContainer() || shouldAddDecorations;
235
236     ExceptionCode ec = 0;
237     m_innerText = TextControlInnerTextElement::create(document);
238     if (!createsContainer) {
239         element()->shadow()->oldestShadowRoot()->appendChild(m_innerText, ec);
240         return;
241     }
242
243     ShadowRoot* shadowRoot = element()->shadow()->oldestShadowRoot();
244     m_container = HTMLDivElement::create(document);
245     m_container->setShadowPseudoId("-webkit-textfield-decoration-container");
246     shadowRoot->appendChild(m_container, ec);
247
248     m_innerBlock = TextControlInnerElement::create(document);
249     m_innerBlock->appendChild(m_innerText, ec);
250     m_container->appendChild(m_innerBlock, ec);
251
252 #if ENABLE(INPUT_SPEECH)
253     ASSERT(!m_speechButton);
254     if (element()->isSpeechEnabled()) {
255         m_speechButton = InputFieldSpeechButtonElement::create(document);
256         m_container->appendChild(m_speechButton, ec);
257     }
258 #endif
259
260     if (shouldHaveSpinButton) {
261         m_innerSpinButton = SpinButtonElement::create(document);
262         m_container->appendChild(m_innerSpinButton, ec);
263     }
264
265     if (shouldAddDecorations)
266         chromeClient->addTextFieldDecorationsTo(element());
267 }
268
269 HTMLElement* TextFieldInputType::containerElement() const
270 {
271     return m_container.get();
272 }
273
274 HTMLElement* TextFieldInputType::innerBlockElement() const
275 {
276     return m_innerBlock.get();
277 }
278
279 HTMLElement* TextFieldInputType::innerTextElement() const
280 {
281     ASSERT(m_innerText);
282     return m_innerText.get();
283 }
284
285 HTMLElement* TextFieldInputType::innerSpinButtonElement() const
286 {
287     return m_innerSpinButton.get();
288 }
289
290 #if ENABLE(INPUT_SPEECH)
291 HTMLElement* TextFieldInputType::speechButtonElement() const
292 {
293     return m_speechButton.get();
294 }
295 #endif
296
297 HTMLElement* TextFieldInputType::placeholderElement() const
298 {
299     return m_placeholder.get();
300 }
301
302 void TextFieldInputType::destroyShadowSubtree()
303 {
304     InputType::destroyShadowSubtree();
305     m_innerText.clear();
306     m_placeholder.clear();
307     m_innerBlock.clear();
308 #if ENABLE(INPUT_SPEECH)
309     m_speechButton.clear();
310 #endif
311     m_innerSpinButton.clear();
312     m_container.clear();
313 }
314
315 void TextFieldInputType::disabledAttributeChanged()
316 {
317     if (m_innerSpinButton)
318         m_innerSpinButton->releaseCapture();
319 }
320
321 void TextFieldInputType::readonlyAttributeChanged()
322 {
323     if (m_innerSpinButton)
324         m_innerSpinButton->releaseCapture();
325 }
326
327 bool TextFieldInputType::shouldUseInputMethod() const
328 {
329     return true;
330 }
331
332 static bool isASCIILineBreak(UChar c)
333 {
334     return c == '\r' || c == '\n';
335 }
336
337 static String limitLength(const String& string, int maxLength)
338 {
339     unsigned newLength = numCharactersInGraphemeClusters(string, maxLength);
340     for (unsigned i = 0; i < newLength; ++i) {
341         const UChar current = string[i];
342         if (current < ' ' && current != '\t') {
343             newLength = i;
344             break;
345         }
346     }
347     return string.left(newLength);
348 }
349
350 String TextFieldInputType::sanitizeValue(const String& proposedValue) const
351 {
352     return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
353 }
354
355 void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
356 {
357     // Make sure that the text to be inserted will not violate the maxLength.
358
359     // We use RenderTextControlSingleLine::text() instead of InputElement::value()
360     // because they can be mismatched by sanitizeValue() in
361     // HTMLInputElement::subtreeHasChanged() in some cases.
362     unsigned oldLength = numGraphemeClusters(element()->innerTextValue());
363
364     // selectionLength represents the selection length of this text field to be
365     // removed by this insertion.
366     // If the text field has no focus, we don't need to take account of the
367     // selection length. The selection is the source of text drag-and-drop in
368     // that case, and nothing in the text field will be removed.
369     unsigned selectionLength = element()->focused() ? numGraphemeClusters(plainText(element()->document()->frame()->selection()->selection().toNormalizedRange().get())) : 0;
370     ASSERT(oldLength >= selectionLength);
371
372     // Selected characters will be removed by the next text event.
373     unsigned baseLength = oldLength - selectionLength;
374     unsigned maxLength = static_cast<unsigned>(isTextType() ? element()->maxLength() : HTMLInputElement::maximumLength); // maxLength can never be negative.
375     unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
376
377     // Truncate the inserted text to avoid violating the maxLength and other constraints.
378     String eventText = event->text();
379     eventText.replace("\r\n", " ");
380     eventText.replace('\r', ' ');
381     eventText.replace('\n', ' ');
382
383     event->setText(limitLength(eventText, appendableLength));
384 }
385
386 bool TextFieldInputType::shouldRespectListAttribute()
387 {
388     return InputType::themeSupportsDataListUI(this);
389 }
390
391 void TextFieldInputType::updatePlaceholderText()
392 {
393     if (!supportsPlaceholder())
394         return;
395     ExceptionCode ec = 0;
396     String placeholderText = usesFixedPlaceholder() ? fixedPlaceholder() : element()->strippedPlaceholder();
397     if (placeholderText.isEmpty()) {
398         if (m_placeholder) {
399             m_placeholder->parentNode()->removeChild(m_placeholder.get(), ec);
400             ASSERT(!ec);
401             m_placeholder.clear();
402         }
403         return;
404     }
405     if (!m_placeholder) {
406         m_placeholder = HTMLDivElement::create(element()->document());
407         m_placeholder->setShadowPseudoId("-webkit-input-placeholder");
408         element()->shadow()->oldestShadowRoot()->insertBefore(m_placeholder, m_container ? m_container->nextSibling() : innerTextElement()->nextSibling(), ec);
409         ASSERT(!ec);
410     }
411     m_placeholder->setInnerText(placeholderText, ec);
412     ASSERT(!ec);
413     element()->fixPlaceholderRenderer(m_placeholder.get(), m_container ? m_container.get() : m_innerText.get());
414 }
415
416 void TextFieldInputType::attach()
417 {
418     InputType::attach();
419     element()->fixPlaceholderRenderer(m_placeholder.get(), m_container ? m_container.get() : m_innerText.get());
420 }
421
422 bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const
423 {
424     InputType::appendFormData(list, multipart);
425     const AtomicString& dirnameAttrValue = element()->fastGetAttribute(dirnameAttr);
426     if (!dirnameAttrValue.isNull())
427         list.appendData(dirnameAttrValue, element()->directionForFormData());
428     return true;
429 }
430
431 } // namespace WebCore