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