6230248729542267a72dbc2a7513276e1b315964
[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 "Frame.h"
37 #include "HTMLInputElement.h"
38 #include "KeyboardEvent.h"
39 #include "RenderTextControlSingleLine.h"
40 #include "RenderTheme.h"
41 #include "ShadowRoot.h"
42 #include "TextControlInnerElements.h"
43 #include "TextEvent.h"
44 #include "TextIterator.h"
45 #include "WheelEvent.h"
46 #include <wtf/text/WTFString.h>
47
48 namespace WebCore {
49
50 TextFieldInputType::TextFieldInputType(HTMLInputElement* element)
51     : InputType(element)
52     , m_innerText(0)
53     , m_innerSpinButton(0)
54     , m_outerSpinButton(0)
55 #if ENABLE(INPUT_SPEECH)
56     , m_speechButton(0)
57 #endif
58 {
59 }
60
61 bool TextFieldInputType::isTextField() const
62 {
63     return true;
64 }
65
66 bool TextFieldInputType::valueMissing(const String& value) const
67 {
68     return value.isEmpty();
69 }
70
71 bool TextFieldInputType::canSetSuggestedValue()
72 {
73     return true;
74 }
75
76 void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
77 {
78     if (!element()->focused())
79         return;
80     Frame* frame = element()->document()->frame();
81     if (!frame || !frame->editor()->doTextFieldCommandFromEvent(element(), event))
82         return;
83     event->setDefaultHandled();
84 }
85
86 void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
87 {
88     if (element()->disabled() || element()->readOnly())
89         return;
90     const String& key = event->keyIdentifier();
91     int step = 0;
92     if (key == "Up")
93         step = 1;
94     else if (key == "Down")
95         step = -1;
96     else
97         return;
98     element()->stepUpFromRenderer(step);
99     event->setDefaultHandled();
100 }
101
102 void TextFieldInputType::handleWheelEventForSpinButton(WheelEvent* event)
103 {
104     if (element()->disabled() || element()->readOnly() || !element()->focused())
105         return;
106     int step = 0;
107     if (event->wheelDeltaY() > 0)
108         step = 1;
109     else if (event->wheelDeltaY() < 0)
110         step = -1;
111     else
112         return;
113     element()->stepUpFromRenderer(step);
114     event->setDefaultHandled();
115 }
116
117 void TextFieldInputType::forwardEvent(Event* event)
118 {
119     if (element()->renderer() && (event->isMouseEvent() || event->isDragEvent() || event->isWheelEvent() || event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent))
120         toRenderTextControlSingleLine(element()->renderer())->forwardEvent(event);
121 }
122
123 bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
124 {
125     return (event->type() == eventNames().textInputEvent && event->isTextEvent() && static_cast<TextEvent*>(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
126 }
127
128 RenderObject* TextFieldInputType::createRenderer(RenderArena* arena, RenderStyle*) const
129 {
130     return new (arena) RenderTextControlSingleLine(element(), element()->placeholderShouldBeVisible());
131 }
132
133 void TextFieldInputType::createShadowSubtree()
134 {
135     ASSERT(!m_innerText);
136     ASSERT(!m_innerSpinButton);
137     ASSERT(!m_outerSpinButton);
138
139     bool shouldHaveSpinButton = RenderTheme::themeForPage(element()->document()->page())->shouldHaveSpinButton(element());
140     bool hasDecorations = shouldHaveSpinButton;
141 #if ENABLE(INPUT_SPEECH)
142     if (element()->isSpeechEnabled())
143         hasDecorations = true;
144 #endif
145
146     ExceptionCode ec = 0;
147     Document* document = element()->document();
148     RefPtr<HTMLElement> innerText = TextControlInnerTextElement::create(document);
149     m_innerText = innerText.get();
150     element()->ensureShadowRoot()->appendChild(innerText.release(), ec);
151     if (!hasDecorations)
152         return;
153
154 #if ENABLE(INPUT_SPEECH)
155     ASSERT(!m_speechButton);
156     if (element()->isSpeechEnabled()) {
157         RefPtr<HTMLElement> speech = InputFieldSpeechButtonElement::create(document);
158         m_speechButton = speech.get();
159         element()->ensureShadowRoot()->appendChild(speech.release(), ec);
160     }
161 #endif
162
163     if (shouldHaveSpinButton) {
164         RefPtr<HTMLElement> inner = SpinButtonElement::createInner(document);
165         RefPtr<HTMLElement> outer = SpinButtonElement::createOuter(document);
166         m_innerSpinButton = inner.get();
167         m_outerSpinButton = outer.get();
168         element()->ensureShadowRoot()->appendChild(inner.release(), ec);
169         element()->ensureShadowRoot()->appendChild(outer.release(), ec);
170     }
171 }
172
173 void TextFieldInputType::destroyShadowSubtree()
174 {
175     InputType::destroyShadowSubtree();
176     m_innerText = 0;
177 #if ENABLE(INPUT_SPEECH)
178     m_speechButton = 0;
179 #endif
180     m_innerSpinButton = 0;
181     m_outerSpinButton = 0;
182 }
183
184 bool TextFieldInputType::shouldUseInputMethod() const
185 {
186     return true;
187 }
188
189 static String replaceEOLAndLimitLength(const String& proposedValue, int maxLength)
190 {
191     String string = proposedValue;
192     string.replace("\r\n", " ");
193     string.replace('\r', ' ');
194     string.replace('\n', ' ');
195
196     unsigned newLength = numCharactersInGraphemeClusters(string, maxLength);
197     for (unsigned i = 0; i < newLength; ++i) {
198         const UChar current = string[i];
199         if (current < ' ' && current != '\t') {
200             newLength = i;
201             break;
202         }
203     }
204     return string.left(newLength);
205 }
206
207 String TextFieldInputType::sanitizeValue(const String& proposedValue)
208 {
209 #if ENABLE(WCSS)
210     if (!element()->isConformToInputMask(proposedValue)) {
211         if (isConformToInputMask(element()->value()))
212             return element->value();
213         return String();
214     }
215 #endif
216     return replaceEOLAndLimitLength(proposedValue, HTMLInputElement::maximumLength);
217 }
218
219 void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
220 {
221     // Make sure that the text to be inserted will not violate the maxLength.
222
223     // We use RenderTextControlSingleLine::text() instead of InputElement::value()
224     // because they can be mismatched by sanitizeValue() in
225     // RenderTextControlSingleLine::subtreeHasChanged() in some cases.
226     unsigned oldLength = numGraphemeClusters(toRenderTextControlSingleLine(element()->renderer())->text());
227
228     // selectionLength represents the selection length of this text field to be
229     // removed by this insertion.
230     // If the text field has no focus, we don't need to take account of the
231     // selection length. The selection is the source of text drag-and-drop in
232     // that case, and nothing in the text field will be removed.
233     unsigned selectionLength = element()->focused() ? numGraphemeClusters(plainText(element()->document()->frame()->selection()->selection().toNormalizedRange().get())) : 0;
234     ASSERT(oldLength >= selectionLength);
235
236     // Selected characters will be removed by the next text event.
237     unsigned baseLength = oldLength - selectionLength;
238     unsigned maxLength = static_cast<unsigned>(isTextType() ? element()->maxLength() : HTMLInputElement::maximumLength); // maxLength can never be negative.
239     unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
240
241     // Truncate the inserted text to avoid violating the maxLength and other constraints.
242 #if ENABLE(WCSS)
243     RefPtr<Range> range = element()->document()->frame()->selection()->selection().toNormalizedRange();
244     String candidateString = toRenderTextControlSingleLine(element()->renderer())->text();
245     if (selectionLength)
246         candidateString.replace(range->startOffset(), range->endOffset(), event->text());
247     else
248         candidateString.insert(event->text(), range->startOffset());
249     if (!element()->isConformToInputMask(candidateString)) {
250         event->setText("");
251         return;
252     }
253 #endif
254     event->setText(replaceEOLAndLimitLength(event->text(), appendableLength));
255 }
256
257 bool TextFieldInputType::shouldRespectListAttribute()
258 {
259     return true;
260 }
261
262 } // namespace WebCore