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 "core/html/forms/TextFieldInputType.h"
34
35#include "bindings/core/v8/ExceptionStatePlaceholder.h"
36#include "core/HTMLNames.h"
37#include "core/dom/NodeRenderStyle.h"
38#include "core/dom/shadow/ShadowRoot.h"
39#include "core/editing/FrameSelection.h"
40#include "core/editing/TextIterator.h"
41#include "core/events/BeforeTextInsertedEvent.h"
42#include "core/events/KeyboardEvent.h"
43#include "core/events/TextEvent.h"
44#include "core/frame/FrameHost.h"
45#include "core/frame/LocalFrame.h"
46#include "core/html/FormDataList.h"
47#include "core/html/HTMLInputElement.h"
48#include "core/html/shadow/ShadowElementNames.h"
49#include "core/html/shadow/TextControlInnerElements.h"
50#include "core/page/Chrome.h"
51#include "core/page/ChromeClient.h"
52#include "core/rendering/RenderDetailsMarker.h"
53#include "core/rendering/RenderLayer.h"
54#include "core/rendering/RenderTextControlSingleLine.h"
55#include "core/rendering/RenderTheme.h"
56#include "wtf/text/WTFString.h"
57
58namespace blink {
59
60using namespace HTMLNames;
61
62class DataListIndicatorElement FINAL : public HTMLDivElement {
63private:
64    inline DataListIndicatorElement(Document& document) : HTMLDivElement(document) { }
65    inline HTMLInputElement* hostInput() const { return toHTMLInputElement(shadowHost()); }
66
67    virtual RenderObject* createRenderer(RenderStyle*) OVERRIDE
68    {
69        return new RenderDetailsMarker(this);
70    }
71
72    virtual void* preDispatchEventHandler(Event* event) OVERRIDE
73    {
74        // Chromium opens autofill popup in a mousedown event listener
75        // associated to the document. We don't want to open it in this case
76        // because we opens a datalist chooser later.
77        // FIXME: We should dispatch mousedown events even in such case.
78        if (event->type() == EventTypeNames::mousedown)
79            event->stopPropagation();
80        return 0;
81    }
82
83    virtual void defaultEventHandler(Event* event) OVERRIDE
84    {
85        ASSERT(document().isActive());
86        if (event->type() != EventTypeNames::click)
87            return;
88        HTMLInputElement* host = hostInput();
89        if (host && !host->isDisabledOrReadOnly()) {
90            document().frameHost()->chrome().openTextDataListChooser(*host);
91            event->setDefaultHandled();
92        }
93    }
94
95    virtual bool willRespondToMouseClickEvents() OVERRIDE
96    {
97        return hostInput() && !hostInput()->isDisabledOrReadOnly() && document().isActive();
98    }
99
100public:
101    static PassRefPtrWillBeRawPtr<DataListIndicatorElement> create(Document& document)
102    {
103        RefPtrWillBeRawPtr<DataListIndicatorElement> element = adoptRefWillBeNoop(new DataListIndicatorElement(document));
104        element->setShadowPseudoId(AtomicString("-webkit-calendar-picker-indicator", AtomicString::ConstructFromLiteral));
105        element->setAttribute(idAttr, ShadowElementNames::pickerIndicator());
106        return element.release();
107    }
108
109};
110
111TextFieldInputType::TextFieldInputType(HTMLInputElement& element)
112    : InputType(element)
113{
114}
115
116TextFieldInputType::~TextFieldInputType()
117{
118#if !ENABLE(OILPAN)
119    if (SpinButtonElement* spinButton = spinButtonElement())
120        spinButton->removeSpinButtonOwner();
121#endif
122}
123
124SpinButtonElement* TextFieldInputType::spinButtonElement() const
125{
126    return toSpinButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
127}
128
129bool TextFieldInputType::shouldShowFocusRingOnMouseFocus() const
130{
131    return true;
132}
133
134bool TextFieldInputType::isTextField() const
135{
136    return true;
137}
138
139bool TextFieldInputType::valueMissing(const String& value) const
140{
141    return element().isRequired() && value.isEmpty();
142}
143
144bool TextFieldInputType::canSetSuggestedValue()
145{
146    return true;
147}
148
149void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
150{
151    // Grab this input element to keep reference even if JS event handler
152    // changes input type.
153    RefPtrWillBeRawPtr<HTMLInputElement> input(element());
154
155    // We don't ask InputType::setValue to dispatch events because
156    // TextFieldInputType dispatches events different way from InputType.
157    InputType::setValue(sanitizedValue, valueChanged, DispatchNoEvent);
158
159    if (valueChanged)
160        input->updateView();
161
162    unsigned max = visibleValue().length();
163    if (input->focused())
164        input->setSelectionRange(max, max);
165    else
166        input->cacheSelectionInResponseToSetValue(max);
167
168    if (!valueChanged)
169        return;
170
171    switch (eventBehavior) {
172    case DispatchChangeEvent:
173        // If the user is still editing this field, dispatch an input event rather than a change event.
174        // The change event will be dispatched when editing finishes.
175        if (input->focused())
176            input->dispatchFormControlInputEvent();
177        else
178            input->dispatchFormControlChangeEvent();
179        break;
180
181    case DispatchInputAndChangeEvent: {
182        input->dispatchFormControlInputEvent();
183        input->dispatchFormControlChangeEvent();
184        break;
185    }
186
187    case DispatchNoEvent:
188        break;
189    }
190
191    if (!input->focused())
192        input->setTextAsOfLastFormControlChangeEvent(sanitizedValue);
193}
194
195void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
196{
197    if (!element().focused())
198        return;
199    if (Chrome* chrome = this->chrome()) {
200        chrome->client().handleKeyboardEventOnTextField(element(), *event);
201        return;
202    }
203    event->setDefaultHandled();
204}
205
206void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
207{
208    if (element().isDisabledOrReadOnly())
209        return;
210    const String& key = event->keyIdentifier();
211    if (key == "Up")
212        spinButtonStepUp();
213    else if (key == "Down" && !event->altKey())
214        spinButtonStepDown();
215    else
216        return;
217    element().dispatchFormControlChangeEvent();
218    event->setDefaultHandled();
219}
220
221void TextFieldInputType::forwardEvent(Event* event)
222{
223    if (SpinButtonElement* spinButton = spinButtonElement()) {
224        spinButton->forwardEvent(event);
225        if (event->defaultHandled())
226            return;
227    }
228
229    if (element().renderer() && (event->isMouseEvent() || event->isDragEvent() || event->hasInterface(EventNames::WheelEvent) || event->type() == EventTypeNames::blur || event->type() == EventTypeNames::focus)) {
230        RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element().renderer());
231        if (event->type() == EventTypeNames::blur) {
232            if (RenderBox* innerEditorRenderer = element().innerEditorElement()->renderBox()) {
233                // FIXME: This class has no need to know about RenderLayer!
234                if (RenderLayer* innerLayer = innerEditorRenderer->layer()) {
235                    if (RenderLayerScrollableArea* innerScrollableArea = innerLayer->scrollableArea()) {
236                        IntSize scrollOffset(!renderTextControl->style()->isLeftToRightDirection() ? innerScrollableArea->scrollWidth().toInt() : 0, 0);
237                        innerScrollableArea->scrollToOffset(scrollOffset, ScrollOffsetClamped);
238                    }
239                }
240            }
241
242            renderTextControl->capsLockStateMayHaveChanged();
243        } else if (event->type() == EventTypeNames::focus) {
244            renderTextControl->capsLockStateMayHaveChanged();
245        }
246
247        element().forwardEvent(event);
248    }
249}
250
251void TextFieldInputType::handleFocusEvent(Element* oldFocusedNode, FocusType focusType)
252{
253    InputType::handleFocusEvent(oldFocusedNode, focusType);
254    element().beginEditing();
255}
256
257void TextFieldInputType::handleBlurEvent()
258{
259    InputType::handleBlurEvent();
260    element().endEditing();
261    if (SpinButtonElement *spinButton = spinButtonElement())
262        spinButton->releaseCapture();
263}
264
265bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
266{
267    return (event->type() == EventTypeNames::textInput && event->hasInterface(EventNames::TextEvent) && toTextEvent(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
268}
269
270RenderObject* TextFieldInputType::createRenderer(RenderStyle*) const
271{
272    return new RenderTextControlSingleLine(&element());
273}
274
275bool TextFieldInputType::shouldHaveSpinButton() const
276{
277    return RenderTheme::theme().shouldHaveSpinButton(&element());
278}
279
280void TextFieldInputType::createShadowSubtree()
281{
282    ASSERT(element().shadow());
283    ShadowRoot* shadowRoot = element().userAgentShadowRoot();
284    ASSERT(!shadowRoot->hasChildren());
285
286    Document& document = element().document();
287    bool shouldHaveSpinButton = this->shouldHaveSpinButton();
288    bool shouldHaveDataListIndicator = element().hasValidDataListOptions();
289    bool createsContainer = shouldHaveSpinButton || shouldHaveDataListIndicator || needsContainer();
290
291    RefPtrWillBeRawPtr<TextControlInnerEditorElement> innerEditor = TextControlInnerEditorElement::create(document);
292    if (!createsContainer) {
293        shadowRoot->appendChild(innerEditor.release());
294        return;
295    }
296
297    RefPtrWillBeRawPtr<TextControlInnerContainer> container = TextControlInnerContainer::create(document);
298    container->setShadowPseudoId(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
299    shadowRoot->appendChild(container);
300
301    RefPtrWillBeRawPtr<EditingViewPortElement> editingViewPort = EditingViewPortElement::create(document);
302    editingViewPort->appendChild(innerEditor.release());
303    container->appendChild(editingViewPort.release());
304
305    if (shouldHaveDataListIndicator)
306        container->appendChild(DataListIndicatorElement::create(document));
307    // FIXME: Because of a special handling for a spin button in
308    // RenderTextControlSingleLine, we need to put it to the last position. It's
309    // inconsistent with multiple-fields date/time types.
310    if (shouldHaveSpinButton)
311        container->appendChild(SpinButtonElement::create(document, *this));
312
313    // See listAttributeTargetChanged too.
314}
315
316Element* TextFieldInputType::containerElement() const
317{
318    return element().userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
319}
320
321void TextFieldInputType::destroyShadowSubtree()
322{
323    InputType::destroyShadowSubtree();
324    if (SpinButtonElement* spinButton = spinButtonElement())
325        spinButton->removeSpinButtonOwner();
326}
327
328void TextFieldInputType::listAttributeTargetChanged()
329{
330    Element* picker = element().userAgentShadowRoot()->getElementById(ShadowElementNames::pickerIndicator());
331    bool didHavePickerIndicator = picker;
332    bool willHavePickerIndicator = element().hasValidDataListOptions();
333    if (didHavePickerIndicator == willHavePickerIndicator)
334        return;
335    if (willHavePickerIndicator) {
336        Document& document = element().document();
337        if (Element* container = containerElement()) {
338            container->insertBefore(DataListIndicatorElement::create(document), spinButtonElement());
339        } else {
340            // FIXME: The following code is similar to createShadowSubtree(),
341            // but they are different. We should simplify the code by making
342            // containerElement mandatory.
343            RefPtrWillBeRawPtr<Element> rpContainer = TextControlInnerContainer::create(document);
344            rpContainer->setShadowPseudoId(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
345            RefPtrWillBeRawPtr<Element> innerEditor = element().innerEditorElement();
346            innerEditor->parentNode()->replaceChild(rpContainer.get(), innerEditor.get());
347            RefPtrWillBeRawPtr<Element> editingViewPort = EditingViewPortElement::create(document);
348            editingViewPort->appendChild(innerEditor.release());
349            rpContainer->appendChild(editingViewPort.release());
350            rpContainer->appendChild(DataListIndicatorElement::create(document));
351            if (element().document().focusedElement() == element())
352                element().updateFocusAppearance(true /* restore selection */);
353        }
354    } else {
355        picker->remove(ASSERT_NO_EXCEPTION);
356    }
357}
358
359void TextFieldInputType::attributeChanged()
360{
361    // FIXME: Updating on any attribute update should be unnecessary. We should
362    // figure out what attributes affect.
363    updateView();
364}
365
366void TextFieldInputType::disabledAttributeChanged()
367{
368    if (SpinButtonElement* spinButton = spinButtonElement())
369        spinButton->releaseCapture();
370}
371
372void TextFieldInputType::readonlyAttributeChanged()
373{
374    if (SpinButtonElement* spinButton = spinButtonElement())
375        spinButton->releaseCapture();
376}
377
378bool TextFieldInputType::supportsReadOnly() const
379{
380    return true;
381}
382
383static bool isASCIILineBreak(UChar c)
384{
385    return c == '\r' || c == '\n';
386}
387
388static String limitLength(const String& string, unsigned maxLength)
389{
390    unsigned newLength = std::min(maxLength, string.length());
391    if (newLength == string.length())
392        return string;
393    if (newLength > 0 && U16_IS_LEAD(string[newLength - 1]))
394        --newLength;
395    return string.left(newLength);
396}
397
398String TextFieldInputType::sanitizeValue(const String& proposedValue) const
399{
400    return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
401}
402
403void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
404{
405    // Make sure that the text to be inserted will not violate the maxLength.
406
407    // We use HTMLInputElement::innerEditorValue() instead of
408    // HTMLInputElement::value() because they can be mismatched by
409    // sanitizeValue() in HTMLInputElement::subtreeHasChanged() in some cases.
410    unsigned oldLength = element().innerEditorValue().length();
411
412    // selectionLength represents the selection length of this text field to be
413    // removed by this insertion.
414    // If the text field has no focus, we don't need to take account of the
415    // selection length. The selection is the source of text drag-and-drop in
416    // that case, and nothing in the text field will be removed.
417    unsigned selectionLength = element().focused() ? plainText(element().document().frame()->selection().selection().toNormalizedRange().get()).length() : 0;
418    ASSERT(oldLength >= selectionLength);
419
420    // Selected characters will be removed by the next text event.
421    unsigned baseLength = oldLength - selectionLength;
422    unsigned maxLength = static_cast<unsigned>(this->maxLength()); // maxLength can never be negative.
423    unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
424
425    // Truncate the inserted text to avoid violating the maxLength and other constraints.
426    String eventText = event->text();
427    unsigned textLength = eventText.length();
428    while (textLength > 0 && isASCIILineBreak(eventText[textLength - 1]))
429        textLength--;
430    eventText.truncate(textLength);
431    eventText.replace("\r\n", " ");
432    eventText.replace('\r', ' ');
433    eventText.replace('\n', ' ');
434
435    event->setText(limitLength(eventText, appendableLength));
436}
437
438bool TextFieldInputType::shouldRespectListAttribute()
439{
440    return true;
441}
442
443void TextFieldInputType::updatePlaceholderText()
444{
445    if (!supportsPlaceholder())
446        return;
447    HTMLElement* placeholder = element().placeholderElement();
448    String placeholderText = element().strippedPlaceholder();
449    if (placeholderText.isEmpty()) {
450        if (placeholder)
451            placeholder->remove(ASSERT_NO_EXCEPTION);
452        return;
453    }
454    if (!placeholder) {
455        RefPtrWillBeRawPtr<HTMLElement> newElement = HTMLDivElement::create(element().document());
456        placeholder = newElement.get();
457        placeholder->setShadowPseudoId(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral));
458        placeholder->setAttribute(idAttr, ShadowElementNames::placeholder());
459        Element* container = containerElement();
460        Node* previous = container ? container : element().innerEditorElement();
461        previous->parentNode()->insertBefore(placeholder, previous->nextSibling());
462        ASSERT_WITH_SECURITY_IMPLICATION(placeholder->parentNode() == previous->parentNode());
463    }
464    placeholder->setTextContent(placeholderText);
465}
466
467bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const
468{
469    InputType::appendFormData(list, multipart);
470    const AtomicString& dirnameAttrValue = element().fastGetAttribute(dirnameAttr);
471    if (!dirnameAttrValue.isNull())
472        list.appendData(dirnameAttrValue, element().directionForFormData());
473    return true;
474}
475
476String TextFieldInputType::convertFromVisibleValue(const String& visibleValue) const
477{
478    return visibleValue;
479}
480
481void TextFieldInputType::subtreeHasChanged()
482{
483    ASSERT(element().renderer());
484
485    bool wasChanged = element().wasChangedSinceLastFormControlChangeEvent();
486    element().setChangedSinceLastFormControlChangeEvent(true);
487
488    // We don't need to call sanitizeUserInputValue() function here because
489    // HTMLInputElement::handleBeforeTextInsertedEvent() has already called
490    // sanitizeUserInputValue().
491    // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
492    element().setValueFromRenderer(sanitizeValue(convertFromVisibleValue(element().innerEditorValue())));
493    element().updatePlaceholderVisibility(false);
494    // Recalc for :invalid change.
495    element().setNeedsStyleRecalc(SubtreeStyleChange);
496
497    didSetValueByUserEdit(wasChanged ? ValueChangeStateChanged : ValueChangeStateNone);
498}
499
500void TextFieldInputType::didSetValueByUserEdit(ValueChangeState state)
501{
502    if (!element().focused())
503        return;
504    if (Chrome* chrome = this->chrome())
505        chrome->client().didChangeValueInTextField(element());
506}
507
508void TextFieldInputType::spinButtonStepDown()
509{
510    stepUpFromRenderer(-1);
511}
512
513void TextFieldInputType::spinButtonStepUp()
514{
515    stepUpFromRenderer(1);
516}
517
518void TextFieldInputType::updateView()
519{
520    if (!element().suggestedValue().isNull()) {
521        element().setInnerEditorValue(element().suggestedValue());
522        element().updatePlaceholderVisibility(false);
523    } else if (element().needsToUpdateViewValue()) {
524        // Update the view only if needsToUpdateViewValue is true. It protects
525        // an unacceptable view value from being overwritten with the DOM value.
526        //
527        // e.g. <input type=number> has a view value "abc", and input.max is
528        // updated. In this case, updateView() is called but we should not
529        // update the view value.
530        element().setInnerEditorValue(visibleValue());
531        element().updatePlaceholderVisibility(false);
532    }
533}
534
535void TextFieldInputType::focusAndSelectSpinButtonOwner()
536{
537    RefPtrWillBeRawPtr<HTMLInputElement> input(element());
538    input->focus();
539    input->select();
540}
541
542bool TextFieldInputType::shouldSpinButtonRespondToMouseEvents()
543{
544    return !element().isDisabledOrReadOnly();
545}
546
547bool TextFieldInputType::shouldSpinButtonRespondToWheelEvents()
548{
549    return shouldSpinButtonRespondToMouseEvents() && element().focused();
550}
551
552void TextFieldInputType::spinButtonDidReleaseMouseCapture(SpinButtonElement::EventDispatch eventDispatch)
553{
554    if (eventDispatch == SpinButtonElement::EventDispatchAllowed)
555        element().dispatchFormControlChangeEvent();
556}
557
558} // namespace blink
559