|
1 /* |
|
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
|
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
|
4 * (C) 2001 Dirk Mueller (mueller@kde.org) |
|
5 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
|
6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
|
7 * |
|
8 * This library is free software; you can redistribute it and/or |
|
9 * modify it under the terms of the GNU Library General Public |
|
10 * License as published by the Free Software Foundation; either |
|
11 * version 2 of the License, or (at your option) any later version. |
|
12 * |
|
13 * This library is distributed in the hope that it will be useful, |
|
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
16 * Library General Public License for more details. |
|
17 * |
|
18 * You should have received a copy of the GNU Library General Public License |
|
19 * along with this library; see the file COPYING.LIB. If not, write to |
|
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
21 * Boston, MA 02110-1301, USA. |
|
22 * |
|
23 */ |
|
24 |
|
25 #include "config.h" |
|
26 #include "HTMLFormControlElement.h" |
|
27 |
|
28 #include "Attribute.h" |
|
29 #include "CharacterNames.h" |
|
30 #include "Chrome.h" |
|
31 #include "ChromeClient.h" |
|
32 #include "Document.h" |
|
33 #include "ElementRareData.h" |
|
34 #include "Event.h" |
|
35 #include "EventHandler.h" |
|
36 #include "EventNames.h" |
|
37 #include "Frame.h" |
|
38 #include "HTMLFormElement.h" |
|
39 #include "HTMLInputElement.h" |
|
40 #include "HTMLNames.h" |
|
41 #include "LegacyHTMLTreeBuilder.h" |
|
42 #include "LegacyHTMLDocumentParser.h" |
|
43 #include "LabelsNodeList.h" |
|
44 #include "Page.h" |
|
45 #include "RenderBox.h" |
|
46 #include "RenderTextControl.h" |
|
47 #include "RenderTheme.h" |
|
48 #include "ScriptEventListener.h" |
|
49 #include "ValidityState.h" |
|
50 #include <wtf/Vector.h> |
|
51 |
|
52 namespace WebCore { |
|
53 |
|
54 using namespace HTMLNames; |
|
55 |
|
56 HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) |
|
57 : HTMLElement(tagName, document) |
|
58 , m_form(form) |
|
59 , m_disabled(false) |
|
60 , m_readOnly(false) |
|
61 , m_required(false) |
|
62 , m_valueMatchesRenderer(false) |
|
63 , m_willValidateInitialized(false) |
|
64 , m_willValidate(true) |
|
65 , m_isValid(true) |
|
66 { |
|
67 if (!m_form) |
|
68 m_form = findFormAncestor(); |
|
69 if (m_form) |
|
70 m_form->registerFormElement(this); |
|
71 } |
|
72 |
|
73 HTMLFormControlElement::~HTMLFormControlElement() |
|
74 { |
|
75 if (m_form) |
|
76 m_form->removeFormElement(this); |
|
77 } |
|
78 |
|
79 bool HTMLFormControlElement::formNoValidate() const |
|
80 { |
|
81 return !getAttribute(formnovalidateAttr).isNull(); |
|
82 } |
|
83 |
|
84 ValidityState* HTMLFormControlElement::validity() |
|
85 { |
|
86 if (!m_validityState) |
|
87 m_validityState = ValidityState::create(this); |
|
88 |
|
89 return m_validityState.get(); |
|
90 } |
|
91 |
|
92 void HTMLFormControlElement::parseMappedAttribute(Attribute* attr) |
|
93 { |
|
94 if (attr->name() == disabledAttr) { |
|
95 bool oldDisabled = m_disabled; |
|
96 m_disabled = !attr->isNull(); |
|
97 if (oldDisabled != m_disabled) { |
|
98 setNeedsStyleRecalc(); |
|
99 if (renderer() && renderer()->style()->hasAppearance()) |
|
100 renderer()->theme()->stateChanged(renderer(), EnabledState); |
|
101 } |
|
102 } else if (attr->name() == readonlyAttr) { |
|
103 bool oldReadOnly = m_readOnly; |
|
104 m_readOnly = !attr->isNull(); |
|
105 if (oldReadOnly != m_readOnly) { |
|
106 setNeedsStyleRecalc(); |
|
107 if (renderer() && renderer()->style()->hasAppearance()) |
|
108 renderer()->theme()->stateChanged(renderer(), ReadOnlyState); |
|
109 } |
|
110 } else if (attr->name() == requiredAttr) { |
|
111 bool oldRequired = m_required; |
|
112 m_required = !attr->isNull(); |
|
113 if (oldRequired != m_required) { |
|
114 setNeedsValidityCheck(); |
|
115 setNeedsStyleRecalc(); // Updates for :required :optional classes. |
|
116 } |
|
117 } else |
|
118 HTMLElement::parseMappedAttribute(attr); |
|
119 setNeedsWillValidateCheck(); |
|
120 } |
|
121 |
|
122 void HTMLFormControlElement::attach() |
|
123 { |
|
124 ASSERT(!attached()); |
|
125 |
|
126 HTMLElement::attach(); |
|
127 |
|
128 // The call to updateFromElement() needs to go after the call through |
|
129 // to the base class's attach() because that can sometimes do a close |
|
130 // on the renderer. |
|
131 if (renderer()) |
|
132 renderer()->updateFromElement(); |
|
133 |
|
134 // Focus the element if it should honour its autofocus attribute. |
|
135 // We have to determine if the element is a TextArea/Input/Button/Select, |
|
136 // if input type hidden ignore autofocus. So if disabled or readonly. |
|
137 bool isInputTypeHidden = false; |
|
138 if (hasTagName(inputTag)) |
|
139 isInputTypeHidden = static_cast<HTMLInputElement*>(this)->isInputTypeHidden(); |
|
140 |
|
141 if (autofocus() && renderer() && !document()->ignoreAutofocus() && !isReadOnlyFormControl() && |
|
142 ((hasTagName(inputTag) && !isInputTypeHidden) || hasTagName(selectTag) || |
|
143 hasTagName(buttonTag) || hasTagName(textareaTag))) |
|
144 focus(); |
|
145 } |
|
146 |
|
147 void HTMLFormControlElement::insertedIntoTree(bool deep) |
|
148 { |
|
149 if (!m_form) { |
|
150 // This handles the case of a new form element being created by |
|
151 // JavaScript and inserted inside a form. In the case of the parser |
|
152 // setting a form, we will already have a non-null value for m_form, |
|
153 // and so we don't need to do anything. |
|
154 m_form = findFormAncestor(); |
|
155 if (m_form) |
|
156 m_form->registerFormElement(this); |
|
157 else |
|
158 document()->checkedRadioButtons().addButton(this); |
|
159 } |
|
160 |
|
161 HTMLElement::insertedIntoTree(deep); |
|
162 } |
|
163 |
|
164 static inline Node* findRoot(Node* n) |
|
165 { |
|
166 Node* root = n; |
|
167 for (; n; n = n->parentNode()) |
|
168 root = n; |
|
169 return root; |
|
170 } |
|
171 |
|
172 void HTMLFormControlElement::removedFromTree(bool deep) |
|
173 { |
|
174 // If the form and element are both in the same tree, preserve the connection to the form. |
|
175 // Otherwise, null out our form and remove ourselves from the form's list of elements. |
|
176 LegacyHTMLTreeBuilder* treeBuilder = 0; |
|
177 if (DocumentParser* parser = document()->parser()) |
|
178 treeBuilder = parser->htmlTreeBuilder(); |
|
179 |
|
180 if (m_form && !(treeBuilder && treeBuilder->isHandlingResidualStyleAcrossBlocks()) && findRoot(this) != findRoot(m_form)) { |
|
181 m_form->removeFormElement(this); |
|
182 m_form = 0; |
|
183 } |
|
184 |
|
185 HTMLElement::removedFromTree(deep); |
|
186 } |
|
187 |
|
188 const AtomicString& HTMLFormControlElement::formControlName() const |
|
189 { |
|
190 const AtomicString& name = fastGetAttribute(nameAttr); |
|
191 return name.isNull() ? emptyAtom : name; |
|
192 } |
|
193 |
|
194 void HTMLFormControlElement::setName(const AtomicString& value) |
|
195 { |
|
196 setAttribute(nameAttr, value); |
|
197 } |
|
198 |
|
199 void HTMLFormControlElement::dispatchFormControlChangeEvent() |
|
200 { |
|
201 dispatchEvent(Event::create(eventNames().changeEvent, true, false)); |
|
202 } |
|
203 |
|
204 void HTMLFormControlElement::setDisabled(bool b) |
|
205 { |
|
206 setAttribute(disabledAttr, b ? "" : 0); |
|
207 } |
|
208 |
|
209 bool HTMLFormControlElement::autofocus() const |
|
210 { |
|
211 return hasAttribute(autofocusAttr); |
|
212 } |
|
213 |
|
214 bool HTMLFormControlElement::required() const |
|
215 { |
|
216 return m_required; |
|
217 } |
|
218 |
|
219 static void updateFromElementCallback(Node* node) |
|
220 { |
|
221 ASSERT_ARG(node, node->isElementNode()); |
|
222 ASSERT_ARG(node, static_cast<Element*>(node)->isFormControlElement()); |
|
223 ASSERT(node->renderer()); |
|
224 if (RenderObject* renderer = node->renderer()) |
|
225 renderer->updateFromElement(); |
|
226 } |
|
227 |
|
228 void HTMLFormControlElement::recalcStyle(StyleChange change) |
|
229 { |
|
230 HTMLElement::recalcStyle(change); |
|
231 |
|
232 // updateFromElement() can cause the selection to change, and in turn |
|
233 // trigger synchronous layout, so it must not be called during style recalc. |
|
234 if (renderer()) |
|
235 queuePostAttachCallback(updateFromElementCallback, this); |
|
236 } |
|
237 |
|
238 bool HTMLFormControlElement::supportsFocus() const |
|
239 { |
|
240 return !disabled(); |
|
241 } |
|
242 |
|
243 bool HTMLFormControlElement::isFocusable() const |
|
244 { |
|
245 if (!renderer() || |
|
246 !renderer()->isBox() || toRenderBox(renderer())->size().isEmpty()) |
|
247 return false; |
|
248 // HTMLElement::isFocusable handles visibility and calls suportsFocus which |
|
249 // will cover the disabled case. |
|
250 return HTMLElement::isFocusable(); |
|
251 } |
|
252 |
|
253 bool HTMLFormControlElement::isKeyboardFocusable(KeyboardEvent* event) const |
|
254 { |
|
255 if (isFocusable()) |
|
256 if (document()->frame()) |
|
257 return document()->frame()->eventHandler()->tabsToAllControls(event); |
|
258 return false; |
|
259 } |
|
260 |
|
261 bool HTMLFormControlElement::isMouseFocusable() const |
|
262 { |
|
263 #if PLATFORM(GTK) |
|
264 return HTMLElement::isMouseFocusable(); |
|
265 #else |
|
266 return false; |
|
267 #endif |
|
268 } |
|
269 |
|
270 short HTMLFormControlElement::tabIndex() const |
|
271 { |
|
272 // Skip the supportsFocus check in HTMLElement. |
|
273 return Element::tabIndex(); |
|
274 } |
|
275 |
|
276 bool HTMLFormControlElement::recalcWillValidate() const |
|
277 { |
|
278 // FIXME: Should return false if this element has a datalist element as an |
|
279 // ancestor. See HTML5 4.10.10 'The datalist element.' |
|
280 return !m_disabled && !m_readOnly; |
|
281 } |
|
282 |
|
283 bool HTMLFormControlElement::willValidate() const |
|
284 { |
|
285 if (!m_willValidateInitialized) { |
|
286 m_willValidateInitialized = true; |
|
287 m_willValidate = recalcWillValidate(); |
|
288 } else { |
|
289 // If the following assertion fails, setNeedsWillValidateCheck() is not |
|
290 // called correctly when something which changes recalcWillValidate() result |
|
291 // is updated. |
|
292 ASSERT(m_willValidate == recalcWillValidate()); |
|
293 } |
|
294 return m_willValidate; |
|
295 } |
|
296 |
|
297 void HTMLFormControlElement::setNeedsWillValidateCheck() |
|
298 { |
|
299 // We need to recalculate willValidte immediately because willValidate |
|
300 // change can causes style change. |
|
301 bool newWillValidate = recalcWillValidate(); |
|
302 if (m_willValidateInitialized && m_willValidate == newWillValidate) |
|
303 return; |
|
304 m_willValidateInitialized = true; |
|
305 m_willValidate = newWillValidate; |
|
306 setNeedsStyleRecalc(); |
|
307 // FIXME: Show/hide a validation message. |
|
308 } |
|
309 |
|
310 String HTMLFormControlElement::validationMessage() |
|
311 { |
|
312 return validity()->validationMessage(); |
|
313 } |
|
314 |
|
315 bool HTMLFormControlElement::checkValidity(Vector<RefPtr<HTMLFormControlElement> >* unhandledInvalidControls) |
|
316 { |
|
317 if (!willValidate() || isValidFormControlElement()) |
|
318 return true; |
|
319 // An event handler can deref this object. |
|
320 RefPtr<HTMLFormControlElement> protector(this); |
|
321 RefPtr<Document> originalDocument(document()); |
|
322 bool needsDefaultAction = dispatchEvent(Event::create(eventNames().invalidEvent, false, true)); |
|
323 if (needsDefaultAction && unhandledInvalidControls && inDocument() && originalDocument == document()) |
|
324 unhandledInvalidControls->append(this); |
|
325 return false; |
|
326 } |
|
327 |
|
328 bool HTMLFormControlElement::isValidFormControlElement() |
|
329 { |
|
330 // If the following assertion fails, setNeedsValidityCheck() is not called |
|
331 // correctly when something which changes validity is updated. |
|
332 ASSERT(m_isValid == validity()->valid()); |
|
333 return m_isValid; |
|
334 } |
|
335 |
|
336 void HTMLFormControlElement::setNeedsValidityCheck() |
|
337 { |
|
338 bool newIsValid = validity()->valid(); |
|
339 if (willValidate() && newIsValid != m_isValid) { |
|
340 // Update style for pseudo classes such as :valid :invalid. |
|
341 setNeedsStyleRecalc(); |
|
342 } |
|
343 m_isValid = newIsValid; |
|
344 // FIXME: show/hide a validation message. |
|
345 } |
|
346 |
|
347 void HTMLFormControlElement::setCustomValidity(const String& error) |
|
348 { |
|
349 validity()->setCustomErrorMessage(error); |
|
350 } |
|
351 |
|
352 void HTMLFormControlElement::dispatchFocusEvent() |
|
353 { |
|
354 if (document()->page()) |
|
355 document()->page()->chrome()->client()->formDidFocus(this); |
|
356 |
|
357 HTMLElement::dispatchFocusEvent(); |
|
358 } |
|
359 |
|
360 void HTMLFormControlElement::dispatchBlurEvent() |
|
361 { |
|
362 if (document()->page()) |
|
363 document()->page()->chrome()->client()->formDidBlur(this); |
|
364 |
|
365 HTMLElement::dispatchBlurEvent(); |
|
366 } |
|
367 |
|
368 HTMLFormElement* HTMLFormControlElement::virtualForm() const |
|
369 { |
|
370 return m_form; |
|
371 } |
|
372 |
|
373 bool HTMLFormControlElement::isDefaultButtonForForm() const |
|
374 { |
|
375 return isSuccessfulSubmitButton() && m_form && m_form->defaultButton() == this; |
|
376 } |
|
377 |
|
378 void HTMLFormControlElement::removeFromForm() |
|
379 { |
|
380 if (!m_form) |
|
381 return; |
|
382 m_form->removeFormElement(this); |
|
383 m_form = 0; |
|
384 } |
|
385 |
|
386 bool HTMLFormControlElement::isLabelable() const |
|
387 { |
|
388 // FIXME: Add meterTag and outputTag to the list once we support them. |
|
389 return hasTagName(buttonTag) || hasTagName(inputTag) || hasTagName(keygenTag) |
|
390 #if ENABLE(METER_TAG) |
|
391 || hasTagName(meterTag) |
|
392 #endif |
|
393 #if ENABLE(PROGRESS_TAG) |
|
394 || hasTagName(progressTag) |
|
395 #endif |
|
396 || hasTagName(selectTag) || hasTagName(textareaTag); |
|
397 } |
|
398 |
|
399 PassRefPtr<NodeList> HTMLFormControlElement::labels() |
|
400 { |
|
401 if (!isLabelable()) |
|
402 return 0; |
|
403 if (!document()) |
|
404 return 0; |
|
405 |
|
406 NodeRareData* data = Node::ensureRareData(); |
|
407 if (!data->nodeLists()) { |
|
408 data->setNodeLists(NodeListsNodeData::create()); |
|
409 document()->addNodeListCache(); |
|
410 } |
|
411 |
|
412 return LabelsNodeList::create(this); |
|
413 } |
|
414 |
|
415 HTMLFormControlElementWithState::HTMLFormControlElementWithState(const QualifiedName& tagName, Document* doc, HTMLFormElement* f) |
|
416 : HTMLFormControlElement(tagName, doc, f) |
|
417 { |
|
418 document()->registerFormElementWithState(this); |
|
419 } |
|
420 |
|
421 HTMLFormControlElementWithState::~HTMLFormControlElementWithState() |
|
422 { |
|
423 document()->unregisterFormElementWithState(this); |
|
424 } |
|
425 |
|
426 void HTMLFormControlElementWithState::willMoveToNewOwnerDocument() |
|
427 { |
|
428 document()->unregisterFormElementWithState(this); |
|
429 HTMLFormControlElement::willMoveToNewOwnerDocument(); |
|
430 } |
|
431 |
|
432 void HTMLFormControlElementWithState::didMoveToNewOwnerDocument() |
|
433 { |
|
434 document()->registerFormElementWithState(this); |
|
435 HTMLFormControlElement::didMoveToNewOwnerDocument(); |
|
436 } |
|
437 |
|
438 bool HTMLFormControlElementWithState::autoComplete() const |
|
439 { |
|
440 if (!form()) |
|
441 return true; |
|
442 return form()->autoComplete(); |
|
443 } |
|
444 |
|
445 bool HTMLFormControlElementWithState::shouldSaveAndRestoreFormControlState() const |
|
446 { |
|
447 // We don't save/restore control state in a form with autocomplete=off. |
|
448 return autoComplete(); |
|
449 } |
|
450 |
|
451 void HTMLFormControlElementWithState::finishParsingChildren() |
|
452 { |
|
453 HTMLFormControlElement::finishParsingChildren(); |
|
454 |
|
455 // We don't save state of a control with shouldSaveAndRestoreFormControlState()=false. |
|
456 // But we need to skip restoring process too because a control in another |
|
457 // form might have the same pair of name and type and saved its state. |
|
458 if (!shouldSaveAndRestoreFormControlState()) |
|
459 return; |
|
460 |
|
461 Document* doc = document(); |
|
462 if (doc->hasStateForNewFormElements()) { |
|
463 String state; |
|
464 if (doc->takeStateForFormElement(name().impl(), type().impl(), state)) |
|
465 restoreFormControlState(state); |
|
466 } |
|
467 } |
|
468 |
|
469 void HTMLFormControlElementWithState::defaultEventHandler(Event* event) |
|
470 { |
|
471 if (event->type() == eventNames().webkitEditableContentChangedEvent && renderer() && renderer()->isTextControl()) { |
|
472 toRenderTextControl(renderer())->subtreeHasChanged(); |
|
473 return; |
|
474 } |
|
475 |
|
476 HTMLFormControlElement::defaultEventHandler(event); |
|
477 } |
|
478 |
|
479 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* form) |
|
480 : HTMLFormControlElementWithState(tagName, doc, form) |
|
481 { |
|
482 } |
|
483 |
|
484 HTMLTextFormControlElement::~HTMLTextFormControlElement() |
|
485 { |
|
486 } |
|
487 |
|
488 void HTMLTextFormControlElement::dispatchFocusEvent() |
|
489 { |
|
490 if (supportsPlaceholder()) |
|
491 updatePlaceholderVisibility(false); |
|
492 handleFocusEvent(); |
|
493 HTMLFormControlElementWithState::dispatchFocusEvent(); |
|
494 } |
|
495 |
|
496 void HTMLTextFormControlElement::dispatchBlurEvent() |
|
497 { |
|
498 if (supportsPlaceholder()) |
|
499 updatePlaceholderVisibility(false); |
|
500 handleBlurEvent(); |
|
501 HTMLFormControlElementWithState::dispatchBlurEvent(); |
|
502 } |
|
503 |
|
504 String HTMLTextFormControlElement::strippedPlaceholder() const |
|
505 { |
|
506 // According to the HTML5 specification, we need to remove CR and LF from |
|
507 // the attribute value. |
|
508 const AtomicString& attributeValue = getAttribute(placeholderAttr); |
|
509 if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn)) |
|
510 return attributeValue; |
|
511 |
|
512 Vector<UChar> stripped; |
|
513 unsigned length = attributeValue.length(); |
|
514 stripped.reserveCapacity(length); |
|
515 for (unsigned i = 0; i < length; ++i) { |
|
516 UChar character = attributeValue[i]; |
|
517 if (character == newlineCharacter || character == carriageReturn) |
|
518 continue; |
|
519 stripped.append(character); |
|
520 } |
|
521 return String::adopt(stripped); |
|
522 } |
|
523 |
|
524 static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; } |
|
525 |
|
526 bool HTMLTextFormControlElement::isPlaceholderEmpty() const |
|
527 { |
|
528 const AtomicString& attributeValue = getAttribute(placeholderAttr); |
|
529 return attributeValue.string().find(isNotLineBreak) == -1; |
|
530 } |
|
531 |
|
532 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const |
|
533 { |
|
534 return supportsPlaceholder() |
|
535 && isEmptyValue() |
|
536 && document()->focusedNode() != this |
|
537 && !isPlaceholderEmpty(); |
|
538 } |
|
539 |
|
540 void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged) |
|
541 { |
|
542 if (supportsPlaceholder() && renderer()) |
|
543 toRenderTextControl(renderer())->updatePlaceholderVisibility(placeholderShouldBeVisible(), placeholderValueChanged); |
|
544 } |
|
545 |
|
546 RenderTextControl* HTMLTextFormControlElement::textRendererAfterUpdateLayout() |
|
547 { |
|
548 if (!isTextFormControl()) |
|
549 return 0; |
|
550 document()->updateLayoutIgnorePendingStylesheets(); |
|
551 return toRenderTextControl(renderer()); |
|
552 } |
|
553 |
|
554 void HTMLTextFormControlElement::setSelectionStart(int start) |
|
555 { |
|
556 if (RenderTextControl* renderer = textRendererAfterUpdateLayout()) |
|
557 renderer->setSelectionStart(start); |
|
558 } |
|
559 |
|
560 void HTMLTextFormControlElement::setSelectionEnd(int end) |
|
561 { |
|
562 if (RenderTextControl* renderer = textRendererAfterUpdateLayout()) |
|
563 renderer->setSelectionEnd(end); |
|
564 } |
|
565 |
|
566 void HTMLTextFormControlElement::select() |
|
567 { |
|
568 if (RenderTextControl* renderer = textRendererAfterUpdateLayout()) |
|
569 renderer->select(); |
|
570 } |
|
571 |
|
572 void HTMLTextFormControlElement::setSelectionRange(int start, int end) |
|
573 { |
|
574 if (RenderTextControl* renderer = textRendererAfterUpdateLayout()) |
|
575 renderer->setSelectionRange(start, end); |
|
576 } |
|
577 |
|
578 int HTMLTextFormControlElement::selectionStart() |
|
579 { |
|
580 if (!isTextFormControl()) |
|
581 return 0; |
|
582 if (document()->focusedNode() != this && cachedSelectionStart() >= 0) |
|
583 return cachedSelectionStart(); |
|
584 if (!renderer()) |
|
585 return 0; |
|
586 return toRenderTextControl(renderer())->selectionStart(); |
|
587 } |
|
588 |
|
589 int HTMLTextFormControlElement::selectionEnd() |
|
590 { |
|
591 if (!isTextFormControl()) |
|
592 return 0; |
|
593 if (document()->focusedNode() != this && cachedSelectionEnd() >= 0) |
|
594 return cachedSelectionEnd(); |
|
595 if (!renderer()) |
|
596 return 0; |
|
597 return toRenderTextControl(renderer())->selectionEnd(); |
|
598 } |
|
599 |
|
600 VisibleSelection HTMLTextFormControlElement::selection() const |
|
601 { |
|
602 if (!renderer() || !isTextFormControl() || cachedSelectionStart() < 0 || cachedSelectionEnd() < 0) |
|
603 return VisibleSelection(); |
|
604 return toRenderTextControl(renderer())->selection(cachedSelectionStart(), cachedSelectionEnd()); |
|
605 } |
|
606 |
|
607 void HTMLTextFormControlElement::parseMappedAttribute(Attribute* attr) |
|
608 { |
|
609 if (attr->name() == placeholderAttr) |
|
610 updatePlaceholderVisibility(true); |
|
611 else if (attr->name() == onselectAttr) |
|
612 setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr)); |
|
613 else if (attr->name() == onchangeAttr) |
|
614 setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr)); |
|
615 else |
|
616 HTMLFormControlElementWithState::parseMappedAttribute(attr); |
|
617 } |
|
618 |
|
619 } // namespace Webcore |