Home | History | Annotate | Download | only in forms
      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 
     58 namespace blink {
     59 
     60 using namespace HTMLNames;
     61 
     62 class DataListIndicatorElement FINAL : public HTMLDivElement {
     63 private:
     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 
    100 public:
    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 
    111 TextFieldInputType::TextFieldInputType(HTMLInputElement& element)
    112     : InputType(element)
    113 {
    114 }
    115 
    116 TextFieldInputType::~TextFieldInputType()
    117 {
    118 #if !ENABLE(OILPAN)
    119     if (SpinButtonElement* spinButton = spinButtonElement())
    120         spinButton->removeSpinButtonOwner();
    121 #endif
    122 }
    123 
    124 SpinButtonElement* TextFieldInputType::spinButtonElement() const
    125 {
    126     return toSpinButtonElement(element().userAgentShadowRoot()->getElementById(ShadowElementNames::spinButton()));
    127 }
    128 
    129 bool TextFieldInputType::shouldShowFocusRingOnMouseFocus() const
    130 {
    131     return true;
    132 }
    133 
    134 bool TextFieldInputType::isTextField() const
    135 {
    136     return true;
    137 }
    138 
    139 bool TextFieldInputType::valueMissing(const String& value) const
    140 {
    141     return element().isRequired() && value.isEmpty();
    142 }
    143 
    144 bool TextFieldInputType::canSetSuggestedValue()
    145 {
    146     return true;
    147 }
    148 
    149 void 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 
    195 void 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 
    206 void 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 
    221 void 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 
    251 void TextFieldInputType::handleFocusEvent(Element* oldFocusedNode, FocusType focusType)
    252 {
    253     InputType::handleFocusEvent(oldFocusedNode, focusType);
    254     element().beginEditing();
    255 }
    256 
    257 void TextFieldInputType::handleBlurEvent()
    258 {
    259     InputType::handleBlurEvent();
    260     element().endEditing();
    261     if (SpinButtonElement *spinButton = spinButtonElement())
    262         spinButton->releaseCapture();
    263 }
    264 
    265 bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
    266 {
    267     return (event->type() == EventTypeNames::textInput && event->hasInterface(EventNames::TextEvent) && toTextEvent(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
    268 }
    269 
    270 RenderObject* TextFieldInputType::createRenderer(RenderStyle*) const
    271 {
    272     return new RenderTextControlSingleLine(&element());
    273 }
    274 
    275 bool TextFieldInputType::shouldHaveSpinButton() const
    276 {
    277     return RenderTheme::theme().shouldHaveSpinButton(&element());
    278 }
    279 
    280 void 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 
    316 Element* TextFieldInputType::containerElement() const
    317 {
    318     return element().userAgentShadowRoot()->getElementById(ShadowElementNames::textFieldContainer());
    319 }
    320 
    321 void TextFieldInputType::destroyShadowSubtree()
    322 {
    323     InputType::destroyShadowSubtree();
    324     if (SpinButtonElement* spinButton = spinButtonElement())
    325         spinButton->removeSpinButtonOwner();
    326 }
    327 
    328 void 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 
    359 void 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 
    366 void TextFieldInputType::disabledAttributeChanged()
    367 {
    368     if (SpinButtonElement* spinButton = spinButtonElement())
    369         spinButton->releaseCapture();
    370 }
    371 
    372 void TextFieldInputType::readonlyAttributeChanged()
    373 {
    374     if (SpinButtonElement* spinButton = spinButtonElement())
    375         spinButton->releaseCapture();
    376 }
    377 
    378 bool TextFieldInputType::supportsReadOnly() 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, 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 
    398 String TextFieldInputType::sanitizeValue(const String& proposedValue) const
    399 {
    400     return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
    401 }
    402 
    403 void 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 
    438 bool TextFieldInputType::shouldRespectListAttribute()
    439 {
    440     return true;
    441 }
    442 
    443 void 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 
    467 bool 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 
    476 String TextFieldInputType::convertFromVisibleValue(const String& visibleValue) const
    477 {
    478     return visibleValue;
    479 }
    480 
    481 void 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 
    500 void TextFieldInputType::didSetValueByUserEdit(ValueChangeState state)
    501 {
    502     if (!element().focused())
    503         return;
    504     if (Chrome* chrome = this->chrome())
    505         chrome->client().didChangeValueInTextField(element());
    506 }
    507 
    508 void TextFieldInputType::spinButtonStepDown()
    509 {
    510     stepUpFromRenderer(-1);
    511 }
    512 
    513 void TextFieldInputType::spinButtonStepUp()
    514 {
    515     stepUpFromRenderer(1);
    516 }
    517 
    518 void 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 
    535 void TextFieldInputType::focusAndSelectSpinButtonOwner()
    536 {
    537     RefPtrWillBeRawPtr<HTMLInputElement> input(element());
    538     input->focus();
    539     input->select();
    540 }
    541 
    542 bool TextFieldInputType::shouldSpinButtonRespondToMouseEvents()
    543 {
    544     return !element().isDisabledOrReadOnly();
    545 }
    546 
    547 bool TextFieldInputType::shouldSpinButtonRespondToWheelEvents()
    548 {
    549     return shouldSpinButtonRespondToMouseEvents() && element().focused();
    550 }
    551 
    552 void TextFieldInputType::spinButtonDidReleaseMouseCapture(SpinButtonElement::EventDispatch eventDispatch)
    553 {
    554     if (eventDispatch == SpinButtonElement::EventDispatchAllowed)
    555         element().dispatchFormControlChangeEvent();
    556 }
    557 
    558 } // namespace blink
    559