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