Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2001 Dirk Mueller (mueller (at) kde.org)
      5  * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
      6  *           (C) 2006 Alexey Proskuryakov (ap (at) 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 "Chrome.h"
     30 #include "ChromeClient.h"
     31 #include "Document.h"
     32 #include "DocumentParser.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 "LabelsNodeList.h"
     42 #include "Page.h"
     43 #include "RenderBox.h"
     44 #include "RenderTextControl.h"
     45 #include "RenderTheme.h"
     46 #include "ScriptEventListener.h"
     47 #include "ValidationMessage.h"
     48 #include "ValidityState.h"
     49 #include <limits>
     50 #include <wtf/Vector.h>
     51 #include <wtf/unicode/CharacterNames.h>
     52 
     53 namespace WebCore {
     54 
     55 using namespace HTMLNames;
     56 using namespace std;
     57 
     58 HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
     59     : HTMLElement(tagName, document)
     60     , FormAssociatedElement(form)
     61     , m_disabled(false)
     62     , m_readOnly(false)
     63     , m_required(false)
     64     , m_valueMatchesRenderer(false)
     65     , m_willValidateInitialized(false)
     66     , m_willValidate(true)
     67     , m_isValid(true)
     68     , m_wasChangedSinceLastFormControlChangeEvent(false)
     69 {
     70     if (!this->form())
     71         setForm(findFormAncestor());
     72     if (this->form())
     73         this->form()->registerFormElement(this);
     74 }
     75 
     76 HTMLFormControlElement::~HTMLFormControlElement()
     77 {
     78     if (form())
     79         form()->removeFormElement(this);
     80 }
     81 
     82 void HTMLFormControlElement::detach()
     83 {
     84     m_validationMessage = 0;
     85     HTMLElement::detach();
     86 }
     87 
     88 bool HTMLFormControlElement::formNoValidate() const
     89 {
     90     return fastHasAttribute(formnovalidateAttr);
     91 }
     92 
     93 void HTMLFormControlElement::parseMappedAttribute(Attribute* attr)
     94 {
     95     if (attr->name() == disabledAttr) {
     96         bool oldDisabled = m_disabled;
     97         m_disabled = !attr->isNull();
     98         if (oldDisabled != m_disabled) {
     99             setNeedsStyleRecalc();
    100             if (renderer() && renderer()->style()->hasAppearance())
    101                 renderer()->theme()->stateChanged(renderer(), EnabledState);
    102         }
    103     } else if (attr->name() == readonlyAttr) {
    104         bool oldReadOnly = m_readOnly;
    105         m_readOnly = !attr->isNull();
    106         if (oldReadOnly != m_readOnly) {
    107             setNeedsStyleRecalc();
    108             if (renderer() && renderer()->style()->hasAppearance())
    109                 renderer()->theme()->stateChanged(renderer(), ReadOnlyState);
    110         }
    111     } else if (attr->name() == requiredAttr) {
    112         bool oldRequired = m_required;
    113         m_required = !attr->isNull();
    114         if (oldRequired != m_required) {
    115             setNeedsValidityCheck();
    116             setNeedsStyleRecalc(); // Updates for :required :optional classes.
    117         }
    118     } else
    119         HTMLElement::parseMappedAttribute(attr);
    120     setNeedsWillValidateCheck();
    121 }
    122 
    123 static bool shouldAutofocus(HTMLFormControlElement* element)
    124 {
    125     if (!element->autofocus())
    126         return false;
    127     if (!element->renderer())
    128         return false;
    129     if (element->document()->ignoreAutofocus())
    130         return false;
    131     if (element->isReadOnlyFormControl())
    132         return false;
    133 
    134     // FIXME: Should this set of hasTagName checks be replaced by a
    135     // virtual member function?
    136     if (element->hasTagName(inputTag))
    137         return !static_cast<HTMLInputElement*>(element)->isInputTypeHidden();
    138     if (element->hasTagName(selectTag))
    139         return true;
    140     if (element->hasTagName(keygenTag))
    141         return true;
    142     if (element->hasTagName(buttonTag))
    143         return true;
    144     if (element->hasTagName(textareaTag))
    145         return true;
    146 
    147     return false;
    148 }
    149 
    150 static void focusPostAttach(Node* element)
    151 {
    152     static_cast<Element*>(element)->focus();
    153     element->deref();
    154 }
    155 
    156 void HTMLFormControlElement::attach()
    157 {
    158     ASSERT(!attached());
    159 
    160     suspendPostAttachCallbacks();
    161 
    162     HTMLElement::attach();
    163 
    164     // The call to updateFromElement() needs to go after the call through
    165     // to the base class's attach() because that can sometimes do a close
    166     // on the renderer.
    167     if (renderer())
    168         renderer()->updateFromElement();
    169 
    170     if (shouldAutofocus(this)) {
    171         ref();
    172         queuePostAttachCallback(focusPostAttach, this);
    173     }
    174 
    175     resumePostAttachCallbacks();
    176 }
    177 
    178 void HTMLFormControlElement::willMoveToNewOwnerDocument()
    179 {
    180     FormAssociatedElement::willMoveToNewOwnerDocument();
    181     HTMLElement::willMoveToNewOwnerDocument();
    182 }
    183 
    184 void HTMLFormControlElement::insertedIntoTree(bool deep)
    185 {
    186     FormAssociatedElement::insertedIntoTree();
    187     if (!form())
    188         document()->checkedRadioButtons().addButton(this);
    189 
    190     HTMLElement::insertedIntoTree(deep);
    191 }
    192 
    193 void HTMLFormControlElement::removedFromTree(bool deep)
    194 {
    195     FormAssociatedElement::removedFromTree();
    196     HTMLElement::removedFromTree(deep);
    197 }
    198 
    199 void HTMLFormControlElement::insertedIntoDocument()
    200 {
    201     HTMLElement::insertedIntoDocument();
    202     FormAssociatedElement::insertedIntoDocument();
    203 }
    204 
    205 void HTMLFormControlElement::removedFromDocument()
    206 {
    207     HTMLElement::removedFromDocument();
    208     FormAssociatedElement::removedFromDocument();
    209 }
    210 
    211 const AtomicString& HTMLFormControlElement::formControlName() const
    212 {
    213     const AtomicString& name = fastGetAttribute(nameAttr);
    214     return name.isNull() ? emptyAtom : name;
    215 }
    216 
    217 void HTMLFormControlElement::setName(const AtomicString& value)
    218 {
    219     setAttribute(nameAttr, value);
    220 }
    221 
    222 bool HTMLFormControlElement::wasChangedSinceLastFormControlChangeEvent() const
    223 {
    224     return m_wasChangedSinceLastFormControlChangeEvent;
    225 }
    226 
    227 void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed)
    228 {
    229     m_wasChangedSinceLastFormControlChangeEvent = changed;
    230 }
    231 
    232 void HTMLFormControlElement::dispatchFormControlChangeEvent()
    233 {
    234     HTMLElement::dispatchChangeEvent();
    235     setChangedSinceLastFormControlChangeEvent(false);
    236 }
    237 
    238 void HTMLFormControlElement::dispatchFormControlInputEvent()
    239 {
    240     setChangedSinceLastFormControlChangeEvent(true);
    241     HTMLElement::dispatchInputEvent();
    242 }
    243 
    244 void HTMLFormControlElement::setDisabled(bool b)
    245 {
    246     setAttribute(disabledAttr, b ? "" : 0);
    247 }
    248 
    249 bool HTMLFormControlElement::autofocus() const
    250 {
    251     return hasAttribute(autofocusAttr);
    252 }
    253 
    254 bool HTMLFormControlElement::required() const
    255 {
    256     return m_required;
    257 }
    258 
    259 static void updateFromElementCallback(Node* node)
    260 {
    261     ASSERT_ARG(node, node->isElementNode());
    262     ASSERT_ARG(node, static_cast<Element*>(node)->isFormControlElement());
    263     ASSERT(node->renderer());
    264     if (RenderObject* renderer = node->renderer())
    265         renderer->updateFromElement();
    266 }
    267 
    268 void HTMLFormControlElement::recalcStyle(StyleChange change)
    269 {
    270     HTMLElement::recalcStyle(change);
    271 
    272     // updateFromElement() can cause the selection to change, and in turn
    273     // trigger synchronous layout, so it must not be called during style recalc.
    274     if (renderer())
    275         queuePostAttachCallback(updateFromElementCallback, this);
    276 }
    277 
    278 bool HTMLFormControlElement::supportsFocus() const
    279 {
    280     return !m_disabled;
    281 }
    282 
    283 bool HTMLFormControlElement::isFocusable() const
    284 {
    285     if (!renderer() ||
    286         !renderer()->isBox() || toRenderBox(renderer())->size().isEmpty())
    287         return false;
    288     // HTMLElement::isFocusable handles visibility and calls suportsFocus which
    289     // will cover the disabled case.
    290     return HTMLElement::isFocusable();
    291 }
    292 
    293 bool HTMLFormControlElement::isKeyboardFocusable(KeyboardEvent* event) const
    294 {
    295     if (isFocusable())
    296         if (document()->frame())
    297             return document()->frame()->eventHandler()->tabsToAllFormControls(event);
    298     return false;
    299 }
    300 
    301 bool HTMLFormControlElement::isMouseFocusable() const
    302 {
    303 #if PLATFORM(GTK) || PLATFORM(QT)
    304     return HTMLElement::isMouseFocusable();
    305 #else
    306     return false;
    307 #endif
    308 }
    309 
    310 short HTMLFormControlElement::tabIndex() const
    311 {
    312     // Skip the supportsFocus check in HTMLElement.
    313     return Element::tabIndex();
    314 }
    315 
    316 bool HTMLFormControlElement::recalcWillValidate() const
    317 {
    318     // FIXME: Should return false if this element has a datalist element as an
    319     // ancestor. See HTML5 4.10.10 'The datalist element.'
    320     return !m_disabled && !m_readOnly;
    321 }
    322 
    323 bool HTMLFormControlElement::willValidate() const
    324 {
    325     if (!m_willValidateInitialized) {
    326         m_willValidateInitialized = true;
    327         m_willValidate = recalcWillValidate();
    328     } else {
    329         // If the following assertion fails, setNeedsWillValidateCheck() is not
    330         // called correctly when something which changes recalcWillValidate() result
    331         // is updated.
    332         ASSERT(m_willValidate == recalcWillValidate());
    333     }
    334     return m_willValidate;
    335 }
    336 
    337 void HTMLFormControlElement::setNeedsWillValidateCheck()
    338 {
    339     // We need to recalculate willValidte immediately because willValidate
    340     // change can causes style change.
    341     bool newWillValidate = recalcWillValidate();
    342     if (m_willValidateInitialized && m_willValidate == newWillValidate)
    343         return;
    344     m_willValidateInitialized = true;
    345     m_willValidate = newWillValidate;
    346     setNeedsStyleRecalc();
    347     if (!m_willValidate)
    348         hideVisibleValidationMessage();
    349 }
    350 
    351 String HTMLFormControlElement::validationMessage()
    352 {
    353     return validity()->validationMessage();
    354 }
    355 
    356 void HTMLFormControlElement::updateVisibleValidationMessage()
    357 {
    358     Page* page = document()->page();
    359     if (!page)
    360         return;
    361     String message;
    362     if (renderer() && willValidate()) {
    363         message = validationMessage().stripWhiteSpace();
    364         // HTML5 specification doesn't ask UA to show the title attribute value
    365         // with the validationMessage.  However, this behavior is same as Opera
    366         // and the specification describes such behavior as an example.
    367         const AtomicString& title = getAttribute(titleAttr);
    368         if (!message.isEmpty() && !title.isEmpty()) {
    369             message.append('\n');
    370             message.append(title);
    371         }
    372     }
    373     if (message.isEmpty()) {
    374         hideVisibleValidationMessage();
    375         return;
    376     }
    377     if (!m_validationMessage) {
    378         m_validationMessage = ValidationMessage::create(this);
    379         m_validationMessage->setMessage(message);
    380     } else {
    381         // Call setMessage() even if m_validationMesage->message() == message
    382         // because the existing message might be to be hidden.
    383         m_validationMessage->setMessage(message);
    384     }
    385 }
    386 
    387 void HTMLFormControlElement::hideVisibleValidationMessage()
    388 {
    389     if (m_validationMessage)
    390         m_validationMessage->requestToHideMessage();
    391 }
    392 
    393 String HTMLFormControlElement::visibleValidationMessage() const
    394 {
    395     return m_validationMessage ? m_validationMessage->message() : String();
    396 }
    397 
    398 bool HTMLFormControlElement::checkValidity(Vector<RefPtr<FormAssociatedElement> >* unhandledInvalidControls)
    399 {
    400     if (!willValidate() || isValidFormControlElement())
    401         return true;
    402     // An event handler can deref this object.
    403     RefPtr<HTMLFormControlElement> protector(this);
    404     RefPtr<Document> originalDocument(document());
    405     bool needsDefaultAction = dispatchEvent(Event::create(eventNames().invalidEvent, false, true));
    406     if (needsDefaultAction && unhandledInvalidControls && inDocument() && originalDocument == document())
    407         unhandledInvalidControls->append(this);
    408     return false;
    409 }
    410 
    411 bool HTMLFormControlElement::isValidFormControlElement()
    412 {
    413     // If the following assertion fails, setNeedsValidityCheck() is not called
    414     // correctly when something which changes validity is updated.
    415     ASSERT(m_isValid == validity()->valid());
    416     return m_isValid;
    417 }
    418 
    419 void HTMLFormControlElement::setNeedsValidityCheck()
    420 {
    421     bool newIsValid = validity()->valid();
    422     if (willValidate() && newIsValid != m_isValid) {
    423         // Update style for pseudo classes such as :valid :invalid.
    424         setNeedsStyleRecalc();
    425     }
    426     m_isValid = newIsValid;
    427 
    428     // Updates only if this control already has a validtion message.
    429     if (!visibleValidationMessage().isEmpty()) {
    430         // Calls updateVisibleValidationMessage() even if m_isValid is not
    431         // changed because a validation message can be chagned.
    432         updateVisibleValidationMessage();
    433     }
    434 }
    435 
    436 void HTMLFormControlElement::setCustomValidity(const String& error)
    437 {
    438     validity()->setCustomErrorMessage(error);
    439 }
    440 
    441 void HTMLFormControlElement::dispatchFocusEvent()
    442 {
    443     if (document()->page())
    444         document()->page()->chrome()->client()->formDidFocus(this);
    445 
    446     HTMLElement::dispatchFocusEvent();
    447 }
    448 
    449 void HTMLFormControlElement::dispatchBlurEvent()
    450 {
    451     if (document()->page())
    452         document()->page()->chrome()->client()->formDidBlur(this);
    453 
    454     HTMLElement::dispatchBlurEvent();
    455     hideVisibleValidationMessage();
    456 }
    457 
    458 HTMLFormElement* HTMLFormControlElement::virtualForm() const
    459 {
    460     return FormAssociatedElement::form();
    461 }
    462 
    463 bool HTMLFormControlElement::isDefaultButtonForForm() const
    464 {
    465     return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this;
    466 }
    467 
    468 void HTMLFormControlElement::attributeChanged(Attribute* attr, bool preserveDecls)
    469 {
    470     if (attr->name() == formAttr) {
    471         formAttributeChanged();
    472         if (!form())
    473             document()->checkedRadioButtons().addButton(this);
    474     } else
    475         HTMLElement::attributeChanged(attr, preserveDecls);
    476 }
    477 
    478 bool HTMLFormControlElement::isLabelable() const
    479 {
    480     // FIXME: Add meterTag and outputTag to the list once we support them.
    481     return hasTagName(buttonTag) || hasTagName(inputTag) || hasTagName(keygenTag)
    482 #if ENABLE(METER_TAG)
    483         || hasTagName(meterTag)
    484 #endif
    485 #if ENABLE(PROGRESS_TAG)
    486         || hasTagName(progressTag)
    487 #endif
    488         || hasTagName(selectTag) || hasTagName(textareaTag);
    489 }
    490 
    491 PassRefPtr<NodeList> HTMLFormControlElement::labels()
    492 {
    493     if (!isLabelable())
    494         return 0;
    495     if (!document())
    496         return 0;
    497 
    498     NodeRareData* data = Node::ensureRareData();
    499     if (!data->nodeLists()) {
    500         data->setNodeLists(NodeListsNodeData::create());
    501         document()->addNodeListCache();
    502     }
    503 
    504     return LabelsNodeList::create(this);
    505 }
    506 
    507 HTMLFormControlElementWithState::HTMLFormControlElementWithState(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
    508     : HTMLFormControlElement(tagName, doc, f)
    509 {
    510     document()->registerFormElementWithState(this);
    511 }
    512 
    513 HTMLFormControlElementWithState::~HTMLFormControlElementWithState()
    514 {
    515     document()->unregisterFormElementWithState(this);
    516 }
    517 
    518 void HTMLFormControlElementWithState::willMoveToNewOwnerDocument()
    519 {
    520     document()->unregisterFormElementWithState(this);
    521     HTMLFormControlElement::willMoveToNewOwnerDocument();
    522 }
    523 
    524 void HTMLFormControlElementWithState::didMoveToNewOwnerDocument()
    525 {
    526     document()->registerFormElementWithState(this);
    527     HTMLFormControlElement::didMoveToNewOwnerDocument();
    528 }
    529 
    530 bool HTMLFormControlElementWithState::autoComplete() const
    531 {
    532     if (!form())
    533         return true;
    534     return form()->autoComplete();
    535 }
    536 
    537 bool HTMLFormControlElementWithState::shouldSaveAndRestoreFormControlState() const
    538 {
    539     // We don't save/restore control state in a form with autocomplete=off.
    540     return attached() && autoComplete();
    541 }
    542 
    543 void HTMLFormControlElementWithState::finishParsingChildren()
    544 {
    545     HTMLFormControlElement::finishParsingChildren();
    546 
    547     // We don't save state of a control with shouldSaveAndRestoreFormControlState()=false.
    548     // But we need to skip restoring process too because a control in another
    549     // form might have the same pair of name and type and saved its state.
    550     if (!shouldSaveAndRestoreFormControlState())
    551         return;
    552 
    553     Document* doc = document();
    554     if (doc->hasStateForNewFormElements()) {
    555         String state;
    556         if (doc->takeStateForFormElement(name().impl(), type().impl(), state))
    557             restoreFormControlState(state);
    558     }
    559 }
    560 
    561 void HTMLFormControlElementWithState::defaultEventHandler(Event* event)
    562 {
    563     if (event->type() == eventNames().webkitEditableContentChangedEvent && renderer() && renderer()->isTextControl()) {
    564         toRenderTextControl(renderer())->subtreeHasChanged();
    565         return;
    566     }
    567 
    568     HTMLFormControlElement::defaultEventHandler(event);
    569 }
    570 
    571 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* form)
    572     : HTMLFormControlElementWithState(tagName, doc, form)
    573 {
    574 }
    575 
    576 HTMLTextFormControlElement::~HTMLTextFormControlElement()
    577 {
    578 }
    579 
    580 void HTMLTextFormControlElement::insertedIntoDocument()
    581 {
    582     HTMLFormControlElement::insertedIntoDocument();
    583     setTextAsOfLastFormControlChangeEvent(value());
    584 }
    585 
    586 void HTMLTextFormControlElement::dispatchFocusEvent()
    587 {
    588     if (supportsPlaceholder())
    589         updatePlaceholderVisibility(false);
    590     handleFocusEvent();
    591     HTMLFormControlElementWithState::dispatchFocusEvent();
    592 }
    593 
    594 void HTMLTextFormControlElement::dispatchBlurEvent()
    595 {
    596     if (supportsPlaceholder())
    597         updatePlaceholderVisibility(false);
    598     handleBlurEvent();
    599     HTMLFormControlElementWithState::dispatchBlurEvent();
    600 }
    601 
    602 String HTMLTextFormControlElement::strippedPlaceholder() const
    603 {
    604     // According to the HTML5 specification, we need to remove CR and LF from
    605     // the attribute value.
    606     const AtomicString& attributeValue = getAttribute(placeholderAttr);
    607     if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn))
    608         return attributeValue;
    609 
    610     Vector<UChar> stripped;
    611     unsigned length = attributeValue.length();
    612     stripped.reserveCapacity(length);
    613     for (unsigned i = 0; i < length; ++i) {
    614         UChar character = attributeValue[i];
    615         if (character == newlineCharacter || character == carriageReturn)
    616             continue;
    617         stripped.append(character);
    618     }
    619     return String::adopt(stripped);
    620 }
    621 
    622 static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
    623 
    624 bool HTMLTextFormControlElement::isPlaceholderEmpty() const
    625 {
    626     const AtomicString& attributeValue = getAttribute(placeholderAttr);
    627     return attributeValue.string().find(isNotLineBreak) == notFound;
    628 }
    629 
    630 bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
    631 {
    632     return supportsPlaceholder()
    633         && isEmptyValue()
    634         && isEmptySuggestedValue()
    635         && !isPlaceholderEmpty()
    636         && (document()->focusedNode() != this || (renderer() && renderer()->theme()->shouldShowPlaceholderWhenFocused()));
    637 }
    638 
    639 void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
    640 {
    641     if (supportsPlaceholder() && renderer())
    642         toRenderTextControl(renderer())->updatePlaceholderVisibility(placeholderShouldBeVisible(), placeholderValueChanged);
    643 }
    644 
    645 RenderTextControl* HTMLTextFormControlElement::textRendererAfterUpdateLayout()
    646 {
    647     if (!isTextFormControl())
    648         return 0;
    649     document()->updateLayoutIgnorePendingStylesheets();
    650     return toRenderTextControl(renderer());
    651 }
    652 
    653 void HTMLTextFormControlElement::setSelectionStart(int start)
    654 {
    655     setSelectionRange(start, max(start, selectionEnd()));
    656 }
    657 
    658 void HTMLTextFormControlElement::setSelectionEnd(int end)
    659 {
    660     setSelectionRange(min(end, selectionStart()), end);
    661 }
    662 
    663 void HTMLTextFormControlElement::select()
    664 {
    665     setSelectionRange(0, numeric_limits<int>::max());
    666 }
    667 
    668 void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
    669 {
    670     if (m_textAsOfLastFormControlChangeEvent != value()) {
    671         HTMLElement::dispatchChangeEvent();
    672         setTextAsOfLastFormControlChangeEvent(value());
    673     }
    674     setChangedSinceLastFormControlChangeEvent(false);
    675 }
    676 
    677 void HTMLTextFormControlElement::setSelectionRange(int start, int end)
    678 {
    679     WebCore::setSelectionRange(this, start, end);
    680 }
    681 
    682 int HTMLTextFormControlElement::selectionStart() const
    683 {
    684     if (!isTextFormControl())
    685         return 0;
    686     if (document()->focusedNode() != this && cachedSelectionStart() >= 0)
    687         return cachedSelectionStart();
    688     if (!renderer())
    689         return 0;
    690     return toRenderTextControl(renderer())->selectionStart();
    691 }
    692 
    693 int HTMLTextFormControlElement::selectionEnd() const
    694 {
    695     if (!isTextFormControl())
    696         return 0;
    697     if (document()->focusedNode() != this && cachedSelectionEnd() >= 0)
    698         return cachedSelectionEnd();
    699     if (!renderer())
    700         return 0;
    701     return toRenderTextControl(renderer())->selectionEnd();
    702 }
    703 
    704 PassRefPtr<Range> HTMLTextFormControlElement::selection() const
    705 {
    706     if (!renderer() || !isTextFormControl() || cachedSelectionStart() < 0 || cachedSelectionEnd() < 0)
    707         return 0;
    708     return toRenderTextControl(renderer())->selection(cachedSelectionStart(), cachedSelectionEnd());
    709 }
    710 
    711 void HTMLTextFormControlElement::parseMappedAttribute(Attribute* attr)
    712 {
    713     if (attr->name() == placeholderAttr)
    714         updatePlaceholderVisibility(true);
    715     else if (attr->name() == onselectAttr)
    716         setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr));
    717     else if (attr->name() == onchangeAttr)
    718         setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr));
    719     else
    720         HTMLFormControlElementWithState::parseMappedAttribute(attr);
    721 }
    722 
    723 } // namespace Webcore
    724