• 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 "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