1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * (C) 2001 Dirk Mueller (mueller@kde.org) 5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25#include "config.h" 26#include "HTMLFormElement.h" 27 28#include "Attribute.h" 29#include "Document.h" 30#include "ElementIterator.h" 31#include "Event.h" 32#include "EventNames.h" 33#include "FormController.h" 34#include "FormData.h" 35#include "Frame.h" 36#include "FrameLoader.h" 37#include "FrameLoaderClient.h" 38#include "HTMLCollection.h" 39#include "HTMLImageElement.h" 40#include "HTMLInputElement.h" 41#include "HTMLNames.h" 42#include "HTMLTableElement.h" 43#include "Page.h" 44#include "RenderTextControl.h" 45#include "ScriptController.h" 46#include "Settings.h" 47#include <limits> 48#include <wtf/Ref.h> 49 50namespace WebCore { 51 52using namespace HTMLNames; 53 54HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document& document) 55 : HTMLElement(tagName, document) 56 , m_associatedElementsBeforeIndex(0) 57 , m_associatedElementsAfterIndex(0) 58 , m_wasUserSubmitted(false) 59 , m_isSubmittingOrPreparingForSubmission(false) 60 , m_shouldSubmit(false) 61 , m_isInResetFunction(false) 62 , m_wasDemoted(false) 63{ 64 ASSERT(hasTagName(formTag)); 65} 66 67PassRefPtr<HTMLFormElement> HTMLFormElement::create(Document& document) 68{ 69 return adoptRef(new HTMLFormElement(formTag, document)); 70} 71 72PassRefPtr<HTMLFormElement> HTMLFormElement::create(const QualifiedName& tagName, Document& document) 73{ 74 return adoptRef(new HTMLFormElement(tagName, document)); 75} 76 77HTMLFormElement::~HTMLFormElement() 78{ 79 document().formController().willDeleteForm(this); 80 if (!shouldAutocomplete()) 81 document().unregisterForPageCacheSuspensionCallbacks(this); 82 83 for (unsigned i = 0; i < m_associatedElements.size(); ++i) 84 m_associatedElements[i]->formWillBeDestroyed(); 85 for (unsigned i = 0; i < m_imageElements.size(); ++i) 86 m_imageElements[i]->m_form = 0; 87} 88 89bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url) 90{ 91 return document().completeURL(url).protocolIs("https"); 92} 93 94bool HTMLFormElement::rendererIsNeeded(const RenderStyle& style) 95{ 96 if (!m_wasDemoted) 97 return HTMLElement::rendererIsNeeded(style); 98 99 auto parent = parentNode(); 100 auto parentRenderer = parent->renderer(); 101 102 if (!parentRenderer) 103 return false; 104 105 // FIXME: Shouldn't we also check for table caption (see |formIsTablePart| below). 106 bool parentIsTableElementPart = (parentRenderer->isTable() && isHTMLTableElement(parent)) 107 || (parentRenderer->isTableRow() && parent->hasTagName(trTag)) 108 || (parentRenderer->isTableSection() && parent->hasTagName(tbodyTag)) 109 || (parentRenderer->isRenderTableCol() && parent->hasTagName(colTag)) 110 || (parentRenderer->isTableCell() && parent->hasTagName(trTag)); 111 112 if (!parentIsTableElementPart) 113 return true; 114 115 EDisplay display = style.display(); 116 bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP 117 || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW 118 || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL 119 || display == TABLE_CAPTION; 120 121 return formIsTablePart; 122} 123 124Node::InsertionNotificationRequest HTMLFormElement::insertedInto(ContainerNode& insertionPoint) 125{ 126 HTMLElement::insertedInto(insertionPoint); 127 if (insertionPoint.inDocument()) 128 document().didAssociateFormControl(this); 129 return InsertionDone; 130} 131 132static inline Node* findRoot(Node* n) 133{ 134 Node* root = n; 135 for (; n; n = n->parentNode()) 136 root = n; 137 return root; 138} 139 140void HTMLFormElement::removedFrom(ContainerNode& insertionPoint) 141{ 142 Node* root = findRoot(this); 143 Vector<FormAssociatedElement*> associatedElements(m_associatedElements); 144 for (unsigned i = 0; i < associatedElements.size(); ++i) 145 associatedElements[i]->formRemovedFromTree(root); 146 HTMLElement::removedFrom(insertionPoint); 147} 148 149void HTMLFormElement::handleLocalEvents(Event& event) 150{ 151 Node* targetNode = event.target()->toNode(); 152 if (event.eventPhase() != Event::CAPTURING_PHASE && targetNode && targetNode != this && (event.type() == eventNames().submitEvent || event.type() == eventNames().resetEvent)) { 153 event.stopPropagation(); 154 return; 155 } 156 HTMLElement::handleLocalEvents(event); 157} 158 159unsigned HTMLFormElement::length() const 160{ 161 unsigned len = 0; 162 for (unsigned i = 0; i < m_associatedElements.size(); ++i) 163 if (m_associatedElements[i]->isEnumeratable()) 164 ++len; 165 return len; 166} 167 168Node* HTMLFormElement::item(unsigned index) 169{ 170 return elements()->item(index); 171} 172 173void HTMLFormElement::submitImplicitly(Event* event, bool fromImplicitSubmissionTrigger) 174{ 175 unsigned submissionTriggerCount = 0; 176 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 177 FormAssociatedElement* formAssociatedElement = m_associatedElements[i]; 178 if (!formAssociatedElement->isFormControlElement()) 179 continue; 180 HTMLFormControlElement* formElement = toHTMLFormControlElement(formAssociatedElement); 181 if (formElement->isSuccessfulSubmitButton()) { 182 if (formElement->renderer()) { 183 formElement->dispatchSimulatedClick(event); 184 return; 185 } 186 } else if (formElement->canTriggerImplicitSubmission()) 187 ++submissionTriggerCount; 188 } 189 190 if (!submissionTriggerCount) 191 return; 192 193 // Older iOS apps using WebViews expect the behavior of auto submitting multi-input forms. 194 Settings* settings = document().settings(); 195 if (fromImplicitSubmissionTrigger && (submissionTriggerCount == 1 || (settings && settings->allowMultiElementImplicitSubmission()))) 196 prepareForSubmission(event); 197} 198 199static inline HTMLFormControlElement* submitElementFromEvent(const Event* event) 200{ 201 for (Node* node = event->target()->toNode(); node; node = node->parentNode()) { 202 if (node->isElementNode() && toElement(node)->isFormControlElement()) 203 return toHTMLFormControlElement(node); 204 } 205 return 0; 206} 207 208bool HTMLFormElement::validateInteractively(Event* event) 209{ 210 ASSERT(event); 211 if (!document().page() || !document().page()->settings().interactiveFormValidationEnabled() || noValidate()) 212 return true; 213 214 HTMLFormControlElement* submitElement = submitElementFromEvent(event); 215 if (submitElement && submitElement->formNoValidate()) 216 return true; 217 218 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 219 if (m_associatedElements[i]->isFormControlElement()) 220 toHTMLFormControlElement(m_associatedElements[i])->hideVisibleValidationMessage(); 221 } 222 223 Vector<RefPtr<FormAssociatedElement>> unhandledInvalidControls; 224 if (!checkInvalidControlsAndCollectUnhandled(unhandledInvalidControls)) 225 return true; 226 // Because the form has invalid controls, we abort the form submission and 227 // show a validation message on a focusable form control. 228 229 // Needs to update layout now because we'd like to call isFocusable(), which 230 // has !renderer()->needsLayout() assertion. 231 document().updateLayoutIgnorePendingStylesheets(); 232 233 Ref<HTMLFormElement> protect(*this); 234 235 // Focus on the first focusable control and show a validation message. 236 for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) { 237 HTMLElement& element = unhandledInvalidControls[i]->asHTMLElement(); 238 if (element.inDocument() && element.isFocusable()) { 239 element.scrollIntoViewIfNeeded(false); 240 element.focus(); 241 if (element.isFormControlElement()) 242 toHTMLFormControlElement(element).updateVisibleValidationMessage(); 243 break; 244 } 245 } 246 247 // Warn about all of unfocusable controls. 248 if (document().frame()) { 249 for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) { 250 FormAssociatedElement& control = *unhandledInvalidControls[i]; 251 HTMLElement& element = control.asHTMLElement(); 252 if (element.inDocument() && element.isFocusable()) 253 continue; 254 String message("An invalid form control with name='%name' is not focusable."); 255 message.replace("%name", control.name()); 256 document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, message); 257 } 258 } 259 260 return false; 261} 262 263void HTMLFormElement::prepareForSubmission(Event* event) 264{ 265 Frame* frame = document().frame(); 266 if (m_isSubmittingOrPreparingForSubmission || !frame) 267 return; 268 269 m_isSubmittingOrPreparingForSubmission = true; 270 m_shouldSubmit = false; 271 272 // Interactive validation must be done before dispatching the submit event. 273 if (!validateInteractively(event)) { 274 m_isSubmittingOrPreparingForSubmission = false; 275 return; 276 } 277 278 StringPairVector controlNamesAndValues; 279 getTextFieldValues(controlNamesAndValues); 280 RefPtr<FormState> formState = FormState::create(this, controlNamesAndValues, &document(), NotSubmittedByJavaScript); 281 frame->loader().client().dispatchWillSendSubmitEvent(formState.release()); 282 283 Ref<HTMLFormElement> protect(*this); 284 // Event handling can result in m_shouldSubmit becoming true, regardless of dispatchEvent() return value. 285 if (dispatchEvent(Event::create(eventNames().submitEvent, true, true))) 286 m_shouldSubmit = true; 287 288 m_isSubmittingOrPreparingForSubmission = false; 289 290 if (m_shouldSubmit) 291 submit(event, true, true, NotSubmittedByJavaScript); 292} 293 294void HTMLFormElement::submit() 295{ 296 submit(0, false, true, NotSubmittedByJavaScript); 297} 298 299void HTMLFormElement::submitFromJavaScript() 300{ 301 submit(0, false, ScriptController::processingUserGesture(), SubmittedByJavaScript); 302} 303 304void HTMLFormElement::getTextFieldValues(StringPairVector& fieldNamesAndValues) const 305{ 306 ASSERT_ARG(fieldNamesAndValues, fieldNamesAndValues.isEmpty()); 307 308 fieldNamesAndValues.reserveCapacity(m_associatedElements.size()); 309 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 310 FormAssociatedElement& control = *m_associatedElements[i]; 311 HTMLElement& element = control.asHTMLElement(); 312 if (!isHTMLInputElement(element)) 313 continue; 314 HTMLInputElement& input = toHTMLInputElement(element); 315 if (!input.isTextField()) 316 continue; 317 fieldNamesAndValues.append(std::make_pair(input.name().string(), input.value())); 318 } 319} 320 321void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger formSubmissionTrigger) 322{ 323 FrameView* view = document().view(); 324 Frame* frame = document().frame(); 325 if (!view || !frame) 326 return; 327 328 if (m_isSubmittingOrPreparingForSubmission) { 329 m_shouldSubmit = true; 330 return; 331 } 332 333 m_isSubmittingOrPreparingForSubmission = true; 334 m_wasUserSubmitted = processingUserGesture; 335 336 RefPtr<HTMLFormControlElement> firstSuccessfulSubmitButton; 337 bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button? 338 339 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 340 FormAssociatedElement* associatedElement = m_associatedElements[i]; 341 if (!associatedElement->isFormControlElement()) 342 continue; 343 if (needButtonActivation) { 344 HTMLFormControlElement* control = toHTMLFormControlElement(associatedElement); 345 if (control->isActivatedSubmit()) 346 needButtonActivation = false; 347 else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton()) 348 firstSuccessfulSubmitButton = control; 349 } 350 } 351 352 if (needButtonActivation && firstSuccessfulSubmitButton) 353 firstSuccessfulSubmitButton->setActivatedSubmit(true); 354 355 LockHistory lockHistory = processingUserGesture ? LockHistory::No : LockHistory::Yes; 356 Ref<HTMLFormElement> protect(*this); // Form submission can execute arbitary JavaScript. 357 frame->loader().submitForm(FormSubmission::create(this, m_attributes, event, lockHistory, formSubmissionTrigger)); 358 359 if (needButtonActivation && firstSuccessfulSubmitButton) 360 firstSuccessfulSubmitButton->setActivatedSubmit(false); 361 362 m_shouldSubmit = false; 363 m_isSubmittingOrPreparingForSubmission = false; 364} 365 366void HTMLFormElement::reset() 367{ 368 Frame* frame = document().frame(); 369 if (m_isInResetFunction || !frame) 370 return; 371 372 m_isInResetFunction = true; 373 374 if (!dispatchEvent(Event::create(eventNames().resetEvent, true, true))) { 375 m_isInResetFunction = false; 376 return; 377 } 378 379 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 380 if (m_associatedElements[i]->isFormControlElement()) 381 toHTMLFormControlElement(m_associatedElements[i])->reset(); 382 } 383 384 m_isInResetFunction = false; 385} 386 387#if ENABLE(IOS_AUTOCORRECT_AND_AUTOCAPITALIZE) 388// FIXME: We should look to share these methods with class HTMLFormControlElement instead of duplicating them. 389 390bool HTMLFormElement::autocorrect() const 391{ 392 const AtomicString& autocorrectValue = fastGetAttribute(autocorrectAttr); 393 if (!autocorrectValue.isEmpty()) 394 return !equalIgnoringCase(autocorrectValue, "off"); 395 if (HTMLFormElement* form = this->form()) 396 return form->autocorrect(); 397 return true; 398} 399 400void HTMLFormElement::setAutocorrect(bool autocorrect) 401{ 402 setAttribute(autocorrectAttr, autocorrect ? AtomicString("on", AtomicString::ConstructFromLiteral) : AtomicString("off", AtomicString::ConstructFromLiteral)); 403} 404 405WebAutocapitalizeType HTMLFormElement::autocapitalizeType() const 406{ 407 return autocapitalizeTypeForAttributeValue(fastGetAttribute(autocapitalizeAttr)); 408} 409 410const AtomicString& HTMLFormElement::autocapitalize() const 411{ 412 return stringForAutocapitalizeType(autocapitalizeType()); 413} 414 415void HTMLFormElement::setAutocapitalize(const AtomicString& value) 416{ 417 setAttribute(autocapitalizeAttr, value); 418} 419#endif 420 421void HTMLFormElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 422{ 423 if (name == actionAttr) 424 m_attributes.parseAction(value); 425 else if (name == targetAttr) 426 m_attributes.setTarget(value); 427 else if (name == methodAttr) 428 m_attributes.updateMethodType(value); 429 else if (name == enctypeAttr) 430 m_attributes.updateEncodingType(value); 431 else if (name == accept_charsetAttr) 432 m_attributes.setAcceptCharset(value); 433 else if (name == autocompleteAttr) { 434 if (!shouldAutocomplete()) 435 document().registerForPageCacheSuspensionCallbacks(this); 436 else 437 document().unregisterForPageCacheSuspensionCallbacks(this); 438 } 439 else 440 HTMLElement::parseAttribute(name, value); 441} 442 443template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item) 444{ 445 size_t size = vec.size(); 446 for (size_t i = 0; i != size; ++i) 447 if (vec[i] == item) { 448 vec.remove(i); 449 break; 450 } 451} 452 453unsigned HTMLFormElement::formElementIndexWithFormAttribute(Element* element, unsigned rangeStart, unsigned rangeEnd) 454{ 455 if (m_associatedElements.isEmpty()) 456 return 0; 457 458 ASSERT(rangeStart <= rangeEnd); 459 460 if (rangeStart == rangeEnd) 461 return rangeStart; 462 463 unsigned left = rangeStart; 464 unsigned right = rangeEnd - 1; 465 unsigned short position; 466 467 // Does binary search on m_associatedElements in order to find the index 468 // to be inserted. 469 while (left != right) { 470 unsigned middle = left + ((right - left) / 2); 471 ASSERT(middle < m_associatedElementsBeforeIndex || middle >= m_associatedElementsAfterIndex); 472 position = element->compareDocumentPosition(&m_associatedElements[middle]->asHTMLElement()); 473 if (position & DOCUMENT_POSITION_FOLLOWING) 474 right = middle; 475 else 476 left = middle + 1; 477 } 478 479 ASSERT(left < m_associatedElementsBeforeIndex || left >= m_associatedElementsAfterIndex); 480 position = element->compareDocumentPosition(&m_associatedElements[left]->asHTMLElement()); 481 if (position & DOCUMENT_POSITION_FOLLOWING) 482 return left; 483 return left + 1; 484} 485 486unsigned HTMLFormElement::formElementIndex(FormAssociatedElement* associatedElement) 487{ 488 ASSERT(associatedElement); 489 490 HTMLElement& associatedHTMLElement = associatedElement->asHTMLElement(); 491 492 // Treats separately the case where this element has the form attribute 493 // for performance consideration. 494 if (associatedHTMLElement.fastHasAttribute(formAttr)) { 495 unsigned short position = compareDocumentPosition(&associatedHTMLElement); 496 if (position & DOCUMENT_POSITION_PRECEDING) { 497 ++m_associatedElementsBeforeIndex; 498 ++m_associatedElementsAfterIndex; 499 return HTMLFormElement::formElementIndexWithFormAttribute(&associatedHTMLElement, 0, m_associatedElementsBeforeIndex - 1); 500 } 501 if (position & DOCUMENT_POSITION_FOLLOWING && !(position & DOCUMENT_POSITION_CONTAINED_BY)) 502 return HTMLFormElement::formElementIndexWithFormAttribute(&associatedHTMLElement, m_associatedElementsAfterIndex, m_associatedElements.size()); 503 } 504 505 unsigned currentAssociatedElementsAfterIndex = m_associatedElementsAfterIndex; 506 ++m_associatedElementsAfterIndex; 507 508 if (!associatedHTMLElement.isDescendantOf(this)) 509 return currentAssociatedElementsAfterIndex; 510 511 // Check for the special case where this element is the very last thing in 512 // the form's tree of children; we don't want to walk the entire tree in that 513 // common case that occurs during parsing; instead we'll just return a value 514 // that says "add this form element to the end of the array". 515 auto descendants = descendantsOfType<HTMLElement>(*this); 516 auto it = descendants.beginAt(associatedHTMLElement); 517 auto end = descendants.end(); 518 if (++it == end) 519 return currentAssociatedElementsAfterIndex; 520 521 unsigned i = m_associatedElementsBeforeIndex; 522 for (auto& element : descendants) { 523 if (&element == &associatedHTMLElement) 524 return i; 525 if (!isHTMLFormControlElement(element) && !isHTMLObjectElement(element)) 526 continue; 527 if (element.form() != this) 528 continue; 529 ++i; 530 } 531 return currentAssociatedElementsAfterIndex; 532} 533 534void HTMLFormElement::registerFormElement(FormAssociatedElement* e) 535{ 536 m_associatedElements.insert(formElementIndex(e), e); 537} 538 539void HTMLFormElement::removeFormElement(FormAssociatedElement* e) 540{ 541 unsigned index; 542 for (index = 0; index < m_associatedElements.size(); ++index) { 543 if (m_associatedElements[index] == e) 544 break; 545 } 546 ASSERT_WITH_SECURITY_IMPLICATION(index < m_associatedElements.size()); 547 if (index < m_associatedElementsBeforeIndex) 548 --m_associatedElementsBeforeIndex; 549 if (index < m_associatedElementsAfterIndex) 550 --m_associatedElementsAfterIndex; 551 removeFromPastNamesMap(e); 552 removeFromVector(m_associatedElements, e); 553} 554 555bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const 556{ 557 return attribute.name() == actionAttr || HTMLElement::isURLAttribute(attribute); 558} 559 560void HTMLFormElement::registerImgElement(HTMLImageElement* e) 561{ 562 ASSERT(m_imageElements.find(e) == notFound); 563 m_imageElements.append(e); 564} 565 566void HTMLFormElement::removeImgElement(HTMLImageElement* e) 567{ 568 ASSERT(m_imageElements.find(e) != notFound); 569 removeFromPastNamesMap(e); 570 removeFromVector(m_imageElements, e); 571} 572 573PassRefPtr<HTMLCollection> HTMLFormElement::elements() 574{ 575 return ensureCachedHTMLCollection(FormControls); 576} 577 578String HTMLFormElement::name() const 579{ 580 return getNameAttribute(); 581} 582 583bool HTMLFormElement::noValidate() const 584{ 585 return fastHasAttribute(novalidateAttr); 586} 587 588// FIXME: This function should be removed because it does not do the same thing as the 589// JavaScript binding for action, which treats action as a URL attribute. Last time I 590// (Darin Adler) removed this, someone added it back, so I am leaving it in for now. 591String HTMLFormElement::action() const 592{ 593 return getAttribute(actionAttr); 594} 595 596void HTMLFormElement::setAction(const String &value) 597{ 598 setAttribute(actionAttr, value); 599} 600 601void HTMLFormElement::setEnctype(const String &value) 602{ 603 setAttribute(enctypeAttr, value); 604} 605 606String HTMLFormElement::method() const 607{ 608 return FormSubmission::Attributes::methodString(m_attributes.method()); 609} 610 611void HTMLFormElement::setMethod(const String &value) 612{ 613 setAttribute(methodAttr, value); 614} 615 616String HTMLFormElement::target() const 617{ 618 return getAttribute(targetAttr); 619} 620 621bool HTMLFormElement::wasUserSubmitted() const 622{ 623 return m_wasUserSubmitted; 624} 625 626HTMLFormControlElement* HTMLFormElement::defaultButton() const 627{ 628 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 629 if (!m_associatedElements[i]->isFormControlElement()) 630 continue; 631 HTMLFormControlElement* control = toHTMLFormControlElement(m_associatedElements[i]); 632 if (control->isSuccessfulSubmitButton()) 633 return control; 634 } 635 636 return 0; 637} 638 639bool HTMLFormElement::checkValidity() 640{ 641 Vector<RefPtr<FormAssociatedElement>> controls; 642 return !checkInvalidControlsAndCollectUnhandled(controls); 643} 644 645bool HTMLFormElement::checkInvalidControlsAndCollectUnhandled(Vector<RefPtr<FormAssociatedElement>>& unhandledInvalidControls) 646{ 647 Ref<HTMLFormElement> protect(*this); 648 // Copy m_associatedElements because event handlers called from 649 // HTMLFormControlElement::checkValidity() might change m_associatedElements. 650 Vector<RefPtr<FormAssociatedElement>> elements; 651 elements.reserveCapacity(m_associatedElements.size()); 652 for (unsigned i = 0; i < m_associatedElements.size(); ++i) 653 elements.append(m_associatedElements[i]); 654 bool hasInvalidControls = false; 655 for (unsigned i = 0; i < elements.size(); ++i) { 656 if (elements[i]->form() == this && elements[i]->isFormControlElement()) { 657 HTMLFormControlElement* control = toHTMLFormControlElement(elements[i].get()); 658 if (!control->checkValidity(&unhandledInvalidControls) && control->form() == this) 659 hasInvalidControls = true; 660 } 661 } 662 return hasInvalidControls; 663} 664 665#ifndef NDEBUG 666void HTMLFormElement::assertItemCanBeInPastNamesMap(FormNamedItem* item) const 667{ 668 ASSERT_WITH_SECURITY_IMPLICATION(item); 669 HTMLElement& element = item->asHTMLElement(); 670 ASSERT_WITH_SECURITY_IMPLICATION(element.form() == this); 671 672 if (item->isFormAssociatedElement()) { 673 ASSERT_WITH_SECURITY_IMPLICATION(m_associatedElements.find(static_cast<FormAssociatedElement*>(item)) != notFound); 674 return; 675 } 676 677 ASSERT_WITH_SECURITY_IMPLICATION(element.hasTagName(imgTag)); 678 ASSERT_WITH_SECURITY_IMPLICATION(m_imageElements.find(&toHTMLImageElement(element)) != notFound); 679} 680#else 681inline void HTMLFormElement::assertItemCanBeInPastNamesMap(FormNamedItem*) const 682{ 683} 684#endif 685 686HTMLElement* HTMLFormElement::elementFromPastNamesMap(const AtomicString& pastName) const 687{ 688 if (pastName.isEmpty() || !m_pastNamesMap) 689 return nullptr; 690 FormNamedItem* item = m_pastNamesMap->get(pastName.impl()); 691 if (!item) 692 return nullptr; 693 assertItemCanBeInPastNamesMap(item); 694 return &item->asHTMLElement(); 695} 696 697void HTMLFormElement::addToPastNamesMap(FormNamedItem* item, const AtomicString& pastName) 698{ 699 assertItemCanBeInPastNamesMap(item); 700 if (pastName.isEmpty()) 701 return; 702 if (!m_pastNamesMap) 703 m_pastNamesMap = std::make_unique<PastNamesMap>(); 704 m_pastNamesMap->set(pastName.impl(), item); 705} 706 707void HTMLFormElement::removeFromPastNamesMap(FormNamedItem* item) 708{ 709 ASSERT(item); 710 if (!m_pastNamesMap) 711 return; 712 713 PastNamesMap::iterator end = m_pastNamesMap->end(); 714 for (PastNamesMap::iterator it = m_pastNamesMap->begin(); it != end; ++it) { 715 if (it->value == item) 716 it->value = 0; // Keep looping. Single element can have multiple names. 717 } 718} 719 720bool HTMLFormElement::hasNamedElement(const AtomicString& name) 721{ 722 return elements()->hasNamedItem(name) || elementFromPastNamesMap(name); 723} 724 725// FIXME: Use RefPtr<HTMLElement> for namedItems. elements()->namedItems never return non-HTMLElement nodes. 726void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<Ref<Element>>& namedItems) 727{ 728 // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem 729 elements()->namedItems(name, namedItems); 730 731 HTMLElement* elementFromPast = elementFromPastNamesMap(name); 732 if (namedItems.size() == 1 && &namedItems.first().get() != elementFromPast) 733 addToPastNamesMap(toHTMLElement(&namedItems.first().get())->asFormNamedItem(), name); 734 else if (elementFromPast && namedItems.isEmpty()) 735 namedItems.append(*elementFromPast); 736} 737 738void HTMLFormElement::documentDidResumeFromPageCache() 739{ 740 ASSERT(!shouldAutocomplete()); 741 742 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 743 if (m_associatedElements[i]->isFormControlElement()) 744 toHTMLFormControlElement(m_associatedElements[i])->reset(); 745 } 746} 747 748void HTMLFormElement::didMoveToNewDocument(Document* oldDocument) 749{ 750 if (!shouldAutocomplete()) { 751 if (oldDocument) 752 oldDocument->unregisterForPageCacheSuspensionCallbacks(this); 753 document().registerForPageCacheSuspensionCallbacks(this); 754 } 755 756 HTMLElement::didMoveToNewDocument(oldDocument); 757} 758 759bool HTMLFormElement::shouldAutocomplete() const 760{ 761 return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off"); 762} 763 764void HTMLFormElement::finishParsingChildren() 765{ 766 HTMLElement::finishParsingChildren(); 767 document().formController().restoreControlStateIn(*this); 768} 769 770void HTMLFormElement::copyNonAttributePropertiesFromElement(const Element& source) 771{ 772 m_wasDemoted = static_cast<const HTMLFormElement&>(source).m_wasDemoted; 773 HTMLElement::copyNonAttributePropertiesFromElement(source); 774} 775 776HTMLFormElement* HTMLFormElement::findClosestFormAncestor(const Element& startElement) 777{ 778 return const_cast<HTMLFormElement*>(ancestorsOfType<HTMLFormElement>(startElement).first()); 779} 780 781} // namespace 782