• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2008, 2009 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 
23 #if ENABLE(WML)
24 #include "WMLInputElement.h"
25 
26 #include "EventNames.h"
27 #include "FormDataList.h"
28 #include "Frame.h"
29 #include "HTMLNames.h"
30 #include "KeyboardEvent.h"
31 #include "MappedAttribute.h"
32 #include "RenderTextControlSingleLine.h"
33 #include "TextEvent.h"
34 #include "WMLDocument.h"
35 #include "WMLNames.h"
36 #include "WMLPageState.h"
37 
38 namespace WebCore {
39 
WMLInputElement(const QualifiedName & tagName,Document * doc)40 WMLInputElement::WMLInputElement(const QualifiedName& tagName, Document* doc)
41     : WMLFormControlElement(tagName, doc)
42     , m_isPasswordField(false)
43     , m_isEmptyOk(false)
44     , m_numOfCharsAllowedByMask(0)
45 {
46 }
47 
~WMLInputElement()48 WMLInputElement::~WMLInputElement()
49 {
50     if (m_isPasswordField)
51         document()->unregisterForDocumentActivationCallbacks(this);
52 }
53 
formatCodes()54 static const AtomicString& formatCodes()
55 {
56     DEFINE_STATIC_LOCAL(AtomicString, codes, ("AaNnXxMm"));
57     return codes;
58 }
59 
isKeyboardFocusable(KeyboardEvent *) const60 bool WMLInputElement::isKeyboardFocusable(KeyboardEvent*) const
61 {
62     return WMLFormControlElement::isFocusable();
63 }
64 
isMouseFocusable() const65 bool WMLInputElement::isMouseFocusable() const
66 {
67     return WMLFormControlElement::isFocusable();
68 }
69 
dispatchFocusEvent()70 void WMLInputElement::dispatchFocusEvent()
71 {
72     InputElement::dispatchFocusEvent(m_data, this, this);
73     WMLElement::dispatchFocusEvent();
74 }
75 
dispatchBlurEvent()76 void WMLInputElement::dispatchBlurEvent()
77 {
78     // Firstly check if it is allowed to leave this input field
79     String val = value();
80     if ((!m_isEmptyOk && val.isEmpty()) || !isConformedToInputMask(val)) {
81         updateFocusAppearance(true);
82         return;
83     }
84 
85     // update the name variable of WML input elmenet
86     String nameVariable = formControlName();
87     if (!nameVariable.isEmpty())
88         wmlPageStateForDocument(document())->storeVariable(nameVariable, val);
89 
90     InputElement::dispatchBlurEvent(m_data, this, this);
91     WMLElement::dispatchBlurEvent();
92 }
93 
updateFocusAppearance(bool restorePreviousSelection)94 void WMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
95 {
96     InputElement::updateFocusAppearance(m_data, this, this, restorePreviousSelection);
97 }
98 
aboutToUnload()99 void WMLInputElement::aboutToUnload()
100 {
101     InputElement::aboutToUnload(this, this);
102 }
103 
size() const104 int WMLInputElement::size() const
105 {
106     return m_data.size();
107 }
108 
formControlType() const109 const AtomicString& WMLInputElement::formControlType() const
110 {
111     // needs to be lowercase according to DOM spec
112     if (m_isPasswordField) {
113         DEFINE_STATIC_LOCAL(const AtomicString, password, ("password"));
114         return password;
115     }
116 
117     DEFINE_STATIC_LOCAL(const AtomicString, text, ("text"));
118     return text;
119 }
120 
formControlName() const121 const AtomicString& WMLInputElement::formControlName() const
122 {
123     return m_data.name();
124 }
125 
value() const126 String WMLInputElement::value() const
127 {
128     String value = m_data.value();
129     if (value.isNull())
130         value = constrainValue(getAttribute(HTMLNames::valueAttr));
131 
132     return value;
133 }
134 
setValue(const String & value)135 void WMLInputElement::setValue(const String& value)
136 {
137     InputElement::updatePlaceholderVisibility(m_data, this, this);
138     setFormControlValueMatchesRenderer(false);
139     m_data.setValue(constrainValue(value));
140     if (inDocument())
141         document()->updateStyleIfNeeded();
142     if (renderer())
143         renderer()->updateFromElement();
144     setNeedsStyleRecalc();
145 
146     unsigned max = m_data.value().length();
147     if (document()->focusedNode() == this)
148         InputElement::updateSelectionRange(this, this, max, max);
149     else
150         cacheSelection(max, max);
151 
152     InputElement::notifyFormStateChanged(this);
153 }
154 
setValueFromRenderer(const String & value)155 void WMLInputElement::setValueFromRenderer(const String& value)
156 {
157     InputElement::setValueFromRenderer(m_data, this, this, value);
158 }
159 
saveFormControlState(String & result) const160 bool WMLInputElement::saveFormControlState(String& result) const
161 {
162     if (m_isPasswordField)
163         return false;
164 
165     result = value();
166     return true;
167 }
168 
restoreFormControlState(const String & state)169 void WMLInputElement::restoreFormControlState(const String& state)
170 {
171     ASSERT(!m_isPasswordField); // should never save/restore password fields
172     setValue(state);
173 }
174 
select()175 void WMLInputElement::select()
176 {
177     if (RenderTextControl* r = toRenderTextControl(renderer()))
178         r->select();
179 }
180 
accessKeyAction(bool)181 void WMLInputElement::accessKeyAction(bool)
182 {
183     // should never restore previous selection here
184     focus(false);
185 }
186 
parseMappedAttribute(MappedAttribute * attr)187 void WMLInputElement::parseMappedAttribute(MappedAttribute* attr)
188 {
189     if (attr->name() == HTMLNames::nameAttr)
190         m_data.setName(parseValueForbiddingVariableReferences(attr->value()));
191     else if (attr->name() == HTMLNames::typeAttr) {
192         String type = parseValueForbiddingVariableReferences(attr->value());
193         m_isPasswordField = (type == "password");
194     } else if (attr->name() == HTMLNames::valueAttr) {
195         // We only need to setChanged if the form is looking at the default value right now.
196         if (m_data.value().isNull())
197             setNeedsStyleRecalc();
198         setFormControlValueMatchesRenderer(false);
199     } else if (attr->name() == HTMLNames::maxlengthAttr)
200         InputElement::parseMaxLengthAttribute(m_data, this, this, attr);
201     else if (attr->name() == HTMLNames::sizeAttr)
202         InputElement::parseSizeAttribute(m_data, this, attr);
203     else if (attr->name() == WMLNames::formatAttr)
204         m_formatMask = validateInputMask(parseValueForbiddingVariableReferences(attr->value()));
205     else if (attr->name() == WMLNames::emptyokAttr)
206         m_isEmptyOk = (attr->value() == "true");
207     else
208         WMLElement::parseMappedAttribute(attr);
209 
210     // FIXME: Handle 'accesskey' attribute
211     // FIXME: Handle 'tabindex' attribute
212     // FIXME: Handle 'title' attribute
213 }
214 
copyNonAttributeProperties(const Element * source)215 void WMLInputElement::copyNonAttributeProperties(const Element* source)
216 {
217     const WMLInputElement* sourceElement = static_cast<const WMLInputElement*>(source);
218     m_data.setValue(sourceElement->m_data.value());
219     WMLElement::copyNonAttributeProperties(source);
220 }
221 
createRenderer(RenderArena * arena,RenderStyle *)222 RenderObject* WMLInputElement::createRenderer(RenderArena* arena, RenderStyle*)
223 {
224     return new (arena) RenderTextControlSingleLine(this);
225 }
226 
detach()227 void WMLInputElement::detach()
228 {
229     WMLElement::detach();
230     setFormControlValueMatchesRenderer(false);
231 }
232 
appendFormData(FormDataList & encoding,bool)233 bool WMLInputElement::appendFormData(FormDataList& encoding, bool)
234 {
235     if (formControlName().isEmpty())
236         return false;
237 
238     encoding.appendData(formControlName(), value());
239     return true;
240 }
241 
reset()242 void WMLInputElement::reset()
243 {
244     setValue(String());
245 }
246 
defaultEventHandler(Event * evt)247 void WMLInputElement::defaultEventHandler(Event* evt)
248 {
249     bool clickDefaultFormButton = false;
250 
251     if (evt->type() == eventNames().textInputEvent && evt->isTextEvent()) {
252         TextEvent* textEvent = static_cast<TextEvent*>(evt);
253         if (textEvent->data() == "\n")
254             clickDefaultFormButton = true;
255         else if (renderer() && !isConformedToInputMask(textEvent->data()[0], toRenderTextControl(renderer())->text().length() + 1))
256             // If the inputed char doesn't conform to the input mask, stop handling
257             return;
258     }
259 
260     if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent() && focused() && document()->frame()
261         && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
262         evt->setDefaultHandled();
263         return;
264     }
265 
266     // Let the key handling done in EventTargetNode take precedence over the event handling here for editable text fields
267     if (!clickDefaultFormButton) {
268         WMLElement::defaultEventHandler(evt);
269         if (evt->defaultHandled())
270             return;
271     }
272 
273     // Use key press event here since sending simulated mouse events
274     // on key down blocks the proper sending of the key press event.
275     if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) {
276         // Simulate mouse click on the default form button for enter for these types of elements.
277         if (static_cast<KeyboardEvent*>(evt)->charCode() == '\r')
278             clickDefaultFormButton = true;
279     }
280 
281     if (clickDefaultFormButton) {
282         // Fire onChange for text fields.
283         RenderObject* r = renderer();
284         if (r && toRenderTextControl(r)->isEdited()) {
285             dispatchEvent(eventNames().changeEvent, true, false);
286 
287             // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
288             r = renderer();
289             if (r)
290                 toRenderTextControl(r)->setEdited(false);
291         }
292 
293         evt->setDefaultHandled();
294         return;
295     }
296 
297     if (evt->isBeforeTextInsertedEvent())
298         InputElement::handleBeforeTextInsertedEvent(m_data, this, document(), evt);
299 
300     if (renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent))
301         toRenderTextControlSingleLine(renderer())->forwardEvent(evt);
302 }
303 
cacheSelection(int start,int end)304 void WMLInputElement::cacheSelection(int start, int end)
305 {
306     m_data.setCachedSelectionStart(start);
307     m_data.setCachedSelectionEnd(end);
308 }
309 
constrainValue(const String & proposedValue) const310 String WMLInputElement::constrainValue(const String& proposedValue) const
311 {
312     return InputElement::constrainValue(this, proposedValue, m_data.maxLength());
313 }
314 
documentDidBecomeActive()315 void WMLInputElement::documentDidBecomeActive()
316 {
317     ASSERT(m_isPasswordField);
318     reset();
319 }
320 
placeholderShouldBeVisible() const321 bool WMLInputElement::placeholderShouldBeVisible() const
322 {
323     return m_data.placeholderShouldBeVisible();
324 }
325 
willMoveToNewOwnerDocument()326 void WMLInputElement::willMoveToNewOwnerDocument()
327 {
328     // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered
329     if (m_isPasswordField)
330         document()->unregisterForDocumentActivationCallbacks(this);
331 
332     WMLElement::willMoveToNewOwnerDocument();
333 }
334 
didMoveToNewOwnerDocument()335 void WMLInputElement::didMoveToNewOwnerDocument()
336 {
337     if (m_isPasswordField)
338         document()->registerForDocumentActivationCallbacks(this);
339 
340     WMLElement::didMoveToNewOwnerDocument();
341 }
342 
initialize()343 void WMLInputElement::initialize()
344 {
345     String nameVariable = formControlName();
346     String variableValue;
347     WMLPageState* pageSate = wmlPageStateForDocument(document());
348     ASSERT(pageSate);
349     if (!nameVariable.isEmpty())
350         variableValue = pageSate->getVariable(nameVariable);
351 
352     if (variableValue.isEmpty() || !isConformedToInputMask(variableValue)) {
353         String val = value();
354         if (isConformedToInputMask(val))
355             variableValue = val;
356         else
357             variableValue = "";
358 
359         pageSate->storeVariable(nameVariable, variableValue);
360     }
361     setValue(variableValue);
362 
363     if (!hasAttribute(WMLNames::emptyokAttr)) {
364         if (m_formatMask.isEmpty() ||
365             // check if the format codes is just "*f"
366            (m_formatMask.length() == 2 && m_formatMask[0] == '*' && formatCodes().find(m_formatMask[1]) != -1))
367             m_isEmptyOk = true;
368     }
369 }
370 
validateInputMask(const String & inputMask)371 String WMLInputElement::validateInputMask(const String& inputMask)
372 {
373     bool isValid = true;
374     bool hasWildcard = false;
375     unsigned escapeCharCount = 0;
376     unsigned maskLength = inputMask.length();
377     UChar formatCode;
378 
379     for (unsigned i = 0; i < maskLength; ++i) {
380         formatCode = inputMask[i];
381         if (formatCodes().find(formatCode) == -1) {
382             if (formatCode == '*' || (WTF::isASCIIDigit(formatCode) && formatCode != '0')) {
383                 // validate codes which ends with '*f' or 'nf'
384                 formatCode = inputMask[++i];
385                 if ((i + 1 != maskLength) || formatCodes().find(formatCode) == -1) {
386                     isValid = false;
387                     break;
388                 }
389                 hasWildcard = true;
390             } else if (formatCode == '\\') {
391                 //skip over the next mask character
392                 ++i;
393                 ++escapeCharCount;
394             } else {
395                 isValid = false;
396                 break;
397             }
398         }
399     }
400 
401     if (!isValid)
402         return String();
403 
404     // calculate the number of characters allowed to be entered by input mask
405     m_numOfCharsAllowedByMask = maskLength;
406 
407     if (escapeCharCount)
408         m_numOfCharsAllowedByMask -= escapeCharCount;
409 
410     if (hasWildcard) {
411         formatCode = inputMask[maskLength - 2];
412         if (formatCode == '*')
413             m_numOfCharsAllowedByMask = m_data.maxLength();
414         else {
415             unsigned leftLen = String(&formatCode).toInt();
416             m_numOfCharsAllowedByMask = leftLen + m_numOfCharsAllowedByMask - 2;
417         }
418     }
419 
420     return inputMask;
421 }
422 
isConformedToInputMask(const String & inputChars)423 bool WMLInputElement::isConformedToInputMask(const String& inputChars)
424 {
425     for (unsigned i = 0; i < inputChars.length(); ++i)
426         if (!isConformedToInputMask(inputChars[i], i + 1, false))
427             return false;
428 
429     return true;
430 }
431 
isConformedToInputMask(UChar inChar,unsigned inputCharCount,bool isUserInput)432 bool WMLInputElement::isConformedToInputMask(UChar inChar, unsigned inputCharCount, bool isUserInput)
433 {
434     if (m_formatMask.isEmpty())
435         return true;
436 
437     if (inputCharCount > m_numOfCharsAllowedByMask)
438         return false;
439 
440     unsigned maskIndex = 0;
441     if (isUserInput) {
442         unsigned cursorPosition = 0;
443         if (renderer())
444             cursorPosition = toRenderTextControl(renderer())->selectionStart();
445         else
446             cursorPosition = m_data.cachedSelectionStart();
447 
448         maskIndex = cursorPositionToMaskIndex(cursorPosition);
449     } else
450         maskIndex = cursorPositionToMaskIndex(inputCharCount - 1);
451 
452     bool ok = true;
453     UChar mask = m_formatMask[maskIndex];
454     // match the inputed character with input mask
455     switch (mask) {
456     case 'A':
457         ok = !WTF::isASCIIDigit(inChar) && !WTF::isASCIILower(inChar) && WTF::isASCIIPrintable(inChar);
458         break;
459     case 'a':
460         ok = !WTF::isASCIIDigit(inChar) && !WTF::isASCIIUpper(inChar) && WTF::isASCIIPrintable(inChar);
461         break;
462     case 'N':
463         ok = WTF::isASCIIDigit(inChar);
464         break;
465     case 'n':
466         ok = !WTF::isASCIIAlpha(inChar) && WTF::isASCIIPrintable(inChar);
467         break;
468     case 'X':
469         ok = !WTF::isASCIILower(inChar) && WTF::isASCIIPrintable(inChar);
470         break;
471     case 'x':
472         ok = !WTF::isASCIIUpper(inChar) && WTF::isASCIIPrintable(inChar);
473         break;
474     case 'M':
475         ok = WTF::isASCIIPrintable(inChar);
476         break;
477     case 'm':
478         ok = WTF::isASCIIPrintable(inChar);
479         break;
480     default:
481         ok = (mask == inChar);
482         break;
483     }
484 
485     return ok;
486 }
487 
cursorPositionToMaskIndex(unsigned cursorPosition)488 unsigned WMLInputElement::cursorPositionToMaskIndex(unsigned cursorPosition)
489 {
490     UChar mask;
491     int index = -1;
492     do {
493         mask = m_formatMask[++index];
494         if (mask == '\\')
495             ++index;
496         else if (mask == '*' || (WTF::isASCIIDigit(mask) && mask != '0')) {
497             index = m_formatMask.length() - 1;
498             break;
499         }
500     } while (cursorPosition--);
501 
502     return index;
503 }
504 
505 }
506 
507 #endif
508