• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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