Caps lock indicator should not be shown in read-only or disabled field
[WebKit-https.git] / Source / WebCore / html / TextFieldInputType.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2011, 2014 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 "Editor.h"
39 #include "FormDataList.h"
40 #include "Frame.h"
41 #include "FrameSelection.h"
42 #include "HTMLInputElement.h"
43 #include "HTMLNames.h"
44 #include "KeyboardEvent.h"
45 #include "LocalizedStrings.h"
46 #include "NodeRenderStyle.h"
47 #include "Page.h"
48 #include "PlatformKeyboardEvent.h"
49 #include "RenderLayer.h"
50 #include "RenderTextControlSingleLine.h"
51 #include "RenderTheme.h"
52 #include "ShadowRoot.h"
53 #include "TextBreakIterator.h"
54 #include "TextControlInnerElements.h"
55 #include "TextEvent.h"
56 #include "TextIterator.h"
57 #include "TextNodeTraversal.h"
58 #include "WheelEvent.h"
59
60 namespace WebCore {
61
62 using namespace HTMLNames;
63
64 TextFieldInputType::TextFieldInputType(HTMLInputElement& element)
65     : InputType(element)
66 {
67 }
68
69 TextFieldInputType::~TextFieldInputType()
70 {
71     if (m_innerSpinButton)
72         m_innerSpinButton->removeSpinButtonOwner();
73 }
74
75 bool TextFieldInputType::isKeyboardFocusable(KeyboardEvent*) const
76 {
77 #if PLATFORM(IOS)
78     if (element().isReadOnly())
79         return false;
80 #endif
81     return element().isTextFormControlFocusable();
82 }
83
84 bool TextFieldInputType::isMouseFocusable() const
85 {
86     return element().isTextFormControlFocusable();
87 }
88
89 bool TextFieldInputType::isTextField() const
90 {
91     return true;
92 }
93
94 bool TextFieldInputType::isEmptyValue() const
95 {
96     TextControlInnerTextElement* innerText = innerTextElement();
97     ASSERT(innerText);
98
99     for (Text* text = TextNodeTraversal::firstWithin(*innerText); text; text = TextNodeTraversal::next(*text, innerText)) {
100         if (text->length())
101             return false;
102     }
103     return true;
104 }
105
106 bool TextFieldInputType::valueMissing(const String& value) const
107 {
108     return element().isRequired() && value.isEmpty();
109 }
110
111 void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
112 {
113     // Grab this input element to keep reference even if JS event handler
114     // changes input type.
115     Ref<HTMLInputElement> input(element());
116
117     // We don't ask InputType::setValue to dispatch events because
118     // TextFieldInputType dispatches events different way from InputType.
119     InputType::setValue(sanitizedValue, valueChanged, DispatchNoEvent);
120
121     if (valueChanged)
122         updateInnerTextValue();
123
124     unsigned max = visibleValue().length();
125     if (input->focused())
126         input->setSelectionRange(max, max);
127     else
128         input->cacheSelectionInResponseToSetValue(max);
129
130     if (!valueChanged)
131         return;
132
133     switch (eventBehavior) {
134     case DispatchChangeEvent:
135         // If the user is still editing this field, dispatch an input event rather than a change event.
136         // The change event will be dispatched when editing finishes.
137         if (input->focused())
138             input->dispatchFormControlInputEvent();
139         else
140             input->dispatchFormControlChangeEvent();
141         break;
142
143     case DispatchInputAndChangeEvent: {
144         input->dispatchFormControlInputEvent();
145         input->dispatchFormControlChangeEvent();
146         break;
147     }
148
149     case DispatchNoEvent:
150         break;
151     }
152
153     // FIXME: Why do we do this when eventBehavior == DispatchNoEvent
154     if (!input->focused() || eventBehavior == DispatchNoEvent)
155         input->setTextAsOfLastFormControlChangeEvent(sanitizedValue);
156 }
157
158 void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
159 {
160     if (!element().focused())
161         return;
162     Frame* frame = element().document().frame();
163     if (!frame || !frame->editor().doTextFieldCommandFromEvent(&element(), event))
164         return;
165     event->setDefaultHandled();
166 }
167
168 void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
169 {
170     if (element().isDisabledOrReadOnly())
171         return;
172     const String& key = event->keyIdentifier();
173     if (key == "Up")
174         spinButtonStepUp();
175     else if (key == "Down")
176         spinButtonStepDown();
177     else
178         return;
179     event->setDefaultHandled();
180 }
181
182 void TextFieldInputType::forwardEvent(Event* event)
183 {
184     if (m_innerSpinButton) {
185         m_innerSpinButton->forwardEvent(event);
186         if (event->defaultHandled())
187             return;
188     }
189
190     if (event->isMouseEvent()
191         || event->isDragEvent()
192         || event->eventInterface() == WheelEventInterfaceType
193         || event->type() == eventNames().blurEvent
194         || event->type() == eventNames().focusEvent)
195     {
196         element().document().updateStyleIfNeeded();
197
198         if (element().renderer()) {
199             RenderTextControlSingleLine& renderTextControl = downcast<RenderTextControlSingleLine>(*element().renderer());
200             if (event->type() == eventNames().blurEvent) {
201                 if (RenderTextControlInnerBlock* innerTextRenderer = innerTextElement()->renderer()) {
202                     if (RenderLayer* innerLayer = innerTextRenderer->layer()) {
203                         IntSize scrollOffset(!renderTextControl.style().isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0);
204                         innerLayer->scrollToOffset(scrollOffset, RenderLayer::ScrollOffsetClamped);
205                     }
206                 }
207
208                 capsLockStateMayHaveChanged();
209             } else if (event->type() == eventNames().focusEvent) {
210                 if (Frame* frame = element().document().frame())
211                     frame->editor().textFieldDidBeginEditing(&element());
212                 capsLockStateMayHaveChanged();
213             }
214
215             element().forwardEvent(event);
216         }
217     }
218 }
219
220 void TextFieldInputType::handleBlurEvent()
221 {
222     InputType::handleBlurEvent();
223     element().endEditing();
224 }
225
226 bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
227 {
228     return (event->type() == eventNames().textInputEvent && is<TextEvent>(*event) && downcast<TextEvent>(*event).data() == "\n")
229         || InputType::shouldSubmitImplicitly(event);
230 }
231
232 RenderPtr<RenderElement> TextFieldInputType::createInputRenderer(Ref<RenderStyle>&& style)
233 {
234     return createRenderer<RenderTextControlSingleLine>(element(), WTF::move(style));
235 }
236
237 bool TextFieldInputType::needsContainer() const
238 {
239     return false;
240 }
241
242 bool TextFieldInputType::shouldHaveSpinButton() const
243 {
244     Document& document = element().document();
245     RefPtr<RenderTheme> theme = document.page() ? &document.page()->theme() : RenderTheme::defaultTheme();
246     return theme->shouldHaveSpinButton(element());
247 }
248
249 bool TextFieldInputType::shouldHaveCapsLockIndicator() const
250 {
251     Document& document = element().document();
252     RefPtr<RenderTheme> theme = document.page() ? &document.page()->theme() : RenderTheme::defaultTheme();
253     return theme->shouldHaveCapsLockIndicator(element());
254 }
255
256 void TextFieldInputType::createShadowSubtree()
257 {
258     ASSERT(element().shadowRoot());
259
260     ASSERT(!m_innerText);
261     ASSERT(!m_innerBlock);
262     ASSERT(!m_innerSpinButton);
263     ASSERT(!m_capsLockIndicator);
264     ASSERT(!m_autoFillButton);
265
266     Document& document = element().document();
267     bool shouldHaveSpinButton = this->shouldHaveSpinButton();
268     bool shouldHaveCapsLockIndicator = this->shouldHaveCapsLockIndicator();
269     bool createsContainer = shouldHaveSpinButton || shouldHaveCapsLockIndicator || needsContainer();
270
271     m_innerText = TextControlInnerTextElement::create(document);
272
273     if (!createsContainer) {
274         element().userAgentShadowRoot()->appendChild(m_innerText, IGNORE_EXCEPTION);
275         updatePlaceholderText();
276         return;
277     }
278
279     createContainer();
280     updatePlaceholderText();
281
282     if (shouldHaveSpinButton) {
283         m_innerSpinButton = SpinButtonElement::create(document, *this);
284         m_container->appendChild(m_innerSpinButton, IGNORE_EXCEPTION);
285     }
286
287     if (shouldHaveCapsLockIndicator) {
288         m_capsLockIndicator = HTMLDivElement::create(document);
289         m_capsLockIndicator->setPseudo(AtomicString("-webkit-caps-lock-indicator", AtomicString::ConstructFromLiteral));
290
291         bool shouldDrawCapsLockIndicator = this->shouldDrawCapsLockIndicator();
292         m_capsLockIndicator->setInlineStyleProperty(CSSPropertyDisplay, shouldDrawCapsLockIndicator ? CSSValueBlock : CSSValueNone, true);
293
294         m_container->appendChild(m_capsLockIndicator, IGNORE_EXCEPTION);
295     }
296
297     updateAutoFillButton();
298 }
299
300 HTMLElement* TextFieldInputType::containerElement() const
301 {
302     return m_container.get();
303 }
304
305 HTMLElement* TextFieldInputType::innerBlockElement() const
306 {
307     return m_innerBlock.get();
308 }
309
310 TextControlInnerTextElement* TextFieldInputType::innerTextElement() const
311 {
312     ASSERT(m_innerText);
313     return m_innerText.get();
314 }
315
316 HTMLElement* TextFieldInputType::innerSpinButtonElement() const
317 {
318     return m_innerSpinButton.get();
319 }
320
321 HTMLElement* TextFieldInputType::capsLockIndicatorElement() const
322 {
323     return m_capsLockIndicator.get();
324 }
325
326 HTMLElement* TextFieldInputType::autoFillButtonElement() const
327 {
328     return m_autoFillButton.get();
329 }
330
331 HTMLElement* TextFieldInputType::placeholderElement() const
332 {
333     return m_placeholder.get();
334 }
335
336 void TextFieldInputType::destroyShadowSubtree()
337 {
338     InputType::destroyShadowSubtree();
339     m_innerText = nullptr;
340     m_placeholder = nullptr;
341     m_innerBlock = nullptr;
342     if (m_innerSpinButton)
343         m_innerSpinButton->removeSpinButtonOwner();
344     m_innerSpinButton = nullptr;
345     m_capsLockIndicator = nullptr;
346     m_autoFillButton = nullptr;
347     m_container = nullptr;
348 }
349
350 void TextFieldInputType::attributeChanged()
351 {
352     // FIXME: Updating the inner text on any attribute update should
353     // be unnecessary. We should figure out what attributes affect.
354     updateInnerTextValue();
355 }
356
357 void TextFieldInputType::disabledAttributeChanged()
358 {
359     if (m_innerSpinButton)
360         m_innerSpinButton->releaseCapture();
361     capsLockStateMayHaveChanged();
362     updateAutoFillButton();
363 }
364
365 void TextFieldInputType::readonlyAttributeChanged()
366 {
367     if (m_innerSpinButton)
368         m_innerSpinButton->releaseCapture();
369     capsLockStateMayHaveChanged();
370     updateAutoFillButton();
371 }
372
373 bool TextFieldInputType::supportsReadOnly() const
374 {
375     return true;
376 }
377
378 bool TextFieldInputType::shouldUseInputMethod() const
379 {
380     return true;
381 }
382
383 static bool isASCIILineBreak(UChar c)
384 {
385     return c == '\r' || c == '\n';
386 }
387
388 static String limitLength(const String& string, int maxLength)
389 {
390     unsigned newLength = numCharactersInGraphemeClusters(string, maxLength);
391     for (unsigned i = 0; i < newLength; ++i) {
392         const UChar current = string[i];
393         if (current < ' ' && current != '\t') {
394             newLength = i;
395             break;
396         }
397     }
398     return string.left(newLength);
399 }
400
401 String TextFieldInputType::sanitizeValue(const String& proposedValue) const
402 {
403     return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
404 }
405
406 void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
407 {
408     // Make sure that the text to be inserted will not violate the maxLength.
409
410     // We use RenderTextControlSingleLine::text() instead of InputElement::value()
411     // because they can be mismatched by sanitizeValue() in
412     // HTMLInputElement::subtreeHasChanged() in some cases.
413     String innerText = element().innerTextValue();
414     unsigned oldLength = numGraphemeClusters(innerText);
415
416     // selectionLength represents the selection length of this text field to be
417     // removed by this insertion.
418     // If the text field has no focus, we don't need to take account of the
419     // selection length. The selection is the source of text drag-and-drop in
420     // that case, and nothing in the text field will be removed.
421     unsigned selectionLength = 0;
422     if (element().focused()) {
423         ASSERT(enclosingTextFormControl(element().document().frame()->selection().selection().start()) == &element());
424         int selectionStart = element().selectionStart();
425         ASSERT(selectionStart <= element().selectionEnd());
426         int selectionCodeUnitCount = element().selectionEnd() - selectionStart;
427         selectionLength = selectionCodeUnitCount ? numGraphemeClusters(innerText.substring(selectionStart, selectionCodeUnitCount)) : 0;
428     }
429     ASSERT(oldLength >= selectionLength);
430
431     // Selected characters will be removed by the next text event.
432     unsigned baseLength = oldLength - selectionLength;
433     unsigned maxLength = static_cast<unsigned>(isTextType() ? element().maxLength() : HTMLInputElement::maximumLength); // maxLength can never be negative.
434     unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
435
436     // Truncate the inserted text to avoid violating the maxLength and other constraints.
437     String eventText = event->text();
438     unsigned textLength = eventText.length();
439     while (textLength > 0 && isASCIILineBreak(eventText[textLength - 1]))
440         textLength--;
441     eventText.truncate(textLength);
442     eventText.replace("\r\n", " ");
443     eventText.replace('\r', ' ');
444     eventText.replace('\n', ' ');
445
446     event->setText(limitLength(eventText, appendableLength));
447 }
448
449 bool TextFieldInputType::shouldRespectListAttribute()
450 {
451     return InputType::themeSupportsDataListUI(this);
452 }
453
454 void TextFieldInputType::updatePlaceholderText()
455 {
456     if (!supportsPlaceholder())
457         return;
458     String placeholderText = element().strippedPlaceholder();
459     if (placeholderText.isEmpty()) {
460         if (m_placeholder) {
461             m_placeholder->parentNode()->removeChild(m_placeholder.get(), ASSERT_NO_EXCEPTION);
462             m_placeholder.clear();
463         }
464         return;
465     }
466     if (!m_placeholder) {
467         m_placeholder = HTMLDivElement::create(element().document());
468         m_placeholder->setPseudo(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral));
469         m_placeholder->setInlineStyleProperty(CSSPropertyDisplay, element().isPlaceholderVisible() ? CSSValueBlock : CSSValueNone, true);
470         element().userAgentShadowRoot()->insertBefore(m_placeholder, m_container ? m_container.get() : innerTextElement(), ASSERT_NO_EXCEPTION);
471         
472     }
473     m_placeholder->setInnerText(placeholderText, ASSERT_NO_EXCEPTION);
474 }
475
476 bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const
477 {
478     InputType::appendFormData(list, multipart);
479     const AtomicString& dirnameAttrValue = element().fastGetAttribute(dirnameAttr);
480     if (!dirnameAttrValue.isNull())
481         list.appendData(dirnameAttrValue, element().directionForFormData());
482     return true;
483 }
484
485 String TextFieldInputType::convertFromVisibleValue(const String& visibleValue) const
486 {
487     return visibleValue;
488 }
489
490 void TextFieldInputType::subtreeHasChanged()
491 {
492     element().setChangedSinceLastFormControlChangeEvent(true);
493
494     // We don't need to call sanitizeUserInputValue() function here because
495     // HTMLInputElement::handleBeforeTextInsertedEvent() has already called
496     // sanitizeUserInputValue().
497     // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
498     element().setValueFromRenderer(sanitizeValue(convertFromVisibleValue(element().innerTextValue())));
499     element().updatePlaceholderVisibility();
500     // Recalc for :invalid change.
501     element().setNeedsStyleRecalc();
502
503     didSetValueByUserEdit();
504 }
505
506 void TextFieldInputType::didSetValueByUserEdit()
507 {
508     if (!element().focused())
509         return;
510     if (Frame* frame = element().document().frame())
511         frame->editor().textDidChangeInTextField(&element());
512 }
513
514 void TextFieldInputType::spinButtonStepDown()
515 {
516     stepUpFromRenderer(-1);
517 }
518
519 void TextFieldInputType::spinButtonStepUp()
520 {
521     stepUpFromRenderer(1);
522 }
523
524 void TextFieldInputType::updateInnerTextValue()
525 {
526     if (!element().formControlValueMatchesRenderer()) {
527         // Update the renderer value if the formControlValueMatchesRenderer() flag is false.
528         // It protects an unacceptable renderer value from being overwritten with the DOM value.
529         element().setInnerTextValue(visibleValue());
530         element().updatePlaceholderVisibility();
531     }
532 }
533
534 void TextFieldInputType::focusAndSelectSpinButtonOwner()
535 {
536     Ref<HTMLInputElement> input(element());
537     input->focus();
538     input->select();
539 }
540
541 bool TextFieldInputType::shouldSpinButtonRespondToMouseEvents()
542 {
543     return !element().isDisabledOrReadOnly();
544 }
545
546 bool TextFieldInputType::shouldSpinButtonRespondToWheelEvents()
547 {
548     return shouldSpinButtonRespondToMouseEvents() && element().focused();
549 }
550
551 bool TextFieldInputType::shouldDrawCapsLockIndicator() const
552 {
553     if (element().document().focusedElement() != &element())
554         return false;
555
556     if (element().isDisabledOrReadOnly())
557         return false;
558
559     Frame* frame = element().document().frame();
560     if (!frame)
561         return false;
562
563     if (!frame->selection().isFocusedAndActive())
564         return false;
565
566     return PlatformKeyboardEvent::currentCapsLockState();
567 }
568
569 void TextFieldInputType::capsLockStateMayHaveChanged()
570 {
571     if (!m_capsLockIndicator)
572         return;
573
574     bool shouldDrawCapsLockIndicator = this->shouldDrawCapsLockIndicator();
575     m_capsLockIndicator->setInlineStyleProperty(CSSPropertyDisplay, shouldDrawCapsLockIndicator ? CSSValueBlock : CSSValueNone, true);
576 }
577
578 bool TextFieldInputType::shouldDrawAutoFillButton() const
579 {
580     return !element().isDisabledOrReadOnly() && element().showAutoFillButton();
581 }
582
583 void TextFieldInputType::autoFillButtonElementWasClicked()
584 {
585     Page* page = element().document().page();
586     if (!page)
587         return;
588
589     page->chrome().client().handleAutoFillButtonClick(element());
590 }
591
592 void TextFieldInputType::createContainer()
593 {
594     ASSERT(!m_container);
595
596     m_container = TextControlInnerContainer::create(element().document());
597     m_container->setPseudo(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
598
599     m_innerBlock = TextControlInnerElement::create(element().document());
600     m_innerBlock->appendChild(m_innerText, IGNORE_EXCEPTION);
601     m_container->appendChild(m_innerBlock, IGNORE_EXCEPTION);
602
603     element().userAgentShadowRoot()->appendChild(m_container, IGNORE_EXCEPTION);
604 }
605
606 void TextFieldInputType::createAutoFillButton()
607 {
608     ASSERT(!m_autoFillButton);
609
610     m_autoFillButton = AutoFillButtonElement::create(element().document(), *this);
611     m_autoFillButton->setPseudo(AtomicString("-webkit-auto-fill-button", AtomicString::ConstructFromLiteral));
612     m_autoFillButton->setAttribute(aria_labelAttr, AtomicString(AXAutoFillButtonText()));
613     m_autoFillButton->setAttribute(roleAttr, AtomicString("button", AtomicString::ConstructFromLiteral));
614     m_container->appendChild(m_autoFillButton, IGNORE_EXCEPTION);
615 }
616
617 void TextFieldInputType::updateAutoFillButton()
618 {
619     if (shouldDrawAutoFillButton()) {
620         if (!m_container)
621             createContainer();
622
623         if (!m_autoFillButton)
624             createAutoFillButton();
625
626         m_autoFillButton->setInlineStyleProperty(CSSPropertyDisplay, CSSValueBlock, true);
627         return;
628     }
629     
630     if (m_autoFillButton)
631         m_autoFillButton->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone, true);        
632 }
633
634 } // namespace WebCore