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