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
44namespace WebCore {
45
46using 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.
52const int InputElement::s_maximumLength = 524288;
53const int InputElement::s_defaultSize = 20;
54
55void 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
65void 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
81void 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
96void 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
107void 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
120void 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
139String InputElement::sanitizeValue(const InputElement* inputElement, const String& proposedValue)
140{
141    return InputElement::sanitizeUserInputValue(inputElement, proposedValue, s_maximumLength);
142}
143
144String 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
165void 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
189void 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
197void 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
212void 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
220void 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
232InputElementData::InputElementData()
233    : m_size(InputElement::s_defaultSize)
234    , m_maxLength(InputElement::s_maximumLength)
235    , m_cachedSelectionStart(-1)
236    , m_cachedSelectionEnd(-1)
237{
238}
239
240const AtomicString& InputElementData::name() const
241{
242    return m_name.isNull() ? emptyAtom : m_name;
243}
244
245InputElement* 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