2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2011 Apple Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "TextFieldInputType.h"
35 #include "BeforeTextInsertedEvent.h"
37 #include "HTMLInputElement.h"
38 #include "KeyboardEvent.h"
40 #include "RenderLayer.h"
41 #include "RenderTextControlSingleLine.h"
42 #include "RenderTheme.h"
43 #include "ShadowRoot.h"
44 #include "TextControlInnerElements.h"
45 #include "TextEvent.h"
46 #include "TextIterator.h"
47 #include "WheelEvent.h"
48 #include <wtf/text/WTFString.h>
52 TextFieldInputType::TextFieldInputType(HTMLInputElement* element)
57 TextFieldInputType::~TextFieldInputType()
61 bool TextFieldInputType::isTextField() const
66 bool TextFieldInputType::valueMissing(const String& value) const
68 return value.isEmpty();
71 bool TextFieldInputType::canSetSuggestedValue()
76 void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, bool sendChangeEvent)
78 InputType::setValue(sanitizedValue, valueChanged, sendChangeEvent);
79 element()->updatePlaceholderVisibility(false);
82 element()->updateInnerTextValue();
84 unsigned max = visibleValue().length();
85 if (element()->focused())
86 element()->setSelectionRange(max, max);
88 element()->cacheSelectionInResponseToSetValue(max);
91 void TextFieldInputType::dispatchChangeEventInResponseToSetValue()
93 // If the user is still editing this field, dispatch an input event rather than a change event.
94 // The change event will be dispatched when editing finishes.
95 if (element()->focused()) {
96 element()->dispatchFormControlInputEvent();
99 InputType::dispatchChangeEventInResponseToSetValue();
102 void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
104 if (!element()->focused())
106 Frame* frame = element()->document()->frame();
107 if (!frame || !frame->editor()->doTextFieldCommandFromEvent(element(), event))
109 event->setDefaultHandled();
112 void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
114 if (element()->disabled() || element()->readOnly())
116 const String& key = event->keyIdentifier();
120 else if (key == "Down")
124 element()->stepUpFromRenderer(step);
125 event->setDefaultHandled();
128 void TextFieldInputType::handleWheelEventForSpinButton(WheelEvent* event)
130 if (element()->disabled() || element()->readOnly() || !element()->focused())
133 if (event->wheelDeltaY() > 0)
135 else if (event->wheelDeltaY() < 0)
139 element()->stepUpFromRenderer(step);
140 event->setDefaultHandled();
143 void TextFieldInputType::forwardEvent(Event* event)
145 if (element()->renderer() && (event->isMouseEvent() || event->isDragEvent() || event->isWheelEvent() || event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)) {
146 RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element()->renderer());
147 if (event->type() == eventNames().blurEvent) {
148 if (RenderBox* innerTextRenderer = innerTextElement()->renderBox()) {
149 if (RenderLayer* innerLayer = innerTextRenderer->layer())
150 innerLayer->scrollToOffset(!renderTextControl->style()->isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0, RenderLayer::ScrollOffsetClamped);
153 renderTextControl->capsLockStateMayHaveChanged();
154 } else if (event->type() == eventNames().focusEvent)
155 renderTextControl->capsLockStateMayHaveChanged();
157 element()->forwardEvent(event);
161 bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
163 return (event->type() == eventNames().textInputEvent && event->isTextEvent() && static_cast<TextEvent*>(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
166 RenderObject* TextFieldInputType::createRenderer(RenderArena* arena, RenderStyle*) const
168 return new (arena) RenderTextControlSingleLine(element());
171 bool TextFieldInputType::needsContainer() const
173 #if ENABLE(INPUT_SPEECH)
174 return element()->isSpeechEnabled();
180 void TextFieldInputType::createShadowSubtree()
182 ASSERT(!m_innerText);
183 ASSERT(!m_innerBlock);
184 ASSERT(!m_innerSpinButton);
186 Document* document = element()->document();
187 RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme();
188 bool shouldHaveSpinButton = theme->shouldHaveSpinButton(element());
189 bool createsContainer = shouldHaveSpinButton || needsContainer();
191 ExceptionCode ec = 0;
192 m_innerText = TextControlInnerTextElement::create(document);
193 if (!createsContainer) {
194 element()->ensureShadowRoot()->appendChild(m_innerText, ec);
198 ShadowRoot* shadowRoot = element()->ensureShadowRoot();
199 m_container = HTMLDivElement::create(document);
200 m_container->setShadowPseudoId("-webkit-textfield-decoration-container");
201 shadowRoot->appendChild(m_container, ec);
203 m_innerBlock = TextControlInnerElement::create(document);
204 m_innerBlock->appendChild(m_innerText, ec);
205 m_container->appendChild(m_innerBlock, ec);
207 #if ENABLE(INPUT_SPEECH)
208 ASSERT(!m_speechButton);
209 if (element()->isSpeechEnabled()) {
210 m_speechButton = InputFieldSpeechButtonElement::create(document);
211 m_container->appendChild(m_speechButton, ec);
215 if (shouldHaveSpinButton) {
216 m_innerSpinButton = SpinButtonElement::create(document);
217 m_container->appendChild(m_innerSpinButton, ec);
221 HTMLElement* TextFieldInputType::containerElement() const
223 return m_container.get();
226 HTMLElement* TextFieldInputType::innerBlockElement() const
228 return m_innerBlock.get();
231 HTMLElement* TextFieldInputType::innerTextElement() const
234 return m_innerText.get();
237 HTMLElement* TextFieldInputType::innerSpinButtonElement() const
239 return m_innerSpinButton.get();
242 #if ENABLE(INPUT_SPEECH)
243 HTMLElement* TextFieldInputType::speechButtonElement() const
245 return m_speechButton.get();
249 HTMLElement* TextFieldInputType::placeholderElement() const
251 return m_placeholder.get();
254 void TextFieldInputType::destroyShadowSubtree()
256 InputType::destroyShadowSubtree();
258 m_placeholder.clear();
259 m_innerBlock.clear();
260 #if ENABLE(INPUT_SPEECH)
261 m_speechButton.clear();
263 m_innerSpinButton.clear();
267 void TextFieldInputType::disabledAttributeChanged()
269 if (m_innerSpinButton)
270 m_innerSpinButton->releaseCapture();
273 void TextFieldInputType::readonlyAttributeChanged()
275 if (m_innerSpinButton)
276 m_innerSpinButton->releaseCapture();
279 bool TextFieldInputType::shouldUseInputMethod() const
284 static bool isASCIILineBreak(UChar c)
286 return c == '\r' || c == '\n';
289 static String limitLength(const String& string, int maxLength)
291 unsigned newLength = numCharactersInGraphemeClusters(string, maxLength);
292 for (unsigned i = 0; i < newLength; ++i) {
293 const UChar current = string[i];
294 if (current < ' ' && current != '\t') {
299 return string.left(newLength);
302 String TextFieldInputType::sanitizeValue(const String& proposedValue)
305 if (!element()->isConformToInputMask(proposedValue)) {
306 if (isConformToInputMask(element()->value()))
307 return element->value();
311 return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
314 void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
316 // Make sure that the text to be inserted will not violate the maxLength.
318 // We use RenderTextControlSingleLine::text() instead of InputElement::value()
319 // because they can be mismatched by sanitizeValue() in
320 // HTMLInputElement::subtreeHasChanged() in some cases.
321 unsigned oldLength = numGraphemeClusters(element()->innerTextValue());
323 // selectionLength represents the selection length of this text field to be
324 // removed by this insertion.
325 // If the text field has no focus, we don't need to take account of the
326 // selection length. The selection is the source of text drag-and-drop in
327 // that case, and nothing in the text field will be removed.
328 unsigned selectionLength = element()->focused() ? numGraphemeClusters(plainText(element()->document()->frame()->selection()->selection().toNormalizedRange().get())) : 0;
329 ASSERT(oldLength >= selectionLength);
331 // Selected characters will be removed by the next text event.
332 unsigned baseLength = oldLength - selectionLength;
333 unsigned maxLength = static_cast<unsigned>(isTextType() ? element()->maxLength() : HTMLInputElement::maximumLength); // maxLength can never be negative.
334 unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
336 // Truncate the inserted text to avoid violating the maxLength and other constraints.
338 RefPtr<Range> range = element()->document()->frame()->selection()->selection().toNormalizedRange();
339 String candidateString = toRenderTextControlSingleLine(element()->renderer())->text();
341 candidateString.replace(range->startOffset(), range->endOffset(), event->text());
343 candidateString.insert(event->text(), range->startOffset());
344 if (!element()->isConformToInputMask(candidateString)) {
350 String eventText = event->text();
351 eventText.replace("\r\n", " ");
352 eventText.replace('\r', ' ');
353 eventText.replace('\n', ' ');
355 event->setText(limitLength(eventText, appendableLength));
358 bool TextFieldInputType::shouldRespectListAttribute()
363 void TextFieldInputType::updatePlaceholderText()
365 if (!supportsPlaceholder())
367 ExceptionCode ec = 0;
368 String placeholderText = element()->strippedPlaceholder();
369 if (placeholderText.isEmpty()) {
371 m_placeholder->parentNode()->removeChild(m_placeholder.get(), ec);
373 m_placeholder.clear();
377 if (!m_placeholder) {
378 m_placeholder = HTMLDivElement::create(element()->document());
379 m_placeholder->setShadowPseudoId("-webkit-input-placeholder");
380 element()->shadowRoot()->insertBefore(m_placeholder, m_container ? m_container->nextSibling() : innerTextElement()->nextSibling(), ec);
383 m_placeholder->setInnerText(placeholderText, ec);
387 } // namespace WebCore