HTMLFormElement.h (20395B)
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/. */ 6 7 #ifndef mozilla_dom_HTMLFormElement_h 8 #define mozilla_dom_HTMLFormElement_h 9 10 #include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration 11 #include "mozilla/AsyncEventDispatcher.h" 12 #include "mozilla/Attributes.h" 13 #include "mozilla/UniquePtr.h" 14 #include "mozilla/dom/BrowsingContext.h" 15 #include "mozilla/dom/PopupBlocker.h" 16 #include "mozilla/dom/RadioGroupContainer.h" 17 #include "nsGenericHTMLElement.h" 18 #include "nsIFormControl.h" 19 #include "nsInterfaceHashtable.h" 20 #include "nsThreadUtils.h" 21 22 class nsIMutableArray; 23 class nsIURI; 24 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; 34 35 class HTMLFormElement final : public nsGenericHTMLElement { 36 friend class HTMLFormControlsCollection; 37 38 public: 39 NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLFormElement, form) 40 41 explicit HTMLFormElement( 42 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); 43 44 enum { FORM_CONTROL_LIST_HASHTABLE_LENGTH = 8 }; 45 46 // nsISupports 47 NS_DECL_ISUPPORTS_INHERITED 48 49 int32_t IndexOfContent(nsIContent* aContent); 50 nsGenericHTMLFormElement* GetDefaultSubmitElement() const; 51 bool IsDefaultSubmitElement(nsGenericHTMLFormElement* aElement) const { 52 return aElement == mDefaultSubmitElement; 53 } 54 55 // EventTarget 56 void AsyncEventRunning(AsyncEventDispatcher* aEvent) override; 57 58 /** Whether we already dispatched a DOMFormHasPassword event or not */ 59 bool mHasPendingPasswordEvent = false; 60 /** Whether we already dispatched a DOMPossibleUsernameInputAdded event or not 61 */ 62 bool mHasPendingPossibleUsernameEvent = false; 63 64 // nsIContent 65 bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 66 const nsAString& aValue, 67 nsIPrincipal* aMaybeScriptedPrincipal, 68 nsAttrValue& aResult) override; 69 void GetEventTargetParent(EventChainPreVisitor& aVisitor) override; 70 void WillHandleEvent(EventChainPostVisitor& aVisitor) override; 71 nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override; 72 73 nsresult BindToTree(BindContext&, nsINode& aParent) override; 74 void UnbindFromTree(UnbindContext&) override; 75 void BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, 76 const nsAttrValue* aValue, bool aNotify) override; 77 78 /** 79 * Forget all information about the current submission (and the fact that we 80 * are currently submitting at all). 81 */ 82 void ForgetCurrentSubmission(); 83 84 nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; 85 86 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLFormElement, 87 nsGenericHTMLElement) 88 89 /** 90 * Remove an element from this form's list of elements 91 * 92 * @param aElement the element to remove 93 * @param aUpdateValidity If true, updates the form validity. 94 * @return NS_OK if the element was successfully removed. 95 */ 96 nsresult RemoveElement(nsGenericHTMLFormElement* aElement, 97 bool aUpdateValidity); 98 99 /** 100 * Remove an element from the lookup table maintained by the form. 101 * We can't fold this method into RemoveElement() because when 102 * RemoveElement() is called it doesn't know if the element is 103 * removed because the id attribute has changed, or because the 104 * name attribute has changed. 105 * 106 * @param aElement the element to remove 107 * @param aName the name or id of the element to remove 108 * @return NS_OK if the element was successfully removed. 109 */ 110 nsresult RemoveElementFromTable(nsGenericHTMLFormElement* aElement, 111 const nsAString& aName); 112 113 /** 114 * Add an element to end of this form's list of elements 115 * 116 * @param aElement the element to add 117 * @param aUpdateValidity If true, the form validity will be updated. 118 * @param aNotify If true, send DocumentObserver notifications as needed. 119 * @return NS_OK if the element was successfully added 120 */ 121 nsresult AddElement(nsGenericHTMLFormElement* aElement, bool aUpdateValidity, 122 bool aNotify); 123 124 /** 125 * Add an element to the lookup table maintained by the form. 126 * 127 * We can't fold this method into AddElement() because when 128 * AddElement() is called, the form control has no 129 * attributes. The name or id attributes of the form control 130 * are used as a key into the table. 131 */ 132 nsresult AddElementToTable(nsGenericHTMLFormElement* aChild, 133 const nsAString& aName); 134 135 /** 136 * Remove an image element from this form's list of image elements 137 * 138 * @param aElement the image element to remove 139 * @return NS_OK if the element was successfully removed. 140 */ 141 nsresult RemoveImageElement(HTMLImageElement* aElement); 142 143 /** 144 * Remove an image element from the lookup table maintained by the form. 145 * We can't fold this method into RemoveImageElement() because when 146 * RemoveImageElement() is called it doesn't know if the element is 147 * removed because the id attribute has changed, or because the 148 * name attribute has changed. 149 * 150 * @param aElement the image element to remove 151 * @param aName the name or id of the element to remove 152 * @return NS_OK if the element was successfully removed. 153 */ 154 nsresult RemoveImageElementFromTable(HTMLImageElement* aElement, 155 const nsAString& aName); 156 /** 157 * Add an image element to the end of this form's list of image elements 158 * 159 * @param aElement the element to add 160 * @return NS_OK if the element was successfully added 161 */ 162 nsresult AddImageElement(HTMLImageElement* aElement); 163 164 /** 165 * Add an image element to the lookup table maintained by the form. 166 * 167 * We can't fold this method into AddImageElement() because when 168 * AddImageElement() is called, the image attributes can change. 169 * The name or id attributes of the image are used as a key into the table. 170 */ 171 nsresult AddImageElementToTable(HTMLImageElement* aChild, 172 const nsAString& aName); 173 174 /** 175 * Returns true if implicit submission of this form is disabled. For more 176 * on implicit submission see: 177 * 178 * http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#implicit-submission 179 */ 180 bool ImplicitSubmissionIsDisabled() const; 181 182 /** 183 * Check whether a given nsGenericHTMLFormElement is the last single line 184 * input control that is not disabled. aElement is expected to not be null. 185 */ 186 bool IsLastActiveElement(const nsGenericHTMLFormElement* aElement) const; 187 188 /** 189 * Flag the form to know that a button or image triggered scripted form 190 * submission. In that case the form will defer the submission until the 191 * script handler returns and the return value is known. 192 */ 193 void OnSubmitClickBegin(); 194 void OnSubmitClickEnd(); 195 196 /** 197 * This method will update the form validity. 198 * 199 * This method has to be called by form elements whenever their validity state 200 * or status regarding constraint validation changes. 201 * 202 * @note This method isn't used for CheckValidity(). 203 * @note If an element becomes barred from constraint validation, it has to be 204 * considered as valid. 205 * 206 * @param aElementValidityState the new validity state of the element 207 */ 208 void UpdateValidity(bool aElementValidityState); 209 210 /** 211 * This method check the form validity and make invalid form elements send 212 * invalid event if needed. 213 * 214 * @return Whether the form is valid. 215 * 216 * @note This method might disappear with bug 592124, hopefuly. 217 * @see 218 * https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#interactively-validate-the-constraints 219 */ 220 MOZ_CAN_RUN_SCRIPT 221 bool CheckValidFormSubmission(); 222 223 /** 224 * Contruct the entry list to get their data pumped into the FormData and 225 * fire a `formdata` event with the entry list in formData attribute. 226 * <https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-form-data-set> 227 * 228 * @param aFormData the form data object 229 */ 230 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230) 231 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult ConstructEntryList(FormData*); 232 233 // WebIDL 234 235 void GetAcceptCharset(DOMString& aValue) { 236 GetHTMLAttr(nsGkAtoms::acceptcharset, aValue); 237 } 238 239 void SetAcceptCharset(const nsAString& aValue, ErrorResult& aRv) { 240 SetHTMLAttr(nsGkAtoms::acceptcharset, aValue, aRv); 241 } 242 243 void GetAction(nsString& aValue); 244 void SetAction(const nsAString& aValue, ErrorResult& aRv) { 245 SetHTMLAttr(nsGkAtoms::action, aValue, aRv); 246 } 247 248 void GetAutocomplete(nsAString& aValue); 249 void SetAutocomplete(const nsAString& aValue, ErrorResult& aRv) { 250 SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv); 251 } 252 253 void GetEnctype(nsAString& aValue); 254 void SetEnctype(const nsAString& aValue, ErrorResult& aRv) { 255 SetHTMLAttr(nsGkAtoms::enctype, aValue, aRv); 256 } 257 258 void GetEncoding(nsAString& aValue) { GetEnctype(aValue); } 259 void SetEncoding(const nsAString& aValue, ErrorResult& aRv) { 260 SetEnctype(aValue, aRv); 261 } 262 263 void GetMethod(nsAString& aValue); 264 void SetMethod(const nsAString& aValue, ErrorResult& aRv) { 265 SetHTMLAttr(nsGkAtoms::method, aValue, aRv); 266 } 267 268 void GetName(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::name, aValue); } 269 270 void SetName(const nsAString& aValue, ErrorResult& aRv) { 271 SetHTMLAttr(nsGkAtoms::name, aValue, aRv); 272 } 273 274 bool NoValidate() const { return GetBoolAttr(nsGkAtoms::novalidate); } 275 276 void SetNoValidate(bool aValue, ErrorResult& aRv) { 277 SetHTMLBoolAttr(nsGkAtoms::novalidate, aValue, aRv); 278 } 279 280 void GetTarget(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::target, aValue); } 281 282 void SetTarget(const nsAString& aValue, ErrorResult& aRv) { 283 SetHTMLAttr(nsGkAtoms::target, aValue, aRv); 284 } 285 286 void GetRel(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::rel, aValue); } 287 void SetRel(const nsAString& aRel, ErrorResult& aError) { 288 SetHTMLAttr(nsGkAtoms::rel, aRel, aError); 289 } 290 nsDOMTokenList* RelList(); 291 292 // it's only out-of-line because the class definition is not available in the 293 // header 294 HTMLFormControlsCollection* Elements(); 295 296 int32_t Length(); 297 298 /** 299 * Check whether submission can proceed for this form then fire submit event. 300 * This basically implements steps 1-6 (more or less) of 301 * <https://html.spec.whatwg.org/multipage/forms.html#concept-form-submit>. 302 * @param aSubmitter If not null, is the "submitter" from that algorithm. 303 * Therefore it must be a valid submit control. 304 */ 305 MOZ_CAN_RUN_SCRIPT void MaybeSubmit(Element* aSubmitter); 306 MOZ_CAN_RUN_SCRIPT void MaybeReset(Element* aSubmitter); 307 void Submit(ErrorResult& aRv); 308 309 /** 310 * Requests to submit the form. Unlike submit(), this method includes 311 * interactive constraint validation and firing a submit event, 312 * either of which can cancel submission. 313 * 314 * @param aSubmitter The submitter argument can be used to point to a specific 315 * submit button. 316 * @param aRv An ErrorResult. 317 * @see 318 * https://html.spec.whatwg.org/multipage/forms.html#dom-form-requestsubmit 319 */ 320 MOZ_CAN_RUN_SCRIPT void RequestSubmit(nsGenericHTMLElement* aSubmitter, 321 ErrorResult& aRv); 322 323 MOZ_CAN_RUN_SCRIPT void Reset(); 324 325 bool CheckValidity() { return CheckFormValidity(nullptr); } 326 327 MOZ_CAN_RUN_SCRIPT 328 bool ReportValidity() { return CheckValidFormSubmission(); } 329 330 Element* IndexedGetter(uint32_t aIndex, bool& aFound); 331 332 already_AddRefed<nsISupports> ResolveName(const nsAString&); 333 already_AddRefed<nsISupports> NamedGetter(const nsAString& aName, 334 bool& aFound); 335 336 void GetSupportedNames(nsTArray<nsString>& aRetval); 337 338 JS::ExpandoAndGeneration mExpandoAndGeneration; 339 340 protected: 341 JSObject* WrapNode(JSContext*, JS::Handle<JSObject*> aGivenProto) override; 342 343 class RemoveElementRunnable; 344 friend class RemoveElementRunnable; 345 346 class RemoveElementRunnable : public Runnable { 347 public: 348 explicit RemoveElementRunnable(HTMLFormElement* aForm) 349 : Runnable("dom::HTMLFormElement::RemoveElementRunnable"), 350 mForm(aForm) {} 351 352 NS_IMETHOD Run() override { 353 mForm->HandleDefaultSubmitRemoval(); 354 return NS_OK; 355 } 356 357 private: 358 RefPtr<HTMLFormElement> mForm; 359 }; 360 361 nsresult DoReset(); 362 363 // Async callback to handle removal of our default submit 364 void HandleDefaultSubmitRemoval(); 365 366 // 367 // Submit Helpers 368 // 369 // 370 /** 371 * Attempt to submit (submission might be deferred) 372 * 373 * @param aPresContext the presentation context 374 * @param aEvent the DOM event that was passed to us for the submit 375 */ 376 nsresult DoSubmit(Event* aEvent = nullptr); 377 378 /** 379 * Prepare the submission object (called by DoSubmit) 380 * 381 * @param aFormSubmission the submission object 382 * @param aEvent the DOM event that was passed to us for the submit 383 */ 384 nsresult BuildSubmission(HTMLFormSubmission** aFormSubmission, Event* aEvent); 385 /** 386 * Perform the submission (called by DoSubmit and FlushPendingSubmission) 387 * 388 * @param aFormSubmission the submission object 389 */ 390 nsresult SubmitSubmission(HTMLFormSubmission* aFormSubmission); 391 392 /** 393 * Submit a form[method=dialog] 394 * @param aFormSubmission the submission object 395 */ 396 nsresult SubmitDialog(DialogFormSubmission* aFormSubmission); 397 398 /** 399 * Dispatch a DOMFormBeforeSubmit chrome-only event. 400 * 401 * @param aCancelSubmit out param where submit observers can specify that the 402 * submit should be cancelled. 403 */ 404 nsresult DispatchBeforeSubmitChromeOnlyEvent(bool* aCancelSubmit); 405 406 /** 407 * If this form submission is secure -> insecure, ask the user if they want 408 * to continue. 409 * 410 * @param aActionURL the URL being submitted to 411 * @param aCancelSubmit out param: will be true if the user wants to cancel 412 */ 413 nsresult DoSecureToInsecureSubmitCheck(nsIURI* aActionURL, 414 bool* aCancelSubmit); 415 416 /** 417 * Check the form validity following this algorithm: 418 * https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#statically-validate-the-constraints 419 * 420 * @param aInvalidElements [out] parameter containing the list of unhandled 421 * invalid controls. 422 * 423 * @return Whether the form is currently valid. 424 */ 425 bool CheckFormValidity(nsTArray<RefPtr<Element>>* aInvalidElements) const; 426 427 // Clear the mImageNameLookupTable and mImageElements. 428 void Clear(); 429 430 // Insert a element into the past names map. 431 void AddToPastNamesMap(const nsAString& aName, nsISupports* aChild); 432 433 // Remove the given element from the past names map. The element must be an 434 // nsGenericHTMLFormElement or HTMLImageElement. 435 void RemoveElementFromPastNamesMap(Element* aElement); 436 437 nsresult AddElementToTableInternal( 438 nsInterfaceHashtable<nsStringHashKey, nsISupports>& aTable, 439 nsIContent* aChild, const nsAString& aName); 440 441 nsresult RemoveElementFromTableInternal( 442 nsInterfaceHashtable<nsStringHashKey, nsISupports>& aTable, 443 nsIContent* aChild, const nsAString& aName); 444 445 public: 446 /** 447 * Flush a possible pending submission. If there was a scripted submission 448 * triggered by a button or image, the submission was defered. This method 449 * forces the pending submission to be submitted. (happens when the handler 450 * returns false or there is an action/target change in the script) 451 */ 452 void FlushPendingSubmission(); 453 454 /** 455 * Get the full URL to submit to. Do not submit if the returned URL is null. 456 * 457 * @param aActionURL the full, unadulterated URL you'll be submitting to [OUT] 458 * @param aOriginatingElement the originating element of the form submission 459 * [IN] 460 */ 461 nsresult GetActionURL(nsIURI** aActionURL, Element* aOriginatingElement); 462 463 // Get the target to submit to. This is either the submitter's |formtarget| or 464 // the form's |target| (Including <base>). 465 void GetSubmissionTarget(nsGenericHTMLElement* aSubmitter, 466 nsAString& aTarget); 467 468 // Returns a number for this form that is unique within its owner document. 469 // This is used by nsContentUtils::GenerateStateKey to identify form controls 470 // that are inserted into the document by the parser. 471 int32_t GetFormNumberForStateKey(); 472 473 /** 474 * Called when we have been cloned and adopted, and the information of the 475 * node has been changed. 476 */ 477 void NodeInfoChanged(Document* aOldDoc) override; 478 479 protected: 480 // 481 // Data members 482 // 483 /** The list of controls (form.elements as well as stuff not in elements) */ 484 RefPtr<HTMLFormControlsCollection> mControls; 485 486 /** The pending submission object */ 487 UniquePtr<HTMLFormSubmission> mPendingSubmission; 488 489 /** The target browsing context, if any. */ 490 RefPtr<BrowsingContext> mTargetContext; 491 /** The load identifier for the pending request created for a 492 * submit, used to be able to block double submits. */ 493 Maybe<uint64_t> mCurrentLoadId; 494 495 /** The default submit element -- WEAK */ 496 nsGenericHTMLFormElement* mDefaultSubmitElement; 497 498 /** The first submit element in mElements -- WEAK */ 499 nsGenericHTMLFormElement* mFirstSubmitInElements; 500 501 /** The first submit element in mNotInElements -- WEAK */ 502 nsGenericHTMLFormElement* mFirstSubmitNotInElements; 503 504 // This array holds on to all HTMLImageElement(s). 505 // This is needed to properly clean up the bi-directional references 506 // (both weak and strong) between the form and its HTMLImageElements. 507 508 // Holds WEAK references 509 TreeOrderedArray<HTMLImageElement*> mImageElements; 510 511 // A map from an ID or NAME attribute to the HTMLImageElement(s), this 512 // hash holds strong references either to the named HTMLImageElement, or 513 // to a list of named HTMLImageElement(s), in the case where this hash 514 // holds on to a list of named HTMLImageElement(s) the list has weak 515 // references to the HTMLImageElement. 516 517 nsInterfaceHashtable<nsStringHashKey, nsISupports> mImageNameLookupTable; 518 519 // A map from names to elements that were gotten by those names from this 520 // form in that past. See "past names map" in the HTML5 specification. 521 522 nsInterfaceHashtable<nsStringHashKey, nsISupports> mPastNameLookupTable; 523 524 /** Keep track of what the popup state was when the submit was initiated */ 525 PopupBlocker::PopupControlState mSubmitPopupState; 526 527 RefPtr<nsDOMTokenList> mRelList; 528 529 /** 530 * Number of invalid and candidate for constraint validation elements in the 531 * form the last time UpdateValidity has been called. 532 */ 533 int32_t mInvalidElementsCount; 534 535 // See GetFormNumberForStateKey. 536 int32_t mFormNumber; 537 538 /** Whether we are currently processing a submit event or not */ 539 bool mGeneratingSubmit; 540 /** Whether we are currently processing a reset event or not */ 541 bool mGeneratingReset; 542 /** Whether the submission is to be deferred in case a script triggers it */ 543 bool mDeferSubmission; 544 /** Whether we notified NS_FORMSUBMIT_SUBJECT listeners already */ 545 bool mNotifiedObservers; 546 /** If we notified the listeners early, what was the result? */ 547 bool mNotifiedObserversResult; 548 /** 549 * Whether the submission of this form has been ever prevented because of 550 * being invalid. 551 */ 552 bool mEverTriedInvalidSubmit; 553 /** Whether we are constructing entry list */ 554 bool mIsConstructingEntryList; 555 /** Whether we are firing submission event */ 556 bool mIsFiringSubmissionEvents; 557 558 private: 559 bool IsSubmitting() const; 560 561 void SetDefaultSubmitElement(nsGenericHTMLFormElement*); 562 563 NotNull<const Encoding*> GetSubmitEncoding(); 564 565 /** 566 * Fire an event when the form is removed from the DOM tree. This is now only 567 * used by the password manager and formautofill. 568 */ 569 void MaybeFireFormRemoved(); 570 571 MOZ_CAN_RUN_SCRIPT 572 void ReportInvalidUnfocusableElements( 573 const nsTArray<RefPtr<Element>>&& aInvalidElements); 574 575 ~HTMLFormElement(); 576 }; 577 578 } // namespace dom 579 580 } // namespace mozilla 581 582 #endif // mozilla_dom_HTMLFormElement_h