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, 2008, 2009 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 "core/html/HTMLFormElement.h"
     27 
     28 #include <limits>
     29 #include "bindings/v8/ScriptController.h"
     30 #include "bindings/v8/ScriptEventListener.h"
     31 #include "core/HTMLNames.h"
     32 #include "core/dom/Attribute.h"
     33 #include "core/dom/Document.h"
     34 #include "core/dom/ElementTraversal.h"
     35 #include "core/dom/IdTargetObserverRegistry.h"
     36 #include "core/events/AutocompleteErrorEvent.h"
     37 #include "core/events/Event.h"
     38 #include "core/events/GenericEventQueue.h"
     39 #include "core/events/ScopedEventQueue.h"
     40 #include "core/frame/LocalDOMWindow.h"
     41 #include "core/frame/LocalFrame.h"
     42 #include "core/frame/UseCounter.h"
     43 #include "core/frame/csp/ContentSecurityPolicy.h"
     44 #include "core/html/HTMLCollection.h"
     45 #include "core/html/HTMLDialogElement.h"
     46 #include "core/html/HTMLFormControlsCollection.h"
     47 #include "core/html/HTMLImageElement.h"
     48 #include "core/html/HTMLInputElement.h"
     49 #include "core/html/HTMLObjectElement.h"
     50 #include "core/html/RadioNodeList.h"
     51 #include "core/html/forms/FormController.h"
     52 #include "core/loader/FrameLoader.h"
     53 #include "core/loader/FrameLoaderClient.h"
     54 #include "core/loader/MixedContentChecker.h"
     55 #include "core/rendering/RenderTextControl.h"
     56 #include "platform/UserGestureIndicator.h"
     57 #include "wtf/text/AtomicString.h"
     58 
     59 namespace WebCore {
     60 
     61 using namespace HTMLNames;
     62 
     63 HTMLFormElement::HTMLFormElement(Document& document)
     64     : HTMLElement(formTag, document)
     65 #if !ENABLE(OILPAN)
     66     , m_weakPtrFactory(this)
     67 #endif
     68     , m_associatedElementsAreDirty(false)
     69     , m_imageElementsAreDirty(false)
     70     , m_hasElementsAssociatedByParser(false)
     71     , m_didFinishParsingChildren(false)
     72     , m_wasUserSubmitted(false)
     73     , m_isSubmittingOrInUserJSSubmitEvent(false)
     74     , m_shouldSubmit(false)
     75     , m_isInResetFunction(false)
     76     , m_wasDemoted(false)
     77     , m_pendingAutocompleteEventsQueue(GenericEventQueue::create(this))
     78 {
     79     ScriptWrappable::init(this);
     80 }
     81 
     82 PassRefPtrWillBeRawPtr<HTMLFormElement> HTMLFormElement::create(Document& document)
     83 {
     84     UseCounter::count(document, UseCounter::FormElement);
     85     return adoptRefWillBeNoop(new HTMLFormElement(document));
     86 }
     87 
     88 HTMLFormElement::~HTMLFormElement()
     89 {
     90 #if !ENABLE(OILPAN)
     91     // With Oilpan, either removedFrom is called or the document and
     92     // form controller are dead as well and there is no need to remove
     93     // this form element from it.
     94     document().formController().willDeleteForm(this);
     95 #endif
     96 }
     97 
     98 void HTMLFormElement::trace(Visitor* visitor)
     99 {
    100 #if ENABLE(OILPAN)
    101     visitor->trace(m_pastNamesMap);
    102     visitor->trace(m_radioButtonGroupScope);
    103     visitor->trace(m_associatedElements);
    104     visitor->trace(m_imageElements);
    105     visitor->trace(m_pendingAutocompleteEventsQueue);
    106 #endif
    107     HTMLElement::trace(visitor);
    108 }
    109 
    110 bool HTMLFormElement::rendererIsNeeded(const RenderStyle& style)
    111 {
    112     if (!m_wasDemoted)
    113         return HTMLElement::rendererIsNeeded(style);
    114 
    115     ContainerNode* node = parentNode();
    116     if (!node || !node->renderer())
    117         return HTMLElement::rendererIsNeeded(style);
    118     RenderObject* parentRenderer = node->renderer();
    119     // FIXME: Shouldn't we also check for table caption (see |formIsTablePart| below).
    120     // FIXME: This check is not correct for Shadow DOM.
    121     bool parentIsTableElementPart = (parentRenderer->isTable() && isHTMLTableElement(*node))
    122         || (parentRenderer->isTableRow() && isHTMLTableRowElement(*node))
    123         || (parentRenderer->isTableSection() && node->hasTagName(tbodyTag))
    124         || (parentRenderer->isRenderTableCol() && node->hasTagName(colTag))
    125         || (parentRenderer->isTableCell() && isHTMLTableRowElement(*node));
    126 
    127     if (!parentIsTableElementPart)
    128         return true;
    129 
    130     EDisplay display = style.display();
    131     bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP
    132         || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW
    133         || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL
    134         || display == TABLE_CAPTION;
    135 
    136     return formIsTablePart;
    137 }
    138 
    139 Node::InsertionNotificationRequest HTMLFormElement::insertedInto(ContainerNode* insertionPoint)
    140 {
    141     HTMLElement::insertedInto(insertionPoint);
    142     if (insertionPoint->inDocument())
    143         this->document().didAssociateFormControl(this);
    144     return InsertionDone;
    145 }
    146 
    147 template<class T>
    148 void notifyFormRemovedFromTree(const T& elements, Node& root)
    149 {
    150     size_t size = elements.size();
    151     for (size_t i = 0; i < size; ++i)
    152         elements[i]->formRemovedFromTree(root);
    153     ASSERT(elements.size() == size);
    154 }
    155 
    156 void HTMLFormElement::removedFrom(ContainerNode* insertionPoint)
    157 {
    158     // We don't need to take care of form association by 'form' content
    159     // attribute becuse IdTargetObserver handles it.
    160     if (m_hasElementsAssociatedByParser) {
    161         Node& root = highestAncestorOrSelf();
    162         if (!m_associatedElementsAreDirty) {
    163             FormAssociatedElement::List elements(associatedElements());
    164             notifyFormRemovedFromTree(elements, root);
    165         } else {
    166             FormAssociatedElement::List elements;
    167             collectAssociatedElements(insertionPoint->highestAncestorOrSelf(), elements);
    168             notifyFormRemovedFromTree(elements, root);
    169             collectAssociatedElements(root, elements);
    170             notifyFormRemovedFromTree(elements, root);
    171         }
    172 
    173         if (!m_imageElementsAreDirty) {
    174             WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> > images(imageElements());
    175             notifyFormRemovedFromTree(images, root);
    176         } else {
    177             WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> > images;
    178             collectImageElements(insertionPoint->highestAncestorOrSelf(), images);
    179             notifyFormRemovedFromTree(images, root);
    180             collectImageElements(root, images);
    181             notifyFormRemovedFromTree(images, root);
    182         }
    183     }
    184 #if ENABLE(OILPAN)
    185     document().formController().willDeleteForm(this);
    186 #endif
    187     HTMLElement::removedFrom(insertionPoint);
    188 }
    189 
    190 void HTMLFormElement::handleLocalEvents(Event* event)
    191 {
    192     Node* targetNode = event->target()->toNode();
    193     if (event->eventPhase() != Event::CAPTURING_PHASE && targetNode && targetNode != this && (event->type() == EventTypeNames::submit || event->type() == EventTypeNames::reset)) {
    194         event->stopPropagation();
    195         return;
    196     }
    197     HTMLElement::handleLocalEvents(event);
    198 }
    199 
    200 unsigned HTMLFormElement::length() const
    201 {
    202     const FormAssociatedElement::List& elements = associatedElements();
    203     unsigned len = 0;
    204     for (unsigned i = 0; i < elements.size(); ++i) {
    205         if (elements[i]->isEnumeratable())
    206             ++len;
    207     }
    208     return len;
    209 }
    210 
    211 Element* HTMLFormElement::item(unsigned index)
    212 {
    213     return elements()->item(index);
    214 }
    215 
    216 void HTMLFormElement::submitImplicitly(Event* event, bool fromImplicitSubmissionTrigger)
    217 {
    218     int submissionTriggerCount = 0;
    219     bool seenDefaultButton = false;
    220     const FormAssociatedElement::List& elements = associatedElements();
    221     for (unsigned i = 0; i < elements.size(); ++i) {
    222         FormAssociatedElement* formAssociatedElement = elements[i];
    223         if (!formAssociatedElement->isFormControlElement())
    224             continue;
    225         HTMLFormControlElement* control = toHTMLFormControlElement(formAssociatedElement);
    226         if (!seenDefaultButton && control->canBeSuccessfulSubmitButton()) {
    227             if (fromImplicitSubmissionTrigger)
    228                 seenDefaultButton = true;
    229             if (control->isSuccessfulSubmitButton()) {
    230                 control->dispatchSimulatedClick(event);
    231                 return;
    232             } else if (fromImplicitSubmissionTrigger) {
    233                 // Default (submit) button is not activated; no implicit submission.
    234                 return;
    235             }
    236         } else if (control->canTriggerImplicitSubmission()) {
    237             ++submissionTriggerCount;
    238         }
    239     }
    240     if (fromImplicitSubmissionTrigger && submissionTriggerCount == 1)
    241         prepareForSubmission(event);
    242 }
    243 
    244 // FIXME: Consolidate this and similar code in FormSubmission.cpp.
    245 static inline HTMLFormControlElement* submitElementFromEvent(const Event* event)
    246 {
    247     for (Node* node = event->target()->toNode(); node; node = node->parentOrShadowHostNode()) {
    248         if (node->isElementNode() && toElement(node)->isFormControlElement())
    249             return toHTMLFormControlElement(node);
    250     }
    251     return 0;
    252 }
    253 
    254 bool HTMLFormElement::validateInteractively(Event* event)
    255 {
    256     ASSERT(event);
    257     if (!document().page() || noValidate())
    258         return true;
    259 
    260     HTMLFormControlElement* submitElement = submitElementFromEvent(event);
    261     if (submitElement && submitElement->formNoValidate())
    262         return true;
    263 
    264     const FormAssociatedElement::List& elements = associatedElements();
    265     for (unsigned i = 0; i < elements.size(); ++i) {
    266         if (elements[i]->isFormControlElement())
    267             toHTMLFormControlElement(elements[i])->hideVisibleValidationMessage();
    268     }
    269 
    270     WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement> > unhandledInvalidControls;
    271     if (!checkInvalidControlsAndCollectUnhandled(&unhandledInvalidControls))
    272         return true;
    273     // Because the form has invalid controls, we abort the form submission and
    274     // show a validation message on a focusable form control.
    275 
    276     // Needs to update layout now because we'd like to call isFocusable(), which
    277     // has !renderer()->needsLayout() assertion.
    278     document().updateLayoutIgnorePendingStylesheets();
    279 
    280     RefPtrWillBeRawPtr<HTMLFormElement> protector(this);
    281     // Focus on the first focusable control and show a validation message.
    282     for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
    283         FormAssociatedElement* unhandledAssociatedElement = unhandledInvalidControls[i].get();
    284         HTMLElement* unhandled = toHTMLElement(unhandledAssociatedElement);
    285         if (unhandled->isFocusable() && unhandled->inDocument()) {
    286             unhandled->scrollIntoViewIfNeeded(false);
    287             unhandled->focus();
    288             if (unhandled->isFormControlElement())
    289                 toHTMLFormControlElement(unhandled)->updateVisibleValidationMessage();
    290             break;
    291         }
    292     }
    293     // Warn about all of unfocusable controls.
    294     if (document().frame()) {
    295         for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
    296             FormAssociatedElement* unhandledAssociatedElement = unhandledInvalidControls[i].get();
    297             HTMLElement* unhandled = toHTMLElement(unhandledAssociatedElement);
    298             if (unhandled->isFocusable() && unhandled->inDocument())
    299                 continue;
    300             String message("An invalid form control with name='%name' is not focusable.");
    301             message.replace("%name", unhandledAssociatedElement->name());
    302             document().addConsoleMessage(RenderingMessageSource, ErrorMessageLevel, message);
    303         }
    304     }
    305     return false;
    306 }
    307 
    308 void HTMLFormElement::prepareForSubmission(Event* event)
    309 {
    310     RefPtrWillBeRawPtr<HTMLFormElement> protector(this);
    311     LocalFrame* frame = document().frame();
    312     if (!frame || m_isSubmittingOrInUserJSSubmitEvent)
    313         return;
    314 
    315     // Interactive validation must be done before dispatching the submit event.
    316     if (!validateInteractively(event))
    317         return;
    318 
    319     m_isSubmittingOrInUserJSSubmitEvent = true;
    320     m_shouldSubmit = false;
    321 
    322     frame->loader().client()->dispatchWillSendSubmitEvent(this);
    323 
    324     if (dispatchEvent(Event::createCancelableBubble(EventTypeNames::submit)))
    325         m_shouldSubmit = true;
    326 
    327     m_isSubmittingOrInUserJSSubmitEvent = false;
    328 
    329     if (m_shouldSubmit)
    330         submit(event, true, true, NotSubmittedByJavaScript);
    331 }
    332 
    333 void HTMLFormElement::submit()
    334 {
    335     submit(0, false, true, NotSubmittedByJavaScript);
    336 }
    337 
    338 void HTMLFormElement::submitFromJavaScript()
    339 {
    340     submit(0, false, UserGestureIndicator::processingUserGesture(), SubmittedByJavaScript);
    341 }
    342 
    343 void HTMLFormElement::submitDialog(PassRefPtrWillBeRawPtr<FormSubmission> formSubmission)
    344 {
    345     for (Node* node = this; node; node = node->parentOrShadowHostNode()) {
    346         if (isHTMLDialogElement(*node)) {
    347             toHTMLDialogElement(*node).closeDialog(formSubmission->result());
    348             return;
    349         }
    350     }
    351 }
    352 
    353 void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger formSubmissionTrigger)
    354 {
    355     FrameView* view = document().view();
    356     LocalFrame* frame = document().frame();
    357     if (!view || !frame || !frame->page())
    358         return;
    359 
    360     if (m_isSubmittingOrInUserJSSubmitEvent) {
    361         m_shouldSubmit = true;
    362         return;
    363     }
    364 
    365     m_isSubmittingOrInUserJSSubmitEvent = true;
    366     m_wasUserSubmitted = processingUserGesture;
    367 
    368     RefPtrWillBeRawPtr<HTMLFormControlElement> firstSuccessfulSubmitButton = nullptr;
    369     bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
    370 
    371     const FormAssociatedElement::List& elements = associatedElements();
    372     for (unsigned i = 0; i < elements.size(); ++i) {
    373         FormAssociatedElement* associatedElement = elements[i];
    374         if (!associatedElement->isFormControlElement())
    375             continue;
    376         if (needButtonActivation) {
    377             HTMLFormControlElement* control = toHTMLFormControlElement(associatedElement);
    378             if (control->isActivatedSubmit())
    379                 needButtonActivation = false;
    380             else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton())
    381                 firstSuccessfulSubmitButton = control;
    382         }
    383     }
    384 
    385     if (needButtonActivation && firstSuccessfulSubmitButton)
    386         firstSuccessfulSubmitButton->setActivatedSubmit(true);
    387 
    388     RefPtrWillBeRawPtr<FormSubmission> formSubmission = FormSubmission::create(this, m_attributes, event, formSubmissionTrigger);
    389     EventQueueScope scopeForDialogClose; // Delay dispatching 'close' to dialog until done submitting.
    390     if (formSubmission->method() == FormSubmission::DialogMethod)
    391         submitDialog(formSubmission.release());
    392     else
    393         scheduleFormSubmission(formSubmission.release());
    394 
    395     if (needButtonActivation && firstSuccessfulSubmitButton)
    396         firstSuccessfulSubmitButton->setActivatedSubmit(false);
    397 
    398     m_shouldSubmit = false;
    399     m_isSubmittingOrInUserJSSubmitEvent = false;
    400 }
    401 
    402 void HTMLFormElement::scheduleFormSubmission(PassRefPtrWillBeRawPtr<FormSubmission> submission)
    403 {
    404     ASSERT(submission->method() == FormSubmission::PostMethod || submission->method() == FormSubmission::GetMethod);
    405     ASSERT(submission->data());
    406     ASSERT(submission->state());
    407     if (submission->action().isEmpty())
    408         return;
    409     if (document().isSandboxed(SandboxForms)) {
    410         // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
    411         document().addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Blocked form submission to '" + submission->action().elidedString() + "' because the form's frame is sandboxed and the 'allow-forms' permission is not set.");
    412         return;
    413     }
    414 
    415     if (protocolIsJavaScript(submission->action())) {
    416         if (!document().contentSecurityPolicy()->allowFormAction(submission->action()))
    417             return;
    418         document().frame()->script().executeScriptIfJavaScriptURL(submission->action());
    419         return;
    420     }
    421 
    422     LocalFrame* targetFrame = document().frame()->loader().findFrameForNavigation(submission->target(), submission->state()->sourceDocument());
    423     if (!targetFrame) {
    424         if (!LocalDOMWindow::allowPopUp(*document().frame()) && !UserGestureIndicator::processingUserGesture())
    425             return;
    426         targetFrame = document().frame();
    427     } else {
    428         submission->clearTarget();
    429     }
    430     if (!targetFrame->page())
    431         return;
    432 
    433     if (MixedContentChecker::isMixedContent(document().securityOrigin(), submission->action())) {
    434         UseCounter::count(document(), UseCounter::MixedContentFormsSubmitted);
    435         if (!document().frame()->loader().mixedContentChecker()->canSubmitToInsecureForm(document().securityOrigin(), submission->action()))
    436             return;
    437     } else {
    438         UseCounter::count(document(), UseCounter::FormsSubmitted);
    439     }
    440 
    441     submission->setReferrer(Referrer(document().outgoingReferrer(), document().referrerPolicy()));
    442     submission->setOrigin(document().outgoingOrigin());
    443 
    444     targetFrame->navigationScheduler().scheduleFormSubmission(submission);
    445 }
    446 
    447 void HTMLFormElement::reset()
    448 {
    449     LocalFrame* frame = document().frame();
    450     if (m_isInResetFunction || !frame)
    451         return;
    452 
    453     m_isInResetFunction = true;
    454 
    455     if (!dispatchEvent(Event::createCancelableBubble(EventTypeNames::reset))) {
    456         m_isInResetFunction = false;
    457         return;
    458     }
    459 
    460     const FormAssociatedElement::List& elements = associatedElements();
    461     for (unsigned i = 0; i < elements.size(); ++i) {
    462         if (elements[i]->isFormControlElement())
    463             toHTMLFormControlElement(elements[i])->reset();
    464     }
    465 
    466     m_isInResetFunction = false;
    467 }
    468 
    469 void HTMLFormElement::requestAutocomplete()
    470 {
    471     String errorMessage;
    472 
    473     if (!document().frame())
    474         errorMessage = "requestAutocomplete: form is not owned by a displayed document.";
    475     else if (!shouldAutocomplete())
    476         errorMessage = "requestAutocomplete: form autocomplete attribute is set to off.";
    477     else if (!UserGestureIndicator::processingUserGesture())
    478         errorMessage = "requestAutocomplete: must be called in response to a user gesture.";
    479 
    480     if (!errorMessage.isEmpty()) {
    481         document().addConsoleMessage(RenderingMessageSource, LogMessageLevel, errorMessage);
    482         finishRequestAutocomplete(AutocompleteResultErrorDisabled);
    483     } else {
    484         document().frame()->loader().client()->didRequestAutocomplete(this);
    485     }
    486 }
    487 
    488 void HTMLFormElement::finishRequestAutocomplete(AutocompleteResult result)
    489 {
    490     RefPtrWillBeRawPtr<Event> event = nullptr;
    491     if (result == AutocompleteResultSuccess)
    492         event = Event::createBubble(EventTypeNames::autocomplete);
    493     else if (result == AutocompleteResultErrorDisabled)
    494         event = AutocompleteErrorEvent::create("disabled");
    495     else if (result == AutocompleteResultErrorCancel)
    496         event = AutocompleteErrorEvent::create("cancel");
    497     else if (result == AutocompleteResultErrorInvalid)
    498         event = AutocompleteErrorEvent::create("invalid");
    499     else
    500         ASSERT_NOT_REACHED();
    501 
    502     event->setTarget(this);
    503     m_pendingAutocompleteEventsQueue->enqueueEvent(event.release());
    504 }
    505 
    506 void HTMLFormElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    507 {
    508     if (name == actionAttr) {
    509         m_attributes.parseAction(value);
    510         // If the new action attribute is pointing to insecure "action" location from a secure page
    511         // it is marked as "passive" mixed content.
    512         KURL actionURL = document().completeURL(m_attributes.action().isEmpty() ? document().url().string() : m_attributes.action());
    513         if (document().frame() && MixedContentChecker::isMixedContent(document().securityOrigin(), actionURL))
    514             document().frame()->loader().mixedContentChecker()->canSubmitToInsecureForm(document().securityOrigin(), actionURL);
    515     } else if (name == targetAttr)
    516         m_attributes.setTarget(value);
    517     else if (name == methodAttr)
    518         m_attributes.updateMethodType(value);
    519     else if (name == enctypeAttr)
    520         m_attributes.updateEncodingType(value);
    521     else if (name == accept_charsetAttr)
    522         m_attributes.setAcceptCharset(value);
    523     else if (name == onautocompleteAttr)
    524         setAttributeEventListener(EventTypeNames::autocomplete, createAttributeEventListener(this, name, value, eventParameterName()));
    525     else if (name == onautocompleteerrorAttr)
    526         setAttributeEventListener(EventTypeNames::autocompleteerror, createAttributeEventListener(this, name, value, eventParameterName()));
    527     else
    528         HTMLElement::parseAttribute(name, value);
    529 }
    530 
    531 void HTMLFormElement::associate(FormAssociatedElement& e)
    532 {
    533     m_associatedElementsAreDirty = true;
    534     m_associatedElements.clear();
    535 }
    536 
    537 void HTMLFormElement::disassociate(FormAssociatedElement& e)
    538 {
    539     m_associatedElementsAreDirty = true;
    540     m_associatedElements.clear();
    541     removeFromPastNamesMap(toHTMLElement(e));
    542 }
    543 
    544 bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const
    545 {
    546     return attribute.name() == actionAttr || HTMLElement::isURLAttribute(attribute);
    547 }
    548 
    549 bool HTMLFormElement::hasLegalLinkAttribute(const QualifiedName& name) const
    550 {
    551     return name == actionAttr || HTMLElement::hasLegalLinkAttribute(name);
    552 }
    553 
    554 void HTMLFormElement::associate(HTMLImageElement& e)
    555 {
    556     m_imageElementsAreDirty = true;
    557     m_imageElements.clear();
    558 }
    559 
    560 void HTMLFormElement::disassociate(HTMLImageElement& e)
    561 {
    562     m_imageElementsAreDirty = true;
    563     m_imageElements.clear();
    564     removeFromPastNamesMap(e);
    565 }
    566 
    567 #if !ENABLE(OILPAN)
    568 WeakPtr<HTMLFormElement> HTMLFormElement::createWeakPtr()
    569 {
    570     return m_weakPtrFactory.createWeakPtr();
    571 }
    572 #endif
    573 
    574 void HTMLFormElement::didAssociateByParser()
    575 {
    576     if (!m_didFinishParsingChildren)
    577         return;
    578     m_hasElementsAssociatedByParser = true;
    579     UseCounter::count(document(), UseCounter::FormAssociationByParser);
    580 }
    581 
    582 PassRefPtrWillBeRawPtr<HTMLFormControlsCollection> HTMLFormElement::elements()
    583 {
    584     return toHTMLFormControlsCollection(ensureCachedHTMLCollection(FormControls).get());
    585 }
    586 
    587 void HTMLFormElement::collectAssociatedElements(Node& root, FormAssociatedElement::List& elements) const
    588 {
    589     elements.clear();
    590     for (HTMLElement* element = Traversal<HTMLElement>::firstWithin(root); element; element = Traversal<HTMLElement>::next(*element)) {
    591         FormAssociatedElement* associatedElement = 0;
    592         if (element->isFormControlElement())
    593             associatedElement = toHTMLFormControlElement(element);
    594         else if (isHTMLObjectElement(*element))
    595             associatedElement = toHTMLObjectElement(element);
    596         else
    597             continue;
    598         if (associatedElement->form()== this)
    599             elements.append(associatedElement);
    600     }
    601 }
    602 
    603 // This function should be const conceptually. However we update some fields
    604 // because of lazy evaluation.
    605 const FormAssociatedElement::List& HTMLFormElement::associatedElements() const
    606 {
    607     if (!m_associatedElementsAreDirty)
    608         return m_associatedElements;
    609     HTMLFormElement* mutableThis = const_cast<HTMLFormElement*>(this);
    610     Node* scope = mutableThis;
    611     if (m_hasElementsAssociatedByParser)
    612         scope = &highestAncestorOrSelf();
    613     if (inDocument() && treeScope().idTargetObserverRegistry().hasObservers(fastGetAttribute(idAttr)))
    614         scope = &treeScope().rootNode();
    615     ASSERT(scope);
    616     collectAssociatedElements(*scope, mutableThis->m_associatedElements);
    617     mutableThis->m_associatedElementsAreDirty = false;
    618     return m_associatedElements;
    619 }
    620 
    621 void HTMLFormElement::collectImageElements(Node& root, WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >& elements)
    622 {
    623     elements.clear();
    624     for (HTMLImageElement* image = Traversal<HTMLImageElement>::firstWithin(root); image; image = Traversal<HTMLImageElement>::next(*image)) {
    625         if (image->formOwner() == this)
    626             elements.append(image);
    627     }
    628 }
    629 
    630 const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >& HTMLFormElement::imageElements()
    631 {
    632     if (!m_imageElementsAreDirty)
    633         return m_imageElements;
    634     collectImageElements(m_hasElementsAssociatedByParser ? highestAncestorOrSelf() : *this, m_imageElements);
    635     m_imageElementsAreDirty = false;
    636     return m_imageElements;
    637 }
    638 
    639 String HTMLFormElement::name() const
    640 {
    641     return getNameAttribute();
    642 }
    643 
    644 bool HTMLFormElement::noValidate() const
    645 {
    646     return fastHasAttribute(novalidateAttr);
    647 }
    648 
    649 // FIXME: This function should be removed because it does not do the same thing as the
    650 // JavaScript binding for action, which treats action as a URL attribute. Last time I
    651 // (Darin Adler) removed this, someone added it back, so I am leaving it in for now.
    652 const AtomicString& HTMLFormElement::action() const
    653 {
    654     return getAttribute(actionAttr);
    655 }
    656 
    657 void HTMLFormElement::setEnctype(const AtomicString& value)
    658 {
    659     setAttribute(enctypeAttr, value);
    660 }
    661 
    662 String HTMLFormElement::method() const
    663 {
    664     return FormSubmission::Attributes::methodString(m_attributes.method());
    665 }
    666 
    667 void HTMLFormElement::setMethod(const AtomicString& value)
    668 {
    669     setAttribute(methodAttr, value);
    670 }
    671 
    672 bool HTMLFormElement::wasUserSubmitted() const
    673 {
    674     return m_wasUserSubmitted;
    675 }
    676 
    677 HTMLFormControlElement* HTMLFormElement::defaultButton() const
    678 {
    679     const FormAssociatedElement::List& elements = associatedElements();
    680     for (unsigned i = 0; i < elements.size(); ++i) {
    681         if (!elements[i]->isFormControlElement())
    682             continue;
    683         HTMLFormControlElement* control = toHTMLFormControlElement(elements[i]);
    684         if (control->isSuccessfulSubmitButton())
    685             return control;
    686     }
    687 
    688     return 0;
    689 }
    690 
    691 bool HTMLFormElement::checkValidity()
    692 {
    693     return !checkInvalidControlsAndCollectUnhandled(0);
    694 }
    695 
    696 bool HTMLFormElement::checkInvalidControlsAndCollectUnhandled(WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement> >* unhandledInvalidControls)
    697 {
    698     RefPtrWillBeRawPtr<HTMLFormElement> protector(this);
    699     // Copy associatedElements because event handlers called from
    700     // HTMLFormControlElement::checkValidity() might change associatedElements.
    701     const FormAssociatedElement::List& associatedElements = this->associatedElements();
    702     WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement> > elements;
    703     elements.reserveCapacity(associatedElements.size());
    704     for (unsigned i = 0; i < associatedElements.size(); ++i)
    705         elements.append(associatedElements[i]);
    706     bool hasInvalidControls = false;
    707     for (unsigned i = 0; i < elements.size(); ++i) {
    708         if (elements[i]->form() == this && elements[i]->isFormControlElement()) {
    709             HTMLFormControlElement* control = toHTMLFormControlElement(elements[i].get());
    710             if (!control->checkValidity(unhandledInvalidControls) && control->formOwner() == this)
    711                 hasInvalidControls = true;
    712         }
    713     }
    714     return hasInvalidControls;
    715 }
    716 
    717 Element* HTMLFormElement::elementFromPastNamesMap(const AtomicString& pastName)
    718 {
    719     if (pastName.isEmpty() || !m_pastNamesMap)
    720         return 0;
    721     Element* element = m_pastNamesMap->get(pastName);
    722 #if ASSERT_ENABLED
    723     if (!element)
    724         return 0;
    725     ASSERT_WITH_SECURITY_IMPLICATION(toHTMLElement(element)->formOwner() == this);
    726     if (isHTMLImageElement(*element)) {
    727         ASSERT_WITH_SECURITY_IMPLICATION(imageElements().find(element) != kNotFound);
    728     } else if (isHTMLObjectElement(*element)) {
    729         ASSERT_WITH_SECURITY_IMPLICATION(associatedElements().find(toHTMLObjectElement(element)) != kNotFound);
    730     } else {
    731         ASSERT_WITH_SECURITY_IMPLICATION(associatedElements().find(toHTMLFormControlElement(element)) != kNotFound);
    732     }
    733 #endif
    734     return element;
    735 }
    736 
    737 void HTMLFormElement::addToPastNamesMap(Element* element, const AtomicString& pastName)
    738 {
    739     if (pastName.isEmpty())
    740         return;
    741     if (!m_pastNamesMap)
    742         m_pastNamesMap = adoptPtrWillBeNoop(new PastNamesMap);
    743     m_pastNamesMap->set(pastName, element);
    744 }
    745 
    746 void HTMLFormElement::removeFromPastNamesMap(HTMLElement& element)
    747 {
    748     if (!m_pastNamesMap)
    749         return;
    750     PastNamesMap::iterator end = m_pastNamesMap->end();
    751     for (PastNamesMap::iterator it = m_pastNamesMap->begin(); it != end; ++it) {
    752         if (it->value == &element) {
    753             it->value = nullptr;
    754             // Keep looping. Single element can have multiple names.
    755         }
    756     }
    757 }
    758 
    759 void HTMLFormElement::getNamedElements(const AtomicString& name, WillBeHeapVector<RefPtrWillBeMember<Element> >& namedItems)
    760 {
    761     // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem
    762     elements()->namedItems(name, namedItems);
    763 
    764     Element* elementFromPast = elementFromPastNamesMap(name);
    765     if (namedItems.size() && namedItems.first() != elementFromPast) {
    766         addToPastNamesMap(namedItems.first().get(), name);
    767     } else if (elementFromPast && namedItems.isEmpty()) {
    768         namedItems.append(elementFromPast);
    769         UseCounter::count(document(), UseCounter::FormNameAccessForPastNamesMap);
    770     }
    771 }
    772 
    773 bool HTMLFormElement::shouldAutocomplete() const
    774 {
    775     return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off");
    776 }
    777 
    778 void HTMLFormElement::finishParsingChildren()
    779 {
    780     HTMLElement::finishParsingChildren();
    781     document().formController().restoreControlStateIn(*this);
    782     m_didFinishParsingChildren = true;
    783 }
    784 
    785 void HTMLFormElement::copyNonAttributePropertiesFromElement(const Element& source)
    786 {
    787     m_wasDemoted = static_cast<const HTMLFormElement&>(source).m_wasDemoted;
    788     HTMLElement::copyNonAttributePropertiesFromElement(source);
    789 }
    790 
    791 void HTMLFormElement::anonymousNamedGetter(const AtomicString& name, bool& returnValue0Enabled, RefPtrWillBeRawPtr<RadioNodeList>& returnValue0, bool& returnValue1Enabled, RefPtrWillBeRawPtr<Element>& returnValue1)
    792 {
    793     // Call getNamedElements twice, first time check if it has a value
    794     // and let HTMLFormElement update its cache.
    795     // See issue: 867404
    796     {
    797         WillBeHeapVector<RefPtrWillBeMember<Element> > elements;
    798         getNamedElements(name, elements);
    799         if (elements.isEmpty())
    800             return;
    801     }
    802 
    803     // Second call may return different results from the first call,
    804     // but if the first the size cannot be zero.
    805     WillBeHeapVector<RefPtrWillBeMember<Element> > elements;
    806     getNamedElements(name, elements);
    807     ASSERT(!elements.isEmpty());
    808 
    809     if (elements.size() == 1) {
    810         returnValue1Enabled = true;
    811         returnValue1 = elements.at(0);
    812         return;
    813     }
    814 
    815     bool onlyMatchImg = !elements.isEmpty() && isHTMLImageElement(*elements.first());
    816     returnValue0Enabled = true;
    817     returnValue0 = radioNodeList(name, onlyMatchImg);
    818 }
    819 
    820 void HTMLFormElement::setDemoted(bool demoted)
    821 {
    822     if (demoted)
    823         UseCounter::count(document(), UseCounter::DemotedFormElement);
    824     m_wasDemoted = demoted;
    825 }
    826 
    827 } // namespace
    828