Bug 1862332 [wpt PR 42877] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / dom / html / HTMLFormElement.h
blob263f665843d71151a08dec9176c54d63acce2644
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/UniquePtr.h"
13 #include "mozilla/dom/BrowsingContext.h"
14 #include "mozilla/dom/PopupBlocker.h"
15 #include "mozilla/dom/RadioGroupContainer.h"
16 #include "nsIFormControl.h"
17 #include "nsGenericHTMLElement.h"
18 #include "nsThreadUtils.h"
19 #include "nsInterfaceHashtable.h"
20 #include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration
22 class nsIMutableArray;
23 class nsIURI;
25 namespace mozilla {
26 class EventChainPostVisitor;
27 class EventChainPreVisitor;
28 namespace dom {
29 class DialogFormSubmission;
30 class HTMLFormControlsCollection;
31 class HTMLFormSubmission;
32 class HTMLImageElement;
33 class FormData;
35 class HTMLFormElement final : public nsGenericHTMLElement {
36 friend class HTMLFormControlsCollection;
38 public:
39 NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLFormElement, form)
41 explicit HTMLFormElement(
42 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
44 enum { FORM_CONTROL_LIST_HASHTABLE_LENGTH = 8 };
46 // nsISupports
47 NS_DECL_ISUPPORTS_INHERITED
49 int32_t IndexOfContent(nsIContent* aContent);
50 nsGenericHTMLFormElement* GetDefaultSubmitElement() const;
51 bool IsDefaultSubmitElement(nsGenericHTMLFormElement* aElement) const {
52 return aElement == mDefaultSubmitElement;
55 // EventTarget
56 void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
58 /** Whether we already dispatched a DOMFormHasPassword event or not */
59 bool mHasPendingPasswordEvent = false;
60 /** Whether we already dispatched a DOMFormHasPossibleUsername event or not */
61 bool mHasPendingPossibleUsernameEvent = false;
63 // nsIContent
64 bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
65 const nsAString& aValue,
66 nsIPrincipal* aMaybeScriptedPrincipal,
67 nsAttrValue& aResult) override;
68 void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
69 void WillHandleEvent(EventChainPostVisitor& aVisitor) override;
70 nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override;
72 nsresult BindToTree(BindContext&, nsINode& aParent) override;
73 void UnbindFromTree(UnbindContext&) override;
74 void BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
75 const nsAttrValue* aValue, bool aNotify) override;
77 /**
78 * Forget all information about the current submission (and the fact that we
79 * are currently submitting at all).
81 void ForgetCurrentSubmission();
83 nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
85 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLFormElement,
86 nsGenericHTMLElement)
88 /**
89 * Remove an element from this form's list of elements
91 * @param aElement the element to remove
92 * @param aUpdateValidity If true, updates the form validity.
93 * @return NS_OK if the element was successfully removed.
95 nsresult RemoveElement(nsGenericHTMLFormElement* aElement,
96 bool aUpdateValidity);
98 /**
99 * Remove an element from the lookup table maintained by the form.
100 * We can't fold this method into RemoveElement() because when
101 * RemoveElement() is called it doesn't know if the element is
102 * removed because the id attribute has changed, or because the
103 * name attribute has changed.
105 * @param aElement the element to remove
106 * @param aName the name or id of the element to remove
107 * @return NS_OK if the element was successfully removed.
109 nsresult RemoveElementFromTable(nsGenericHTMLFormElement* aElement,
110 const nsAString& aName);
113 * Add an element to end of this form's list of elements
115 * @param aElement the element to add
116 * @param aUpdateValidity If true, the form validity will be updated.
117 * @param aNotify If true, send DocumentObserver notifications as needed.
118 * @return NS_OK if the element was successfully added
120 nsresult AddElement(nsGenericHTMLFormElement* aElement, bool aUpdateValidity,
121 bool aNotify);
124 * Add an element to the lookup table maintained by the form.
126 * We can't fold this method into AddElement() because when
127 * AddElement() is called, the form control has no
128 * attributes. The name or id attributes of the form control
129 * are used as a key into the table.
131 nsresult AddElementToTable(nsGenericHTMLFormElement* aChild,
132 const nsAString& aName);
135 * Remove an image element from this form's list of image elements
137 * @param aElement the image element to remove
138 * @return NS_OK if the element was successfully removed.
140 nsresult RemoveImageElement(HTMLImageElement* aElement);
143 * Remove an image element from the lookup table maintained by the form.
144 * We can't fold this method into RemoveImageElement() because when
145 * RemoveImageElement() is called it doesn't know if the element is
146 * removed because the id attribute has changed, or because the
147 * name attribute has changed.
149 * @param aElement the image element to remove
150 * @param aName the name or id of the element to remove
151 * @return NS_OK if the element was successfully removed.
153 nsresult RemoveImageElementFromTable(HTMLImageElement* aElement,
154 const nsAString& aName);
156 * Add an image element to the end of this form's list of image elements
158 * @param aElement the element to add
159 * @return NS_OK if the element was successfully added
161 nsresult AddImageElement(HTMLImageElement* aElement);
164 * Add an image element to the lookup table maintained by the form.
166 * We can't fold this method into AddImageElement() because when
167 * AddImageElement() is called, the image attributes can change.
168 * The name or id attributes of the image are used as a key into the table.
170 nsresult AddImageElementToTable(HTMLImageElement* aChild,
171 const nsAString& aName);
174 * Returns true if implicit submission of this form is disabled. For more
175 * on implicit submission see:
177 * http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#implicit-submission
179 bool ImplicitSubmissionIsDisabled() const;
182 * Check whether a given nsGenericHTMLFormElement is the last single line
183 * input control that is not disabled. aElement is expected to not be null.
185 bool IsLastActiveElement(const nsGenericHTMLFormElement* aElement) const;
188 * Flag the form to know that a button or image triggered scripted form
189 * submission. In that case the form will defer the submission until the
190 * script handler returns and the return value is known.
192 void OnSubmitClickBegin(Element* aOriginatingElement);
193 void OnSubmitClickEnd();
196 * This method will update the form validity.
198 * This method has to be called by form elements whenever their validity state
199 * or status regarding constraint validation changes.
201 * @note This method isn't used for CheckValidity().
202 * @note If an element becomes barred from constraint validation, it has to be
203 * considered as valid.
205 * @param aElementValidityState the new validity state of the element
207 void UpdateValidity(bool aElementValidityState);
210 * This method check the form validity and make invalid form elements send
211 * invalid event if needed.
213 * @return Whether the form is valid.
215 * @note This method might disappear with bug 592124, hopefuly.
216 * @see
217 * https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#interactively-validate-the-constraints
219 MOZ_CAN_RUN_SCRIPT
220 bool CheckValidFormSubmission();
223 * Contruct the entry list to get their data pumped into the FormData and
224 * fire a `formdata` event with the entry list in formData attribute.
225 * <https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-form-data-set>
227 * @param aFormData the form data object
229 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
230 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult ConstructEntryList(FormData*);
233 * Implements form[name]. Returns form controls in this form with the correct
234 * value of the name attribute.
236 already_AddRefed<nsISupports> FindNamedItem(const nsAString& aName,
237 nsWrapperCache** aCache);
239 // WebIDL
241 void GetAcceptCharset(DOMString& aValue) {
242 GetHTMLAttr(nsGkAtoms::acceptcharset, aValue);
245 void SetAcceptCharset(const nsAString& aValue, ErrorResult& aRv) {
246 SetHTMLAttr(nsGkAtoms::acceptcharset, aValue, aRv);
249 void GetAction(nsString& aValue);
250 void SetAction(const nsAString& aValue, ErrorResult& aRv) {
251 SetHTMLAttr(nsGkAtoms::action, aValue, aRv);
254 void GetAutocomplete(nsAString& aValue);
255 void SetAutocomplete(const nsAString& aValue, ErrorResult& aRv) {
256 SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv);
259 void GetEnctype(nsAString& aValue);
260 void SetEnctype(const nsAString& aValue, ErrorResult& aRv) {
261 SetHTMLAttr(nsGkAtoms::enctype, aValue, aRv);
264 void GetEncoding(nsAString& aValue) { GetEnctype(aValue); }
265 void SetEncoding(const nsAString& aValue, ErrorResult& aRv) {
266 SetEnctype(aValue, aRv);
269 void GetMethod(nsAString& aValue);
270 void SetMethod(const nsAString& aValue, ErrorResult& aRv) {
271 SetHTMLAttr(nsGkAtoms::method, aValue, aRv);
274 void GetName(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::name, aValue); }
276 void SetName(const nsAString& aValue, ErrorResult& aRv) {
277 SetHTMLAttr(nsGkAtoms::name, aValue, aRv);
280 bool NoValidate() const { return GetBoolAttr(nsGkAtoms::novalidate); }
282 void SetNoValidate(bool aValue, ErrorResult& aRv) {
283 SetHTMLBoolAttr(nsGkAtoms::novalidate, aValue, aRv);
286 void GetTarget(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::target, aValue); }
288 void SetTarget(const nsAString& aValue, ErrorResult& aRv) {
289 SetHTMLAttr(nsGkAtoms::target, aValue, aRv);
292 void GetRel(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::rel, aValue); }
293 void SetRel(const nsAString& aRel, ErrorResult& aError) {
294 SetHTMLAttr(nsGkAtoms::rel, aRel, aError);
296 nsDOMTokenList* RelList();
298 // it's only out-of-line because the class definition is not available in the
299 // header
300 HTMLFormControlsCollection* Elements();
302 int32_t Length();
305 * Check whether submission can proceed for this form then fire submit event.
306 * This basically implements steps 1-6 (more or less) of
307 * <https://html.spec.whatwg.org/multipage/forms.html#concept-form-submit>.
308 * @param aSubmitter If not null, is the "submitter" from that algorithm.
309 * Therefore it must be a valid submit control.
311 MOZ_CAN_RUN_SCRIPT void MaybeSubmit(Element* aSubmitter);
312 MOZ_CAN_RUN_SCRIPT void MaybeReset(Element* aSubmitter);
313 void Submit(ErrorResult& aRv);
316 * Requests to submit the form. Unlike submit(), this method includes
317 * interactive constraint validation and firing a submit event,
318 * either of which can cancel submission.
320 * @param aSubmitter The submitter argument can be used to point to a specific
321 * submit button.
322 * @param aRv An ErrorResult.
323 * @see
324 * https://html.spec.whatwg.org/multipage/forms.html#dom-form-requestsubmit
326 MOZ_CAN_RUN_SCRIPT void RequestSubmit(nsGenericHTMLElement* aSubmitter,
327 ErrorResult& aRv);
329 MOZ_CAN_RUN_SCRIPT void Reset();
331 bool CheckValidity() { return CheckFormValidity(nullptr); }
333 MOZ_CAN_RUN_SCRIPT
334 bool ReportValidity() { return CheckValidFormSubmission(); }
336 Element* IndexedGetter(uint32_t aIndex, bool& aFound);
338 already_AddRefed<nsISupports> NamedGetter(const nsAString& aName,
339 bool& aFound);
341 void GetSupportedNames(nsTArray<nsString>& aRetval);
343 #ifdef DEBUG
344 static void AssertDocumentOrder(
345 const nsTArray<nsGenericHTMLFormElement*>& aControls, nsIContent* aForm);
346 static void AssertDocumentOrder(
347 const nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls,
348 nsIContent* aForm);
349 #endif
351 JS::ExpandoAndGeneration mExpandoAndGeneration;
353 protected:
354 JSObject* WrapNode(JSContext*, JS::Handle<JSObject*> aGivenProto) override;
356 class RemoveElementRunnable;
357 friend class RemoveElementRunnable;
359 class RemoveElementRunnable : public Runnable {
360 public:
361 explicit RemoveElementRunnable(HTMLFormElement* aForm)
362 : Runnable("dom::HTMLFormElement::RemoveElementRunnable"),
363 mForm(aForm) {}
365 NS_IMETHOD Run() override {
366 mForm->HandleDefaultSubmitRemoval();
367 return NS_OK;
370 private:
371 RefPtr<HTMLFormElement> mForm;
374 nsresult DoReset();
376 // Async callback to handle removal of our default submit
377 void HandleDefaultSubmitRemoval();
380 // Submit Helpers
384 * Attempt to submit (submission might be deferred)
386 * @param aPresContext the presentation context
387 * @param aEvent the DOM event that was passed to us for the submit
389 nsresult DoSubmit(Event* aEvent = nullptr);
392 * Prepare the submission object (called by DoSubmit)
394 * @param aFormSubmission the submission object
395 * @param aEvent the DOM event that was passed to us for the submit
397 nsresult BuildSubmission(HTMLFormSubmission** aFormSubmission, Event* aEvent);
399 * Perform the submission (called by DoSubmit and FlushPendingSubmission)
401 * @param aFormSubmission the submission object
403 nsresult SubmitSubmission(HTMLFormSubmission* aFormSubmission);
406 * Submit a form[method=dialog]
407 * @param aFormSubmission the submission object
409 nsresult SubmitDialog(DialogFormSubmission* aFormSubmission);
412 * Notify any submit observers of the submit.
414 * @param aActionURL the URL being submitted to
415 * @param aCancelSubmit out param where submit observers can specify that the
416 * submit should be cancelled.
418 nsresult NotifySubmitObservers(nsIURI* aActionURL, bool* aCancelSubmit,
419 bool aEarlyNotify);
422 * If this form submission is secure -> insecure, ask the user if they want
423 * to continue.
425 * @param aActionURL the URL being submitted to
426 * @param aCancelSubmit out param: will be true if the user wants to cancel
428 nsresult DoSecureToInsecureSubmitCheck(nsIURI* aActionURL,
429 bool* aCancelSubmit);
432 * Find form controls in this form with the correct value in the name
433 * attribute.
435 already_AddRefed<nsISupports> DoResolveName(const nsAString& aName);
438 * Check the form validity following this algorithm:
439 * https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#statically-validate-the-constraints
441 * @param aInvalidElements [out] parameter containing the list of unhandled
442 * invalid controls.
444 * @return Whether the form is currently valid.
446 bool CheckFormValidity(nsTArray<RefPtr<Element>>* aInvalidElements) const;
448 // Clear the mImageNameLookupTable and mImageElements.
449 void Clear();
451 // Insert a element into the past names map.
452 void AddToPastNamesMap(const nsAString& aName, nsISupports* aChild);
454 // Remove the given element from the past names map. The element must be an
455 // nsGenericHTMLFormElement or HTMLImageElement.
456 void RemoveElementFromPastNamesMap(Element* aElement);
458 nsresult AddElementToTableInternal(
459 nsInterfaceHashtable<nsStringHashKey, nsISupports>& aTable,
460 nsIContent* aChild, const nsAString& aName);
462 nsresult RemoveElementFromTableInternal(
463 nsInterfaceHashtable<nsStringHashKey, nsISupports>& aTable,
464 nsIContent* aChild, const nsAString& aName);
466 public:
468 * Flush a possible pending submission. If there was a scripted submission
469 * triggered by a button or image, the submission was defered. This method
470 * forces the pending submission to be submitted. (happens when the handler
471 * returns false or there is an action/target change in the script)
473 void FlushPendingSubmission();
476 * Get the full URL to submit to. Do not submit if the returned URL is null.
478 * @param aActionURL the full, unadulterated URL you'll be submitting to [OUT]
479 * @param aOriginatingElement the originating element of the form submission
480 * [IN]
482 nsresult GetActionURL(nsIURI** aActionURL, Element* aOriginatingElement);
484 // Returns a number for this form that is unique within its owner document.
485 // This is used by nsContentUtils::GenerateStateKey to identify form controls
486 // that are inserted into the document by the parser.
487 int32_t GetFormNumberForStateKey();
490 * Called when we have been cloned and adopted, and the information of the
491 * node has been changed.
493 void NodeInfoChanged(Document* aOldDoc) override;
495 protected:
497 // Data members
499 /** The list of controls (form.elements as well as stuff not in elements) */
500 RefPtr<HTMLFormControlsCollection> mControls;
502 /** The pending submission object */
503 UniquePtr<HTMLFormSubmission> mPendingSubmission;
505 /** The target browsing context, if any. */
506 RefPtr<BrowsingContext> mTargetContext;
507 /** The load identifier for the pending request created for a
508 * submit, used to be able to block double submits. */
509 Maybe<uint64_t> mCurrentLoadId;
511 /** The default submit element -- WEAK */
512 nsGenericHTMLFormElement* mDefaultSubmitElement;
514 /** The first submit element in mElements -- WEAK */
515 nsGenericHTMLFormElement* mFirstSubmitInElements;
517 /** The first submit element in mNotInElements -- WEAK */
518 nsGenericHTMLFormElement* mFirstSubmitNotInElements;
520 // This array holds on to all HTMLImageElement(s).
521 // This is needed to properly clean up the bi-directional references
522 // (both weak and strong) between the form and its HTMLImageElements.
524 // Holds WEAK references
525 TreeOrderedArray<HTMLImageElement*> mImageElements;
527 // A map from an ID or NAME attribute to the HTMLImageElement(s), this
528 // hash holds strong references either to the named HTMLImageElement, or
529 // to a list of named HTMLImageElement(s), in the case where this hash
530 // holds on to a list of named HTMLImageElement(s) the list has weak
531 // references to the HTMLImageElement.
533 nsInterfaceHashtable<nsStringHashKey, nsISupports> mImageNameLookupTable;
535 // A map from names to elements that were gotten by those names from this
536 // form in that past. See "past names map" in the HTML5 specification.
538 nsInterfaceHashtable<nsStringHashKey, nsISupports> mPastNameLookupTable;
540 /** Keep track of what the popup state was when the submit was initiated */
541 PopupBlocker::PopupControlState mSubmitPopupState;
543 RefPtr<nsDOMTokenList> mRelList;
546 * Number of invalid and candidate for constraint validation elements in the
547 * form the last time UpdateValidity has been called.
549 int32_t mInvalidElementsCount;
551 // See GetFormNumberForStateKey.
552 int32_t mFormNumber;
554 /** Whether we are currently processing a submit event or not */
555 bool mGeneratingSubmit;
556 /** Whether we are currently processing a reset event or not */
557 bool mGeneratingReset;
558 /** Whether the submission is to be deferred in case a script triggers it */
559 bool mDeferSubmission;
560 /** Whether we notified NS_FORMSUBMIT_SUBJECT listeners already */
561 bool mNotifiedObservers;
562 /** If we notified the listeners early, what was the result? */
563 bool mNotifiedObserversResult;
565 * Whether the submission of this form has been ever prevented because of
566 * being invalid.
568 bool mEverTriedInvalidSubmit;
569 /** Whether we are constructing entry list */
570 bool mIsConstructingEntryList;
571 /** Whether we are firing submission event */
572 bool mIsFiringSubmissionEvents;
574 private:
575 bool IsSubmitting() const;
577 void SetDefaultSubmitElement(nsGenericHTMLFormElement*);
579 NotNull<const Encoding*> GetSubmitEncoding();
582 * Fire an event when the form is removed from the DOM tree. This is now only
583 * used by the password manager and formautofill.
585 void MaybeFireFormRemoved();
587 MOZ_CAN_RUN_SCRIPT
588 void ReportInvalidUnfocusableElements(
589 const nsTArray<RefPtr<Element>>&& aInvalidElements);
591 ~HTMLFormElement();
594 } // namespace dom
596 } // namespace mozilla
598 #endif // mozilla_dom_HTMLFormElement_h