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 "core/html/FormAssociatedElement.h" 27 28#include "core/HTMLNames.h" 29#include "core/dom/IdTargetObserver.h" 30#include "core/dom/NodeTraversal.h" 31#include "core/html/HTMLFormControlElement.h" 32#include "core/html/HTMLFormElement.h" 33#include "core/html/HTMLLabelElement.h" 34#include "core/html/HTMLObjectElement.h" 35#include "core/html/ValidityState.h" 36 37namespace blink { 38 39using namespace HTMLNames; 40 41class FormAttributeTargetObserver : public IdTargetObserver { 42 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED; 43public: 44 static PassOwnPtrWillBeRawPtr<FormAttributeTargetObserver> create(const AtomicString& id, FormAssociatedElement*); 45 virtual void trace(Visitor*) OVERRIDE; 46 virtual void idTargetChanged() OVERRIDE; 47 48private: 49 FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement*); 50 51 RawPtrWillBeMember<FormAssociatedElement> m_element; 52}; 53 54FormAssociatedElement::FormAssociatedElement() 55 : m_formWasSetByParser(false) 56{ 57} 58 59FormAssociatedElement::~FormAssociatedElement() 60{ 61 // We can't call setForm here because it contains virtual calls. 62} 63 64void FormAssociatedElement::trace(Visitor* visitor) 65{ 66 visitor->trace(m_formAttributeTargetObserver); 67 visitor->trace(m_form); 68 visitor->trace(m_validityState); 69} 70 71ValidityState* FormAssociatedElement::validity() 72{ 73 if (!m_validityState) 74 m_validityState = ValidityState::create(this); 75 76 return m_validityState.get(); 77} 78 79void FormAssociatedElement::didMoveToNewDocument(Document& oldDocument) 80{ 81 HTMLElement* element = toHTMLElement(this); 82 if (element->fastHasAttribute(formAttr)) 83 setFormAttributeTargetObserver(nullptr); 84} 85 86void FormAssociatedElement::insertedInto(ContainerNode* insertionPoint) 87{ 88 if (!m_formWasSetByParser || !m_form || NodeTraversal::highestAncestorOrSelf(*insertionPoint) != NodeTraversal::highestAncestorOrSelf(*m_form.get())) 89 resetFormOwner(); 90 91 if (!insertionPoint->inDocument()) 92 return; 93 94 HTMLElement* element = toHTMLElement(this); 95 if (element->fastHasAttribute(formAttr)) 96 resetFormAttributeTargetObserver(); 97} 98 99void FormAssociatedElement::removedFrom(ContainerNode* insertionPoint) 100{ 101 HTMLElement* element = toHTMLElement(this); 102 if (insertionPoint->inDocument() && element->fastHasAttribute(formAttr)) 103 setFormAttributeTargetObserver(nullptr); 104 // If the form and element are both in the same tree, preserve the connection to the form. 105 // Otherwise, null out our form and remove ourselves from the form's list of elements. 106 if (m_form && NodeTraversal::highestAncestorOrSelf(*element) != NodeTraversal::highestAncestorOrSelf(*m_form.get())) 107 resetFormOwner(); 108} 109 110HTMLFormElement* FormAssociatedElement::findAssociatedForm(const HTMLElement* element) 111{ 112 const AtomicString& formId(element->fastGetAttribute(formAttr)); 113 // 3. If the element is reassociateable, has a form content attribute, and 114 // is itself in a Document, then run these substeps: 115 if (!formId.isNull() && element->inDocument()) { 116 // 3.1. If the first element in the Document to have an ID that is 117 // case-sensitively equal to the element's form content attribute's 118 // value is a form element, then associate the form-associated element 119 // with that form element. 120 // 3.2. Abort the "reset the form owner" steps. 121 Element* newFormCandidate = element->treeScope().getElementById(formId); 122 return isHTMLFormElement(newFormCandidate) ? toHTMLFormElement(newFormCandidate) : 0; 123 } 124 // 4. Otherwise, if the form-associated element in question has an ancestor 125 // form element, then associate the form-associated element with the nearest 126 // such ancestor form element. 127 return element->findFormAncestor(); 128} 129 130void FormAssociatedElement::formRemovedFromTree(const Node& formRoot) 131{ 132 ASSERT(m_form); 133 if (NodeTraversal::highestAncestorOrSelf(toHTMLElement(*this)) == formRoot) 134 return; 135 resetFormOwner(); 136} 137 138void FormAssociatedElement::associateByParser(HTMLFormElement* form) 139{ 140 if (form && form->inDocument()) { 141 m_formWasSetByParser = true; 142 setForm(form); 143 form->didAssociateByParser(); 144 } 145} 146 147void FormAssociatedElement::setForm(HTMLFormElement* newForm) 148{ 149 if (m_form.get() == newForm) 150 return; 151 willChangeForm(); 152 if (m_form) 153 m_form->disassociate(*this); 154 if (newForm) { 155#if ENABLE(OILPAN) 156 m_form = newForm; 157#else 158 m_form = newForm->createWeakPtr(); 159#endif 160 m_form->associate(*this); 161 } else { 162#if ENABLE(OILPAN) 163 m_form = nullptr; 164#else 165 m_form = WeakPtr<HTMLFormElement>(); 166#endif 167 } 168 didChangeForm(); 169} 170 171void FormAssociatedElement::willChangeForm() 172{ 173} 174 175void FormAssociatedElement::didChangeForm() 176{ 177} 178 179void FormAssociatedElement::resetFormOwner() 180{ 181 m_formWasSetByParser = false; 182 HTMLElement* element = toHTMLElement(this); 183 const AtomicString& formId(element->fastGetAttribute(formAttr)); 184 HTMLFormElement* nearestForm = element->findFormAncestor(); 185 // 1. If the element's form owner is not null, and either the element is not 186 // reassociateable or its form content attribute is not present, and the 187 // element's form owner is its nearest form element ancestor after the 188 // change to the ancestor chain, then do nothing, and abort these steps. 189 if (m_form && formId.isNull() && m_form.get() == nearestForm) 190 return; 191 192 HTMLFormElement* originalForm = m_form.get(); 193 setForm(findAssociatedForm(element)); 194 // FIXME: Move didAssociateFormControl call to didChangeForm or 195 // HTMLFormElement::associate. 196 if (m_form && m_form.get() != originalForm && m_form->inDocument()) 197 element->document().didAssociateFormControl(element); 198} 199 200void FormAssociatedElement::formAttributeChanged() 201{ 202 resetFormOwner(); 203 resetFormAttributeTargetObserver(); 204} 205 206bool FormAssociatedElement::customError() const 207{ 208 const HTMLElement* element = toHTMLElement(this); 209 return element->willValidate() && !m_customValidationMessage.isEmpty(); 210} 211 212bool FormAssociatedElement::hasBadInput() const 213{ 214 return false; 215} 216 217bool FormAssociatedElement::patternMismatch() const 218{ 219 return false; 220} 221 222bool FormAssociatedElement::rangeOverflow() const 223{ 224 return false; 225} 226 227bool FormAssociatedElement::rangeUnderflow() const 228{ 229 return false; 230} 231 232bool FormAssociatedElement::stepMismatch() const 233{ 234 return false; 235} 236 237bool FormAssociatedElement::tooLong() const 238{ 239 return false; 240} 241 242bool FormAssociatedElement::typeMismatch() const 243{ 244 return false; 245} 246 247bool FormAssociatedElement::valid() const 248{ 249 bool someError = typeMismatch() || stepMismatch() || rangeUnderflow() || rangeOverflow() 250 || tooLong() || patternMismatch() || valueMissing() || hasBadInput() || customError(); 251 return !someError; 252} 253 254bool FormAssociatedElement::valueMissing() const 255{ 256 return false; 257} 258 259String FormAssociatedElement::customValidationMessage() const 260{ 261 return m_customValidationMessage; 262} 263 264String FormAssociatedElement::validationMessage() const 265{ 266 return customError() ? m_customValidationMessage : String(); 267} 268 269void FormAssociatedElement::setCustomValidity(const String& error) 270{ 271 m_customValidationMessage = error; 272} 273 274void FormAssociatedElement::setFormAttributeTargetObserver(PassOwnPtrWillBeRawPtr<FormAttributeTargetObserver> newObserver) 275{ 276 if (m_formAttributeTargetObserver) 277 m_formAttributeTargetObserver->unregister(); 278 m_formAttributeTargetObserver = newObserver; 279} 280 281void FormAssociatedElement::resetFormAttributeTargetObserver() 282{ 283 HTMLElement* element = toHTMLElement(this); 284 const AtomicString& formId(element->fastGetAttribute(formAttr)); 285 if (!formId.isNull() && element->inDocument()) 286 setFormAttributeTargetObserver(FormAttributeTargetObserver::create(formId, this)); 287 else 288 setFormAttributeTargetObserver(nullptr); 289} 290 291void FormAssociatedElement::formAttributeTargetChanged() 292{ 293 resetFormOwner(); 294} 295 296const AtomicString& FormAssociatedElement::name() const 297{ 298 const AtomicString& name = toHTMLElement(this)->getNameAttribute(); 299 return name.isNull() ? emptyAtom : name; 300} 301 302bool FormAssociatedElement::isFormControlElementWithState() const 303{ 304 return false; 305} 306 307const HTMLElement& toHTMLElement(const FormAssociatedElement& associatedElement) 308{ 309 if (associatedElement.isFormControlElement()) 310 return toHTMLFormControlElement(associatedElement); 311 else if (associatedElement.isLabelElement()) 312 return toHTMLLabelElement(associatedElement); 313 else 314 return toHTMLObjectElement(associatedElement); 315} 316 317const HTMLElement* toHTMLElement(const FormAssociatedElement* associatedElement) 318{ 319 ASSERT(associatedElement); 320 return &toHTMLElement(*associatedElement); 321} 322 323HTMLElement* toHTMLElement(FormAssociatedElement* associatedElement) 324{ 325 return const_cast<HTMLElement*>(toHTMLElement(static_cast<const FormAssociatedElement*>(associatedElement))); 326} 327 328HTMLElement& toHTMLElement(FormAssociatedElement& associatedElement) 329{ 330 return const_cast<HTMLElement&>(toHTMLElement(static_cast<const FormAssociatedElement&>(associatedElement))); 331} 332 333PassOwnPtrWillBeRawPtr<FormAttributeTargetObserver> FormAttributeTargetObserver::create(const AtomicString& id, FormAssociatedElement* element) 334{ 335 return adoptPtrWillBeNoop(new FormAttributeTargetObserver(id, element)); 336} 337 338FormAttributeTargetObserver::FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement* element) 339 : IdTargetObserver(toHTMLElement(element)->treeScope().idTargetObserverRegistry(), id) 340 , m_element(element) 341{ 342} 343 344void FormAttributeTargetObserver::trace(Visitor* visitor) 345{ 346 visitor->trace(m_element); 347 IdTargetObserver::trace(visitor); 348} 349 350void FormAttributeTargetObserver::idTargetChanged() 351{ 352 m_element->formAttributeTargetChanged(); 353} 354 355} // namespace blink 356