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