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 "ChromeClient.h"
26 #include "Document.h"
27 #include "Event.h"
28 #include "EventNames.h"
29 #include "FormControlElement.h"
30 #include "Frame.h"
31 #include "HTMLInputElement.h"
32 #include "HTMLNames.h"
33 #include "Page.h"
34 #include "RenderTextControlSingleLine.h"
35 #include "SelectionController.h"
36 #include "TextIterator.h"
37 #include "TextBreakIterator.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 http://bugs.webkit.org/show_bugs.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
dispatchFocusEvent(InputElementData & data,Document * document)55 void InputElement::dispatchFocusEvent(InputElementData& data, Document* document)
56 {
57 if (!data.inputElement()->isTextField())
58 return;
59
60 updatePlaceholderVisibility(data, document);
61
62 if (data.inputElement()->isPasswordField() && document->frame())
63 document->setUseSecureKeyboardEntryWhenActive(true);
64 }
65
dispatchBlurEvent(InputElementData & data,Document * document)66 void InputElement::dispatchBlurEvent(InputElementData& data, Document* document)
67 {
68 if (!data.inputElement()->isTextField())
69 return;
70
71 Frame* frame = document->frame();
72 if (!frame)
73 return;
74
75 updatePlaceholderVisibility(data, document);
76
77 if (data.inputElement()->isPasswordField())
78 document->setUseSecureKeyboardEntryWhenActive(false);
79
80 frame->textFieldDidEndEditing(data.element());
81 }
82
updatePlaceholderVisibility(InputElementData & data,Document * document,bool placeholderValueChanged)83 void InputElement::updatePlaceholderVisibility(InputElementData& data, Document* document, bool placeholderValueChanged)
84 {
85 ASSERT(data.inputElement()->isTextField());
86
87 bool oldPlaceholderShouldBeVisible = data.placeholderShouldBeVisible();
88 Element* element = data.element();
89
90 data.setPlaceholderShouldBeVisible(data.inputElement()->value().isEmpty()
91 && document->focusedNode() != element
92 && !data.inputElement()->placeholderValue().isEmpty());
93
94 if ((oldPlaceholderShouldBeVisible != data.placeholderShouldBeVisible() || placeholderValueChanged) && element->renderer())
95 static_cast<RenderTextControlSingleLine*>(element->renderer())->updatePlaceholderVisibility();
96 }
97
updateFocusAppearance(InputElementData & data,Document * document,bool restorePreviousSelection)98 void InputElement::updateFocusAppearance(InputElementData& data, Document* document, bool restorePreviousSelection)
99 {
100 ASSERT(data.inputElement()->isTextField());
101
102 if (!restorePreviousSelection || data.cachedSelectionStart() == -1)
103 data.inputElement()->select();
104 else
105 // Restore the cached selection.
106 updateSelectionRange(data, data.cachedSelectionStart(), data.cachedSelectionEnd());
107
108 if (document && document->frame())
109 document->frame()->revealSelection();
110 }
111
updateSelectionRange(InputElementData & data,int start,int end)112 void InputElement::updateSelectionRange(InputElementData& data, int start, int end)
113 {
114 if (!data.inputElement()->isTextField())
115 return;
116
117 if (RenderTextControl* renderer = static_cast<RenderTextControl*>(data.element()->renderer()))
118 renderer->setSelectionRange(start, end);
119 }
120
aboutToUnload(InputElementData & data,Document * document)121 void InputElement::aboutToUnload(InputElementData& data, Document* document)
122 {
123 if (!data.inputElement()->isTextField() || !data.element()->focused() || !document->frame())
124 return;
125
126 document->frame()->textFieldDidEndEditing(data.element());
127 }
128
setValueFromRenderer(InputElementData & data,Document * document,const String & value)129 void InputElement::setValueFromRenderer(InputElementData& data, Document* document, const String& value)
130 {
131 // Renderer and our event handler are responsible for constraining values.
132 ASSERT(value == data.inputElement()->constrainValue(value) || data.inputElement()->constrainValue(value).isEmpty());
133
134 if (data.inputElement()->isTextField())
135 updatePlaceholderVisibility(data, document);
136
137 // Workaround for bug where trailing \n is included in the result of textContent.
138 // The assert macro above may also be simplified to: value == constrainValue(value)
139 // http://bugs.webkit.org/show_bug.cgi?id=9661
140 if (value == "\n")
141 data.setValue("");
142 else
143 data.setValue(value);
144
145 Element* element = data.element();
146 FormControlElement* formControlElement = toFormControlElement(element);
147 ASSERT(formControlElement);
148 formControlElement->setValueMatchesRenderer();
149
150 // Fire the "input" DOM event
151 element->dispatchEventForType(eventNames().inputEvent, true, false);
152 notifyFormStateChanged(data, document);
153 }
154
numCharactersInGraphemeClusters(StringImpl * s,int numGraphemeClusters)155 static int numCharactersInGraphemeClusters(StringImpl* s, int numGraphemeClusters)
156 {
157 if (!s)
158 return 0;
159
160 TextBreakIterator* it = characterBreakIterator(s->characters(), s->length());
161 if (!it)
162 return 0;
163
164 for (int i = 0; i < numGraphemeClusters; ++i) {
165 if (textBreakNext(it) == TextBreakDone)
166 return s->length();
167 }
168
169 return textBreakCurrent(it);
170 }
171
constrainValue(const InputElementData & data,const String & proposedValue,int maxLength)172 String InputElement::constrainValue(const InputElementData& data, const String& proposedValue, int maxLength)
173 {
174 String string = proposedValue;
175 if (!data.inputElement()->isTextField())
176 return string;
177
178 string.replace("\r\n", " ");
179 string.replace('\r', ' ');
180 string.replace('\n', ' ');
181
182 StringImpl* s = string.impl();
183 int newLength = numCharactersInGraphemeClusters(s, maxLength);
184 for (int i = 0; i < newLength; ++i) {
185 const UChar& current = (*s)[i];
186 if (current < ' ' && current != '\t') {
187 newLength = i;
188 break;
189 }
190 }
191
192 if (newLength < static_cast<int>(string.length()))
193 return string.left(newLength);
194
195 return string;
196 }
197
numGraphemeClusters(StringImpl * s)198 static int numGraphemeClusters(StringImpl* s)
199 {
200 if (!s)
201 return 0;
202
203 TextBreakIterator* it = characterBreakIterator(s->characters(), s->length());
204 if (!it)
205 return 0;
206
207 int num = 0;
208 while (textBreakNext(it) != TextBreakDone)
209 ++num;
210
211 return num;
212 }
213
handleBeforeTextInsertedEvent(InputElementData & data,Document * document,Event * event)214 void InputElement::handleBeforeTextInsertedEvent(InputElementData& data, Document* document, Event* event)
215 {
216 ASSERT(event->isBeforeTextInsertedEvent());
217
218 // Make sure that the text to be inserted will not violate the maxLength.
219 int oldLength = numGraphemeClusters(data.inputElement()->value().impl());
220 ASSERT(oldLength <= data.maxLength());
221 int selectionLength = numGraphemeClusters(plainText(document->frame()->selection()->selection().toRange().get()).impl());
222 ASSERT(oldLength >= selectionLength);
223 int maxNewLength = data.maxLength() - (oldLength - selectionLength);
224
225 // Truncate the inserted text to avoid violating the maxLength and other constraints.
226 BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(event);
227 textEvent->setText(constrainValue(data, textEvent->text(), maxNewLength));
228 }
229
parseSizeAttribute(InputElementData & data,MappedAttribute * attribute)230 void InputElement::parseSizeAttribute(InputElementData& data, MappedAttribute* attribute)
231 {
232 data.setSize(attribute->isNull() ? InputElement::s_defaultSize : attribute->value().toInt());
233
234 if (RenderObject* renderer = data.element()->renderer())
235 renderer->setNeedsLayoutAndPrefWidthsRecalc();
236 }
237
parseMaxLengthAttribute(InputElementData & data,MappedAttribute * attribute)238 void InputElement::parseMaxLengthAttribute(InputElementData& data, MappedAttribute* attribute)
239 {
240 int maxLength = attribute->isNull() ? InputElement::s_maximumLength : attribute->value().toInt();
241 if (maxLength <= 0 || maxLength > InputElement::s_maximumLength)
242 maxLength = InputElement::s_maximumLength;
243
244 int oldMaxLength = data.maxLength();
245 data.setMaxLength(maxLength);
246
247 if (oldMaxLength != maxLength)
248 updateValueIfNeeded(data);
249
250 data.element()->setChanged();
251 }
252
updateValueIfNeeded(InputElementData & data)253 void InputElement::updateValueIfNeeded(InputElementData& data)
254 {
255 String oldValue = data.value();
256 String newValue = data.inputElement()->constrainValue(oldValue);
257 if (newValue != oldValue)
258 data.inputElement()->setValue(newValue);
259 }
260
notifyFormStateChanged(InputElementData & data,Document * document)261 void InputElement::notifyFormStateChanged(InputElementData& data, Document* document)
262 {
263 Frame* frame = document->frame();
264 if (!frame)
265 return;
266
267 if (Page* page = frame->page())
268 page->chrome()->client()->formStateDidChange(data.element());
269 }
270
271 // InputElementData
InputElementData(InputElement * inputElement,Element * element)272 InputElementData::InputElementData(InputElement* inputElement, Element* element)
273 : m_inputElement(inputElement)
274 , m_element(element)
275 , m_placeholderShouldBeVisible(false)
276 , m_size(InputElement::s_defaultSize)
277 , m_maxLength(InputElement::s_maximumLength)
278 , m_cachedSelectionStart(-1)
279 , m_cachedSelectionEnd(-1)
280 {
281 ASSERT(m_inputElement);
282 ASSERT(m_element);
283 }
284
~InputElementData()285 InputElementData::~InputElementData()
286 {
287 }
288
name() const289 const AtomicString& InputElementData::name() const
290 {
291 return m_name.isNull() ? emptyAtom : m_name;
292 }
293
toInputElement(Element * element)294 InputElement* toInputElement(Element* element)
295 {
296 if (element->isHTMLElement() && (element->hasTagName(inputTag) || element->hasTagName(isindexTag)))
297 return static_cast<HTMLInputElement*>(element);
298
299 #if ENABLE(WML)
300 if (element->isWMLElement() && element->hasTagName(WMLNames::inputTag))
301 return static_cast<WMLInputElement*>(element);
302 #endif
303
304 return 0;
305 }
306
307 }
308