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 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 "HTMLFormControlElement.h" 27 28#include "Attribute.h" 29#include "Chrome.h" 30#include "ChromeClient.h" 31#include "Document.h" 32#include "DocumentParser.h" 33#include "ElementRareData.h" 34#include "Event.h" 35#include "EventHandler.h" 36#include "EventNames.h" 37#include "Frame.h" 38#include "HTMLFormElement.h" 39#include "HTMLInputElement.h" 40#include "HTMLNames.h" 41#include "LabelsNodeList.h" 42#include "Page.h" 43#include "RenderBox.h" 44#include "RenderTextControl.h" 45#include "RenderTheme.h" 46#include "ScriptEventListener.h" 47#include "ValidationMessage.h" 48#include "ValidityState.h" 49#include <limits> 50#include <wtf/Vector.h> 51#include <wtf/unicode/CharacterNames.h> 52 53namespace WebCore { 54 55using namespace HTMLNames; 56using namespace std; 57 58HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) 59 : HTMLElement(tagName, document) 60 , FormAssociatedElement(form) 61 , m_disabled(false) 62 , m_readOnly(false) 63 , m_required(false) 64 , m_valueMatchesRenderer(false) 65 , m_willValidateInitialized(false) 66 , m_willValidate(true) 67 , m_isValid(true) 68 , m_wasChangedSinceLastFormControlChangeEvent(false) 69{ 70 if (!this->form()) 71 setForm(findFormAncestor()); 72 if (this->form()) 73 this->form()->registerFormElement(this); 74} 75 76HTMLFormControlElement::~HTMLFormControlElement() 77{ 78 if (form()) 79 form()->removeFormElement(this); 80} 81 82void HTMLFormControlElement::detach() 83{ 84 m_validationMessage = 0; 85 HTMLElement::detach(); 86} 87 88bool HTMLFormControlElement::formNoValidate() const 89{ 90 return fastHasAttribute(formnovalidateAttr); 91} 92 93void HTMLFormControlElement::parseMappedAttribute(Attribute* attr) 94{ 95 if (attr->name() == disabledAttr) { 96 bool oldDisabled = m_disabled; 97 m_disabled = !attr->isNull(); 98 if (oldDisabled != m_disabled) { 99 setNeedsStyleRecalc(); 100 if (renderer() && renderer()->style()->hasAppearance()) 101 renderer()->theme()->stateChanged(renderer(), EnabledState); 102 } 103 } else if (attr->name() == readonlyAttr) { 104 bool oldReadOnly = m_readOnly; 105 m_readOnly = !attr->isNull(); 106 if (oldReadOnly != m_readOnly) { 107 setNeedsStyleRecalc(); 108 if (renderer() && renderer()->style()->hasAppearance()) 109 renderer()->theme()->stateChanged(renderer(), ReadOnlyState); 110 } 111 } else if (attr->name() == requiredAttr) { 112 bool oldRequired = m_required; 113 m_required = !attr->isNull(); 114 if (oldRequired != m_required) { 115 setNeedsValidityCheck(); 116 setNeedsStyleRecalc(); // Updates for :required :optional classes. 117 } 118 } else 119 HTMLElement::parseMappedAttribute(attr); 120 setNeedsWillValidateCheck(); 121} 122 123static bool shouldAutofocus(HTMLFormControlElement* element) 124{ 125 if (!element->autofocus()) 126 return false; 127 if (!element->renderer()) 128 return false; 129 if (element->document()->ignoreAutofocus()) 130 return false; 131 if (element->isReadOnlyFormControl()) 132 return false; 133 134 // FIXME: Should this set of hasTagName checks be replaced by a 135 // virtual member function? 136 if (element->hasTagName(inputTag)) 137 return !static_cast<HTMLInputElement*>(element)->isInputTypeHidden(); 138 if (element->hasTagName(selectTag)) 139 return true; 140 if (element->hasTagName(keygenTag)) 141 return true; 142 if (element->hasTagName(buttonTag)) 143 return true; 144 if (element->hasTagName(textareaTag)) 145 return true; 146 147 return false; 148} 149 150static void focusPostAttach(Node* element) 151{ 152 static_cast<Element*>(element)->focus(); 153 element->deref(); 154} 155 156void HTMLFormControlElement::attach() 157{ 158 ASSERT(!attached()); 159 160 suspendPostAttachCallbacks(); 161 162 HTMLElement::attach(); 163 164 // The call to updateFromElement() needs to go after the call through 165 // to the base class's attach() because that can sometimes do a close 166 // on the renderer. 167 if (renderer()) 168 renderer()->updateFromElement(); 169 170 if (shouldAutofocus(this)) { 171 ref(); 172 queuePostAttachCallback(focusPostAttach, this); 173 } 174 175 resumePostAttachCallbacks(); 176} 177 178void HTMLFormControlElement::willMoveToNewOwnerDocument() 179{ 180 FormAssociatedElement::willMoveToNewOwnerDocument(); 181 HTMLElement::willMoveToNewOwnerDocument(); 182} 183 184void HTMLFormControlElement::insertedIntoTree(bool deep) 185{ 186 FormAssociatedElement::insertedIntoTree(); 187 if (!form()) 188 document()->checkedRadioButtons().addButton(this); 189 190 HTMLElement::insertedIntoTree(deep); 191} 192 193void HTMLFormControlElement::removedFromTree(bool deep) 194{ 195 FormAssociatedElement::removedFromTree(); 196 HTMLElement::removedFromTree(deep); 197} 198 199void HTMLFormControlElement::insertedIntoDocument() 200{ 201 HTMLElement::insertedIntoDocument(); 202 FormAssociatedElement::insertedIntoDocument(); 203} 204 205void HTMLFormControlElement::removedFromDocument() 206{ 207 HTMLElement::removedFromDocument(); 208 FormAssociatedElement::removedFromDocument(); 209} 210 211const AtomicString& HTMLFormControlElement::formControlName() const 212{ 213 const AtomicString& name = fastGetAttribute(nameAttr); 214 return name.isNull() ? emptyAtom : name; 215} 216 217void HTMLFormControlElement::setName(const AtomicString& value) 218{ 219 setAttribute(nameAttr, value); 220} 221 222bool HTMLFormControlElement::wasChangedSinceLastFormControlChangeEvent() const 223{ 224 return m_wasChangedSinceLastFormControlChangeEvent; 225} 226 227void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed) 228{ 229 m_wasChangedSinceLastFormControlChangeEvent = changed; 230} 231 232void HTMLFormControlElement::dispatchFormControlChangeEvent() 233{ 234 HTMLElement::dispatchChangeEvent(); 235 setChangedSinceLastFormControlChangeEvent(false); 236} 237 238void HTMLFormControlElement::dispatchFormControlInputEvent() 239{ 240 setChangedSinceLastFormControlChangeEvent(true); 241 HTMLElement::dispatchInputEvent(); 242} 243 244void HTMLFormControlElement::setDisabled(bool b) 245{ 246 setAttribute(disabledAttr, b ? "" : 0); 247} 248 249bool HTMLFormControlElement::autofocus() const 250{ 251 return hasAttribute(autofocusAttr); 252} 253 254bool HTMLFormControlElement::required() const 255{ 256 return m_required; 257} 258 259static void updateFromElementCallback(Node* node) 260{ 261 ASSERT_ARG(node, node->isElementNode()); 262 ASSERT_ARG(node, static_cast<Element*>(node)->isFormControlElement()); 263 ASSERT(node->renderer()); 264 if (RenderObject* renderer = node->renderer()) 265 renderer->updateFromElement(); 266} 267 268void HTMLFormControlElement::recalcStyle(StyleChange change) 269{ 270 HTMLElement::recalcStyle(change); 271 272 // updateFromElement() can cause the selection to change, and in turn 273 // trigger synchronous layout, so it must not be called during style recalc. 274 if (renderer()) 275 queuePostAttachCallback(updateFromElementCallback, this); 276} 277 278bool HTMLFormControlElement::supportsFocus() const 279{ 280 return !m_disabled; 281} 282 283bool HTMLFormControlElement::isFocusable() const 284{ 285 if (!renderer() || 286 !renderer()->isBox() || toRenderBox(renderer())->size().isEmpty()) 287 return false; 288 // HTMLElement::isFocusable handles visibility and calls suportsFocus which 289 // will cover the disabled case. 290 return HTMLElement::isFocusable(); 291} 292 293bool HTMLFormControlElement::isKeyboardFocusable(KeyboardEvent* event) const 294{ 295 if (isFocusable()) 296 if (document()->frame()) 297 return document()->frame()->eventHandler()->tabsToAllFormControls(event); 298 return false; 299} 300 301bool HTMLFormControlElement::isMouseFocusable() const 302{ 303#if PLATFORM(GTK) || PLATFORM(QT) 304 return HTMLElement::isMouseFocusable(); 305#else 306 return false; 307#endif 308} 309 310short HTMLFormControlElement::tabIndex() const 311{ 312 // Skip the supportsFocus check in HTMLElement. 313 return Element::tabIndex(); 314} 315 316bool HTMLFormControlElement::recalcWillValidate() const 317{ 318 // FIXME: Should return false if this element has a datalist element as an 319 // ancestor. See HTML5 4.10.10 'The datalist element.' 320 return !m_disabled && !m_readOnly; 321} 322 323bool HTMLFormControlElement::willValidate() const 324{ 325 if (!m_willValidateInitialized) { 326 m_willValidateInitialized = true; 327 m_willValidate = recalcWillValidate(); 328 } else { 329 // If the following assertion fails, setNeedsWillValidateCheck() is not 330 // called correctly when something which changes recalcWillValidate() result 331 // is updated. 332 ASSERT(m_willValidate == recalcWillValidate()); 333 } 334 return m_willValidate; 335} 336 337void HTMLFormControlElement::setNeedsWillValidateCheck() 338{ 339 // We need to recalculate willValidte immediately because willValidate 340 // change can causes style change. 341 bool newWillValidate = recalcWillValidate(); 342 if (m_willValidateInitialized && m_willValidate == newWillValidate) 343 return; 344 m_willValidateInitialized = true; 345 m_willValidate = newWillValidate; 346 setNeedsStyleRecalc(); 347 if (!m_willValidate) 348 hideVisibleValidationMessage(); 349} 350 351String HTMLFormControlElement::validationMessage() 352{ 353 return validity()->validationMessage(); 354} 355 356void HTMLFormControlElement::updateVisibleValidationMessage() 357{ 358 Page* page = document()->page(); 359 if (!page) 360 return; 361 String message; 362 if (renderer() && willValidate()) { 363 message = validationMessage().stripWhiteSpace(); 364 // HTML5 specification doesn't ask UA to show the title attribute value 365 // with the validationMessage. However, this behavior is same as Opera 366 // and the specification describes such behavior as an example. 367 const AtomicString& title = getAttribute(titleAttr); 368 if (!message.isEmpty() && !title.isEmpty()) { 369 message.append('\n'); 370 message.append(title); 371 } 372 } 373 if (message.isEmpty()) { 374 hideVisibleValidationMessage(); 375 return; 376 } 377 if (!m_validationMessage) { 378 m_validationMessage = ValidationMessage::create(this); 379 m_validationMessage->setMessage(message); 380 } else { 381 // Call setMessage() even if m_validationMesage->message() == message 382 // because the existing message might be to be hidden. 383 m_validationMessage->setMessage(message); 384 } 385} 386 387void HTMLFormControlElement::hideVisibleValidationMessage() 388{ 389 if (m_validationMessage) 390 m_validationMessage->requestToHideMessage(); 391} 392 393String HTMLFormControlElement::visibleValidationMessage() const 394{ 395 return m_validationMessage ? m_validationMessage->message() : String(); 396} 397 398bool HTMLFormControlElement::checkValidity(Vector<RefPtr<FormAssociatedElement> >* unhandledInvalidControls) 399{ 400 if (!willValidate() || isValidFormControlElement()) 401 return true; 402 // An event handler can deref this object. 403 RefPtr<HTMLFormControlElement> protector(this); 404 RefPtr<Document> originalDocument(document()); 405 bool needsDefaultAction = dispatchEvent(Event::create(eventNames().invalidEvent, false, true)); 406 if (needsDefaultAction && unhandledInvalidControls && inDocument() && originalDocument == document()) 407 unhandledInvalidControls->append(this); 408 return false; 409} 410 411bool HTMLFormControlElement::isValidFormControlElement() 412{ 413 // If the following assertion fails, setNeedsValidityCheck() is not called 414 // correctly when something which changes validity is updated. 415 ASSERT(m_isValid == validity()->valid()); 416 return m_isValid; 417} 418 419void HTMLFormControlElement::setNeedsValidityCheck() 420{ 421 bool newIsValid = validity()->valid(); 422 if (willValidate() && newIsValid != m_isValid) { 423 // Update style for pseudo classes such as :valid :invalid. 424 setNeedsStyleRecalc(); 425 } 426 m_isValid = newIsValid; 427 428 // Updates only if this control already has a validtion message. 429 if (!visibleValidationMessage().isEmpty()) { 430 // Calls updateVisibleValidationMessage() even if m_isValid is not 431 // changed because a validation message can be chagned. 432 updateVisibleValidationMessage(); 433 } 434} 435 436void HTMLFormControlElement::setCustomValidity(const String& error) 437{ 438 validity()->setCustomErrorMessage(error); 439} 440 441void HTMLFormControlElement::dispatchFocusEvent() 442{ 443 if (document()->page()) 444 document()->page()->chrome()->client()->formDidFocus(this); 445 446 HTMLElement::dispatchFocusEvent(); 447} 448 449void HTMLFormControlElement::dispatchBlurEvent() 450{ 451 if (document()->page()) 452 document()->page()->chrome()->client()->formDidBlur(this); 453 454 HTMLElement::dispatchBlurEvent(); 455 hideVisibleValidationMessage(); 456} 457 458HTMLFormElement* HTMLFormControlElement::virtualForm() const 459{ 460 return FormAssociatedElement::form(); 461} 462 463bool HTMLFormControlElement::isDefaultButtonForForm() const 464{ 465 return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this; 466} 467 468void HTMLFormControlElement::attributeChanged(Attribute* attr, bool preserveDecls) 469{ 470 if (attr->name() == formAttr) { 471 formAttributeChanged(); 472 if (!form()) 473 document()->checkedRadioButtons().addButton(this); 474 } else 475 HTMLElement::attributeChanged(attr, preserveDecls); 476} 477 478bool HTMLFormControlElement::isLabelable() const 479{ 480 // FIXME: Add meterTag and outputTag to the list once we support them. 481 return hasTagName(buttonTag) || hasTagName(inputTag) || hasTagName(keygenTag) 482#if ENABLE(METER_TAG) 483 || hasTagName(meterTag) 484#endif 485#if ENABLE(PROGRESS_TAG) 486 || hasTagName(progressTag) 487#endif 488 || hasTagName(selectTag) || hasTagName(textareaTag); 489} 490 491PassRefPtr<NodeList> HTMLFormControlElement::labels() 492{ 493 if (!isLabelable()) 494 return 0; 495 if (!document()) 496 return 0; 497 498 NodeRareData* data = Node::ensureRareData(); 499 if (!data->nodeLists()) { 500 data->setNodeLists(NodeListsNodeData::create()); 501 document()->addNodeListCache(); 502 } 503 504 return LabelsNodeList::create(this); 505} 506 507HTMLFormControlElementWithState::HTMLFormControlElementWithState(const QualifiedName& tagName, Document* doc, HTMLFormElement* f) 508 : HTMLFormControlElement(tagName, doc, f) 509{ 510 document()->registerFormElementWithState(this); 511} 512 513HTMLFormControlElementWithState::~HTMLFormControlElementWithState() 514{ 515 document()->unregisterFormElementWithState(this); 516} 517 518void HTMLFormControlElementWithState::willMoveToNewOwnerDocument() 519{ 520 document()->unregisterFormElementWithState(this); 521 HTMLFormControlElement::willMoveToNewOwnerDocument(); 522} 523 524void HTMLFormControlElementWithState::didMoveToNewOwnerDocument() 525{ 526 document()->registerFormElementWithState(this); 527 HTMLFormControlElement::didMoveToNewOwnerDocument(); 528} 529 530bool HTMLFormControlElementWithState::autoComplete() const 531{ 532 if (!form()) 533 return true; 534 return form()->autoComplete(); 535} 536 537bool HTMLFormControlElementWithState::shouldSaveAndRestoreFormControlState() const 538{ 539 // We don't save/restore control state in a form with autocomplete=off. 540 return attached() && autoComplete(); 541} 542 543void HTMLFormControlElementWithState::finishParsingChildren() 544{ 545 HTMLFormControlElement::finishParsingChildren(); 546 547 // We don't save state of a control with shouldSaveAndRestoreFormControlState()=false. 548 // But we need to skip restoring process too because a control in another 549 // form might have the same pair of name and type and saved its state. 550 if (!shouldSaveAndRestoreFormControlState()) 551 return; 552 553 Document* doc = document(); 554 if (doc->hasStateForNewFormElements()) { 555 String state; 556 if (doc->takeStateForFormElement(name().impl(), type().impl(), state)) 557 restoreFormControlState(state); 558 } 559} 560 561void HTMLFormControlElementWithState::defaultEventHandler(Event* event) 562{ 563 if (event->type() == eventNames().webkitEditableContentChangedEvent && renderer() && renderer()->isTextControl()) { 564 toRenderTextControl(renderer())->subtreeHasChanged(); 565 return; 566 } 567 568 HTMLFormControlElement::defaultEventHandler(event); 569} 570 571HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* form) 572 : HTMLFormControlElementWithState(tagName, doc, form) 573{ 574} 575 576HTMLTextFormControlElement::~HTMLTextFormControlElement() 577{ 578} 579 580void HTMLTextFormControlElement::insertedIntoDocument() 581{ 582 HTMLFormControlElement::insertedIntoDocument(); 583 setTextAsOfLastFormControlChangeEvent(value()); 584} 585 586void HTMLTextFormControlElement::dispatchFocusEvent() 587{ 588 if (supportsPlaceholder()) 589 updatePlaceholderVisibility(false); 590 handleFocusEvent(); 591 HTMLFormControlElementWithState::dispatchFocusEvent(); 592} 593 594void HTMLTextFormControlElement::dispatchBlurEvent() 595{ 596 if (supportsPlaceholder()) 597 updatePlaceholderVisibility(false); 598 handleBlurEvent(); 599 HTMLFormControlElementWithState::dispatchBlurEvent(); 600} 601 602String HTMLTextFormControlElement::strippedPlaceholder() const 603{ 604 // According to the HTML5 specification, we need to remove CR and LF from 605 // the attribute value. 606 const AtomicString& attributeValue = getAttribute(placeholderAttr); 607 if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn)) 608 return attributeValue; 609 610 Vector<UChar> stripped; 611 unsigned length = attributeValue.length(); 612 stripped.reserveCapacity(length); 613 for (unsigned i = 0; i < length; ++i) { 614 UChar character = attributeValue[i]; 615 if (character == newlineCharacter || character == carriageReturn) 616 continue; 617 stripped.append(character); 618 } 619 return String::adopt(stripped); 620} 621 622static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; } 623 624bool HTMLTextFormControlElement::isPlaceholderEmpty() const 625{ 626 const AtomicString& attributeValue = getAttribute(placeholderAttr); 627 return attributeValue.string().find(isNotLineBreak) == notFound; 628} 629 630bool HTMLTextFormControlElement::placeholderShouldBeVisible() const 631{ 632 return supportsPlaceholder() 633 && isEmptyValue() 634 && isEmptySuggestedValue() 635 && !isPlaceholderEmpty() 636 && (document()->focusedNode() != this || (renderer() && renderer()->theme()->shouldShowPlaceholderWhenFocused())); 637} 638 639void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged) 640{ 641 if (supportsPlaceholder() && renderer()) 642 toRenderTextControl(renderer())->updatePlaceholderVisibility(placeholderShouldBeVisible(), placeholderValueChanged); 643} 644 645RenderTextControl* HTMLTextFormControlElement::textRendererAfterUpdateLayout() 646{ 647 if (!isTextFormControl()) 648 return 0; 649 document()->updateLayoutIgnorePendingStylesheets(); 650 return toRenderTextControl(renderer()); 651} 652 653void HTMLTextFormControlElement::setSelectionStart(int start) 654{ 655 setSelectionRange(start, max(start, selectionEnd())); 656} 657 658void HTMLTextFormControlElement::setSelectionEnd(int end) 659{ 660 setSelectionRange(min(end, selectionStart()), end); 661} 662 663void HTMLTextFormControlElement::select() 664{ 665 setSelectionRange(0, numeric_limits<int>::max()); 666} 667 668void HTMLTextFormControlElement::dispatchFormControlChangeEvent() 669{ 670 if (m_textAsOfLastFormControlChangeEvent != value()) { 671 HTMLElement::dispatchChangeEvent(); 672 setTextAsOfLastFormControlChangeEvent(value()); 673 } 674 setChangedSinceLastFormControlChangeEvent(false); 675} 676 677void HTMLTextFormControlElement::setSelectionRange(int start, int end) 678{ 679 WebCore::setSelectionRange(this, start, end); 680} 681 682int HTMLTextFormControlElement::selectionStart() const 683{ 684 if (!isTextFormControl()) 685 return 0; 686 if (document()->focusedNode() != this && cachedSelectionStart() >= 0) 687 return cachedSelectionStart(); 688 if (!renderer()) 689 return 0; 690 return toRenderTextControl(renderer())->selectionStart(); 691} 692 693int HTMLTextFormControlElement::selectionEnd() const 694{ 695 if (!isTextFormControl()) 696 return 0; 697 if (document()->focusedNode() != this && cachedSelectionEnd() >= 0) 698 return cachedSelectionEnd(); 699 if (!renderer()) 700 return 0; 701 return toRenderTextControl(renderer())->selectionEnd(); 702} 703 704PassRefPtr<Range> HTMLTextFormControlElement::selection() const 705{ 706 if (!renderer() || !isTextFormControl() || cachedSelectionStart() < 0 || cachedSelectionEnd() < 0) 707 return 0; 708 return toRenderTextControl(renderer())->selection(cachedSelectionStart(), cachedSelectionEnd()); 709} 710 711void HTMLTextFormControlElement::parseMappedAttribute(Attribute* attr) 712{ 713 if (attr->name() == placeholderAttr) 714 updatePlaceholderVisibility(true); 715 else if (attr->name() == onselectAttr) 716 setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr)); 717 else if (attr->name() == onchangeAttr) 718 setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr)); 719 else 720 HTMLFormControlElementWithState::parseMappedAttribute(attr); 721} 722 723} // namespace Webcore 724