1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_HTMLFormElement_h
8 #define mozilla_dom_HTMLFormElement_h
10 #include "mozilla/AsyncEventDispatcher.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/dom/HTMLFormSubmission.h"
13 #include "nsAutoPtr.h"
16 #include "nsIFormControl.h"
17 #include "nsGenericHTMLElement.h"
18 #include "nsIWebProgressListener.h"
19 #include "nsIRadioGroupContainer.h"
20 #include "nsIWeakReferenceUtils.h"
21 #include "nsThreadUtils.h"
22 #include "nsInterfaceHashtable.h"
23 #include "nsRefPtrHashtable.h"
24 #include "nsDataHashtable.h"
25 #include "jsfriendapi.h" // For js::ExpandoAndGeneration
27 class nsIMutableArray
;
31 class EventChainPostVisitor
;
32 class EventChainPreVisitor
;
34 class HTMLFormControlsCollection
;
35 class HTMLImageElement
;
37 class HTMLFormElement final
: public nsGenericHTMLElement
,
38 public nsIWebProgressListener
,
40 public nsIRadioGroupContainer
42 friend class HTMLFormControlsCollection
;
45 NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLFormElement
, form
)
47 explicit HTMLFormElement(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
);
50 FORM_CONTROL_LIST_HASHTABLE_LENGTH
= 8
54 NS_DECL_ISUPPORTS_INHERITED
56 // nsIWebProgressListener
57 NS_DECL_NSIWEBPROGRESSLISTENER
60 NS_IMETHOD_(nsIFormControl
*) GetElementAt(int32_t aIndex
) const override
;
61 NS_IMETHOD_(uint32_t) GetElementCount() const override
;
62 NS_IMETHOD_(int32_t) IndexOfControl(nsIFormControl
* aControl
) override
;
63 NS_IMETHOD_(nsIFormControl
*) GetDefaultSubmitElement() const override
;
65 // nsIRadioGroupContainer
66 void SetCurrentRadioButton(const nsAString
& aName
,
67 HTMLInputElement
* aRadio
) override
;
68 HTMLInputElement
* GetCurrentRadioButton(const nsAString
& aName
) override
;
69 NS_IMETHOD
GetNextRadioButton(const nsAString
& aName
,
71 HTMLInputElement
* aFocusedRadio
,
72 HTMLInputElement
** aRadioOut
) override
;
73 NS_IMETHOD
WalkRadioGroup(const nsAString
& aName
, nsIRadioVisitor
* aVisitor
,
74 bool aFlushContent
) override
;
75 void AddToRadioGroup(const nsAString
& aName
,
76 HTMLInputElement
* aRadio
) override
;
77 void RemoveFromRadioGroup(const nsAString
& aName
,
78 HTMLInputElement
* aRadio
) override
;
79 virtual uint32_t GetRequiredRadioCount(const nsAString
& aName
) const override
;
80 virtual void RadioRequiredWillChange(const nsAString
& aName
,
81 bool aRequiredAdded
) override
;
82 virtual bool GetValueMissingState(const nsAString
& aName
) const override
;
83 virtual void SetValueMissingState(const nsAString
& aName
, bool aValue
) override
;
85 virtual EventStates
IntrinsicState() const override
;
88 virtual void AsyncEventRunning(AsyncEventDispatcher
* aEvent
) override
;
91 virtual bool ParseAttribute(int32_t aNamespaceID
,
93 const nsAString
& aValue
,
94 nsIPrincipal
* aMaybeScriptedPrincipal
,
95 nsAttrValue
& aResult
) override
;
96 void GetEventTargetParent(EventChainPreVisitor
& aVisitor
) override
;
97 void WillHandleEvent(EventChainPostVisitor
& aVisitor
) override
;
98 virtual nsresult
PostHandleEvent(
99 EventChainPostVisitor
& aVisitor
) override
;
101 virtual nsresult
BindToTree(nsIDocument
* aDocument
, nsIContent
* aParent
,
102 nsIContent
* aBindingParent
) override
;
103 virtual void UnbindFromTree(bool aDeep
= true,
104 bool aNullParent
= true) override
;
105 virtual nsresult
BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
106 const nsAttrValueOrString
* aValue
,
107 bool aNotify
) override
;
108 virtual nsresult
AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
109 const nsAttrValue
* aValue
,
110 const nsAttrValue
* aOldValue
,
111 nsIPrincipal
* aSubjectPrincipal
,
112 bool aNotify
) override
;
115 * Forget all information about the current submission (and the fact that we
116 * are currently submitting at all).
118 void ForgetCurrentSubmission();
120 virtual nsresult
Clone(dom::NodeInfo
*, nsINode
** aResult
) const override
;
122 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLFormElement
,
123 nsGenericHTMLElement
)
126 * Remove an element from this form's list of elements
128 * @param aElement the element to remove
129 * @param aUpdateValidity If true, updates the form validity.
130 * @return NS_OK if the element was successfully removed.
132 nsresult
RemoveElement(nsGenericHTMLFormElement
* aElement
,
133 bool aUpdateValidity
);
136 * Remove an element from the lookup table maintained by the form.
137 * We can't fold this method into RemoveElement() because when
138 * RemoveElement() is called it doesn't know if the element is
139 * removed because the id attribute has changed, or because the
140 * name attribute has changed.
142 * @param aElement the element to remove
143 * @param aName the name or id of the element to remove
144 * @return NS_OK if the element was successfully removed.
146 nsresult
RemoveElementFromTable(nsGenericHTMLFormElement
* aElement
,
147 const nsAString
& aName
);
150 * Add an element to end of this form's list of elements
152 * @param aElement the element to add
153 * @param aUpdateValidity If true, the form validity will be updated.
154 * @param aNotify If true, send nsIDocumentObserver notifications as needed.
155 * @return NS_OK if the element was successfully added
157 nsresult
AddElement(nsGenericHTMLFormElement
* aElement
, bool aUpdateValidity
,
161 * Add an element to the lookup table maintained by the form.
163 * We can't fold this method into AddElement() because when
164 * AddElement() is called, the form control has no
165 * attributes. The name or id attributes of the form control
166 * are used as a key into the table.
168 nsresult
AddElementToTable(nsGenericHTMLFormElement
* aChild
,
169 const nsAString
& aName
);
172 * Remove an image element from this form's list of image elements
174 * @param aElement the image element to remove
175 * @return NS_OK if the element was successfully removed.
177 nsresult
RemoveImageElement(HTMLImageElement
* aElement
);
180 * Remove an image element from the lookup table maintained by the form.
181 * We can't fold this method into RemoveImageElement() because when
182 * RemoveImageElement() is called it doesn't know if the element is
183 * removed because the id attribute has changed, or because the
184 * name attribute has changed.
186 * @param aElement the image element to remove
187 * @param aName the name or id of the element to remove
188 * @return NS_OK if the element was successfully removed.
190 nsresult
RemoveImageElementFromTable(HTMLImageElement
* aElement
,
191 const nsAString
& aName
);
193 * Add an image element to the end of this form's list of image elements
195 * @param aElement the element to add
196 * @return NS_OK if the element was successfully added
198 nsresult
AddImageElement(HTMLImageElement
* aElement
);
201 * Add an image element to the lookup table maintained by the form.
203 * We can't fold this method into AddImageElement() because when
204 * AddImageElement() is called, the image attributes can change.
205 * The name or id attributes of the image are used as a key into the table.
207 nsresult
AddImageElementToTable(HTMLImageElement
* aChild
,
208 const nsAString
& aName
);
211 * Returns true if implicit submission of this form is disabled. For more
212 * on implicit submission see:
214 * http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#implicit-submission
216 bool ImplicitSubmissionIsDisabled() const;
219 * Check whether a given nsIFormControl is the last single line input control
220 * that is not disabled. aControl is expected to not be null.
222 bool IsLastActiveElement(const nsIFormControl
* aControl
) const;
225 * Check whether a given nsIFormControl is the default submit
226 * element. This is different from just comparing to
227 * GetDefaultSubmitElement() in certain situations inside an update
228 * when GetDefaultSubmitElement() might not be up to date. aControl
229 * is expected to not be null.
231 bool IsDefaultSubmitElement(const nsIFormControl
* aControl
) const;
234 * Flag the form to know that a button or image triggered scripted form
235 * submission. In that case the form will defer the submission until the
236 * script handler returns and the return value is known.
238 void OnSubmitClickBegin(Element
* aOriginatingElement
);
239 void OnSubmitClickEnd();
242 * This method will update the form validity so the submit controls states
243 * will be updated (for -moz-submit-invalid pseudo-class).
244 * This method has to be called by form elements whenever their validity state
245 * or status regarding constraint validation changes.
247 * @note This method isn't used for CheckValidity().
248 * @note If an element becomes barred from constraint validation, it has to be
249 * considered as valid.
251 * @param aElementValidityState the new validity state of the element
253 void UpdateValidity(bool aElementValidityState
);
256 * Returns the form validity based on the last UpdateValidity() call.
258 * @return Whether the form was valid the last time UpdateValidity() was called.
260 * @note This method may not return the *current* validity state!
262 bool GetValidity() const { return !mInvalidElementsCount
; }
265 * This method check the form validity and make invalid form elements send
266 * invalid event if needed.
268 * @return Whether the form is valid.
270 * @note Do not call this method if novalidate/formnovalidate is used.
271 * @note This method might disappear with bug 592124, hopefuly.
273 bool CheckValidFormSubmission();
276 * Check whether submission can proceed for this form. This basically
277 * implements steps 1-4 (more or less) of
278 * <https://html.spec.whatwg.org/multipage/forms.html#concept-form-submit>.
279 * aSubmitter, if not null, is the "submitter" from that algorithm. Therefore
280 * it must be a valid submit control.
282 bool SubmissionCanProceed(Element
* aSubmitter
);
285 * Walk over the form elements and call SubmitNamesValues() on them to get
286 * their data pumped into the FormSubmitter.
288 * @param aFormSubmission the form submission object
290 nsresult
WalkFormElements(HTMLFormSubmission
* aFormSubmission
);
293 * Whether the submission of this form has been ever prevented because of
296 * @return Whether the submission of this form has been prevented because of
299 bool HasEverTriedInvalidSubmit() const { return mEverTriedInvalidSubmit
; }
302 * Implements form[name]. Returns form controls in this form with the correct
303 * value of the name attribute.
305 already_AddRefed
<nsISupports
>
306 FindNamedItem(const nsAString
& aName
, nsWrapperCache
** aCache
);
310 void GetAcceptCharset(DOMString
& aValue
)
312 GetHTMLAttr(nsGkAtoms::acceptcharset
, aValue
);
315 void SetAcceptCharset(const nsAString
& aValue
, ErrorResult
& aRv
)
317 SetHTMLAttr(nsGkAtoms::acceptcharset
, aValue
, aRv
);
320 void GetAction(nsString
& aValue
);
321 void SetAction(const nsAString
& aValue
, ErrorResult
& aRv
)
323 SetHTMLAttr(nsGkAtoms::action
, aValue
, aRv
);
326 void GetAutocomplete(nsAString
& aValue
);
327 void SetAutocomplete(const nsAString
& aValue
, ErrorResult
& aRv
)
329 SetHTMLAttr(nsGkAtoms::autocomplete
, aValue
, aRv
);
332 void GetEnctype(nsAString
& aValue
);
333 void SetEnctype(const nsAString
& aValue
, ErrorResult
& aRv
)
335 SetHTMLAttr(nsGkAtoms::enctype
, aValue
, aRv
);
338 void GetEncoding(nsAString
& aValue
)
342 void SetEncoding(const nsAString
& aValue
, ErrorResult
& aRv
)
344 SetEnctype(aValue
, aRv
);
347 void GetMethod(nsAString
& aValue
);
348 void SetMethod(const nsAString
& aValue
, ErrorResult
& aRv
)
350 SetHTMLAttr(nsGkAtoms::method
, aValue
, aRv
);
353 void GetName(DOMString
& aValue
)
355 GetHTMLAttr(nsGkAtoms::name
, aValue
);
358 void SetName(const nsAString
& aValue
, ErrorResult
& aRv
)
360 SetHTMLAttr(nsGkAtoms::name
, aValue
, aRv
);
363 bool NoValidate() const
365 return GetBoolAttr(nsGkAtoms::novalidate
);
368 void SetNoValidate(bool aValue
, ErrorResult
& aRv
)
370 SetHTMLBoolAttr(nsGkAtoms::novalidate
, aValue
, aRv
);
373 void GetTarget(DOMString
& aValue
)
375 GetHTMLAttr(nsGkAtoms::target
, aValue
);
378 void SetTarget(const nsAString
& aValue
, ErrorResult
& aRv
)
380 SetHTMLAttr(nsGkAtoms::target
, aValue
, aRv
);
383 // it's only out-of-line because the class definition is not available in the
385 nsIHTMLCollection
* Elements();
389 void Submit(ErrorResult
& aRv
);
394 return CheckFormValidity(nullptr);
397 bool ReportValidity()
399 return CheckValidFormSubmission();
403 IndexedGetter(uint32_t aIndex
, bool &aFound
);
405 already_AddRefed
<nsISupports
>
406 NamedGetter(const nsAString
& aName
, bool &aFound
);
408 void GetSupportedNames(nsTArray
<nsString
>& aRetval
);
411 CompareFormControlPosition(Element
* aElement1
, Element
* aElement2
,
412 const nsIContent
* aForm
);
415 AssertDocumentOrder(const nsTArray
<nsGenericHTMLFormElement
*>& aControls
,
418 AssertDocumentOrder(const nsTArray
<RefPtr
<nsGenericHTMLFormElement
>>& aControls
,
422 js::ExpandoAndGeneration mExpandoAndGeneration
;
425 virtual JSObject
* WrapNode(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) override
;
427 void PostPasswordEvent();
429 RefPtr
<AsyncEventDispatcher
> mFormPasswordEventDispatcher
;
431 class RemoveElementRunnable
;
432 friend class RemoveElementRunnable
;
434 class RemoveElementRunnable
: public Runnable
{
436 explicit RemoveElementRunnable(HTMLFormElement
* aForm
)
437 : Runnable("dom::HTMLFormElement::RemoveElementRunnable")
441 NS_IMETHOD
Run() override
{
442 mForm
->HandleDefaultSubmitRemoval();
447 RefPtr
<HTMLFormElement
> mForm
;
450 nsresult
DoSubmitOrReset(WidgetEvent
* aEvent
,
451 EventMessage aMessage
);
454 // Async callback to handle removal of our default submit
455 void HandleDefaultSubmitRemoval();
462 * Attempt to submit (submission might be deferred)
463 * (called by DoSubmitOrReset)
465 * @param aPresContext the presentation context
466 * @param aEvent the DOM event that was passed to us for the submit
468 nsresult
DoSubmit(WidgetEvent
* aEvent
);
471 * Prepare the submission object (called by DoSubmit)
473 * @param aFormSubmission the submission object
474 * @param aEvent the DOM event that was passed to us for the submit
476 nsresult
BuildSubmission(HTMLFormSubmission
** aFormSubmission
,
477 WidgetEvent
* aEvent
);
479 * Perform the submission (called by DoSubmit and FlushPendingSubmission)
481 * @param aFormSubmission the submission object
483 nsresult
SubmitSubmission(HTMLFormSubmission
* aFormSubmission
);
486 * Notify any submit observers of the submit.
488 * @param aActionURL the URL being submitted to
489 * @param aCancelSubmit out param where submit observers can specify that the
490 * submit should be cancelled.
492 nsresult
NotifySubmitObservers(nsIURI
* aActionURL
, bool* aCancelSubmit
,
496 * If this form submission is secure -> insecure, ask the user if they want
499 * @param aActionURL the URL being submitted to
500 * @param aCancelSubmit out param: will be true if the user wants to cancel
502 nsresult
DoSecureToInsecureSubmitCheck(nsIURI
* aActionURL
,
503 bool* aCancelSubmit
);
506 * Find form controls in this form with the correct value in the name
509 already_AddRefed
<nsISupports
> DoResolveName(const nsAString
& aName
, bool aFlushContent
);
512 * Check the form validity following this algorithm:
513 * http://www.whatwg.org/specs/web-apps/current-work/#statically-validate-the-constraints
515 * @param aInvalidElements [out] parameter containing the list of unhandled
518 * @return Whether the form is currently valid.
520 bool CheckFormValidity(nsTArray
<RefPtr
<Element
>>* aInvalidElements
) const;
522 // Clear the mImageNameLookupTable and mImageElements.
525 // Insert a element into the past names map.
526 void AddToPastNamesMap(const nsAString
& aName
, nsISupports
* aChild
);
528 // Remove the given element from the past names map. The element must be an
529 // nsGenericHTMLFormElement or HTMLImageElement.
530 void RemoveElementFromPastNamesMap(Element
* aElement
);
533 AddElementToTableInternal(
534 nsInterfaceHashtable
<nsStringHashKey
,nsISupports
>& aTable
,
535 nsIContent
* aChild
, const nsAString
& aName
);
538 RemoveElementFromTableInternal(
539 nsInterfaceHashtable
<nsStringHashKey
,nsISupports
>& aTable
,
540 nsIContent
* aChild
, const nsAString
& aName
);
544 * Flush a possible pending submission. If there was a scripted submission
545 * triggered by a button or image, the submission was defered. This method
546 * forces the pending submission to be submitted. (happens when the handler
547 * returns false or there is an action/target change in the script)
549 void FlushPendingSubmission();
552 * Get the full URL to submit to. Do not submit if the returned URL is null.
554 * @param aActionURL the full, unadulterated URL you'll be submitting to [OUT]
555 * @param aOriginatingElement the originating element of the form submission [IN]
557 nsresult
GetActionURL(nsIURI
** aActionURL
, Element
* aOriginatingElement
);
563 /** The list of controls (form.elements as well as stuff not in elements) */
564 RefPtr
<HTMLFormControlsCollection
> mControls
;
565 /** The currently selected radio button of each group */
566 nsRefPtrHashtable
<nsStringHashKey
, HTMLInputElement
> mSelectedRadioButtons
;
567 /** The number of required radio button of each group */
568 nsDataHashtable
<nsStringCaseInsensitiveHashKey
,uint32_t> mRequiredRadioButtonCounts
;
569 /** The value missing state of each group */
570 nsDataHashtable
<nsStringCaseInsensitiveHashKey
,bool> mValueMissingRadioGroups
;
572 /** The pending submission object */
573 nsAutoPtr
<HTMLFormSubmission
> mPendingSubmission
;
574 /** The request currently being submitted */
575 nsCOMPtr
<nsIRequest
> mSubmittingRequest
;
576 /** The web progress object we are currently listening to */
577 nsWeakPtr mWebProgress
;
579 /** The default submit element -- WEAK */
580 nsGenericHTMLFormElement
* mDefaultSubmitElement
;
582 /** The first submit element in mElements -- WEAK */
583 nsGenericHTMLFormElement
* mFirstSubmitInElements
;
585 /** The first submit element in mNotInElements -- WEAK */
586 nsGenericHTMLFormElement
* mFirstSubmitNotInElements
;
588 // This array holds on to all HTMLImageElement(s).
589 // This is needed to properly clean up the bi-directional references
590 // (both weak and strong) between the form and its HTMLImageElements.
592 nsTArray
<HTMLImageElement
*> mImageElements
; // Holds WEAK references
594 // A map from an ID or NAME attribute to the HTMLImageElement(s), this
595 // hash holds strong references either to the named HTMLImageElement, or
596 // to a list of named HTMLImageElement(s), in the case where this hash
597 // holds on to a list of named HTMLImageElement(s) the list has weak
598 // references to the HTMLImageElement.
600 nsInterfaceHashtable
<nsStringHashKey
,nsISupports
> mImageNameLookupTable
;
602 // A map from names to elements that were gotten by those names from this
603 // form in that past. See "past names map" in the HTML5 specification.
605 nsInterfaceHashtable
<nsStringHashKey
,nsISupports
> mPastNameLookupTable
;
607 /** Keep track of what the popup state was when the submit was initiated */
608 PopupControlState mSubmitPopupState
;
611 * Number of invalid and candidate for constraint validation elements in the
612 * form the last time UpdateValidity has been called.
613 * @note Should only be used by UpdateValidity() and GetValidity()!
615 int32_t mInvalidElementsCount
;
617 /** Whether we are currently processing a submit event or not */
618 bool mGeneratingSubmit
;
619 /** Whether we are currently processing a reset event or not */
620 bool mGeneratingReset
;
621 /** Whether we are submitting currently */
623 /** Whether the submission is to be deferred in case a script triggers it */
624 bool mDeferSubmission
;
625 /** Whether we notified NS_FORMSUBMIT_SUBJECT listeners already */
626 bool mNotifiedObservers
;
627 /** If we notified the listeners early, what was the result? */
628 bool mNotifiedObserversResult
;
629 /** Keep track of whether a submission was user-initiated or not */
630 bool mSubmitInitiatedFromUserInput
;
632 * Whether the submission of this form has been ever prevented because of
635 bool mEverTriedInvalidSubmit
;
638 /** Detection of first form to notify observers */
639 static bool gFirstFormSubmitted
;
640 /** Detection of first password input to initialize the password manager */
641 static bool gPasswordManagerInitialized
;
649 } // namespace mozilla
651 #endif // mozilla_dom_HTMLFormElement_h