Bug 1494333 - index crons just like artifacts r=Callek
[gecko.git] / dom / html / HTMLFormElement.h
blobdb1bd7bafac319577e1e1ebfeab6fa9533a15957
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"
14 #include "nsCOMPtr.h"
15 #include "nsIForm.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;
28 class nsIURI;
30 namespace mozilla {
31 class EventChainPostVisitor;
32 class EventChainPreVisitor;
33 namespace dom {
34 class HTMLFormControlsCollection;
35 class HTMLImageElement;
37 class HTMLFormElement final : public nsGenericHTMLElement,
38 public nsIWebProgressListener,
39 public nsIForm,
40 public nsIRadioGroupContainer
42 friend class HTMLFormControlsCollection;
44 public:
45 NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLFormElement, form)
47 explicit HTMLFormElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
49 enum {
50 FORM_CONTROL_LIST_HASHTABLE_LENGTH = 8
53 // nsISupports
54 NS_DECL_ISUPPORTS_INHERITED
56 // nsIWebProgressListener
57 NS_DECL_NSIWEBPROGRESSLISTENER
59 // nsIForm
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,
70 const bool aPrevious,
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;
87 // EventTarget
88 virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
90 // nsIContent
91 virtual bool ParseAttribute(int32_t aNamespaceID,
92 nsAtom* aAttribute,
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,
158 bool aNotify);
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
294 * being invalid.
296 * @return Whether the submission of this form has been prevented because of
297 * being invalid.
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);
308 // WebIDL
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)
340 GetEnctype(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
384 // header
385 nsIHTMLCollection* Elements();
387 int32_t Length();
389 void Submit(ErrorResult& aRv);
390 void Reset();
392 bool CheckValidity()
394 return CheckFormValidity(nullptr);
397 bool ReportValidity()
399 return CheckValidFormSubmission();
402 Element*
403 IndexedGetter(uint32_t aIndex, bool &aFound);
405 already_AddRefed<nsISupports>
406 NamedGetter(const nsAString& aName, bool &aFound);
408 void GetSupportedNames(nsTArray<nsString>& aRetval);
410 static int32_t
411 CompareFormControlPosition(Element* aElement1, Element* aElement2,
412 const nsIContent* aForm);
413 #ifdef DEBUG
414 static void
415 AssertDocumentOrder(const nsTArray<nsGenericHTMLFormElement*>& aControls,
416 nsIContent* aForm);
417 static void
418 AssertDocumentOrder(const nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls,
419 nsIContent* aForm);
420 #endif
422 js::ExpandoAndGeneration mExpandoAndGeneration;
424 protected:
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 {
435 public:
436 explicit RemoveElementRunnable(HTMLFormElement* aForm)
437 : Runnable("dom::HTMLFormElement::RemoveElementRunnable")
438 , mForm(aForm)
441 NS_IMETHOD Run() override {
442 mForm->HandleDefaultSubmitRemoval();
443 return NS_OK;
446 private:
447 RefPtr<HTMLFormElement> mForm;
450 nsresult DoSubmitOrReset(WidgetEvent* aEvent,
451 EventMessage aMessage);
452 nsresult DoReset();
454 // Async callback to handle removal of our default submit
455 void HandleDefaultSubmitRemoval();
458 // Submit Helpers
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,
493 bool aEarlyNotify);
496 * If this form submission is secure -> insecure, ask the user if they want
497 * to continue.
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
507 * attribute.
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
516 * invalid controls.
518 * @return Whether the form is currently valid.
520 bool CheckFormValidity(nsTArray<RefPtr<Element>>* aInvalidElements) const;
522 // Clear the mImageNameLookupTable and mImageElements.
523 void Clear();
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);
532 nsresult
533 AddElementToTableInternal(
534 nsInterfaceHashtable<nsStringHashKey,nsISupports>& aTable,
535 nsIContent* aChild, const nsAString& aName);
537 nsresult
538 RemoveElementFromTableInternal(
539 nsInterfaceHashtable<nsStringHashKey,nsISupports>& aTable,
540 nsIContent* aChild, const nsAString& aName);
542 public:
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);
558 protected:
561 // Data members
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 */
622 bool mIsSubmitting;
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
633 * being invalid.
635 bool mEverTriedInvalidSubmit;
637 protected:
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;
643 private:
644 ~HTMLFormElement();
647 } // namespace dom
649 } // namespace mozilla
651 #endif // mozilla_dom_HTMLFormElement_h