Page::theme() should return a reference.
[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     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     Ref<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->eventInterface() == WheelEventInterfaceType || event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)) {
176         RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element().renderer());
177         if (event->type() == eventNames().blurEvent) {
178             if (RenderTextControlInnerBlock* innerTextRenderer = innerTextElement()->renderer()) {
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->eventInterface() == TextEventInterfaceType && static_cast<TextEvent*>(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
202 }
203
204 RenderElement* TextFieldInputType::createRenderer(PassRef<RenderStyle> style) const
205 {
206     return new RenderTextControlSingleLine(element(), std::move(style));
207 }
208
209 bool TextFieldInputType::needsContainer() const
210 {
211 #if ENABLE(INPUT_SPEECH)
212     return element().isSpeechEnabled();
213 #else
214     return false;
215 #endif
216 }
217
218 bool TextFieldInputType::shouldHaveSpinButton() const
219 {
220     Document& document = element().document();
221     RefPtr<RenderTheme> theme = document.page() ? &document.page()->theme() : RenderTheme::defaultTheme();
222     return theme->shouldHaveSpinButton(&element());
223 }
224
225 void TextFieldInputType::createShadowSubtree()
226 {
227     ASSERT(element().shadowRoot());
228
229     ASSERT(!m_innerText);
230     ASSERT(!m_innerBlock);
231     ASSERT(!m_innerSpinButton);
232
233     Document& document = element().document();
234     bool shouldHaveSpinButton = this->shouldHaveSpinButton();
235     bool createsContainer = shouldHaveSpinButton || needsContainer();
236
237     m_innerText = TextControlInnerTextElement::create(document);
238     if (!createsContainer) {
239         element().userAgentShadowRoot()->appendChild(m_innerText, IGNORE_EXCEPTION);
240         return;
241     }
242
243     ShadowRoot* shadowRoot = element().userAgentShadowRoot();
244     m_container = TextControlInnerContainer::create(document);
245     m_container->setPseudo(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
246     shadowRoot->appendChild(m_container, IGNORE_EXCEPTION);
247
248     m_innerBlock = TextControlInnerElement::create(document);
249     m_innerBlock->appendChild(m_innerText, IGNORE_EXCEPTION);
250     m_container->appendChild(m_innerBlock, IGNORE_EXCEPTION);
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, IGNORE_EXCEPTION);
257     }
258 #endif
259
260     if (shouldHaveSpinButton) {
261         m_innerSpinButton = SpinButtonElement::create(document, *this);
262         m_container->appendChild(m_innerSpinButton, IGNORE_EXCEPTION);
263     }
264 }
265
266 HTMLElement* TextFieldInputType::containerElement() const
267 {
268     return m_container.get();
269 }
270
271 HTMLElement* TextFieldInputType::innerBlockElement() const
272 {
273     return m_innerBlock.get();
274 }
275
276 TextControlInnerTextElement* TextFieldInputType::innerTextElement() const
277 {
278     ASSERT(m_innerText);
279     return m_innerText.get();
280 }
281
282 HTMLElement* TextFieldInputType::innerSpinButtonElement() const
283 {
284     return m_innerSpinButton.get();
285 }
286
287 #if ENABLE(INPUT_SPEECH)
288 HTMLElement* TextFieldInputType::speechButtonElement() const
289 {
290     return m_speechButton.get();
291 }
292 #endif
293
294 HTMLElement* TextFieldInputType::placeholderElement() const
295 {
296     return m_placeholder.get();
297 }
298
299 void TextFieldInputType::destroyShadowSubtree()
300 {
301     InputType::destroyShadowSubtree();
302     m_innerText.clear();
303     m_placeholder.clear();
304     m_innerBlock.clear();
305 #if ENABLE(INPUT_SPEECH)
306     m_speechButton.clear();
307 #endif
308     if (m_innerSpinButton)
309         m_innerSpinButton->removeSpinButtonOwner();
310     m_innerSpinButton.clear();
311     m_container.clear();
312 }
313
314 void TextFieldInputType::attributeChanged()
315 {
316     // FIXME: Updating the inner text on any attribute update should
317     // be unnecessary. We should figure out what attributes affect.
318     updateInnerTextValue();
319 }
320
321 void TextFieldInputType::disabledAttributeChanged()
322 {
323     if (m_innerSpinButton)
324         m_innerSpinButton->releaseCapture();
325 }
326
327 void TextFieldInputType::readonlyAttributeChanged()
328 {
329     if (m_innerSpinButton)
330         m_innerSpinButton->releaseCapture();
331 }
332
333 bool TextFieldInputType::supportsReadOnly() const
334 {
335     return true;
336 }
337
338 bool TextFieldInputType::shouldUseInputMethod() const
339 {
340     return true;
341 }
342
343 static bool isASCIILineBreak(UChar c)
344 {
345     return c == '\r' || c == '\n';
346 }
347
348 static String limitLength(const String& string, int maxLength)
349 {
350     unsigned newLength = numCharactersInGraphemeClusters(string, maxLength);
351     for (unsigned i = 0; i < newLength; ++i) {
352         const UChar current = string[i];
353         if (current < ' ' && current != '\t') {
354             newLength = i;
355             break;
356         }
357     }
358     return string.left(newLength);
359 }
360
361 String TextFieldInputType::sanitizeValue(const String& proposedValue) const
362 {
363     return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
364 }
365
366 void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
367 {
368     // Make sure that the text to be inserted will not violate the maxLength.
369
370     // We use RenderTextControlSingleLine::text() instead of InputElement::value()
371     // because they can be mismatched by sanitizeValue() in
372     // HTMLInputElement::subtreeHasChanged() in some cases.
373     unsigned oldLength = numGraphemeClusters(element().innerTextValue());
374
375     // selectionLength represents the selection length of this text field to be
376     // removed by this insertion.
377     // If the text field has no focus, we don't need to take account of the
378     // selection length. The selection is the source of text drag-and-drop in
379     // that case, and nothing in the text field will be removed.
380     unsigned selectionLength = element().focused() ? numGraphemeClusters(plainText(element().document().frame()->selection().selection().toNormalizedRange().get())) : 0;
381     ASSERT(oldLength >= selectionLength);
382
383     // Selected characters will be removed by the next text event.
384     unsigned baseLength = oldLength - selectionLength;
385     unsigned maxLength = static_cast<unsigned>(isTextType() ? element().maxLength() : HTMLInputElement::maximumLength); // maxLength can never be negative.
386     unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
387
388     // Truncate the inserted text to avoid violating the maxLength and other constraints.
389     String eventText = event->text();
390     unsigned textLength = eventText.length();
391     while (textLength > 0 && isASCIILineBreak(eventText[textLength - 1]))
392         textLength--;
393     eventText.truncate(textLength);
394     eventText.replace("\r\n", " ");
395     eventText.replace('\r', ' ');
396     eventText.replace('\n', ' ');
397
398     event->setText(limitLength(eventText, appendableLength));
399 }
400
401 bool TextFieldInputType::shouldRespectListAttribute()
402 {
403     return InputType::themeSupportsDataListUI(this);
404 }
405
406 void TextFieldInputType::updatePlaceholderText()
407 {
408     if (!supportsPlaceholder())
409         return;
410     String placeholderText = element().strippedPlaceholder();
411     if (placeholderText.isEmpty()) {
412         if (m_placeholder) {
413             m_placeholder->parentNode()->removeChild(m_placeholder.get(), ASSERT_NO_EXCEPTION);
414             m_placeholder.clear();
415         }
416         return;
417     }
418     if (!m_placeholder) {
419         m_placeholder = HTMLDivElement::create(element().document());
420         m_placeholder->setPseudo(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral));
421         element().userAgentShadowRoot()->insertBefore(m_placeholder, m_container ? m_container.get() : innerTextElement(), ASSERT_NO_EXCEPTION);
422     }
423     m_placeholder->setInnerText(placeholderText, ASSERT_NO_EXCEPTION);
424 }
425
426 bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const
427 {
428     InputType::appendFormData(list, multipart);
429     const AtomicString& dirnameAttrValue = element().fastGetAttribute(dirnameAttr);
430     if (!dirnameAttrValue.isNull())
431         list.appendData(dirnameAttrValue, element().directionForFormData());
432     return true;
433 }
434
435 String TextFieldInputType::convertFromVisibleValue(const String& visibleValue) const
436 {
437     return visibleValue;
438 }
439
440 void TextFieldInputType::subtreeHasChanged()
441 {
442     ASSERT(element().renderer());
443
444     bool wasChanged = element().wasChangedSinceLastFormControlChangeEvent();
445     element().setChangedSinceLastFormControlChangeEvent(true);
446
447     // We don't need to call sanitizeUserInputValue() function here because
448     // HTMLInputElement::handleBeforeTextInsertedEvent() has already called
449     // sanitizeUserInputValue().
450     // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
451     element().setValueFromRenderer(sanitizeValue(convertFromVisibleValue(element().innerTextValue())));
452     element().updatePlaceholderVisibility(false);
453     // Recalc for :invalid change.
454     element().setNeedsStyleRecalc();
455
456     didSetValueByUserEdit(wasChanged ? ValueChangeStateChanged : ValueChangeStateNone);
457 }
458
459 void TextFieldInputType::didSetValueByUserEdit(ValueChangeState state)
460 {
461     if (!element().focused())
462         return;
463     if (Frame* frame = element().document().frame()) {
464         if (state == ValueChangeStateNone)
465             frame->editor().textFieldDidBeginEditing(&element());
466         frame->editor().textDidChangeInTextField(&element());
467     }
468 }
469
470 void TextFieldInputType::spinButtonStepDown()
471 {
472     stepUpFromRenderer(-1);
473 }
474
475 void TextFieldInputType::spinButtonStepUp()
476 {
477     stepUpFromRenderer(1);
478 }
479
480 void TextFieldInputType::updateInnerTextValue()
481 {
482     if (!element().suggestedValue().isNull()) {
483         element().setInnerTextValue(element().suggestedValue());
484         element().updatePlaceholderVisibility(false);
485     } else if (!element().formControlValueMatchesRenderer()) {
486         // Update the renderer value if the formControlValueMatchesRenderer() flag is false.
487         // It protects an unacceptable renderer value from being overwritten with the DOM value.
488         element().setInnerTextValue(visibleValue());
489         element().updatePlaceholderVisibility(false);
490     }
491 }
492
493 void TextFieldInputType::focusAndSelectSpinButtonOwner()
494 {
495     Ref<HTMLInputElement> input(element());
496     input->focus();
497     input->select();
498 }
499
500 bool TextFieldInputType::shouldSpinButtonRespondToMouseEvents()
501 {
502     return !element().isDisabledOrReadOnly();
503 }
504
505 bool TextFieldInputType::shouldSpinButtonRespondToWheelEvents()
506 {
507     return shouldSpinButtonRespondToMouseEvents() && element().focused();
508 }
509
510 } // namespace WebCore