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(m_data, 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(m_data, 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
value() const126 String WMLInputElement::value() const
127 {
128 String value = m_data.value();
129 if (value.isNull())
130 value = constrainValue(getAttribute(HTMLNames::valueAttr));
131
132 return value;
133 }
134
setValue(const String & value)135 void WMLInputElement::setValue(const String& value)
136 {
137 InputElement::updatePlaceholderVisibility(m_data, this, this);
138 setFormControlValueMatchesRenderer(false);
139 m_data.setValue(constrainValue(value));
140 if (inDocument())
141 document()->updateStyleIfNeeded();
142 if (renderer())
143 renderer()->updateFromElement();
144 setNeedsStyleRecalc();
145
146 unsigned max = m_data.value().length();
147 if (document()->focusedNode() == this)
148 InputElement::updateSelectionRange(this, this, max, max);
149 else
150 cacheSelection(max, max);
151
152 InputElement::notifyFormStateChanged(this);
153 }
154
setValueFromRenderer(const String & value)155 void WMLInputElement::setValueFromRenderer(const String& value)
156 {
157 InputElement::setValueFromRenderer(m_data, this, this, value);
158 }
159
saveFormControlState(String & result) const160 bool WMLInputElement::saveFormControlState(String& result) const
161 {
162 if (m_isPasswordField)
163 return false;
164
165 result = value();
166 return true;
167 }
168
restoreFormControlState(const String & state)169 void WMLInputElement::restoreFormControlState(const String& state)
170 {
171 ASSERT(!m_isPasswordField); // should never save/restore password fields
172 setValue(state);
173 }
174
select()175 void WMLInputElement::select()
176 {
177 if (RenderTextControl* r = toRenderTextControl(renderer()))
178 r->select();
179 }
180
accessKeyAction(bool)181 void WMLInputElement::accessKeyAction(bool)
182 {
183 // should never restore previous selection here
184 focus(false);
185 }
186
parseMappedAttribute(MappedAttribute * attr)187 void WMLInputElement::parseMappedAttribute(MappedAttribute* attr)
188 {
189 if (attr->name() == HTMLNames::nameAttr)
190 m_data.setName(parseValueForbiddingVariableReferences(attr->value()));
191 else if (attr->name() == HTMLNames::typeAttr) {
192 String type = parseValueForbiddingVariableReferences(attr->value());
193 m_isPasswordField = (type == "password");
194 } else if (attr->name() == HTMLNames::valueAttr) {
195 // We only need to setChanged if the form is looking at the default value right now.
196 if (m_data.value().isNull())
197 setNeedsStyleRecalc();
198 setFormControlValueMatchesRenderer(false);
199 } else if (attr->name() == HTMLNames::maxlengthAttr)
200 InputElement::parseMaxLengthAttribute(m_data, this, this, attr);
201 else if (attr->name() == HTMLNames::sizeAttr)
202 InputElement::parseSizeAttribute(m_data, this, attr);
203 else if (attr->name() == WMLNames::formatAttr)
204 m_formatMask = validateInputMask(parseValueForbiddingVariableReferences(attr->value()));
205 else if (attr->name() == WMLNames::emptyokAttr)
206 m_isEmptyOk = (attr->value() == "true");
207 else
208 WMLElement::parseMappedAttribute(attr);
209
210 // FIXME: Handle 'accesskey' attribute
211 // FIXME: Handle 'tabindex' attribute
212 // FIXME: Handle 'title' attribute
213 }
214
copyNonAttributeProperties(const Element * source)215 void WMLInputElement::copyNonAttributeProperties(const Element* source)
216 {
217 const WMLInputElement* sourceElement = static_cast<const WMLInputElement*>(source);
218 m_data.setValue(sourceElement->m_data.value());
219 WMLElement::copyNonAttributeProperties(source);
220 }
221
createRenderer(RenderArena * arena,RenderStyle *)222 RenderObject* WMLInputElement::createRenderer(RenderArena* arena, RenderStyle*)
223 {
224 return new (arena) RenderTextControlSingleLine(this);
225 }
226
detach()227 void WMLInputElement::detach()
228 {
229 WMLElement::detach();
230 setFormControlValueMatchesRenderer(false);
231 }
232
appendFormData(FormDataList & encoding,bool)233 bool WMLInputElement::appendFormData(FormDataList& encoding, bool)
234 {
235 if (formControlName().isEmpty())
236 return false;
237
238 encoding.appendData(formControlName(), value());
239 return true;
240 }
241
reset()242 void WMLInputElement::reset()
243 {
244 setValue(String());
245 }
246
defaultEventHandler(Event * evt)247 void WMLInputElement::defaultEventHandler(Event* evt)
248 {
249 bool clickDefaultFormButton = false;
250
251 if (evt->type() == eventNames().textInputEvent && evt->isTextEvent()) {
252 TextEvent* textEvent = static_cast<TextEvent*>(evt);
253 if (textEvent->data() == "\n")
254 clickDefaultFormButton = true;
255 else if (renderer() && !isConformedToInputMask(textEvent->data()[0], toRenderTextControl(renderer())->text().length() + 1))
256 // If the inputed char doesn't conform to the input mask, stop handling
257 return;
258 }
259
260 if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent() && focused() && document()->frame()
261 && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
262 evt->setDefaultHandled();
263 return;
264 }
265
266 // Let the key handling done in EventTargetNode take precedence over the event handling here for editable text fields
267 if (!clickDefaultFormButton) {
268 WMLElement::defaultEventHandler(evt);
269 if (evt->defaultHandled())
270 return;
271 }
272
273 // Use key press event here since sending simulated mouse events
274 // on key down blocks the proper sending of the key press event.
275 if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) {
276 // Simulate mouse click on the default form button for enter for these types of elements.
277 if (static_cast<KeyboardEvent*>(evt)->charCode() == '\r')
278 clickDefaultFormButton = true;
279 }
280
281 if (clickDefaultFormButton) {
282 // Fire onChange for text fields.
283 RenderObject* r = renderer();
284 if (r && toRenderTextControl(r)->isEdited()) {
285 dispatchEvent(eventNames().changeEvent, true, false);
286
287 // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
288 r = renderer();
289 if (r)
290 toRenderTextControl(r)->setEdited(false);
291 }
292
293 evt->setDefaultHandled();
294 return;
295 }
296
297 if (evt->isBeforeTextInsertedEvent())
298 InputElement::handleBeforeTextInsertedEvent(m_data, this, document(), evt);
299
300 if (renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent))
301 toRenderTextControlSingleLine(renderer())->forwardEvent(evt);
302 }
303
cacheSelection(int start,int end)304 void WMLInputElement::cacheSelection(int start, int end)
305 {
306 m_data.setCachedSelectionStart(start);
307 m_data.setCachedSelectionEnd(end);
308 }
309
constrainValue(const String & proposedValue) const310 String WMLInputElement::constrainValue(const String& proposedValue) const
311 {
312 return InputElement::constrainValue(this, proposedValue, m_data.maxLength());
313 }
314
documentDidBecomeActive()315 void WMLInputElement::documentDidBecomeActive()
316 {
317 ASSERT(m_isPasswordField);
318 reset();
319 }
320
placeholderShouldBeVisible() const321 bool WMLInputElement::placeholderShouldBeVisible() const
322 {
323 return m_data.placeholderShouldBeVisible();
324 }
325
willMoveToNewOwnerDocument()326 void WMLInputElement::willMoveToNewOwnerDocument()
327 {
328 // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered
329 if (m_isPasswordField)
330 document()->unregisterForDocumentActivationCallbacks(this);
331
332 WMLElement::willMoveToNewOwnerDocument();
333 }
334
didMoveToNewOwnerDocument()335 void WMLInputElement::didMoveToNewOwnerDocument()
336 {
337 if (m_isPasswordField)
338 document()->registerForDocumentActivationCallbacks(this);
339
340 WMLElement::didMoveToNewOwnerDocument();
341 }
342
initialize()343 void WMLInputElement::initialize()
344 {
345 String nameVariable = formControlName();
346 String variableValue;
347 WMLPageState* pageSate = wmlPageStateForDocument(document());
348 ASSERT(pageSate);
349 if (!nameVariable.isEmpty())
350 variableValue = pageSate->getVariable(nameVariable);
351
352 if (variableValue.isEmpty() || !isConformedToInputMask(variableValue)) {
353 String val = value();
354 if (isConformedToInputMask(val))
355 variableValue = val;
356 else
357 variableValue = "";
358
359 pageSate->storeVariable(nameVariable, variableValue);
360 }
361 setValue(variableValue);
362
363 if (!hasAttribute(WMLNames::emptyokAttr)) {
364 if (m_formatMask.isEmpty() ||
365 // check if the format codes is just "*f"
366 (m_formatMask.length() == 2 && m_formatMask[0] == '*' && formatCodes().find(m_formatMask[1]) != -1))
367 m_isEmptyOk = true;
368 }
369 }
370
validateInputMask(const String & inputMask)371 String WMLInputElement::validateInputMask(const String& inputMask)
372 {
373 bool isValid = true;
374 bool hasWildcard = false;
375 unsigned escapeCharCount = 0;
376 unsigned maskLength = inputMask.length();
377 UChar formatCode;
378
379 for (unsigned i = 0; i < maskLength; ++i) {
380 formatCode = inputMask[i];
381 if (formatCodes().find(formatCode) == -1) {
382 if (formatCode == '*' || (WTF::isASCIIDigit(formatCode) && formatCode != '0')) {
383 // validate codes which ends with '*f' or 'nf'
384 formatCode = inputMask[++i];
385 if ((i + 1 != maskLength) || formatCodes().find(formatCode) == -1) {
386 isValid = false;
387 break;
388 }
389 hasWildcard = true;
390 } else if (formatCode == '\\') {
391 //skip over the next mask character
392 ++i;
393 ++escapeCharCount;
394 } else {
395 isValid = false;
396 break;
397 }
398 }
399 }
400
401 if (!isValid)
402 return String();
403
404 // calculate the number of characters allowed to be entered by input mask
405 m_numOfCharsAllowedByMask = maskLength;
406
407 if (escapeCharCount)
408 m_numOfCharsAllowedByMask -= escapeCharCount;
409
410 if (hasWildcard) {
411 formatCode = inputMask[maskLength - 2];
412 if (formatCode == '*')
413 m_numOfCharsAllowedByMask = m_data.maxLength();
414 else {
415 unsigned leftLen = String(&formatCode).toInt();
416 m_numOfCharsAllowedByMask = leftLen + m_numOfCharsAllowedByMask - 2;
417 }
418 }
419
420 return inputMask;
421 }
422
isConformedToInputMask(const String & inputChars)423 bool WMLInputElement::isConformedToInputMask(const String& inputChars)
424 {
425 for (unsigned i = 0; i < inputChars.length(); ++i)
426 if (!isConformedToInputMask(inputChars[i], i + 1, false))
427 return false;
428
429 return true;
430 }
431
isConformedToInputMask(UChar inChar,unsigned inputCharCount,bool isUserInput)432 bool WMLInputElement::isConformedToInputMask(UChar inChar, unsigned inputCharCount, bool isUserInput)
433 {
434 if (m_formatMask.isEmpty())
435 return true;
436
437 if (inputCharCount > m_numOfCharsAllowedByMask)
438 return false;
439
440 unsigned maskIndex = 0;
441 if (isUserInput) {
442 unsigned cursorPosition = 0;
443 if (renderer())
444 cursorPosition = toRenderTextControl(renderer())->selectionStart();
445 else
446 cursorPosition = m_data.cachedSelectionStart();
447
448 maskIndex = cursorPositionToMaskIndex(cursorPosition);
449 } else
450 maskIndex = cursorPositionToMaskIndex(inputCharCount - 1);
451
452 bool ok = true;
453 UChar mask = m_formatMask[maskIndex];
454 // match the inputed character with input mask
455 switch (mask) {
456 case 'A':
457 ok = !WTF::isASCIIDigit(inChar) && !WTF::isASCIILower(inChar) && WTF::isASCIIPrintable(inChar);
458 break;
459 case 'a':
460 ok = !WTF::isASCIIDigit(inChar) && !WTF::isASCIIUpper(inChar) && WTF::isASCIIPrintable(inChar);
461 break;
462 case 'N':
463 ok = WTF::isASCIIDigit(inChar);
464 break;
465 case 'n':
466 ok = !WTF::isASCIIAlpha(inChar) && WTF::isASCIIPrintable(inChar);
467 break;
468 case 'X':
469 ok = !WTF::isASCIILower(inChar) && WTF::isASCIIPrintable(inChar);
470 break;
471 case 'x':
472 ok = !WTF::isASCIIUpper(inChar) && WTF::isASCIIPrintable(inChar);
473 break;
474 case 'M':
475 ok = WTF::isASCIIPrintable(inChar);
476 break;
477 case 'm':
478 ok = WTF::isASCIIPrintable(inChar);
479 break;
480 default:
481 ok = (mask == inChar);
482 break;
483 }
484
485 return ok;
486 }
487
cursorPositionToMaskIndex(unsigned cursorPosition)488 unsigned WMLInputElement::cursorPositionToMaskIndex(unsigned cursorPosition)
489 {
490 UChar mask;
491 int index = -1;
492 do {
493 mask = m_formatMask[++index];
494 if (mask == '\\')
495 ++index;
496 else if (mask == '*' || (WTF::isASCIIDigit(mask) && mask != '0')) {
497 index = m_formatMask.length() - 1;
498 break;
499 }
500 } while (cursorPosition--);
501
502 return index;
503 }
504
505 }
506
507 #endif
508