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