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 "DOMFormData.h" 30#include "DOMWindow.h" 31#include "Document.h" 32#include "Event.h" 33#include "EventNames.h" 34#include "FileList.h" 35#include "FileSystem.h" 36#include "FormData.h" 37#include "FormDataList.h" 38#include "FormState.h" 39#include "Frame.h" 40#include "FrameLoader.h" 41#include "FrameLoaderClient.h" 42#include "HTMLDocument.h" 43#include "HTMLFormCollection.h" 44#include "HTMLImageElement.h" 45#include "HTMLInputElement.h" 46#include "HTMLNames.h" 47#include "MIMETypeRegistry.h" 48#include "Page.h" 49#include "RenderTextControl.h" 50#include "ScriptEventListener.h" 51#include "Settings.h" 52#include "ValidityState.h" 53#include <limits> 54 55#if PLATFORM(WX) 56#include <wx/defs.h> 57#include <wx/filename.h> 58#endif 59 60using namespace std; 61 62namespace WebCore { 63 64using namespace HTMLNames; 65 66HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* document) 67 : HTMLElement(tagName, document) 68 , m_associatedElementsBeforeIndex(0) 69 , m_associatedElementsAfterIndex(0) 70 , m_wasUserSubmitted(false) 71 , m_isSubmittingOrPreparingForSubmission(false) 72 , m_shouldSubmit(false) 73 , m_isInResetFunction(false) 74 , m_wasMalformed(false) 75 , m_wasDemoted(false) 76{ 77 ASSERT(hasTagName(formTag)); 78} 79 80PassRefPtr<HTMLFormElement> HTMLFormElement::create(Document* document) 81{ 82 return adoptRef(new HTMLFormElement(formTag, document)); 83} 84 85PassRefPtr<HTMLFormElement> HTMLFormElement::create(const QualifiedName& tagName, Document* document) 86{ 87 return adoptRef(new HTMLFormElement(tagName, document)); 88} 89 90HTMLFormElement::~HTMLFormElement() 91{ 92 if (!autoComplete()) 93 document()->unregisterForDocumentActivationCallbacks(this); 94 95 for (unsigned i = 0; i < m_associatedElements.size(); ++i) 96 m_associatedElements[i]->formDestroyed(); 97 for (unsigned i = 0; i < m_imageElements.size(); ++i) 98 m_imageElements[i]->m_form = 0; 99} 100 101bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url) 102{ 103 return document()->completeURL(url).protocolIs("https"); 104} 105 106bool HTMLFormElement::rendererIsNeeded(RenderStyle* style) 107{ 108 if (!m_wasDemoted) 109 return HTMLElement::rendererIsNeeded(style); 110 111 ContainerNode* node = parentNode(); 112 RenderObject* parentRenderer = node->renderer(); 113 bool parentIsTableElementPart = (parentRenderer->isTable() && node->hasTagName(tableTag)) 114 || (parentRenderer->isTableRow() && node->hasTagName(trTag)) 115 || (parentRenderer->isTableSection() && node->hasTagName(tbodyTag)) 116 || (parentRenderer->isTableCol() && node->hasTagName(colTag)) 117 || (parentRenderer->isTableCell() && node->hasTagName(trTag)); 118 119 if (!parentIsTableElementPart) 120 return true; 121 122 EDisplay display = style->display(); 123 bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP 124 || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW 125 || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL 126 || display == TABLE_CAPTION; 127 128 return formIsTablePart; 129} 130 131void HTMLFormElement::insertedIntoDocument() 132{ 133 if (document()->isHTMLDocument()) 134 static_cast<HTMLDocument*>(document())->addNamedItem(m_name); 135 136 HTMLElement::insertedIntoDocument(); 137 138 if (hasID()) 139 document()->resetFormElementsOwner(this); 140} 141 142void HTMLFormElement::removedFromDocument() 143{ 144 if (document()->isHTMLDocument()) 145 static_cast<HTMLDocument*>(document())->removeNamedItem(m_name); 146 147 HTMLElement::removedFromDocument(); 148 149 if (hasID()) 150 document()->resetFormElementsOwner(0); 151} 152 153void HTMLFormElement::handleLocalEvents(Event* event) 154{ 155 Node* targetNode = event->target()->toNode(); 156 if (event->eventPhase() != Event::CAPTURING_PHASE && targetNode && targetNode != this && (event->type() == eventNames().submitEvent || event->type() == eventNames().resetEvent)) { 157 event->stopPropagation(); 158 return; 159 } 160 HTMLElement::handleLocalEvents(event); 161} 162 163unsigned HTMLFormElement::length() const 164{ 165 unsigned len = 0; 166 for (unsigned i = 0; i < m_associatedElements.size(); ++i) 167 if (m_associatedElements[i]->isEnumeratable()) 168 ++len; 169 return len; 170} 171 172Node* HTMLFormElement::item(unsigned index) 173{ 174 return elements()->item(index); 175} 176 177void HTMLFormElement::submitImplicitly(Event* event, bool fromImplicitSubmissionTrigger) 178{ 179 int submissionTriggerCount = 0; 180 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 181 FormAssociatedElement* formAssociatedElement = m_associatedElements[i]; 182 if (!formAssociatedElement->isFormControlElement()) 183 continue; 184 HTMLFormControlElement* formElement = static_cast<HTMLFormControlElement*>(formAssociatedElement); 185 if (formElement->isSuccessfulSubmitButton()) { 186 if (formElement->renderer()) { 187 formElement->dispatchSimulatedClick(event); 188 return; 189 } 190 } else if (formElement->canTriggerImplicitSubmission()) 191 ++submissionTriggerCount; 192 } 193 if (fromImplicitSubmissionTrigger && submissionTriggerCount == 1) 194 prepareForSubmission(event); 195} 196 197static inline HTMLFormControlElement* submitElementFromEvent(const Event* event) 198{ 199 Node* targetNode = event->target()->toNode(); 200 if (!targetNode || !targetNode->isElementNode()) 201 return 0; 202 Element* targetElement = static_cast<Element*>(targetNode); 203 if (!targetElement->isFormControlElement()) 204 return 0; 205 return static_cast<HTMLFormControlElement*>(targetElement); 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 static_cast<HTMLFormControlElement*>(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 RefPtr<HTMLFormElement> protector(this); 234 // Focus on the first focusable control and show a validation message. 235 for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) { 236 FormAssociatedElement* unhandledAssociatedElement = unhandledInvalidControls[i].get(); 237 HTMLElement* unhandled = toHTMLElement(unhandledAssociatedElement); 238 if (unhandled->isFocusable() && unhandled->inDocument()) { 239 unhandled->scrollIntoViewIfNeeded(false); 240 unhandled->focus(); 241 if (unhandled->isFormControlElement()) 242 static_cast<HTMLFormControlElement*>(unhandled)->updateVisibleValidationMessage(); 243 break; 244 } 245 } 246 // Warn about all of unfocusable controls. 247 if (Frame* frame = document()->frame()) { 248 for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) { 249 FormAssociatedElement* unhandledAssociatedElement = unhandledInvalidControls[i].get(); 250 HTMLElement* unhandled = toHTMLElement(unhandledAssociatedElement); 251 if (unhandled->isFocusable() && unhandled->inDocument()) 252 continue; 253 String message("An invalid form control with name='%name' is not focusable."); 254 message.replace("%name", unhandledAssociatedElement->name()); 255 frame->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, ErrorMessageLevel, message, 0, document()->url().string()); 256 } 257 } 258 return false; 259} 260 261bool HTMLFormElement::prepareForSubmission(Event* event) 262{ 263 Frame* frame = document()->frame(); 264 if (m_isSubmittingOrPreparingForSubmission || !frame) 265 return m_isSubmittingOrPreparingForSubmission; 266 267 m_isSubmittingOrPreparingForSubmission = true; 268 m_shouldSubmit = false; 269 270 // Interactive validation must be done before dispatching the submit event. 271 if (!validateInteractively(event)) { 272 m_isSubmittingOrPreparingForSubmission = false; 273 return false; 274 } 275 276 frame->loader()->client()->dispatchWillSendSubmitEvent(this); 277 278 if (dispatchEvent(Event::create(eventNames().submitEvent, true, true))) 279 m_shouldSubmit = true; 280 281 m_isSubmittingOrPreparingForSubmission = false; 282 283 if (m_shouldSubmit) 284 submit(event, true, true, NotSubmittedByJavaScript); 285 286 return m_shouldSubmit; 287} 288 289void HTMLFormElement::submit() 290{ 291 submit(0, false, true, NotSubmittedByJavaScript); 292} 293 294void HTMLFormElement::submitFromJavaScript() 295{ 296 Frame* frame = document()->frame(); 297 if (!frame) 298 return; 299 submit(0, false, frame->script()->anyPageIsProcessingUserGesture(), SubmittedByJavaScript); 300} 301 302void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger formSubmissionTrigger) 303{ 304 FrameView* view = document()->view(); 305 Frame* frame = document()->frame(); 306 if (!view || !frame) 307 return; 308 309 if (m_isSubmittingOrPreparingForSubmission) { 310 m_shouldSubmit = true; 311 return; 312 } 313 314 m_isSubmittingOrPreparingForSubmission = true; 315 m_wasUserSubmitted = processingUserGesture; 316 317 HTMLFormControlElement* firstSuccessfulSubmitButton = 0; 318 bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button? 319 320 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 321 FormAssociatedElement* associatedElement = m_associatedElements[i]; 322 if (!associatedElement->isFormControlElement()) 323 continue; 324 if (needButtonActivation) { 325 HTMLFormControlElement* control = static_cast<HTMLFormControlElement*>(associatedElement); 326 if (control->isActivatedSubmit()) 327 needButtonActivation = false; 328 else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton()) 329 firstSuccessfulSubmitButton = control; 330 } 331 } 332 333 if (needButtonActivation && firstSuccessfulSubmitButton) 334 firstSuccessfulSubmitButton->setActivatedSubmit(true); 335 336 frame->loader()->submitForm(FormSubmission::create(this, m_attributes, event, !processingUserGesture, formSubmissionTrigger)); 337 338 if (needButtonActivation && firstSuccessfulSubmitButton) 339 firstSuccessfulSubmitButton->setActivatedSubmit(false); 340 341 m_shouldSubmit = false; 342 m_isSubmittingOrPreparingForSubmission = false; 343} 344 345void HTMLFormElement::reset() 346{ 347 Frame* frame = document()->frame(); 348 if (m_isInResetFunction || !frame) 349 return; 350 351 m_isInResetFunction = true; 352 353 if (!dispatchEvent(Event::create(eventNames().resetEvent, true, true))) { 354 m_isInResetFunction = false; 355 return; 356 } 357 358 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 359 if (m_associatedElements[i]->isFormControlElement()) 360 static_cast<HTMLFormControlElement*>(m_associatedElements[i])->reset(); 361 } 362 363 m_isInResetFunction = false; 364} 365 366void HTMLFormElement::parseMappedAttribute(Attribute* attr) 367{ 368 if (attr->name() == actionAttr) 369 m_attributes.parseAction(attr->value()); 370 else if (attr->name() == targetAttr) 371 m_attributes.setTarget(attr->value()); 372 else if (attr->name() == methodAttr) 373 m_attributes.parseMethodType(attr->value()); 374 else if (attr->name() == enctypeAttr) 375 m_attributes.parseEncodingType(attr->value()); 376 else if (attr->name() == accept_charsetAttr) 377 m_attributes.setAcceptCharset(attr->value()); 378 else if (attr->name() == autocompleteAttr) { 379 if (!autoComplete()) 380 document()->registerForDocumentActivationCallbacks(this); 381 else 382 document()->unregisterForDocumentActivationCallbacks(this); 383 } else if (attr->name() == onsubmitAttr) 384 setAttributeEventListener(eventNames().submitEvent, createAttributeEventListener(this, attr)); 385 else if (attr->name() == onresetAttr) 386 setAttributeEventListener(eventNames().resetEvent, createAttributeEventListener(this, attr)); 387 else if (attr->name() == nameAttr) { 388 const AtomicString& newName = attr->value(); 389 if (inDocument() && document()->isHTMLDocument()) { 390 HTMLDocument* document = static_cast<HTMLDocument*>(this->document()); 391 document->removeNamedItem(m_name); 392 document->addNamedItem(newName); 393 } 394 m_name = newName; 395 } else 396 HTMLElement::parseMappedAttribute(attr); 397} 398 399template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item) 400{ 401 size_t size = vec.size(); 402 for (size_t i = 0; i != size; ++i) 403 if (vec[i] == item) { 404 vec.remove(i); 405 break; 406 } 407} 408 409unsigned HTMLFormElement::formElementIndexWithFormAttribute(Element* element) 410{ 411 // Compares the position of the form element and the inserted element. 412 // Updates the indeces in order to the relation of the position: 413 unsigned short position = compareDocumentPosition(element); 414 if (position & (DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_CONTAINED_BY)) 415 ++m_associatedElementsAfterIndex; 416 else if (position & DOCUMENT_POSITION_PRECEDING) { 417 ++m_associatedElementsBeforeIndex; 418 ++m_associatedElementsAfterIndex; 419 } 420 421 if (m_associatedElements.isEmpty()) 422 return 0; 423 424 // Does binary search on m_associatedElements in order to find the index 425 // to be inserted. 426 unsigned left = 0, right = m_associatedElements.size() - 1; 427 while (left != right) { 428 unsigned middle = left + ((right - left) / 2); 429 position = element->compareDocumentPosition(toHTMLElement(m_associatedElements[middle])); 430 if (position & DOCUMENT_POSITION_FOLLOWING) 431 right = middle; 432 else 433 left = middle + 1; 434 } 435 436 position = element->compareDocumentPosition(toHTMLElement(m_associatedElements[left])); 437 if (position & DOCUMENT_POSITION_FOLLOWING) 438 return left; 439 return left + 1; 440} 441 442unsigned HTMLFormElement::formElementIndex(FormAssociatedElement* associatedElement) 443{ 444 HTMLElement* element = toHTMLElement(associatedElement); 445 // Treats separately the case where this element has the form attribute 446 // for performance consideration. 447 if (element->fastHasAttribute(formAttr)) 448 return formElementIndexWithFormAttribute(element); 449 450 // Check for the special case where this element is the very last thing in 451 // the form's tree of children; we don't want to walk the entire tree in that 452 // common case that occurs during parsing; instead we'll just return a value 453 // that says "add this form element to the end of the array". 454 if (element->traverseNextNode(this)) { 455 unsigned i = m_associatedElementsBeforeIndex; 456 for (Node* node = this; node; node = node->traverseNextNode(this)) { 457 if (node == element) { 458 ++m_associatedElementsAfterIndex; 459 return i; 460 } 461 if (node->isHTMLElement() 462 && (static_cast<Element*>(node)->isFormControlElement() 463 || node->hasTagName(objectTag)) 464 && toHTMLElement(node)->form() == this) 465 ++i; 466 } 467 } 468 return m_associatedElementsAfterIndex++; 469} 470 471void HTMLFormElement::registerFormElement(FormAssociatedElement* e) 472{ 473 if (e->isFormControlElement()) { 474 HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(e); 475 document()->checkedRadioButtons().removeButton(element); 476 m_checkedRadioButtons.addButton(element); 477 } 478 m_associatedElements.insert(formElementIndex(e), e); 479} 480 481void HTMLFormElement::removeFormElement(FormAssociatedElement* e) 482{ 483 if (e->isFormControlElement()) 484 m_checkedRadioButtons.removeButton(static_cast<HTMLFormControlElement*>(e)); 485 unsigned index; 486 for (index = 0; index < m_associatedElements.size(); ++index) { 487 if (m_associatedElements[index] == e) 488 break; 489 } 490 ASSERT(index < m_associatedElements.size()); 491 if (index < m_associatedElementsBeforeIndex) 492 --m_associatedElementsBeforeIndex; 493 if (index < m_associatedElementsAfterIndex) 494 --m_associatedElementsAfterIndex; 495 removeFromVector(m_associatedElements, e); 496} 497 498bool HTMLFormElement::isURLAttribute(Attribute* attr) const 499{ 500 return attr->name() == actionAttr; 501} 502 503void HTMLFormElement::registerImgElement(HTMLImageElement* e) 504{ 505 ASSERT(m_imageElements.find(e) == notFound); 506 m_imageElements.append(e); 507} 508 509void HTMLFormElement::removeImgElement(HTMLImageElement* e) 510{ 511 ASSERT(m_imageElements.find(e) != notFound); 512 removeFromVector(m_imageElements, e); 513} 514 515PassRefPtr<HTMLCollection> HTMLFormElement::elements() 516{ 517 return HTMLFormCollection::create(this); 518} 519 520String HTMLFormElement::name() const 521{ 522 return getAttribute(nameAttr); 523} 524 525bool HTMLFormElement::noValidate() const 526{ 527 return fastHasAttribute(novalidateAttr); 528} 529 530// FIXME: This function should be removed because it does not do the same thing as the 531// JavaScript binding for action, which treats action as a URL attribute. Last time I 532// (Darin Adler) removed this, someone added it back, so I am leaving it in for now. 533String HTMLFormElement::action() const 534{ 535 return getAttribute(actionAttr); 536} 537 538void HTMLFormElement::setAction(const String &value) 539{ 540 setAttribute(actionAttr, value); 541} 542 543void HTMLFormElement::setEnctype(const String &value) 544{ 545 setAttribute(enctypeAttr, value); 546} 547 548String HTMLFormElement::method() const 549{ 550 return getAttribute(methodAttr); 551} 552 553void HTMLFormElement::setMethod(const String &value) 554{ 555 setAttribute(methodAttr, value); 556} 557 558String HTMLFormElement::target() const 559{ 560 return getAttribute(targetAttr); 561} 562 563bool HTMLFormElement::wasUserSubmitted() const 564{ 565 return m_wasUserSubmitted; 566} 567 568HTMLFormControlElement* HTMLFormElement::defaultButton() const 569{ 570 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 571 if (!m_associatedElements[i]->isFormControlElement()) 572 continue; 573 HTMLFormControlElement* control = static_cast<HTMLFormControlElement*>(m_associatedElements[i]); 574 if (control->isSuccessfulSubmitButton()) 575 return control; 576 } 577 578 return 0; 579} 580 581bool HTMLFormElement::checkValidity() 582{ 583 Vector<RefPtr<FormAssociatedElement> > controls; 584 return !checkInvalidControlsAndCollectUnhandled(controls); 585} 586 587bool HTMLFormElement::checkInvalidControlsAndCollectUnhandled(Vector<RefPtr<FormAssociatedElement> >& unhandledInvalidControls) 588{ 589 RefPtr<HTMLFormElement> protector(this); 590 // Copy m_associatedElements because event handlers called from 591 // HTMLFormControlElement::checkValidity() might change m_associatedElements. 592 Vector<RefPtr<FormAssociatedElement> > elements; 593 elements.reserveCapacity(m_associatedElements.size()); 594 for (unsigned i = 0; i < m_associatedElements.size(); ++i) 595 elements.append(m_associatedElements[i]); 596 bool hasInvalidControls = false; 597 for (unsigned i = 0; i < elements.size(); ++i) { 598 if (elements[i]->form() == this && elements[i]->isFormControlElement()) { 599 HTMLFormControlElement* control = static_cast<HTMLFormControlElement*>(elements[i].get()); 600 if (!control->checkValidity(&unhandledInvalidControls) && control->form() == this) 601 hasInvalidControls = true; 602 } 603 } 604 return hasInvalidControls; 605} 606 607HTMLFormControlElement* HTMLFormElement::elementForAlias(const AtomicString& alias) 608{ 609 if (alias.isEmpty() || !m_elementAliases) 610 return 0; 611 return m_elementAliases->get(alias.impl()).get(); 612} 613 614void HTMLFormElement::addElementAlias(HTMLFormControlElement* element, const AtomicString& alias) 615{ 616 if (alias.isEmpty()) 617 return; 618 if (!m_elementAliases) 619 m_elementAliases = adoptPtr(new AliasMap); 620 m_elementAliases->set(alias.impl(), element); 621} 622 623void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems) 624{ 625 elements()->namedItems(name, namedItems); 626 627 HTMLFormControlElement* aliasElement = elementForAlias(name); 628 if (aliasElement) { 629 if (namedItems.find(aliasElement) == notFound) { 630 // We have seen it before but it is gone now. Still, we need to return it. 631 // FIXME: The above comment is not clear enough; it does not say why we need to do this. 632 namedItems.append(aliasElement); 633 } 634 } 635 if (namedItems.size() && namedItems.first() != aliasElement) 636 addElementAlias(static_cast<HTMLFormControlElement*>(namedItems.first().get()), name); 637} 638 639void HTMLFormElement::documentDidBecomeActive() 640{ 641 ASSERT(!autoComplete()); 642 643 for (unsigned i = 0; i < m_associatedElements.size(); ++i) { 644 if (m_associatedElements[i]->isFormControlElement()) 645 static_cast<HTMLFormControlElement*>(m_associatedElements[i])->reset(); 646 } 647} 648 649void HTMLFormElement::willMoveToNewOwnerDocument() 650{ 651 if (!autoComplete()) 652 document()->unregisterForDocumentActivationCallbacks(this); 653 HTMLElement::willMoveToNewOwnerDocument(); 654} 655 656void HTMLFormElement::didMoveToNewOwnerDocument() 657{ 658 if (!autoComplete()) 659 document()->registerForDocumentActivationCallbacks(this); 660 HTMLElement::didMoveToNewOwnerDocument(); 661} 662 663bool HTMLFormElement::autoComplete() const 664{ 665 return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off"); 666} 667 668} // namespace 669