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