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