Bug 1795723 - Unified extensions UI should support High Contrast Mode. r=ayeddi,deskt...
[gecko.git] / dom / html / HTMLFormElement.h
blob59cd049c31bbb394ca05e5ae0fdff1f320728448
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/RadioGroupManager.h"
16 #include "nsCOMPtr.h"
17 #include "nsIFormControl.h"
18 #include "nsGenericHTMLElement.h"
19 #include "nsIRadioGroupContainer.h"
20 #include "nsIWeakReferenceUtils.h"
21 #include "nsThreadUtils.h"
22 #include "nsInterfaceHashtable.h"
23 #include "nsRefPtrHashtable.h"
24 #include "nsTHashMap.h"
25 #include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration
27 class nsIMutableArray;
28 class nsIURI;
30 namespace mozilla {
31 class EventChainPostVisitor;
32 class EventChainPreVisitor;
33 namespace dom {
34 class DialogFormSubmission;
35 class HTMLFormControlsCollection;
36 class HTMLFormSubmission;
37 class HTMLImageElement;
38 class FormData;
40 class HTMLFormElement final : public nsGenericHTMLElement,
41 public nsIRadioGroupContainer,
42 RadioGroupManager {
43 friend class HTMLFormControlsCollection;
45 public:
46 NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLFormElement, form)
48 explicit HTMLFormElement(
49 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
51 enum { FORM_CONTROL_LIST_HASHTABLE_LENGTH = 8 };
53 // nsISupports
54 NS_DECL_ISUPPORTS_INHERITED
56 int32_t IndexOfContent(nsIContent* aContent);
57 nsGenericHTMLFormElement* GetDefaultSubmitElement() const;
59 // nsIRadioGroupContainer
60 void SetCurrentRadioButton(const nsAString& aName,
61 HTMLInputElement* aRadio) override;
62 HTMLInputElement* GetCurrentRadioButton(const nsAString& aName) override;
63 NS_IMETHOD GetNextRadioButton(const nsAString& aName, const bool aPrevious,
64 HTMLInputElement* aFocusedRadio,
65 HTMLInputElement** aRadioOut) override;
66 NS_IMETHOD WalkRadioGroup(const nsAString& aName,
67 nsIRadioVisitor* aVisitor) override;
68 void AddToRadioGroup(const nsAString& aName,
69 HTMLInputElement* aRadio) override;
70 void RemoveFromRadioGroup(const nsAString& aName,
71 HTMLInputElement* aRadio) override;
72 virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const override;
73 virtual void RadioRequiredWillChange(const nsAString& aName,
74 bool aRequiredAdded) override;
75 virtual bool GetValueMissingState(const nsAString& aName) const override;
76 virtual void SetValueMissingState(const nsAString& aName,
77 bool aValue) override;
79 virtual ElementState IntrinsicState() const override;
81 // EventTarget
82 virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
84 // nsIContent
85 virtual bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
86 const nsAString& aValue,
87 nsIPrincipal* aMaybeScriptedPrincipal,
88 nsAttrValue& aResult) override;
89 void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
90 void WillHandleEvent(EventChainPostVisitor& aVisitor) override;
91 virtual nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override;
93 virtual nsresult BindToTree(BindContext&, nsINode& aParent) override;
94 virtual void UnbindFromTree(bool aNullParent = true) override;
95 virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
96 const nsAttrValueOrString* aValue,
97 bool aNotify) override;
99 /**
100 * Forget all information about the current submission (and the fact that we
101 * are currently submitting at all).
103 void ForgetCurrentSubmission();
105 virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
107 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLFormElement,
108 nsGenericHTMLElement)
111 * Remove an element from this form's list of elements
113 * @param aElement the element to remove
114 * @param aUpdateValidity If true, updates the form validity.
115 * @return NS_OK if the element was successfully removed.
117 nsresult RemoveElement(nsGenericHTMLFormElement* aElement,
118 bool aUpdateValidity);
121 * Remove an element from the lookup table maintained by the form.
122 * We can't fold this method into RemoveElement() because when
123 * RemoveElement() is called it doesn't know if the element is
124 * removed because the id attribute has changed, or because the
125 * name attribute has changed.
127 * @param aElement the element to remove
128 * @param aName the name or id of the element to remove
129 * @return NS_OK if the element was successfully removed.
131 nsresult RemoveElementFromTable(nsGenericHTMLFormElement* aElement,
132 const nsAString& aName);
135 * Add an element to end of this form's list of elements
137 * @param aElement the element to add
138 * @param aUpdateValidity If true, the form validity will be updated.
139 * @param aNotify If true, send DocumentObserver notifications as needed.
140 * @return NS_OK if the element was successfully added
142 nsresult AddElement(nsGenericHTMLFormElement* aElement, bool aUpdateValidity,
143 bool aNotify);
146 * Add an element to the lookup table maintained by the form.
148 * We can't fold this method into AddElement() because when
149 * AddElement() is called, the form control has no
150 * attributes. The name or id attributes of the form control
151 * are used as a key into the table.
153 nsresult AddElementToTable(nsGenericHTMLFormElement* aChild,
154 const nsAString& aName);
157 * Remove an image element from this form's list of image elements
159 * @param aElement the image element to remove
160 * @return NS_OK if the element was successfully removed.
162 nsresult RemoveImageElement(HTMLImageElement* aElement);
165 * Remove an image element from the lookup table maintained by the form.
166 * We can't fold this method into RemoveImageElement() because when
167 * RemoveImageElement() is called it doesn't know if the element is
168 * removed because the id attribute has changed, or because the
169 * name attribute has changed.
171 * @param aElement the image element to remove
172 * @param aName the name or id of the element to remove
173 * @return NS_OK if the element was successfully removed.
175 nsresult RemoveImageElementFromTable(HTMLImageElement* aElement,
176 const nsAString& aName);
178 * Add an image element to the end of this form's list of image elements
180 * @param aElement the element to add
181 * @return NS_OK if the element was successfully added
183 nsresult AddImageElement(HTMLImageElement* aElement);
186 * Add an image element to the lookup table maintained by the form.
188 * We can't fold this method into AddImageElement() because when
189 * AddImageElement() is called, the image attributes can change.
190 * The name or id attributes of the image are used as a key into the table.
192 nsresult AddImageElementToTable(HTMLImageElement* aChild,
193 const nsAString& aName);
196 * Returns true if implicit submission of this form is disabled. For more
197 * on implicit submission see:
199 * http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#implicit-submission
201 bool ImplicitSubmissionIsDisabled() const;
204 * Check whether a given nsGenericHTMLFormElement is the last single line
205 * input control that is not disabled. aElement is expected to not be null.
207 bool IsLastActiveElement(const nsGenericHTMLFormElement* aElement) const;
210 * Check whether a given nsGenericHTMLFormElement is the default submit
211 * element. This is different from just comparing to
212 * GetDefaultSubmitElement() in certain situations inside an update
213 * when GetDefaultSubmitElement() might not be up to date. aElement
214 * is expected to not be null.
216 bool IsDefaultSubmitElement(const nsGenericHTMLFormElement* aElement) const;
219 * Flag the form to know that a button or image triggered scripted form
220 * submission. In that case the form will defer the submission until the
221 * script handler returns and the return value is known.
223 void OnSubmitClickBegin(Element* aOriginatingElement);
224 void OnSubmitClickEnd();
227 * This method will update the form validity.
229 * This method has to be called by form elements whenever their validity state
230 * or status regarding constraint validation changes.
232 * @note This method isn't used for CheckValidity().
233 * @note If an element becomes barred from constraint validation, it has to be
234 * considered as valid.
236 * @param aElementValidityState the new validity state of the element
238 void UpdateValidity(bool aElementValidityState);
241 * This method check the form validity and make invalid form elements send
242 * invalid event if needed.
244 * @return Whether the form is valid.
246 * @note Do not call this method if novalidate/formnovalidate is used.
247 * @note This method might disappear with bug 592124, hopefuly.
248 * @see
249 * https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#interactively-validate-the-constraints
251 bool CheckValidFormSubmission();
254 * Contruct the entry list to get their data pumped into the FormData and
255 * fire a `formdata` event with the entry list in formData attribute.
256 * <https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-form-data-set>
258 * @param aFormData the form data object
260 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
261 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult ConstructEntryList(FormData* aFormData);
264 * Whether the submission of this form has been ever prevented because of
265 * being invalid.
267 * @return Whether the submission of this form has been prevented because of
268 * being invalid.
270 bool HasEverTriedInvalidSubmit() const { return mEverTriedInvalidSubmit; }
273 * Implements form[name]. Returns form controls in this form with the correct
274 * value of the name attribute.
276 already_AddRefed<nsISupports> FindNamedItem(const nsAString& aName,
277 nsWrapperCache** aCache);
279 // WebIDL
281 void GetAcceptCharset(DOMString& aValue) {
282 GetHTMLAttr(nsGkAtoms::acceptcharset, aValue);
285 void SetAcceptCharset(const nsAString& aValue, ErrorResult& aRv) {
286 SetHTMLAttr(nsGkAtoms::acceptcharset, aValue, aRv);
289 void GetAction(nsString& aValue);
290 void SetAction(const nsAString& aValue, ErrorResult& aRv) {
291 SetHTMLAttr(nsGkAtoms::action, aValue, aRv);
294 void GetAutocomplete(nsAString& aValue);
295 void SetAutocomplete(const nsAString& aValue, ErrorResult& aRv) {
296 SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv);
299 void GetEnctype(nsAString& aValue);
300 void SetEnctype(const nsAString& aValue, ErrorResult& aRv) {
301 SetHTMLAttr(nsGkAtoms::enctype, aValue, aRv);
304 void GetEncoding(nsAString& aValue) { GetEnctype(aValue); }
305 void SetEncoding(const nsAString& aValue, ErrorResult& aRv) {
306 SetEnctype(aValue, aRv);
309 void GetMethod(nsAString& aValue);
310 void SetMethod(const nsAString& aValue, ErrorResult& aRv) {
311 SetHTMLAttr(nsGkAtoms::method, aValue, aRv);
314 void GetName(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::name, aValue); }
316 void SetName(const nsAString& aValue, ErrorResult& aRv) {
317 SetHTMLAttr(nsGkAtoms::name, aValue, aRv);
320 bool NoValidate() const { return GetBoolAttr(nsGkAtoms::novalidate); }
322 void SetNoValidate(bool aValue, ErrorResult& aRv) {
323 SetHTMLBoolAttr(nsGkAtoms::novalidate, aValue, aRv);
326 void GetTarget(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::target, aValue); }
328 void SetTarget(const nsAString& aValue, ErrorResult& aRv) {
329 SetHTMLAttr(nsGkAtoms::target, aValue, aRv);
332 // it's only out-of-line because the class definition is not available in the
333 // header
334 nsIHTMLCollection* Elements();
336 int32_t Length();
339 * Check whether submission can proceed for this form then fire submit event.
340 * This basically implements steps 1-6 (more or less) of
341 * <https://html.spec.whatwg.org/multipage/forms.html#concept-form-submit>.
342 * @param aSubmitter If not null, is the "submitter" from that algorithm.
343 * Therefore it must be a valid submit control.
345 MOZ_CAN_RUN_SCRIPT void MaybeSubmit(Element* aSubmitter);
346 MOZ_CAN_RUN_SCRIPT void MaybeReset(Element* aSubmitter);
347 void Submit(ErrorResult& aRv);
350 * Requests to submit the form. Unlike submit(), this method includes
351 * interactive constraint validation and firing a submit event,
352 * either of which can cancel submission.
354 * @param aSubmitter The submitter argument can be used to point to a specific
355 * submit button.
356 * @param aRv An ErrorResult.
357 * @see
358 * https://html.spec.whatwg.org/multipage/forms.html#dom-form-requestsubmit
360 MOZ_CAN_RUN_SCRIPT void RequestSubmit(nsGenericHTMLElement* aSubmitter,
361 ErrorResult& aRv);
363 MOZ_CAN_RUN_SCRIPT void Reset();
365 bool CheckValidity() { return CheckFormValidity(nullptr); }
367 bool ReportValidity() { return CheckValidFormSubmission(); }
369 Element* IndexedGetter(uint32_t aIndex, bool& aFound);
371 already_AddRefed<nsISupports> NamedGetter(const nsAString& aName,
372 bool& aFound);
374 void GetSupportedNames(nsTArray<nsString>& aRetval);
376 static int32_t CompareFormControlPosition(Element* aElement1,
377 Element* aElement2,
378 const nsIContent* aForm);
379 #ifdef DEBUG
380 static void AssertDocumentOrder(
381 const nsTArray<nsGenericHTMLFormElement*>& aControls, nsIContent* aForm);
382 static void AssertDocumentOrder(
383 const nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls,
384 nsIContent* aForm);
385 #endif
387 JS::ExpandoAndGeneration mExpandoAndGeneration;
389 protected:
390 virtual JSObject* WrapNode(JSContext* aCx,
391 JS::Handle<JSObject*> aGivenProto) override;
393 void PostPasswordEvent();
394 void PostPossibleUsernameEvent();
396 RefPtr<AsyncEventDispatcher> mFormPasswordEventDispatcher;
397 RefPtr<AsyncEventDispatcher> mFormPossibleUsernameEventDispatcher;
399 class RemoveElementRunnable;
400 friend class RemoveElementRunnable;
402 class RemoveElementRunnable : public Runnable {
403 public:
404 explicit RemoveElementRunnable(HTMLFormElement* aForm)
405 : Runnable("dom::HTMLFormElement::RemoveElementRunnable"),
406 mForm(aForm) {}
408 NS_IMETHOD Run() override {
409 mForm->HandleDefaultSubmitRemoval();
410 return NS_OK;
413 private:
414 RefPtr<HTMLFormElement> mForm;
417 nsresult DoReset();
419 // Async callback to handle removal of our default submit
420 void HandleDefaultSubmitRemoval();
423 // Submit Helpers
427 * Attempt to submit (submission might be deferred)
429 * @param aPresContext the presentation context
430 * @param aEvent the DOM event that was passed to us for the submit
432 nsresult DoSubmit(Event* aEvent = nullptr);
435 * Prepare the submission object (called by DoSubmit)
437 * @param aFormSubmission the submission object
438 * @param aEvent the DOM event that was passed to us for the submit
440 nsresult BuildSubmission(HTMLFormSubmission** aFormSubmission, Event* aEvent);
442 * Perform the submission (called by DoSubmit and FlushPendingSubmission)
444 * @param aFormSubmission the submission object
446 nsresult SubmitSubmission(HTMLFormSubmission* aFormSubmission);
449 * Submit a form[method=dialog]
450 * @param aFormSubmission the submission object
452 nsresult SubmitDialog(DialogFormSubmission* aFormSubmission);
455 * Notify any submit observers of the submit.
457 * @param aActionURL the URL being submitted to
458 * @param aCancelSubmit out param where submit observers can specify that the
459 * submit should be cancelled.
461 nsresult NotifySubmitObservers(nsIURI* aActionURL, bool* aCancelSubmit,
462 bool aEarlyNotify);
465 * If this form submission is secure -> insecure, ask the user if they want
466 * to continue.
468 * @param aActionURL the URL being submitted to
469 * @param aCancelSubmit out param: will be true if the user wants to cancel
471 nsresult DoSecureToInsecureSubmitCheck(nsIURI* aActionURL,
472 bool* aCancelSubmit);
475 * Find form controls in this form with the correct value in the name
476 * attribute.
478 already_AddRefed<nsISupports> DoResolveName(const nsAString& aName);
481 * Check the form validity following this algorithm:
482 * https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#statically-validate-the-constraints
484 * @param aInvalidElements [out] parameter containing the list of unhandled
485 * invalid controls.
487 * @return Whether the form is currently valid.
489 bool CheckFormValidity(nsTArray<RefPtr<Element>>* aInvalidElements) const;
491 // Clear the mImageNameLookupTable and mImageElements.
492 void Clear();
494 // Insert a element into the past names map.
495 void AddToPastNamesMap(const nsAString& aName, nsISupports* aChild);
497 // Remove the given element from the past names map. The element must be an
498 // nsGenericHTMLFormElement or HTMLImageElement.
499 void RemoveElementFromPastNamesMap(Element* aElement);
501 nsresult AddElementToTableInternal(
502 nsInterfaceHashtable<nsStringHashKey, nsISupports>& aTable,
503 nsIContent* aChild, const nsAString& aName);
505 nsresult RemoveElementFromTableInternal(
506 nsInterfaceHashtable<nsStringHashKey, nsISupports>& aTable,
507 nsIContent* aChild, const nsAString& aName);
509 public:
511 * Flush a possible pending submission. If there was a scripted submission
512 * triggered by a button or image, the submission was defered. This method
513 * forces the pending submission to be submitted. (happens when the handler
514 * returns false or there is an action/target change in the script)
516 void FlushPendingSubmission();
519 * Get the full URL to submit to. Do not submit if the returned URL is null.
521 * @param aActionURL the full, unadulterated URL you'll be submitting to [OUT]
522 * @param aOriginatingElement the originating element of the form submission
523 * [IN]
525 nsresult GetActionURL(nsIURI** aActionURL, Element* aOriginatingElement);
527 // Returns a number for this form that is unique within its owner document.
528 // This is used by nsContentUtils::GenerateStateKey to identify form controls
529 // that are inserted into the document by the parser.
530 int32_t GetFormNumberForStateKey();
533 * Called when we have been cloned and adopted, and the information of the
534 * node has been changed.
536 void NodeInfoChanged(Document* aOldDoc) override;
538 protected:
540 // Data members
542 /** The list of controls (form.elements as well as stuff not in elements) */
543 RefPtr<HTMLFormControlsCollection> mControls;
545 /** The pending submission object */
546 UniquePtr<HTMLFormSubmission> mPendingSubmission;
548 /** The target browsing context, if any. */
549 RefPtr<BrowsingContext> mTargetContext;
550 /** The load identifier for the pending request created for a
551 * submit, used to be able to block double submits. */
552 Maybe<uint64_t> mCurrentLoadId;
554 /** The default submit element -- WEAK */
555 nsGenericHTMLFormElement* mDefaultSubmitElement;
557 /** The first submit element in mElements -- WEAK */
558 nsGenericHTMLFormElement* mFirstSubmitInElements;
560 /** The first submit element in mNotInElements -- WEAK */
561 nsGenericHTMLFormElement* mFirstSubmitNotInElements;
563 // This array holds on to all HTMLImageElement(s).
564 // This is needed to properly clean up the bi-directional references
565 // (both weak and strong) between the form and its HTMLImageElements.
567 nsTArray<HTMLImageElement*> mImageElements; // Holds WEAK references
569 // A map from an ID or NAME attribute to the HTMLImageElement(s), this
570 // hash holds strong references either to the named HTMLImageElement, or
571 // to a list of named HTMLImageElement(s), in the case where this hash
572 // holds on to a list of named HTMLImageElement(s) the list has weak
573 // references to the HTMLImageElement.
575 nsInterfaceHashtable<nsStringHashKey, nsISupports> mImageNameLookupTable;
577 // A map from names to elements that were gotten by those names from this
578 // form in that past. See "past names map" in the HTML5 specification.
580 nsInterfaceHashtable<nsStringHashKey, nsISupports> mPastNameLookupTable;
582 /** Keep track of what the popup state was when the submit was initiated */
583 PopupBlocker::PopupControlState mSubmitPopupState;
586 * Number of invalid and candidate for constraint validation elements in the
587 * form the last time UpdateValidity has been called.
589 int32_t mInvalidElementsCount;
591 // See GetFormNumberForStateKey.
592 int32_t mFormNumber;
594 /** Whether we are currently processing a submit event or not */
595 bool mGeneratingSubmit;
596 /** Whether we are currently processing a reset event or not */
597 bool mGeneratingReset;
598 /** Whether the submission is to be deferred in case a script triggers it */
599 bool mDeferSubmission;
600 /** Whether we notified NS_FORMSUBMIT_SUBJECT listeners already */
601 bool mNotifiedObservers;
602 /** If we notified the listeners early, what was the result? */
603 bool mNotifiedObserversResult;
605 * Whether the submission of this form has been ever prevented because of
606 * being invalid.
608 bool mEverTriedInvalidSubmit;
609 /** Whether we are constructing entry list */
610 bool mIsConstructingEntryList;
611 /** Whether we are firing submission event */
612 bool mIsFiringSubmissionEvents;
614 private:
615 bool IsSubmitting() const;
617 NotNull<const Encoding*> GetSubmitEncoding();
620 * Fire an event when the form is removed from the DOM tree. This is now only
621 * used by the password manager.
623 void MaybeFireFormRemoved();
625 ~HTMLFormElement();
628 } // namespace dom
630 } // namespace mozilla
632 #endif // mozilla_dom_HTMLFormElement_h