Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      3  *
      4  * This library is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU Library General Public
      6  * License as published by the Free Software Foundation; either
      7  * version 2 of the License, or (at your option) any later version.
      8  *
      9  * This library is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * Library General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Library General Public License
     15  * along with this library; see the file COPYING.LIB.  If not, write to
     16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     17  * Boston, MA 02110-1301, USA.
     18  *
     19  */
     20 
     21 #include "config.h"
     22 #include "InputElement.h"
     23 
     24 #include "BeforeTextInsertedEvent.h"
     25 #include "Chrome.h"
     26 #include "ChromeClient.h"
     27 #include "Document.h"
     28 #include "Event.h"
     29 #include "EventNames.h"
     30 #include "Frame.h"
     31 #include "HTMLInputElement.h"
     32 #include "HTMLNames.h"
     33 #include "MappedAttribute.h"
     34 #include "Page.h"
     35 #include "RenderTextControlSingleLine.h"
     36 #include "SelectionController.h"
     37 #include "TextIterator.h"
     38 
     39 #if ENABLE(WML)
     40 #include "WMLInputElement.h"
     41 #include "WMLNames.h"
     42 #endif
     43 
     44 namespace WebCore {
     45 
     46 using namespace HTMLNames;
     47 
     48 // FIXME: According to HTML4, the length attribute's value can be arbitrarily
     49 // large. However, due to https://bugs.webkit.org/show_bug.cgi?id=14536 things
     50 // get rather sluggish when a text field has a larger number of characters than
     51 // this, even when just clicking in the text field.
     52 const int InputElement::s_maximumLength = 524288;
     53 const int InputElement::s_defaultSize = 20;
     54 
     55 void InputElement::dispatchFocusEvent(InputElement* inputElement, Element* element)
     56 {
     57     if (!inputElement->isTextField())
     58         return;
     59 
     60     Document* document = element->document();
     61     if (inputElement->isPasswordField() && document->frame())
     62         document->setUseSecureKeyboardEntryWhenActive(true);
     63 }
     64 
     65 void InputElement::dispatchBlurEvent(InputElement* inputElement, Element* element)
     66 {
     67     if (!inputElement->isTextField())
     68         return;
     69 
     70     Document* document = element->document();
     71     Frame* frame = document->frame();
     72     if (!frame)
     73         return;
     74 
     75     if (inputElement->isPasswordField())
     76         document->setUseSecureKeyboardEntryWhenActive(false);
     77 
     78     frame->textFieldDidEndEditing(element);
     79 }
     80 
     81 void InputElement::updateFocusAppearance(InputElementData& data, InputElement* inputElement, Element* element, bool restorePreviousSelection)
     82 {
     83     ASSERT(inputElement->isTextField());
     84 
     85     if (!restorePreviousSelection || data.cachedSelectionStart() == -1)
     86         inputElement->select();
     87     else
     88         // Restore the cached selection.
     89         updateSelectionRange(inputElement, element, data.cachedSelectionStart(), data.cachedSelectionEnd());
     90 
     91     Document* document = element->document();
     92     if (document && document->frame())
     93         document->frame()->revealSelection();
     94 }
     95 
     96 void InputElement::updateSelectionRange(InputElement* inputElement, Element* element, int start, int end)
     97 {
     98     if (!inputElement->isTextField())
     99         return;
    100 
    101     element->document()->updateLayoutIgnorePendingStylesheets();
    102 
    103     if (RenderTextControl* renderer = toRenderTextControl(element->renderer()))
    104         renderer->setSelectionRange(start, end);
    105 }
    106 
    107 void InputElement::aboutToUnload(InputElement* inputElement, Element* element)
    108 {
    109     if (!inputElement->isTextField() || !element->focused())
    110         return;
    111 
    112     Document* document = element->document();
    113     Frame* frame = document->frame();
    114     if (!frame)
    115         return;
    116 
    117     frame->textFieldDidEndEditing(element);
    118 }
    119 
    120 void InputElement::setValueFromRenderer(InputElementData& data, InputElement* inputElement, Element* element, const String& value)
    121 {
    122     // Renderer and our event handler are responsible for sanitizing values.
    123     ASSERT_UNUSED(inputElement, value == inputElement->sanitizeValue(value) || inputElement->sanitizeValue(value).isEmpty());
    124 
    125     // Workaround for bug where trailing \n is included in the result of textContent.
    126     // The assert macro above may also be simplified to:  value == constrainValue(value)
    127     // http://bugs.webkit.org/show_bug.cgi?id=9661
    128     if (value == "\n")
    129         data.setValue("");
    130     else
    131         data.setValue(value);
    132 
    133     element->setFormControlValueMatchesRenderer(true);
    134 
    135     element->dispatchEvent(Event::create(eventNames().inputEvent, true, false));
    136     notifyFormStateChanged(element);
    137 }
    138 
    139 String InputElement::sanitizeValue(const InputElement* inputElement, const String& proposedValue)
    140 {
    141     return InputElement::sanitizeUserInputValue(inputElement, proposedValue, s_maximumLength);
    142 }
    143 
    144 String InputElement::sanitizeUserInputValue(const InputElement* inputElement, const String& proposedValue, int maxLength)
    145 {
    146     if (!inputElement->isTextField())
    147         return proposedValue;
    148 
    149     String string = proposedValue;
    150     string.replace("\r\n", " ");
    151     string.replace('\r', ' ');
    152     string.replace('\n', ' ');
    153 
    154     unsigned newLength = string.numCharactersInGraphemeClusters(maxLength);
    155     for (unsigned i = 0; i < newLength; ++i) {
    156         const UChar current = string[i];
    157         if (current < ' ' && current != '\t') {
    158             newLength = i;
    159             break;
    160         }
    161     }
    162     return string.left(newLength);
    163 }
    164 
    165 void InputElement::handleBeforeTextInsertedEvent(InputElementData& data, InputElement* inputElement, Element* element, Event* event)
    166 {
    167     ASSERT(event->isBeforeTextInsertedEvent());
    168     // Make sure that the text to be inserted will not violate the maxLength.
    169 
    170     // We use RenderTextControlSingleLine::text() instead of InputElement::value()
    171     // because they can be mismatched by sanitizeValue() in
    172     // RenderTextControlSingleLine::subtreeHasChanged() in some cases.
    173     unsigned oldLength = toRenderTextControlSingleLine(element->renderer())->text().numGraphemeClusters();
    174 
    175     // selection() may be a pre-edit text.
    176     unsigned selectionLength = plainText(element->document()->frame()->selection()->selection().toNormalizedRange().get()).numGraphemeClusters();
    177     ASSERT(oldLength >= selectionLength);
    178 
    179     // Selected characters will be removed by the next text event.
    180     unsigned baseLength = oldLength - selectionLength;
    181     unsigned maxLength = static_cast<unsigned>(data.maxLength()); // maxLength() can never be negative.
    182     unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
    183 
    184     // Truncate the inserted text to avoid violating the maxLength and other constraints.
    185     BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(event);
    186     textEvent->setText(sanitizeUserInputValue(inputElement, textEvent->text(), appendableLength));
    187 }
    188 
    189 void InputElement::parseSizeAttribute(InputElementData& data, Element* element, MappedAttribute* attribute)
    190 {
    191     data.setSize(attribute->isNull() ? InputElement::s_defaultSize : attribute->value().toInt());
    192 
    193     if (RenderObject* renderer = element->renderer())
    194         renderer->setNeedsLayoutAndPrefWidthsRecalc();
    195 }
    196 
    197 void InputElement::parseMaxLengthAttribute(InputElementData& data, InputElement* inputElement, Element* element, MappedAttribute* attribute)
    198 {
    199     int maxLength = attribute->isNull() ? InputElement::s_maximumLength : attribute->value().toInt();
    200     if (maxLength <= 0 || maxLength > InputElement::s_maximumLength)
    201         maxLength = InputElement::s_maximumLength;
    202 
    203     int oldMaxLength = data.maxLength();
    204     data.setMaxLength(maxLength);
    205 
    206     if (oldMaxLength != maxLength)
    207         updateValueIfNeeded(data, inputElement);
    208 
    209     element->setNeedsStyleRecalc();
    210 }
    211 
    212 void InputElement::updateValueIfNeeded(InputElementData& data, InputElement* inputElement)
    213 {
    214     String oldValue = data.value();
    215     String newValue = sanitizeValue(inputElement, oldValue);
    216     if (newValue != oldValue)
    217         inputElement->setValue(newValue);
    218 }
    219 
    220 void InputElement::notifyFormStateChanged(Element* element)
    221 {
    222     Document* document = element->document();
    223     Frame* frame = document->frame();
    224     if (!frame)
    225         return;
    226 
    227     if (Page* page = frame->page())
    228         page->chrome()->client()->formStateDidChange(element);
    229 }
    230 
    231 // InputElementData
    232 InputElementData::InputElementData()
    233     : m_size(InputElement::s_defaultSize)
    234     , m_maxLength(InputElement::s_maximumLength)
    235     , m_cachedSelectionStart(-1)
    236     , m_cachedSelectionEnd(-1)
    237 {
    238 }
    239 
    240 const AtomicString& InputElementData::name() const
    241 {
    242     return m_name.isNull() ? emptyAtom : m_name;
    243 }
    244 
    245 InputElement* toInputElement(Element* element)
    246 {
    247     if (element->isHTMLElement() && (element->hasTagName(inputTag) || element->hasTagName(isindexTag)))
    248         return static_cast<HTMLInputElement*>(element);
    249 
    250 #if ENABLE(WML)
    251     if (element->isWMLElement() && element->hasTagName(WMLNames::inputTag))
    252         return static_cast<WMLInputElement*>(element);
    253 #endif
    254 
    255     return 0;
    256 }
    257 
    258 }
    259