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/HTMLFormControlElement.h"
27
28 #include "core/events/Event.h"
29 #include "core/html/HTMLDataListElement.h"
30 #include "core/html/HTMLFieldSetElement.h"
31 #include "core/html/HTMLFormElement.h"
32 #include "core/html/HTMLInputElement.h"
33 #include "core/html/HTMLLegendElement.h"
34 #include "core/html/ValidityState.h"
35 #include "core/html/forms/ValidationMessage.h"
36 #include "core/frame/UseCounter.h"
37 #include "core/rendering/RenderBox.h"
38 #include "core/rendering/RenderTheme.h"
39 #include "wtf/Vector.h"
40
41 namespace WebCore {
42
43 using namespace HTMLNames;
44
HTMLFormControlElement(const QualifiedName & tagName,Document & document,HTMLFormElement * form)45 HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
46 : LabelableElement(tagName, document)
47 , m_disabled(false)
48 , m_isAutofilled(false)
49 , m_isReadOnly(false)
50 , m_isRequired(false)
51 , m_ancestorDisabledState(AncestorDisabledStateUnknown)
52 , m_dataListAncestorState(Unknown)
53 , m_willValidateInitialized(false)
54 , m_willValidate(true)
55 , m_isValid(true)
56 , m_wasChangedSinceLastFormControlChangeEvent(false)
57 , m_wasFocusedByMouse(false)
58 {
59 setHasCustomStyleCallbacks();
60 associateByParser(form);
61 }
62
~HTMLFormControlElement()63 HTMLFormControlElement::~HTMLFormControlElement()
64 {
65 #if !ENABLE(OILPAN)
66 setForm(0);
67 #endif
68 }
69
trace(Visitor * visitor)70 void HTMLFormControlElement::trace(Visitor* visitor)
71 {
72 FormAssociatedElement::trace(visitor);
73 LabelableElement::trace(visitor);
74 }
75
formEnctype() const76 String HTMLFormControlElement::formEnctype() const
77 {
78 const AtomicString& formEnctypeAttr = fastGetAttribute(formenctypeAttr);
79 if (formEnctypeAttr.isNull())
80 return emptyString();
81 return FormSubmission::Attributes::parseEncodingType(formEnctypeAttr);
82 }
83
setFormEnctype(const AtomicString & value)84 void HTMLFormControlElement::setFormEnctype(const AtomicString& value)
85 {
86 setAttribute(formenctypeAttr, value);
87 }
88
formMethod() const89 String HTMLFormControlElement::formMethod() const
90 {
91 const AtomicString& formMethodAttr = fastGetAttribute(formmethodAttr);
92 if (formMethodAttr.isNull())
93 return emptyString();
94 return FormSubmission::Attributes::methodString(FormSubmission::Attributes::parseMethodType(formMethodAttr));
95 }
96
setFormMethod(const AtomicString & value)97 void HTMLFormControlElement::setFormMethod(const AtomicString& value)
98 {
99 setAttribute(formmethodAttr, value);
100 }
101
formNoValidate() const102 bool HTMLFormControlElement::formNoValidate() const
103 {
104 return fastHasAttribute(formnovalidateAttr);
105 }
106
updateAncestorDisabledState() const107 void HTMLFormControlElement::updateAncestorDisabledState() const
108 {
109 HTMLFieldSetElement* fieldSetAncestor = 0;
110 ContainerNode* legendAncestor = 0;
111 for (HTMLElement* ancestor = Traversal<HTMLElement>::firstAncestor(*this); ancestor; ancestor = Traversal<HTMLElement>::firstAncestor(*ancestor)) {
112 if (!legendAncestor && isHTMLLegendElement(*ancestor))
113 legendAncestor = ancestor;
114 if (isHTMLFieldSetElement(*ancestor)) {
115 fieldSetAncestor = toHTMLFieldSetElement(ancestor);
116 break;
117 }
118 }
119 m_ancestorDisabledState = (fieldSetAncestor && fieldSetAncestor->isDisabledFormControl() && !(legendAncestor && legendAncestor == fieldSetAncestor->legend())) ? AncestorDisabledStateDisabled : AncestorDisabledStateEnabled;
120 }
121
ancestorDisabledStateWasChanged()122 void HTMLFormControlElement::ancestorDisabledStateWasChanged()
123 {
124 m_ancestorDisabledState = AncestorDisabledStateUnknown;
125 disabledAttributeChanged();
126 }
127
reset()128 void HTMLFormControlElement::reset()
129 {
130 setAutofilled(false);
131 resetImpl();
132 }
133
parseAttribute(const QualifiedName & name,const AtomicString & value)134 void HTMLFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
135 {
136 if (name == formAttr) {
137 formAttributeChanged();
138 UseCounter::count(document(), UseCounter::FormAttribute);
139 } else if (name == disabledAttr) {
140 bool oldDisabled = m_disabled;
141 m_disabled = !value.isNull();
142 if (oldDisabled != m_disabled)
143 disabledAttributeChanged();
144 } else if (name == readonlyAttr) {
145 bool wasReadOnly = m_isReadOnly;
146 m_isReadOnly = !value.isNull();
147 if (wasReadOnly != m_isReadOnly) {
148 setNeedsWillValidateCheck();
149 setNeedsStyleRecalc(SubtreeStyleChange);
150 if (renderer() && renderer()->style()->hasAppearance())
151 RenderTheme::theme().stateChanged(renderer(), ReadOnlyControlState);
152 }
153 } else if (name == requiredAttr) {
154 bool wasRequired = m_isRequired;
155 m_isRequired = !value.isNull();
156 if (wasRequired != m_isRequired)
157 requiredAttributeChanged();
158 UseCounter::count(document(), UseCounter::RequiredAttribute);
159 } else if (name == autofocusAttr) {
160 HTMLElement::parseAttribute(name, value);
161 UseCounter::count(document(), UseCounter::AutoFocusAttribute);
162 } else
163 HTMLElement::parseAttribute(name, value);
164 }
165
disabledAttributeChanged()166 void HTMLFormControlElement::disabledAttributeChanged()
167 {
168 setNeedsWillValidateCheck();
169 didAffectSelector(AffectedSelectorDisabled | AffectedSelectorEnabled);
170 if (renderer() && renderer()->style()->hasAppearance())
171 RenderTheme::theme().stateChanged(renderer(), EnabledControlState);
172 if (isDisabledFormControl() && treeScope().adjustedFocusedElement() == this) {
173 // We might want to call blur(), but it's dangerous to dispatch events
174 // here.
175 document().setNeedsFocusedElementCheck();
176 }
177 }
178
requiredAttributeChanged()179 void HTMLFormControlElement::requiredAttributeChanged()
180 {
181 setNeedsValidityCheck();
182 // Style recalculation is needed because style selectors may include
183 // :required and :optional pseudo-classes.
184 setNeedsStyleRecalc(SubtreeStyleChange);
185 }
186
supportsAutofocus() const187 bool HTMLFormControlElement::supportsAutofocus() const
188 {
189 return false;
190 }
191
isAutofocusable() const192 bool HTMLFormControlElement::isAutofocusable() const
193 {
194 return fastHasAttribute(autofocusAttr) && supportsAutofocus();
195 }
196
setAutofilled(bool autofilled)197 void HTMLFormControlElement::setAutofilled(bool autofilled)
198 {
199 if (autofilled == m_isAutofilled)
200 return;
201
202 m_isAutofilled = autofilled;
203 setNeedsStyleRecalc(SubtreeStyleChange);
204 }
205
shouldAutofocusOnAttach(const HTMLFormControlElement * element)206 static bool shouldAutofocusOnAttach(const HTMLFormControlElement* element)
207 {
208 if (!element->isAutofocusable())
209 return false;
210 if (element->document().isSandboxed(SandboxAutomaticFeatures)) {
211 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
212 element->document().addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Blocked autofocusing on a form control because the form's frame is sandboxed and the 'allow-scripts' permission is not set.");
213 return false;
214 }
215
216 return true;
217 }
218
attach(const AttachContext & context)219 void HTMLFormControlElement::attach(const AttachContext& context)
220 {
221 HTMLElement::attach(context);
222
223 if (!renderer())
224 return;
225
226 // The call to updateFromElement() needs to go after the call through
227 // to the base class's attach() because that can sometimes do a close
228 // on the renderer.
229 renderer()->updateFromElement();
230
231 // FIXME: Autofocus handling should be moved to insertedInto according to
232 // the standard.
233 if (shouldAutofocusOnAttach(this))
234 document().setAutofocusElement(this);
235 }
236
didMoveToNewDocument(Document & oldDocument)237 void HTMLFormControlElement::didMoveToNewDocument(Document& oldDocument)
238 {
239 FormAssociatedElement::didMoveToNewDocument(oldDocument);
240 HTMLElement::didMoveToNewDocument(oldDocument);
241 }
242
insertedInto(ContainerNode * insertionPoint)243 Node::InsertionNotificationRequest HTMLFormControlElement::insertedInto(ContainerNode* insertionPoint)
244 {
245 m_ancestorDisabledState = AncestorDisabledStateUnknown;
246 m_dataListAncestorState = Unknown;
247 setNeedsWillValidateCheck();
248 HTMLElement::insertedInto(insertionPoint);
249 FormAssociatedElement::insertedInto(insertionPoint);
250 return InsertionDone;
251 }
252
removedFrom(ContainerNode * insertionPoint)253 void HTMLFormControlElement::removedFrom(ContainerNode* insertionPoint)
254 {
255 hideVisibleValidationMessage();
256 m_validationMessage = nullptr;
257 m_ancestorDisabledState = AncestorDisabledStateUnknown;
258 m_dataListAncestorState = Unknown;
259 HTMLElement::removedFrom(insertionPoint);
260 FormAssociatedElement::removedFrom(insertionPoint);
261 }
262
setChangedSinceLastFormControlChangeEvent(bool changed)263 void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed)
264 {
265 m_wasChangedSinceLastFormControlChangeEvent = changed;
266 }
267
dispatchChangeEvent()268 void HTMLFormControlElement::dispatchChangeEvent()
269 {
270 dispatchScopedEvent(Event::createBubble(EventTypeNames::change));
271 }
272
dispatchFormControlChangeEvent()273 void HTMLFormControlElement::dispatchFormControlChangeEvent()
274 {
275 dispatchChangeEvent();
276 setChangedSinceLastFormControlChangeEvent(false);
277 }
278
dispatchFormControlInputEvent()279 void HTMLFormControlElement::dispatchFormControlInputEvent()
280 {
281 setChangedSinceLastFormControlChangeEvent(true);
282 HTMLElement::dispatchInputEvent();
283 }
284
formOwner() const285 HTMLFormElement* HTMLFormControlElement::formOwner() const
286 {
287 return FormAssociatedElement::form();
288 }
289
isDisabledFormControl() const290 bool HTMLFormControlElement::isDisabledFormControl() const
291 {
292 if (m_disabled)
293 return true;
294
295 if (m_ancestorDisabledState == AncestorDisabledStateUnknown)
296 updateAncestorDisabledState();
297 return m_ancestorDisabledState == AncestorDisabledStateDisabled;
298 }
299
isRequired() const300 bool HTMLFormControlElement::isRequired() const
301 {
302 return m_isRequired;
303 }
304
resultForDialogSubmit()305 String HTMLFormControlElement::resultForDialogSubmit()
306 {
307 return fastGetAttribute(valueAttr);
308 }
309
didRecalcStyle(StyleRecalcChange)310 void HTMLFormControlElement::didRecalcStyle(StyleRecalcChange)
311 {
312 if (RenderObject* renderer = this->renderer())
313 renderer->updateFromElement();
314 }
315
supportsFocus() const316 bool HTMLFormControlElement::supportsFocus() const
317 {
318 return !isDisabledFormControl();
319 }
320
isKeyboardFocusable() const321 bool HTMLFormControlElement::isKeyboardFocusable() const
322 {
323 // Skip tabIndex check in a parent class.
324 return isFocusable();
325 }
326
shouldShowFocusRingOnMouseFocus() const327 bool HTMLFormControlElement::shouldShowFocusRingOnMouseFocus() const
328 {
329 return false;
330 }
331
dispatchFocusEvent(Element * oldFocusedElement,FocusType type)332 void HTMLFormControlElement::dispatchFocusEvent(Element* oldFocusedElement, FocusType type)
333 {
334 if (type != FocusTypePage)
335 m_wasFocusedByMouse = type == FocusTypeMouse;
336 HTMLElement::dispatchFocusEvent(oldFocusedElement, type);
337 }
338
shouldHaveFocusAppearance() const339 bool HTMLFormControlElement::shouldHaveFocusAppearance() const
340 {
341 ASSERT(focused());
342 return shouldShowFocusRingOnMouseFocus() || !m_wasFocusedByMouse;
343 }
344
willCallDefaultEventHandler(const Event & event)345 void HTMLFormControlElement::willCallDefaultEventHandler(const Event& event)
346 {
347 if (!event.isKeyboardEvent() || event.type() != EventTypeNames::keydown)
348 return;
349 if (!m_wasFocusedByMouse)
350 return;
351 m_wasFocusedByMouse = false;
352 if (renderer())
353 renderer()->paintInvalidationForWholeRenderer();
354 }
355
356
tabIndex() const357 short HTMLFormControlElement::tabIndex() const
358 {
359 // Skip the supportsFocus check in HTMLElement.
360 return Element::tabIndex();
361 }
362
recalcWillValidate() const363 bool HTMLFormControlElement::recalcWillValidate() const
364 {
365 if (m_dataListAncestorState == Unknown) {
366 if (Traversal<HTMLDataListElement>::firstAncestor(*this))
367 m_dataListAncestorState = InsideDataList;
368 else
369 m_dataListAncestorState = NotInsideDataList;
370 }
371 return m_dataListAncestorState == NotInsideDataList && !isDisabledOrReadOnly();
372 }
373
willValidate() const374 bool HTMLFormControlElement::willValidate() const
375 {
376 if (!m_willValidateInitialized || m_dataListAncestorState == Unknown) {
377 m_willValidateInitialized = true;
378 bool newWillValidate = recalcWillValidate();
379 if (m_willValidate != newWillValidate) {
380 m_willValidate = newWillValidate;
381 const_cast<HTMLFormControlElement*>(this)->setNeedsValidityCheck();
382 }
383 } else {
384 // If the following assertion fails, setNeedsWillValidateCheck() is not
385 // called correctly when something which changes recalcWillValidate() result
386 // is updated.
387 ASSERT(m_willValidate == recalcWillValidate());
388 }
389 return m_willValidate;
390 }
391
setNeedsWillValidateCheck()392 void HTMLFormControlElement::setNeedsWillValidateCheck()
393 {
394 // We need to recalculate willValidate immediately because willValidate change can causes style change.
395 bool newWillValidate = recalcWillValidate();
396 if (m_willValidateInitialized && m_willValidate == newWillValidate)
397 return;
398 m_willValidateInitialized = true;
399 m_willValidate = newWillValidate;
400 setNeedsValidityCheck();
401 setNeedsStyleRecalc(SubtreeStyleChange);
402 if (!m_willValidate)
403 hideVisibleValidationMessage();
404 }
405
updateVisibleValidationMessage()406 void HTMLFormControlElement::updateVisibleValidationMessage()
407 {
408 Page* page = document().page();
409 if (!page)
410 return;
411 String message;
412 if (renderer() && willValidate())
413 message = validationMessage().stripWhiteSpace();
414 if (!m_validationMessage)
415 m_validationMessage = ValidationMessage::create(this);
416 m_validationMessage->updateValidationMessage(message);
417 }
418
hideVisibleValidationMessage()419 void HTMLFormControlElement::hideVisibleValidationMessage()
420 {
421 if (m_validationMessage)
422 m_validationMessage->requestToHideMessage();
423 }
424
checkValidity(WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement>> * unhandledInvalidControls)425 bool HTMLFormControlElement::checkValidity(WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement> >* unhandledInvalidControls)
426 {
427 if (!willValidate() || isValidFormControlElement())
428 return true;
429 // An event handler can deref this object.
430 RefPtrWillBeRawPtr<HTMLFormControlElement> protector(this);
431 RefPtrWillBeRawPtr<Document> originalDocument(document());
432 bool needsDefaultAction = dispatchEvent(Event::createCancelable(EventTypeNames::invalid));
433 if (needsDefaultAction && unhandledInvalidControls && inDocument() && originalDocument == document())
434 unhandledInvalidControls->append(this);
435 return false;
436 }
437
isValidFormControlElement()438 bool HTMLFormControlElement::isValidFormControlElement()
439 {
440 // If the following assertion fails, setNeedsValidityCheck() is not called
441 // correctly when something which changes validity is updated.
442 ASSERT(m_isValid == valid());
443 return m_isValid;
444 }
445
setNeedsValidityCheck()446 void HTMLFormControlElement::setNeedsValidityCheck()
447 {
448 bool newIsValid = valid();
449 if (willValidate() && newIsValid != m_isValid) {
450 // Update style for pseudo classes such as :valid :invalid.
451 setNeedsStyleRecalc(SubtreeStyleChange);
452 }
453 m_isValid = newIsValid;
454
455 // Updates only if this control already has a validation message.
456 if (m_validationMessage && m_validationMessage->isVisible()) {
457 // Calls updateVisibleValidationMessage() even if m_isValid is not
458 // changed because a validation message can be chagned.
459 updateVisibleValidationMessage();
460 }
461 }
462
setCustomValidity(const String & error)463 void HTMLFormControlElement::setCustomValidity(const String& error)
464 {
465 FormAssociatedElement::setCustomValidity(error);
466 setNeedsValidityCheck();
467 }
468
dispatchBlurEvent(Element * newFocusedElement)469 void HTMLFormControlElement::dispatchBlurEvent(Element* newFocusedElement)
470 {
471 HTMLElement::dispatchBlurEvent(newFocusedElement);
472 hideVisibleValidationMessage();
473 }
474
isSuccessfulSubmitButton() const475 bool HTMLFormControlElement::isSuccessfulSubmitButton() const
476 {
477 return canBeSuccessfulSubmitButton() && !isDisabledFormControl();
478 }
479
isDefaultButtonForForm() const480 bool HTMLFormControlElement::isDefaultButtonForForm() const
481 {
482 return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this;
483 }
484
enclosingFormControlElement(Node * node)485 HTMLFormControlElement* HTMLFormControlElement::enclosingFormControlElement(Node* node)
486 {
487 if (!node)
488 return 0;
489 return Traversal<HTMLFormControlElement>::firstAncestorOrSelf(*node);
490 }
491
nameForAutofill() const492 String HTMLFormControlElement::nameForAutofill() const
493 {
494 String fullName = name();
495 String trimmedName = fullName.stripWhiteSpace();
496 if (!trimmedName.isEmpty())
497 return trimmedName;
498 fullName = getIdAttribute();
499 trimmedName = fullName.stripWhiteSpace();
500 return trimmedName;
501 }
502
setFocus(bool flag)503 void HTMLFormControlElement::setFocus(bool flag)
504 {
505 LabelableElement::setFocus(flag);
506
507 if (!flag && wasChangedSinceLastFormControlChangeEvent())
508 dispatchFormControlChangeEvent();
509 }
510
511 } // namespace Webcore
512