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