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
38namespace WebCore {
39
40WMLInputElement::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
49PassRefPtr<WMLInputElement> WMLInputElement::create(const QualifiedName& tagName, Document* document)
50{
51    return adoptRef(new WMLInputElement(tagName, document));
52}
53
54WMLInputElement::~WMLInputElement()
55{
56    if (m_isPasswordField)
57        document()->unregisterForDocumentActivationCallbacks(this);
58}
59
60static const AtomicString& formatCodes()
61{
62    DEFINE_STATIC_LOCAL(AtomicString, codes, ("AaNnXxMm"));
63    return codes;
64}
65
66bool WMLInputElement::isKeyboardFocusable(KeyboardEvent*) const
67{
68    return WMLFormControlElement::isFocusable();
69}
70
71bool WMLInputElement::isMouseFocusable() const
72{
73    return WMLFormControlElement::isFocusable();
74}
75
76void WMLInputElement::dispatchFocusEvent()
77{
78    InputElement::dispatchFocusEvent(this, this);
79    WMLElement::dispatchFocusEvent();
80}
81
82void 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
100void WMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
101{
102    InputElement::updateFocusAppearance(m_data, this, this, restorePreviousSelection);
103}
104
105void WMLInputElement::aboutToUnload()
106{
107    InputElement::aboutToUnload(this, this);
108}
109
110int WMLInputElement::size() const
111{
112    return m_data.size();
113}
114
115const 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
127const AtomicString& WMLInputElement::formControlName() const
128{
129    return m_data.name();
130}
131
132const String& WMLInputElement::suggestedValue() const
133{
134    return m_data.suggestedValue();
135}
136
137String 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
146void 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
165void 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
172void WMLInputElement::setValueFromRenderer(const String& value)
173{
174    InputElement::setValueFromRenderer(m_data, this, this, value);
175}
176
177bool WMLInputElement::wasChangedSinceLastFormControlChangeEvent() const
178{
179    return m_wasChangedSinceLastChangeEvent;
180}
181
182void WMLInputElement::setChangedSinceLastFormControlChangeEvent(bool changed)
183{
184    m_wasChangedSinceLastChangeEvent = changed;
185}
186
187bool WMLInputElement::saveFormControlState(String& result) const
188{
189    if (m_isPasswordField)
190        return false;
191
192    result = value();
193    return true;
194}
195
196void WMLInputElement::restoreFormControlState(const String& state)
197{
198    ASSERT(!m_isPasswordField); // should never save/restore password fields
199    setValue(state);
200}
201
202void WMLInputElement::select()
203{
204    if (RenderTextControl* r = toRenderTextControl(renderer()))
205        setSelectionRange(this, 0, r->text().length());
206}
207
208void WMLInputElement::accessKeyAction(bool)
209{
210    // should never restore previous selection here
211    focus(false);
212}
213
214void 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
242void 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
249RenderObject* WMLInputElement::createRenderer(RenderArena* arena, RenderStyle*)
250{
251    return new (arena) RenderTextControlSingleLine(this, false);
252}
253
254void WMLInputElement::detach()
255{
256    WMLElement::detach();
257    setFormControlValueMatchesRenderer(false);
258}
259
260bool WMLInputElement::appendFormData(FormDataList& encoding, bool)
261{
262    if (formControlName().isEmpty())
263        return false;
264
265    encoding.appendData(formControlName(), value());
266    return true;
267}
268
269void WMLInputElement::reset()
270{
271    setValue(String());
272}
273
274void 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
326void WMLInputElement::cacheSelection(int start, int end)
327{
328    m_data.setCachedSelectionStart(start);
329    m_data.setCachedSelectionEnd(end);
330}
331
332String WMLInputElement::constrainValue(const String& proposedValue) const
333{
334    return InputElement::sanitizeUserInputValue(this, proposedValue, m_data.maxLength());
335}
336
337void WMLInputElement::documentDidBecomeActive()
338{
339    ASSERT(m_isPasswordField);
340    reset();
341}
342
343void 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
352void WMLInputElement::didMoveToNewOwnerDocument()
353{
354    if (m_isPasswordField)
355        document()->registerForDocumentActivationCallbacks(this);
356
357    WMLElement::didMoveToNewOwnerDocument();
358}
359
360void 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
388String 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
440bool 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
449bool 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
505unsigned 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