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