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 "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 
     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 
     48 WMLInputElement::~WMLInputElement()
     49 {
     50     if (m_isPasswordField)
     51         document()->unregisterForDocumentActivationCallbacks(this);
     52 }
     53 
     54 static const AtomicString& formatCodes()
     55 {
     56     DEFINE_STATIC_LOCAL(AtomicString, codes, ("AaNnXxMm"));
     57     return codes;
     58 }
     59 
     60 bool WMLInputElement::isKeyboardFocusable(KeyboardEvent*) const
     61 {
     62     return WMLFormControlElement::isFocusable();
     63 }
     64 
     65 bool WMLInputElement::isMouseFocusable() const
     66 {
     67     return WMLFormControlElement::isFocusable();
     68 }
     69 
     70 void WMLInputElement::dispatchFocusEvent()
     71 {
     72     InputElement::dispatchFocusEvent(this, this);
     73     WMLElement::dispatchFocusEvent();
     74 }
     75 
     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 
     94 void WMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
     95 {
     96     InputElement::updateFocusAppearance(m_data, this, this, restorePreviousSelection);
     97 }
     98 
     99 void WMLInputElement::aboutToUnload()
    100 {
    101     InputElement::aboutToUnload(this, this);
    102 }
    103 
    104 int WMLInputElement::size() const
    105 {
    106     return m_data.size();
    107 }
    108 
    109 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 
    121 const AtomicString& WMLInputElement::formControlName() const
    122 {
    123     return m_data.name();
    124 }
    125 
    126 const String& WMLInputElement::suggestedValue() const
    127 {
    128     return m_data.suggestedValue();
    129 }
    130 
    131 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 
    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 
    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 
    166 void WMLInputElement::setValueFromRenderer(const String& value)
    167 {
    168     InputElement::setValueFromRenderer(m_data, this, this, value);
    169 }
    170 
    171 bool WMLInputElement::saveFormControlState(String& result) const
    172 {
    173     if (m_isPasswordField)
    174         return false;
    175 
    176     result = value();
    177     return true;
    178 }
    179 
    180 void WMLInputElement::restoreFormControlState(const String& state)
    181 {
    182     ASSERT(!m_isPasswordField); // should never save/restore password fields
    183     setValue(state);
    184 }
    185 
    186 void WMLInputElement::select()
    187 {
    188     if (RenderTextControl* r = toRenderTextControl(renderer()))
    189         r->select();
    190 }
    191 
    192 void WMLInputElement::accessKeyAction(bool)
    193 {
    194     // should never restore previous selection here
    195     focus(false);
    196 }
    197 
    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 
    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 
    233 RenderObject* WMLInputElement::createRenderer(RenderArena* arena, RenderStyle*)
    234 {
    235     return new (arena) RenderTextControlSingleLine(this, false);
    236 }
    237 
    238 void WMLInputElement::detach()
    239 {
    240     WMLElement::detach();
    241     setFormControlValueMatchesRenderer(false);
    242 }
    243 
    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 
    253 void WMLInputElement::reset()
    254 {
    255     setValue(String());
    256 }
    257 
    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 
    315 void WMLInputElement::cacheSelection(int start, int end)
    316 {
    317     m_data.setCachedSelectionStart(start);
    318     m_data.setCachedSelectionEnd(end);
    319 }
    320 
    321 String WMLInputElement::constrainValue(const String& proposedValue) const
    322 {
    323     return InputElement::sanitizeUserInputValue(this, proposedValue, m_data.maxLength());
    324 }
    325 
    326 void WMLInputElement::documentDidBecomeActive()
    327 {
    328     ASSERT(m_isPasswordField);
    329     reset();
    330 }
    331 
    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 
    341 void WMLInputElement::didMoveToNewOwnerDocument()
    342 {
    343     if (m_isPasswordField)
    344         document()->registerForDocumentActivationCallbacks(this);
    345 
    346     WMLElement::didMoveToNewOwnerDocument();
    347 }
    348 
    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 
    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 
    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 
    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 
    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