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