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 "ControlStates.h" 30#include "Event.h" 31#include "EventHandler.h" 32#include "EventNames.h" 33#include "Frame.h" 34#include "HTMLFieldSetElement.h" 35#include "HTMLFormElement.h" 36#include "HTMLInputElement.h" 37#include "HTMLLegendElement.h" 38#include "HTMLTextAreaElement.h" 39#include "RenderBox.h" 40#include "RenderTheme.h" 41#include "ValidationMessage.h" 42#include <wtf/Ref.h> 43#include <wtf/Vector.h> 44 45namespace WebCore { 46 47using namespace HTMLNames; 48 49HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form) 50 : LabelableElement(tagName, document) 51 , m_disabled(false) 52 , m_isReadOnly(false) 53 , m_isRequired(false) 54 , m_valueMatchesRenderer(false) 55 , m_disabledByAncestorFieldset(false) 56 , m_dataListAncestorState(Unknown) 57 , m_willValidateInitialized(false) 58 , m_willValidate(true) 59 , m_isValid(true) 60 , m_wasChangedSinceLastFormControlChangeEvent(false) 61 , m_hasAutofocused(false) 62{ 63 setForm(form ? form : HTMLFormElement::findClosestFormAncestor(*this)); 64 setHasCustomStyleResolveCallbacks(); 65} 66 67HTMLFormControlElement::~HTMLFormControlElement() 68{ 69} 70 71String HTMLFormControlElement::formEnctype() const 72{ 73 const AtomicString& formEnctypeAttr = fastGetAttribute(formenctypeAttr); 74 if (formEnctypeAttr.isNull()) 75 return emptyString(); 76 return FormSubmission::Attributes::parseEncodingType(formEnctypeAttr); 77} 78 79void HTMLFormControlElement::setFormEnctype(const String& value) 80{ 81 setAttribute(formenctypeAttr, value); 82} 83 84String HTMLFormControlElement::formMethod() const 85{ 86 const AtomicString& formMethodAttr = fastGetAttribute(formmethodAttr); 87 if (formMethodAttr.isNull()) 88 return emptyString(); 89 return FormSubmission::Attributes::methodString(FormSubmission::Attributes::parseMethodType(formMethodAttr)); 90} 91 92void HTMLFormControlElement::setFormMethod(const String& value) 93{ 94 setAttribute(formmethodAttr, value); 95} 96 97bool HTMLFormControlElement::formNoValidate() const 98{ 99 return fastHasAttribute(formnovalidateAttr); 100} 101 102bool HTMLFormControlElement::computeIsDisabledByFieldsetAncestor() const 103{ 104 Element* previousAncestor = nullptr; 105 for (Element* ancestor = parentElement(); ancestor; ancestor = ancestor->parentElement()) { 106 if (isHTMLFieldSetElement(ancestor) && ancestor->hasAttribute(disabledAttr)) { 107 HTMLFieldSetElement& fieldSetAncestor = toHTMLFieldSetElement(*ancestor); 108 bool isInFirstLegend = previousAncestor && isHTMLLegendElement(previousAncestor) && previousAncestor == fieldSetAncestor.legend(); 109 return !isInFirstLegend; 110 } 111 previousAncestor = ancestor; 112 } 113 return false; 114} 115 116void HTMLFormControlElement::setAncestorDisabled(bool isDisabled) 117{ 118 ASSERT(computeIsDisabledByFieldsetAncestor() == isDisabled); 119 bool oldValue = m_disabledByAncestorFieldset; 120 m_disabledByAncestorFieldset = isDisabled; 121 if (oldValue != m_disabledByAncestorFieldset) 122 disabledStateChanged(); 123} 124 125void HTMLFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 126{ 127 if (name == formAttr) 128 formAttributeChanged(); 129 else if (name == disabledAttr) { 130 bool oldDisabled = m_disabled; 131 m_disabled = !value.isNull(); 132 if (oldDisabled != m_disabled) 133 disabledAttributeChanged(); 134 } else if (name == readonlyAttr) { 135 bool wasReadOnly = m_isReadOnly; 136 m_isReadOnly = !value.isNull(); 137 if (wasReadOnly != m_isReadOnly) 138 readOnlyAttributeChanged(); 139 } else if (name == requiredAttr) { 140 bool wasRequired = m_isRequired; 141 m_isRequired = !value.isNull(); 142 if (wasRequired != m_isRequired) 143 requiredAttributeChanged(); 144 } else 145 HTMLElement::parseAttribute(name, value); 146} 147 148void HTMLFormControlElement::disabledAttributeChanged() 149{ 150 disabledStateChanged(); 151} 152 153void HTMLFormControlElement::disabledStateChanged() 154{ 155 setNeedsWillValidateCheck(); 156 didAffectSelector(AffectedSelectorDisabled | AffectedSelectorEnabled); 157 if (renderer() && renderer()->style().hasAppearance()) 158 renderer()->theme().stateChanged(*renderer(), ControlStates::EnabledState); 159} 160 161void HTMLFormControlElement::readOnlyAttributeChanged() 162{ 163 setNeedsWillValidateCheck(); 164 setNeedsStyleRecalc(); 165 if (renderer() && renderer()->style().hasAppearance()) 166 renderer()->theme().stateChanged(*renderer(), ControlStates::ReadOnlyState); 167} 168 169void HTMLFormControlElement::requiredAttributeChanged() 170{ 171 setNeedsValidityCheck(); 172 // Style recalculation is needed because style selectors may include 173 // :required and :optional pseudo-classes. 174 setNeedsStyleRecalc(); 175} 176 177static bool shouldAutofocus(HTMLFormControlElement* element) 178{ 179 if (!element->renderer()) 180 return false; 181 if (!element->fastHasAttribute(autofocusAttr)) 182 return false; 183 if (!element->inDocument() || !element->document().renderView()) 184 return false; 185 if (element->document().ignoreAutofocus()) 186 return false; 187 if (element->document().isSandboxed(SandboxAutomaticFeatures)) { 188 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. 189 element->document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, ASCIILiteral("Blocked autofocusing on a form control because the form's frame is sandboxed and the 'allow-scripts' permission is not set.")); 190 return false; 191 } 192 if (element->hasAutofocused()) 193 return false; 194 195 // FIXME: Should this set of hasTagName checks be replaced by a 196 // virtual member function? 197 if (isHTMLInputElement(element)) 198 return !toHTMLInputElement(element)->isInputTypeHidden(); 199 if (element->hasTagName(selectTag)) 200 return true; 201 if (element->hasTagName(keygenTag)) 202 return true; 203 if (element->hasTagName(buttonTag)) 204 return true; 205 if (isHTMLTextAreaElement(element)) 206 return true; 207 208 return false; 209} 210 211void HTMLFormControlElement::didAttachRenderers() 212{ 213 // The call to updateFromElement() needs to go after the call through 214 // to the base class's attach() because that can sometimes do a close 215 // on the renderer. 216 if (renderer()) 217 renderer()->updateFromElement(); 218 219 if (shouldAutofocus(this)) { 220 setAutofocused(); 221 222 RefPtr<HTMLFormControlElement> element = this; 223 Style::queuePostResolutionCallback([element] { 224 element->focus(); 225 }); 226 } 227} 228 229void HTMLFormControlElement::didMoveToNewDocument(Document* oldDocument) 230{ 231 FormAssociatedElement::didMoveToNewDocument(oldDocument); 232 HTMLElement::didMoveToNewDocument(oldDocument); 233} 234 235Node::InsertionNotificationRequest HTMLFormControlElement::insertedInto(ContainerNode& insertionPoint) 236{ 237 if (document().hasDisabledFieldsetElement()) 238 setAncestorDisabled(computeIsDisabledByFieldsetAncestor()); 239 m_dataListAncestorState = Unknown; 240 setNeedsWillValidateCheck(); 241 HTMLElement::insertedInto(insertionPoint); 242 FormAssociatedElement::insertedInto(insertionPoint); 243 return InsertionDone; 244} 245 246void HTMLFormControlElement::removedFrom(ContainerNode& insertionPoint) 247{ 248 m_validationMessage = nullptr; 249 if (m_disabledByAncestorFieldset) 250 setAncestorDisabled(computeIsDisabledByFieldsetAncestor()); 251 m_dataListAncestorState = Unknown; 252 HTMLElement::removedFrom(insertionPoint); 253 FormAssociatedElement::removedFrom(insertionPoint); 254} 255 256void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed) 257{ 258 m_wasChangedSinceLastFormControlChangeEvent = changed; 259} 260 261void HTMLFormControlElement::dispatchChangeEvent() 262{ 263 dispatchScopedEvent(Event::create(eventNames().changeEvent, true, false)); 264} 265 266void HTMLFormControlElement::dispatchFormControlChangeEvent() 267{ 268 dispatchChangeEvent(); 269 setChangedSinceLastFormControlChangeEvent(false); 270} 271 272void HTMLFormControlElement::dispatchFormControlInputEvent() 273{ 274 setChangedSinceLastFormControlChangeEvent(true); 275 HTMLElement::dispatchInputEvent(); 276} 277 278bool HTMLFormControlElement::isDisabledFormControl() const 279{ 280 return m_disabled || m_disabledByAncestorFieldset; 281} 282 283bool HTMLFormControlElement::isRequired() const 284{ 285 return m_isRequired; 286} 287 288void HTMLFormControlElement::didRecalcStyle(Style::Change) 289{ 290 // updateFromElement() can cause the selection to change, and in turn 291 // trigger synchronous layout, so it must not be called during style recalc. 292 if (renderer()) { 293 RefPtr<HTMLFormControlElement> element = this; 294 Style::queuePostResolutionCallback([element]{ 295 if (auto* renderer = element->renderer()) 296 renderer->updateFromElement(); 297 }); 298 } 299} 300 301bool HTMLFormControlElement::supportsFocus() const 302{ 303 return !isDisabledFormControl(); 304} 305 306bool HTMLFormControlElement::isFocusable() const 307{ 308 // If there's a renderer, make sure the size isn't empty, but if there's no renderer, 309 // it might still be focusable if it's in a canvas subtree (handled in Node::isFocusable). 310 if (renderer() && (!renderer()->isBox() || toRenderBox(renderer())->size().isEmpty())) 311 return false; 312 // HTMLElement::isFocusable handles visibility and calls suportsFocus which 313 // will cover the disabled case. 314 return HTMLElement::isFocusable(); 315} 316 317bool HTMLFormControlElement::isKeyboardFocusable(KeyboardEvent* event) const 318{ 319 if (isFocusable()) 320 if (document().frame()) 321 return document().frame()->eventHandler().tabsToAllFormControls(event); 322 return false; 323} 324 325bool HTMLFormControlElement::isMouseFocusable() const 326{ 327#if PLATFORM(GTK) 328 return HTMLElement::isMouseFocusable(); 329#else 330 return false; 331#endif 332} 333 334short HTMLFormControlElement::tabIndex() const 335{ 336 // Skip the supportsFocus check in HTMLElement. 337 return Element::tabIndex(); 338} 339 340bool HTMLFormControlElement::recalcWillValidate() const 341{ 342 if (m_dataListAncestorState == Unknown) { 343 for (ContainerNode* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) { 344 if (ancestor->hasTagName(datalistTag)) { 345 m_dataListAncestorState = InsideDataList; 346 break; 347 } 348 } 349 if (m_dataListAncestorState == Unknown) 350 m_dataListAncestorState = NotInsideDataList; 351 } 352 return m_dataListAncestorState == NotInsideDataList && !isDisabledOrReadOnly(); 353} 354 355bool HTMLFormControlElement::willValidate() const 356{ 357 if (!m_willValidateInitialized || m_dataListAncestorState == Unknown) { 358 m_willValidateInitialized = true; 359 bool newWillValidate = recalcWillValidate(); 360 if (m_willValidate != newWillValidate) { 361 m_willValidate = newWillValidate; 362 const_cast<HTMLFormControlElement*>(this)->setNeedsValidityCheck(); 363 } 364 } else { 365 // If the following assertion fails, setNeedsWillValidateCheck() is not 366 // called correctly when something which changes recalcWillValidate() result 367 // is updated. 368 ASSERT(m_willValidate == recalcWillValidate()); 369 } 370 return m_willValidate; 371} 372 373void HTMLFormControlElement::setNeedsWillValidateCheck() 374{ 375 // We need to recalculate willValidate immediately because willValidate change can causes style change. 376 bool newWillValidate = recalcWillValidate(); 377 if (m_willValidateInitialized && m_willValidate == newWillValidate) 378 return; 379 m_willValidateInitialized = true; 380 m_willValidate = newWillValidate; 381 setNeedsValidityCheck(); 382 setNeedsStyleRecalc(); 383 if (!m_willValidate) 384 hideVisibleValidationMessage(); 385} 386 387void HTMLFormControlElement::updateVisibleValidationMessage() 388{ 389 Page* page = document().page(); 390 if (!page) 391 return; 392 String message; 393 if (renderer() && willValidate()) 394 message = validationMessage().stripWhiteSpace(); 395 if (!m_validationMessage) 396 m_validationMessage = std::make_unique<ValidationMessage>(this); 397 m_validationMessage->updateValidationMessage(message); 398} 399 400void HTMLFormControlElement::hideVisibleValidationMessage() 401{ 402 if (m_validationMessage) 403 m_validationMessage->requestToHideMessage(); 404} 405 406bool HTMLFormControlElement::checkValidity(Vector<RefPtr<FormAssociatedElement>>* unhandledInvalidControls) 407{ 408 if (!willValidate() || isValidFormControlElement()) 409 return true; 410 // An event handler can deref this object. 411 Ref<HTMLFormControlElement> protect(*this); 412 Ref<Document> originalDocument(document()); 413 bool needsDefaultAction = dispatchEvent(Event::create(eventNames().invalidEvent, false, true)); 414 if (needsDefaultAction && unhandledInvalidControls && inDocument() && &originalDocument.get() == &document()) 415 unhandledInvalidControls->append(this); 416 return false; 417} 418 419bool HTMLFormControlElement::isValidFormControlElement() const 420{ 421 // If the following assertion fails, setNeedsValidityCheck() is not called 422 // correctly when something which changes validity is updated. 423 ASSERT(m_isValid == valid()); 424 return m_isValid; 425} 426 427void HTMLFormControlElement::setNeedsValidityCheck() 428{ 429 bool newIsValid = valid(); 430 if (willValidate() && newIsValid != m_isValid) { 431 // Update style for pseudo classes such as :valid :invalid. 432 setNeedsStyleRecalc(); 433 } 434 m_isValid = newIsValid; 435 436 // Updates only if this control already has a validtion message. 437 if (m_validationMessage && m_validationMessage->isVisible()) { 438 // Calls updateVisibleValidationMessage() even if m_isValid is not 439 // changed because a validation message can be chagned. 440 updateVisibleValidationMessage(); 441 } 442} 443 444void HTMLFormControlElement::setCustomValidity(const String& error) 445{ 446 FormAssociatedElement::setCustomValidity(error); 447 setNeedsValidityCheck(); 448} 449 450bool HTMLFormControlElement::validationMessageShadowTreeContains(const Node& node) const 451{ 452 return m_validationMessage && m_validationMessage->shadowTreeContains(node); 453} 454 455void HTMLFormControlElement::dispatchBlurEvent(PassRefPtr<Element> newFocusedElement) 456{ 457 HTMLElement::dispatchBlurEvent(newFocusedElement); 458 hideVisibleValidationMessage(); 459} 460 461HTMLFormElement* HTMLFormControlElement::virtualForm() const 462{ 463 return FormAssociatedElement::form(); 464} 465 466bool HTMLFormControlElement::isDefaultButtonForForm() const 467{ 468 return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this; 469} 470 471#if ENABLE(IOS_AUTOCORRECT_AND_AUTOCAPITALIZE) 472// FIXME: We should look to share these methods with class HTMLFormElement instead of duplicating them. 473 474bool HTMLFormControlElement::autocorrect() const 475{ 476 const AtomicString& autocorrectValue = fastGetAttribute(autocorrectAttr); 477 if (!autocorrectValue.isEmpty()) 478 return !equalIgnoringCase(autocorrectValue, "off"); 479 if (HTMLFormElement* form = this->form()) 480 return form->autocorrect(); 481 return true; 482} 483 484void HTMLFormControlElement::setAutocorrect(bool autocorrect) 485{ 486 setAttribute(autocorrectAttr, autocorrect ? AtomicString("on", AtomicString::ConstructFromLiteral) : AtomicString("off", AtomicString::ConstructFromLiteral)); 487} 488 489WebAutocapitalizeType HTMLFormControlElement::autocapitalizeType() const 490{ 491 WebAutocapitalizeType type = autocapitalizeTypeForAttributeValue(fastGetAttribute(autocapitalizeAttr)); 492 if (type == WebAutocapitalizeTypeDefault) { 493 if (HTMLFormElement* form = this->form()) 494 return form->autocapitalizeType(); 495 } 496 return type; 497} 498 499const AtomicString& HTMLFormControlElement::autocapitalize() const 500{ 501 return stringForAutocapitalizeType(autocapitalizeType()); 502} 503 504void HTMLFormControlElement::setAutocapitalize(const AtomicString& value) 505{ 506 setAttribute(autocapitalizeAttr, value); 507} 508#endif 509 510HTMLFormControlElement* HTMLFormControlElement::enclosingFormControlElement(Node* node) 511{ 512 for (; node; node = node->parentNode()) { 513 if (node->isElementNode() && toElement(node)->isFormControlElement()) 514 return toHTMLFormControlElement(node); 515 } 516 return 0; 517} 518 519} // namespace Webcore 520