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