LCOV - code coverage report
Current view: top level - dom/html - HTMLFormElement.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1054 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 121 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "mozilla/dom/HTMLFormElement.h"
       8             : 
       9             : #include "jsapi.h"
      10             : #include "mozilla/ContentEvents.h"
      11             : #include "mozilla/EventDispatcher.h"
      12             : #include "mozilla/EventStateManager.h"
      13             : #include "mozilla/EventStates.h"
      14             : #include "mozilla/dom/nsCSPUtils.h"
      15             : #include "mozilla/dom/nsCSPContext.h"
      16             : #include "mozilla/dom/nsMixedContentBlocker.h"
      17             : #include "mozilla/dom/HTMLFormControlsCollection.h"
      18             : #include "mozilla/dom/HTMLFormElementBinding.h"
      19             : #include "mozilla/Move.h"
      20             : #include "nsIHTMLDocument.h"
      21             : #include "nsGkAtoms.h"
      22             : #include "nsStyleConsts.h"
      23             : #include "nsPresContext.h"
      24             : #include "nsIDocument.h"
      25             : #include "nsIFormControlFrame.h"
      26             : #include "nsError.h"
      27             : #include "nsContentUtils.h"
      28             : #include "nsInterfaceHashtable.h"
      29             : #include "nsContentList.h"
      30             : #include "nsCOMArray.h"
      31             : #include "nsAutoPtr.h"
      32             : #include "nsTArray.h"
      33             : #include "nsIMutableArray.h"
      34             : #include "mozilla/BinarySearch.h"
      35             : #include "nsQueryObject.h"
      36             : 
      37             : // form submission
      38             : #include "HTMLFormSubmissionConstants.h"
      39             : #include "mozilla/dom/FormData.h"
      40             : #include "mozilla/Telemetry.h"
      41             : #include "nsIFormSubmitObserver.h"
      42             : #include "nsIObserverService.h"
      43             : #include "nsICategoryManager.h"
      44             : #include "nsCategoryManagerUtils.h"
      45             : #include "nsISimpleEnumerator.h"
      46             : #include "nsRange.h"
      47             : #include "nsIScriptError.h"
      48             : #include "nsIScriptSecurityManager.h"
      49             : #include "nsNetUtil.h"
      50             : #include "nsIInterfaceRequestorUtils.h"
      51             : #include "nsIWebProgress.h"
      52             : #include "nsIDocShell.h"
      53             : #include "nsIPrompt.h"
      54             : #include "nsISecurityUITelemetry.h"
      55             : #include "nsIStringBundle.h"
      56             : 
      57             : // radio buttons
      58             : #include "mozilla/dom/HTMLInputElement.h"
      59             : #include "nsIRadioVisitor.h"
      60             : #include "RadioNodeList.h"
      61             : 
      62             : #include "nsLayoutUtils.h"
      63             : 
      64             : #include "mozAutoDocUpdate.h"
      65             : #include "nsIHTMLCollection.h"
      66             : 
      67             : #include "nsIConstraintValidation.h"
      68             : 
      69             : #include "nsIDOMHTMLButtonElement.h"
      70             : #include "nsSandboxFlags.h"
      71             : 
      72             : #include "nsIContentSecurityPolicy.h"
      73             : 
      74             : // images
      75             : #include "mozilla/dom/HTMLImageElement.h"
      76             : 
      77             : // construction, destruction
      78           0 : NS_IMPL_NS_NEW_HTML_ELEMENT(Form)
      79             : 
      80             : namespace mozilla {
      81             : namespace dom {
      82             : 
      83             : static const uint8_t NS_FORM_AUTOCOMPLETE_ON  = 1;
      84             : static const uint8_t NS_FORM_AUTOCOMPLETE_OFF = 0;
      85             : 
      86             : static const nsAttrValue::EnumTable kFormAutocompleteTable[] = {
      87             :   { "on",  NS_FORM_AUTOCOMPLETE_ON },
      88             :   { "off", NS_FORM_AUTOCOMPLETE_OFF },
      89             :   { nullptr, 0 }
      90             : };
      91             : // Default autocomplete value is 'on'.
      92             : static const nsAttrValue::EnumTable* kFormDefaultAutocomplete = &kFormAutocompleteTable[0];
      93             : 
      94             : bool HTMLFormElement::gFirstFormSubmitted = false;
      95             : bool HTMLFormElement::gPasswordManagerInitialized = false;
      96             : 
      97           0 : HTMLFormElement::HTMLFormElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
      98             :   : nsGenericHTMLElement(aNodeInfo),
      99           0 :     mControls(new HTMLFormControlsCollection(this)),
     100             :     mSelectedRadioButtons(2),
     101             :     mRequiredRadioButtonCounts(2),
     102             :     mValueMissingRadioGroups(2),
     103             :     mPendingSubmission(nullptr),
     104             :     mSubmittingRequest(nullptr),
     105             :     mDefaultSubmitElement(nullptr),
     106             :     mFirstSubmitInElements(nullptr),
     107             :     mFirstSubmitNotInElements(nullptr),
     108             :     mImageNameLookupTable(FORM_CONTROL_LIST_HASHTABLE_LENGTH),
     109             :     mPastNameLookupTable(FORM_CONTROL_LIST_HASHTABLE_LENGTH),
     110             :     mSubmitPopupState(openAbused),
     111             :     mInvalidElementsCount(0),
     112             :     mGeneratingSubmit(false),
     113             :     mGeneratingReset(false),
     114             :     mIsSubmitting(false),
     115             :     mDeferSubmission(false),
     116             :     mNotifiedObservers(false),
     117             :     mNotifiedObserversResult(false),
     118             :     mSubmitInitiatedFromUserInput(false),
     119           0 :     mEverTriedInvalidSubmit(false)
     120             : {
     121             :   // We start out valid.
     122           0 :   AddStatesSilently(NS_EVENT_STATE_VALID);
     123           0 : }
     124             : 
     125           0 : HTMLFormElement::~HTMLFormElement()
     126             : {
     127           0 :   if (mControls) {
     128           0 :     mControls->DropFormReference();
     129             :   }
     130             : 
     131           0 :   Clear();
     132           0 : }
     133             : 
     134             : // nsISupports
     135             : 
     136             : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormElement)
     137             : 
     138           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLFormElement,
     139             :                                                   nsGenericHTMLElement)
     140           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControls)
     141           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageNameLookupTable)
     142           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPastNameLookupTable)
     143           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedRadioButtons)
     144           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     145             : 
     146           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLFormElement,
     147             :                                                 nsGenericHTMLElement)
     148           0 :   tmp->Clear();
     149           0 :   tmp->mExpandoAndGeneration.OwnerUnlinked();
     150           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     151             : 
     152           0 : NS_IMPL_ADDREF_INHERITED(HTMLFormElement, Element)
     153           0 : NS_IMPL_RELEASE_INHERITED(HTMLFormElement, Element)
     154             : 
     155             : 
     156             : // QueryInterface implementation for HTMLFormElement
     157           0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLFormElement)
     158           0 :   NS_INTERFACE_TABLE_INHERITED(HTMLFormElement,
     159             :                                nsIDOMHTMLFormElement,
     160             :                                nsIForm,
     161             :                                nsIWebProgressListener,
     162             :                                nsIRadioGroupContainer)
     163           0 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
     164             : 
     165             : // EventTarget
     166             : void
     167           0 : HTMLFormElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
     168             : {
     169           0 :   if (mFormPasswordEventDispatcher == aEvent) {
     170           0 :     mFormPasswordEventDispatcher = nullptr;
     171             :   }
     172           0 : }
     173             : 
     174             : // nsIDOMHTMLFormElement
     175             : 
     176           0 : NS_IMPL_ELEMENT_CLONE(HTMLFormElement)
     177             : 
     178             : nsIHTMLCollection*
     179           0 : HTMLFormElement::Elements()
     180             : {
     181           0 :   return mControls;
     182             : }
     183             : 
     184             : NS_IMETHODIMP
     185           0 : HTMLFormElement::GetElements(nsIDOMHTMLCollection** aElements)
     186             : {
     187           0 :   *aElements = Elements();
     188           0 :   NS_ADDREF(*aElements);
     189           0 :   return NS_OK;
     190             : }
     191             : 
     192             : nsresult
     193           0 : HTMLFormElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
     194             :                                const nsAttrValueOrString* aValue, bool aNotify)
     195             : {
     196           0 :   if (aNamespaceID == kNameSpaceID_None) {
     197           0 :     if (aName == nsGkAtoms::action || aName == nsGkAtoms::target) {
     198             :       // This check is mostly to preserve previous behavior.
     199           0 :       if (aValue) {
     200           0 :         if (mPendingSubmission) {
     201             :           // aha, there is a pending submission that means we're in
     202             :           // the script and we need to flush it. let's tell it
     203             :           // that the event was ignored to force the flush.
     204             :           // the second argument is not playing a role at all.
     205           0 :           FlushPendingSubmission();
     206             :         }
     207             :         // Don't forget we've notified the password manager already if the
     208             :         // page sets the action/target in the during submit. (bug 343182)
     209           0 :         bool notifiedObservers = mNotifiedObservers;
     210           0 :         ForgetCurrentSubmission();
     211           0 :         mNotifiedObservers = notifiedObservers;
     212             :       }
     213             :     }
     214             :   }
     215             : 
     216           0 :   return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue,
     217           0 :                                              aNotify);
     218             : }
     219             : 
     220             : nsresult
     221           0 : HTMLFormElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
     222             :                               const nsAttrValue* aValue,
     223             :                               const nsAttrValue* aOldValue, bool aNotify)
     224             : {
     225           0 :   if (aName == nsGkAtoms::novalidate && aNameSpaceID == kNameSpaceID_None) {
     226             :     // Update all form elements states because they might be [no longer]
     227             :     // affected by :-moz-ui-valid or :-moz-ui-invalid.
     228           0 :     for (uint32_t i = 0, length = mControls->mElements.Length();
     229           0 :          i < length; ++i) {
     230           0 :       mControls->mElements[i]->UpdateState(true);
     231             :     }
     232             : 
     233           0 :     for (uint32_t i = 0, length = mControls->mNotInElements.Length();
     234           0 :          i < length; ++i) {
     235           0 :       mControls->mNotInElements[i]->UpdateState(true);
     236             :     }
     237             :   }
     238             : 
     239           0 :   return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
     240           0 :                                             aOldValue, aNotify);
     241             : }
     242             : 
     243           0 : NS_IMPL_STRING_ATTR(HTMLFormElement, AcceptCharset, acceptcharset)
     244           0 : NS_IMPL_ACTION_ATTR(HTMLFormElement, Action, action)
     245           0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLFormElement, Autocomplete, autocomplete,
     246             :                                 kFormDefaultAutocomplete->tag)
     247           0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLFormElement, Enctype, enctype,
     248             :                                 kFormDefaultEnctype->tag)
     249           0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLFormElement, Method, method,
     250             :                                 kFormDefaultMethod->tag)
     251           0 : NS_IMPL_BOOL_ATTR(HTMLFormElement, NoValidate, novalidate)
     252           0 : NS_IMPL_STRING_ATTR(HTMLFormElement, Name, name)
     253           0 : NS_IMPL_STRING_ATTR(HTMLFormElement, Target, target)
     254             : 
     255             : void
     256           0 : HTMLFormElement::Submit(ErrorResult& aRv)
     257             : {
     258             :   // Send the submit event
     259           0 :   if (mPendingSubmission) {
     260             :     // aha, we have a pending submission that was not flushed
     261             :     // (this happens when form.submit() is called twice)
     262             :     // we have to delete it and build a new one since values
     263             :     // might have changed inbetween (we emulate IE here, that's all)
     264           0 :     mPendingSubmission = nullptr;
     265             :   }
     266             : 
     267           0 :   aRv = DoSubmitOrReset(nullptr, eFormSubmit);
     268           0 : }
     269             : 
     270             : NS_IMETHODIMP
     271           0 : HTMLFormElement::Submit()
     272             : {
     273           0 :   ErrorResult rv;
     274           0 :   Submit(rv);
     275           0 :   return rv.StealNSResult();
     276             : }
     277             : 
     278             : NS_IMETHODIMP
     279           0 : HTMLFormElement::Reset()
     280             : {
     281           0 :   InternalFormEvent event(true, eFormReset);
     282           0 :   EventDispatcher::Dispatch(static_cast<nsIContent*>(this), nullptr, &event);
     283           0 :   return NS_OK;
     284             : }
     285             : 
     286             : NS_IMETHODIMP
     287           0 : HTMLFormElement::CheckValidity(bool* retVal)
     288             : {
     289           0 :   *retVal = CheckValidity();
     290           0 :   return NS_OK;
     291             : }
     292             : 
     293             : bool
     294           0 : HTMLFormElement::ParseAttribute(int32_t aNamespaceID,
     295             :                                 nsIAtom* aAttribute,
     296             :                                 const nsAString& aValue,
     297             :                                 nsAttrValue& aResult)
     298             : {
     299           0 :   if (aNamespaceID == kNameSpaceID_None) {
     300           0 :     if (aAttribute == nsGkAtoms::method) {
     301           0 :       return aResult.ParseEnumValue(aValue, kFormMethodTable, false);
     302             :     }
     303           0 :     if (aAttribute == nsGkAtoms::enctype) {
     304           0 :       return aResult.ParseEnumValue(aValue, kFormEnctypeTable, false);
     305             :     }
     306           0 :     if (aAttribute == nsGkAtoms::autocomplete) {
     307           0 :       return aResult.ParseEnumValue(aValue, kFormAutocompleteTable, false);
     308             :     }
     309             :   }
     310             : 
     311           0 :   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
     312           0 :                                               aResult);
     313             : }
     314             : 
     315             : nsresult
     316           0 : HTMLFormElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
     317             :                             nsIContent* aBindingParent,
     318             :                             bool aCompileEventHandlers)
     319             : {
     320           0 :   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
     321             :                                                  aBindingParent,
     322           0 :                                                  aCompileEventHandlers);
     323           0 :   NS_ENSURE_SUCCESS(rv, rv);
     324             : 
     325           0 :   nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(aDocument));
     326           0 :   if (htmlDoc) {
     327           0 :     htmlDoc->AddedForm();
     328             :   }
     329             : 
     330           0 :   return rv;
     331             : }
     332             : 
     333             : template<typename T>
     334             : static void
     335           0 : MarkOrphans(const nsTArray<T*>& aArray)
     336             : {
     337           0 :   uint32_t length = aArray.Length();
     338           0 :   for (uint32_t i = 0; i < length; ++i) {
     339           0 :     aArray[i]->SetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
     340             :   }
     341           0 : }
     342             : 
     343             : static void
     344           0 : CollectOrphans(nsINode* aRemovalRoot,
     345             :                const nsTArray<nsGenericHTMLFormElement*>& aArray
     346             : #ifdef DEBUG
     347             :                , nsIDOMHTMLFormElement* aThisForm
     348             : #endif
     349             :                )
     350             : {
     351             :   // Put a script blocker around all the notifications we're about to do.
     352           0 :   nsAutoScriptBlocker scriptBlocker;
     353             : 
     354             :   // Walk backwards so that if we remove elements we can just keep iterating
     355           0 :   uint32_t length = aArray.Length();
     356           0 :   for (uint32_t i = length; i > 0; --i) {
     357           0 :     nsGenericHTMLFormElement* node = aArray[i-1];
     358             : 
     359             :     // Now if MAYBE_ORPHAN_FORM_ELEMENT is not set, that would mean that the
     360             :     // node is in fact a descendant of the form and hence should stay in the
     361             :     // form.  If it _is_ set, then we need to check whether the node is a
     362             :     // descendant of aRemovalRoot.  If it is, we leave it in the form.
     363             : #ifdef DEBUG
     364           0 :     bool removed = false;
     365             : #endif
     366           0 :     if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) {
     367           0 :       node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
     368           0 :       if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) {
     369           0 :         node->ClearForm(true, false);
     370             : 
     371             :         // When a form control loses its form owner, its state can change.
     372           0 :         node->UpdateState(true);
     373             : #ifdef DEBUG
     374           0 :         removed = true;
     375             : #endif
     376             :       }
     377             :     }
     378             : 
     379             : #ifdef DEBUG
     380           0 :     if (!removed) {
     381           0 :       nsCOMPtr<nsIDOMHTMLFormElement> form;
     382           0 :       node->GetForm(getter_AddRefs(form));
     383           0 :       NS_ASSERTION(form == aThisForm, "How did that happen?");
     384             :     }
     385             : #endif /* DEBUG */
     386             :   }
     387           0 : }
     388             : 
     389             : static void
     390           0 : CollectOrphans(nsINode* aRemovalRoot,
     391             :                const nsTArray<HTMLImageElement*>& aArray
     392             : #ifdef DEBUG
     393             :                , nsIDOMHTMLFormElement* aThisForm
     394             : #endif
     395             :                )
     396             : {
     397             :   // Walk backwards so that if we remove elements we can just keep iterating
     398           0 :   uint32_t length = aArray.Length();
     399           0 :   for (uint32_t i = length; i > 0; --i) {
     400           0 :     HTMLImageElement* node = aArray[i-1];
     401             : 
     402             :     // Now if MAYBE_ORPHAN_FORM_ELEMENT is not set, that would mean that the
     403             :     // node is in fact a descendant of the form and hence should stay in the
     404             :     // form.  If it _is_ set, then we need to check whether the node is a
     405             :     // descendant of aRemovalRoot.  If it is, we leave it in the form.
     406             : #ifdef DEBUG
     407           0 :     bool removed = false;
     408             : #endif
     409           0 :     if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) {
     410           0 :       node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
     411           0 :       if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) {
     412           0 :         node->ClearForm(true);
     413             : 
     414             : #ifdef DEBUG
     415           0 :         removed = true;
     416             : #endif
     417             :       }
     418             :     }
     419             : 
     420             : #ifdef DEBUG
     421           0 :     if (!removed) {
     422           0 :       nsCOMPtr<nsIDOMHTMLFormElement> form = node->GetForm();
     423           0 :       NS_ASSERTION(form == aThisForm, "How did that happen?");
     424             :     }
     425             : #endif /* DEBUG */
     426             :   }
     427           0 : }
     428             : 
     429             : void
     430           0 : HTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent)
     431             : {
     432           0 :   nsCOMPtr<nsIHTMLDocument> oldDocument = do_QueryInterface(GetUncomposedDoc());
     433             : 
     434             :   // Mark all of our controls as maybe being orphans
     435           0 :   MarkOrphans(mControls->mElements);
     436           0 :   MarkOrphans(mControls->mNotInElements);
     437           0 :   MarkOrphans(mImageElements);
     438             : 
     439           0 :   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
     440             : 
     441           0 :   nsINode* ancestor = this;
     442             :   nsINode* cur;
     443             :   do {
     444           0 :     cur = ancestor->GetParentNode();
     445           0 :     if (!cur) {
     446           0 :       break;
     447             :     }
     448           0 :     ancestor = cur;
     449             :   } while (1);
     450             : 
     451           0 :   CollectOrphans(ancestor, mControls->mElements
     452             : #ifdef DEBUG
     453             :                  , this
     454             : #endif
     455           0 :                  );
     456           0 :   CollectOrphans(ancestor, mControls->mNotInElements
     457             : #ifdef DEBUG
     458             :                  , this
     459             : #endif
     460           0 :                  );
     461           0 :   CollectOrphans(ancestor, mImageElements
     462             : #ifdef DEBUG
     463             :                  , this
     464             : #endif
     465           0 :                  );
     466             : 
     467           0 :   if (oldDocument) {
     468           0 :     oldDocument->RemovedForm();
     469             :   }
     470           0 :   ForgetCurrentSubmission();
     471           0 : }
     472             : 
     473             : nsresult
     474           0 : HTMLFormElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
     475             : {
     476           0 :   aVisitor.mWantsWillHandleEvent = true;
     477           0 :   if (aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this)) {
     478           0 :     uint32_t msg = aVisitor.mEvent->mMessage;
     479           0 :     if (msg == eFormSubmit) {
     480           0 :       if (mGeneratingSubmit) {
     481           0 :         aVisitor.mCanHandle = false;
     482           0 :         return NS_OK;
     483             :       }
     484           0 :       mGeneratingSubmit = true;
     485             : 
     486             :       // let the form know that it needs to defer the submission,
     487             :       // that means that if there are scripted submissions, the
     488             :       // latest one will be deferred until after the exit point of the handler.
     489           0 :       mDeferSubmission = true;
     490           0 :     } else if (msg == eFormReset) {
     491           0 :       if (mGeneratingReset) {
     492           0 :         aVisitor.mCanHandle = false;
     493           0 :         return NS_OK;
     494             :       }
     495           0 :       mGeneratingReset = true;
     496             :     }
     497             :   }
     498           0 :   return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
     499             : }
     500             : 
     501             : nsresult
     502           0 : HTMLFormElement::WillHandleEvent(EventChainPostVisitor& aVisitor)
     503             : {
     504             :   // If this is the bubble stage and there is a nested form below us which
     505             :   // received a submit event we do *not* want to handle the submit event
     506             :   // for this form too.
     507           0 :   if ((aVisitor.mEvent->mMessage == eFormSubmit ||
     508           0 :        aVisitor.mEvent->mMessage == eFormReset) &&
     509           0 :       aVisitor.mEvent->mFlags.mInBubblingPhase &&
     510           0 :       aVisitor.mEvent->mOriginalTarget != static_cast<nsIContent*>(this)) {
     511           0 :     aVisitor.mEvent->StopPropagation();
     512             :   }
     513           0 :   return NS_OK;
     514             : }
     515             : 
     516             : nsresult
     517           0 : HTMLFormElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
     518             : {
     519           0 :   if (aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this)) {
     520           0 :     EventMessage msg = aVisitor.mEvent->mMessage;
     521           0 :     if (msg == eFormSubmit) {
     522             :       // let the form know not to defer subsequent submissions
     523           0 :       mDeferSubmission = false;
     524             :     }
     525             : 
     526           0 :     if (aVisitor.mEventStatus == nsEventStatus_eIgnore) {
     527           0 :       switch (msg) {
     528             :         case eFormReset:
     529             :         case eFormSubmit: {
     530           0 :           if (mPendingSubmission && msg == eFormSubmit) {
     531             :             // tell the form to forget a possible pending submission.
     532             :             // the reason is that the script returned true (the event was
     533             :             // ignored) so if there is a stored submission, it will miss
     534             :             // the name/value of the submitting element, thus we need
     535             :             // to forget it and the form element will build a new one
     536           0 :             mPendingSubmission = nullptr;
     537             :           }
     538           0 :           DoSubmitOrReset(aVisitor.mEvent, msg);
     539           0 :           break;
     540             :         }
     541             :         default:
     542           0 :           break;
     543             :       }
     544             :     } else {
     545           0 :       if (msg == eFormSubmit) {
     546             :         // tell the form to flush a possible pending submission.
     547             :         // the reason is that the script returned false (the event was
     548             :         // not ignored) so if there is a stored submission, it needs to
     549             :         // be submitted immediatelly.
     550           0 :         FlushPendingSubmission();
     551             :       }
     552             :     }
     553             : 
     554           0 :     if (msg == eFormSubmit) {
     555           0 :       mGeneratingSubmit = false;
     556           0 :     } else if (msg == eFormReset) {
     557           0 :       mGeneratingReset = false;
     558             :     }
     559             :   }
     560           0 :   return NS_OK;
     561             : }
     562             : 
     563             : nsresult
     564           0 : HTMLFormElement::DoSubmitOrReset(WidgetEvent* aEvent,
     565             :                                  EventMessage aMessage)
     566             : {
     567             :   // Make sure the presentation is up-to-date
     568           0 :   nsIDocument* doc = GetComposedDoc();
     569           0 :   if (doc) {
     570           0 :     doc->FlushPendingNotifications(FlushType::ContentAndNotify);
     571             :   }
     572             : 
     573             :   // JBK Don't get form frames anymore - bug 34297
     574             : 
     575             :   // Submit or Reset the form
     576           0 :   if (eFormReset == aMessage) {
     577           0 :     return DoReset();
     578             :   }
     579             : 
     580           0 :   if (eFormSubmit == aMessage) {
     581             :     // Don't submit if we're not in a document or if we're in
     582             :     // a sandboxed frame and form submit is disabled.
     583           0 :     if (!doc || (doc->GetSandboxFlags() & SANDBOXED_FORMS)) {
     584           0 :       return NS_OK;
     585             :     }
     586           0 :     return DoSubmit(aEvent);
     587             :   }
     588             : 
     589           0 :   MOZ_ASSERT(false);
     590             :   return NS_OK;
     591             : }
     592             : 
     593             : nsresult
     594           0 : HTMLFormElement::DoReset()
     595             : {
     596             :   // JBK walk the elements[] array instead of form frame controls - bug 34297
     597           0 :   uint32_t numElements = GetElementCount();
     598           0 :   for (uint32_t elementX = 0; elementX < numElements; ++elementX) {
     599             :     // Hold strong ref in case the reset does something weird
     600           0 :     nsCOMPtr<nsIFormControl> controlNode = GetElementAt(elementX);
     601           0 :     if (controlNode) {
     602           0 :       controlNode->Reset();
     603             :     }
     604             :   }
     605             : 
     606           0 :   return NS_OK;
     607             : }
     608             : 
     609             : #define NS_ENSURE_SUBMIT_SUCCESS(rv)                                          \
     610             :   if (NS_FAILED(rv)) {                                                        \
     611             :     ForgetCurrentSubmission();                                                \
     612             :     return rv;                                                                \
     613             :   }
     614             : 
     615             : nsresult
     616           0 : HTMLFormElement::DoSubmit(WidgetEvent* aEvent)
     617             : {
     618           0 :   NS_ASSERTION(GetComposedDoc(), "Should never get here without a current doc");
     619             : 
     620           0 :   if (mIsSubmitting) {
     621           0 :     NS_WARNING("Preventing double form submission");
     622             :     // XXX Should this return an error?
     623           0 :     return NS_OK;
     624             :   }
     625             : 
     626             :   // Mark us as submitting so that we don't try to submit again
     627           0 :   mIsSubmitting = true;
     628           0 :   NS_ASSERTION(!mWebProgress && !mSubmittingRequest, "Web progress / submitting request should not exist here!");
     629             : 
     630           0 :   nsAutoPtr<HTMLFormSubmission> submission;
     631             : 
     632             :   //
     633             :   // prepare the submission object
     634             :   //
     635           0 :   nsresult rv = BuildSubmission(getter_Transfers(submission), aEvent);
     636           0 :   if (NS_FAILED(rv)) {
     637           0 :     mIsSubmitting = false;
     638           0 :     return rv;
     639             :   }
     640             : 
     641             :   // XXXbz if the script global is that for an sXBL/XBL2 doc, it won't
     642             :   // be a window...
     643           0 :   nsPIDOMWindowOuter *window = OwnerDoc()->GetWindow();
     644             : 
     645           0 :   if (window) {
     646           0 :     mSubmitPopupState = window->GetPopupControlState();
     647             :   } else {
     648           0 :     mSubmitPopupState = openAbused;
     649             :   }
     650             : 
     651           0 :   mSubmitInitiatedFromUserInput = EventStateManager::IsHandlingUserInput();
     652             : 
     653           0 :   if(mDeferSubmission) {
     654             :     // we are in an event handler, JS submitted so we have to
     655             :     // defer this submission. let's remember it and return
     656             :     // without submitting
     657           0 :     mPendingSubmission = submission;
     658             :     // ensure reentrancy
     659           0 :     mIsSubmitting = false;
     660           0 :     return NS_OK;
     661             :   }
     662             : 
     663             :   //
     664             :   // perform the submission
     665             :   //
     666           0 :   return SubmitSubmission(submission);
     667             : }
     668             : 
     669             : nsresult
     670           0 : HTMLFormElement::BuildSubmission(HTMLFormSubmission** aFormSubmission,
     671             :                                  WidgetEvent* aEvent)
     672             : {
     673           0 :   NS_ASSERTION(!mPendingSubmission, "tried to build two submissions!");
     674             : 
     675             :   // Get the originating frame (failure is non-fatal)
     676           0 :   nsGenericHTMLElement* originatingElement = nullptr;
     677           0 :   if (aEvent) {
     678           0 :     InternalFormEvent* formEvent = aEvent->AsFormEvent();
     679           0 :     if (formEvent) {
     680           0 :       nsIContent* originator = formEvent->mOriginator;
     681           0 :       if (originator) {
     682           0 :         if (!originator->IsHTMLElement()) {
     683           0 :           return NS_ERROR_UNEXPECTED;
     684             :         }
     685           0 :         originatingElement = static_cast<nsGenericHTMLElement*>(originator);
     686             :       }
     687             :     }
     688             :   }
     689             : 
     690             :   nsresult rv;
     691             : 
     692             :   //
     693             :   // Get the submission object
     694             :   //
     695           0 :   rv = HTMLFormSubmission::GetFromForm(this, originatingElement,
     696           0 :                                        aFormSubmission);
     697           0 :   NS_ENSURE_SUBMIT_SUCCESS(rv);
     698             : 
     699             :   //
     700             :   // Dump the data into the submission object
     701             :   //
     702           0 :   rv = WalkFormElements(*aFormSubmission);
     703           0 :   NS_ENSURE_SUBMIT_SUCCESS(rv);
     704             : 
     705           0 :   return NS_OK;
     706             : }
     707             : 
     708             : nsresult
     709           0 : HTMLFormElement::SubmitSubmission(HTMLFormSubmission* aFormSubmission)
     710             : {
     711             :   nsresult rv;
     712           0 :   nsIContent* originatingElement = aFormSubmission->GetOriginatingElement();
     713             : 
     714             :   //
     715             :   // Get the action and target
     716             :   //
     717           0 :   nsCOMPtr<nsIURI> actionURI;
     718           0 :   rv = GetActionURL(getter_AddRefs(actionURI), originatingElement);
     719           0 :   NS_ENSURE_SUBMIT_SUCCESS(rv);
     720             : 
     721           0 :   if (!actionURI) {
     722           0 :     mIsSubmitting = false;
     723           0 :     return NS_OK;
     724             :   }
     725             : 
     726             :   // If there is no link handler, then we won't actually be able to submit.
     727           0 :   nsIDocument* doc = GetComposedDoc();
     728           0 :   nsCOMPtr<nsISupports> container = doc ? doc->GetContainer() : nullptr;
     729           0 :   nsCOMPtr<nsILinkHandler> linkHandler(do_QueryInterface(container));
     730           0 :   if (!linkHandler || IsEditable()) {
     731           0 :     mIsSubmitting = false;
     732           0 :     return NS_OK;
     733             :   }
     734             : 
     735             :   // javascript URIs are not really submissions; they just call a function.
     736             :   // Also, they may synchronously call submit(), and we want them to be able to
     737             :   // do so while still disallowing other double submissions. (Bug 139798)
     738             :   // Note that any other URI types that are of equivalent type should also be
     739             :   // added here.
     740             :   // XXXbz this is a mess.  The real issue here is that nsJSChannel sets the
     741             :   // LOAD_BACKGROUND flag, so doesn't notify us, compounded by the fact that
     742             :   // the JS executes before we forget the submission in OnStateChange on
     743             :   // STATE_STOP.  As a result, we have to make sure that we simply pretend
     744             :   // we're not submitting when submitting to a JS URL.  That's kinda bogus, but
     745             :   // there we are.
     746           0 :   bool schemeIsJavaScript = false;
     747           0 :   if (NS_SUCCEEDED(actionURI->SchemeIs("javascript", &schemeIsJavaScript)) &&
     748             :       schemeIsJavaScript) {
     749           0 :     mIsSubmitting = false;
     750             :   }
     751             : 
     752             :   // The target is the originating element formtarget attribute if the element
     753             :   // is a submit control and has such an attribute.
     754             :   // Otherwise, the target is the form owner's target attribute,
     755             :   // if it has such an attribute.
     756             :   // Finally, if one of the child nodes of the head element is a base element
     757             :   // with a target attribute, then the value of the target attribute of the
     758             :   // first such base element; or, if there is no such element, the empty string.
     759           0 :   nsAutoString target;
     760           0 :   if (!(originatingElement && originatingElement->GetAttr(kNameSpaceID_None,
     761             :                                                           nsGkAtoms::formtarget,
     762           0 :                                                           target)) &&
     763           0 :       !GetAttr(kNameSpaceID_None, nsGkAtoms::target, target)) {
     764           0 :     GetBaseTarget(target);
     765             :   }
     766             : 
     767             :   //
     768             :   // Notify observers of submit
     769             :   //
     770           0 :   bool cancelSubmit = false;
     771           0 :   if (mNotifiedObservers) {
     772           0 :     cancelSubmit = mNotifiedObserversResult;
     773             :   } else {
     774           0 :     rv = NotifySubmitObservers(actionURI, &cancelSubmit, true);
     775           0 :     NS_ENSURE_SUBMIT_SUCCESS(rv);
     776             :   }
     777             : 
     778           0 :   if (cancelSubmit) {
     779           0 :     mIsSubmitting = false;
     780           0 :     return NS_OK;
     781             :   }
     782             : 
     783           0 :   cancelSubmit = false;
     784           0 :   rv = NotifySubmitObservers(actionURI, &cancelSubmit, false);
     785           0 :   NS_ENSURE_SUBMIT_SUCCESS(rv);
     786             : 
     787           0 :   if (cancelSubmit) {
     788           0 :     mIsSubmitting = false;
     789           0 :     return NS_OK;
     790             :   }
     791             : 
     792             :   //
     793             :   // Submit
     794             :   //
     795           0 :   nsCOMPtr<nsIDocShell> docShell;
     796             : 
     797             :   {
     798           0 :     nsAutoPopupStatePusher popupStatePusher(mSubmitPopupState);
     799             : 
     800             :     AutoHandlingUserInputStatePusher userInpStatePusher(
     801           0 :                                        mSubmitInitiatedFromUserInput,
     802           0 :                                        nullptr, doc);
     803             : 
     804           0 :     nsCOMPtr<nsIInputStream> postDataStream;
     805           0 :     rv = aFormSubmission->GetEncodedSubmission(actionURI,
     806           0 :                                                getter_AddRefs(postDataStream));
     807           0 :     NS_ENSURE_SUBMIT_SUCCESS(rv);
     808             : 
     809           0 :     rv = linkHandler->OnLinkClickSync(this, actionURI,
     810             :                                       target.get(),
     811           0 :                                       NullString(),
     812             :                                       postDataStream, nullptr, false,
     813           0 :                                       getter_AddRefs(docShell),
     814           0 :                                       getter_AddRefs(mSubmittingRequest));
     815           0 :     NS_ENSURE_SUBMIT_SUCCESS(rv);
     816             :   }
     817             : 
     818             :   // Even if the submit succeeds, it's possible for there to be no docshell
     819             :   // or request; for example, if it's to a named anchor within the same page
     820             :   // the submit will not really do anything.
     821           0 :   if (docShell) {
     822             :     // If the channel is pending, we have to listen for web progress.
     823           0 :     bool pending = false;
     824           0 :     mSubmittingRequest->IsPending(&pending);
     825           0 :     if (pending && !schemeIsJavaScript) {
     826           0 :       nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
     827           0 :       NS_ASSERTION(webProgress, "nsIDocShell not converted to nsIWebProgress!");
     828           0 :       rv = webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_ALL);
     829           0 :       NS_ENSURE_SUBMIT_SUCCESS(rv);
     830           0 :       mWebProgress = do_GetWeakReference(webProgress);
     831           0 :       NS_ASSERTION(mWebProgress, "can't hold weak ref to webprogress!");
     832             :     } else {
     833           0 :       ForgetCurrentSubmission();
     834             :     }
     835             :   } else {
     836           0 :     ForgetCurrentSubmission();
     837             :   }
     838             : 
     839           0 :   return rv;
     840             : }
     841             : 
     842             : nsresult
     843           0 : HTMLFormElement::DoSecureToInsecureSubmitCheck(nsIURI* aActionURL,
     844             :                                                bool* aCancelSubmit)
     845             : {
     846           0 :   *aCancelSubmit = false;
     847             : 
     848             :   // Only ask the user about posting from a secure URI to an insecure URI if
     849             :   // this element is in the root document. When this is not the case, the mixed
     850             :   // content blocker will take care of security for us.
     851           0 :   nsIDocument* parent = OwnerDoc()->GetParentDocument();
     852           0 :   bool isRootDocument = (!parent || nsContentUtils::IsChromeDoc(parent));
     853           0 :   if (!isRootDocument) {
     854           0 :     return NS_OK;
     855             :   }
     856             : 
     857           0 :   nsIPrincipal* principal = NodePrincipal();
     858           0 :   if (!principal) {
     859           0 :     *aCancelSubmit = true;
     860           0 :     return NS_OK;
     861             :   }
     862           0 :   nsCOMPtr<nsIURI> principalURI;
     863           0 :   nsresult rv = principal->GetURI(getter_AddRefs(principalURI));
     864           0 :   if (NS_FAILED(rv)) {
     865           0 :     return rv;
     866             :   }
     867           0 :   if (!principalURI) {
     868           0 :     principalURI = OwnerDoc()->GetDocumentURI();
     869             :   }
     870             :   bool formIsHTTPS;
     871           0 :   rv = principalURI->SchemeIs("https", &formIsHTTPS);
     872           0 :   if (NS_FAILED(rv)) {
     873           0 :     return rv;
     874             :   }
     875             :   bool actionIsHTTPS;
     876           0 :   rv = aActionURL->SchemeIs("https", &actionIsHTTPS);
     877           0 :   if (NS_FAILED(rv)) {
     878           0 :     return rv;
     879             :   }
     880             :   bool actionIsJS;
     881           0 :   rv = aActionURL->SchemeIs("javascript", &actionIsJS);
     882           0 :   if (NS_FAILED(rv)) {
     883           0 :     return rv;
     884             :   }
     885             : 
     886           0 :   if (!formIsHTTPS || actionIsHTTPS || actionIsJS) {
     887           0 :     return NS_OK;
     888             :   }
     889             : 
     890           0 :   if (nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aActionURL)) {
     891           0 :     return NS_OK;
     892             :   }
     893             : 
     894           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = OwnerDoc()->GetWindow();
     895           0 :   if (!window) {
     896           0 :     return NS_ERROR_FAILURE;
     897             :   }
     898           0 :   nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
     899           0 :   if (!docShell) {
     900           0 :     return NS_ERROR_FAILURE;
     901             :   }
     902           0 :   nsCOMPtr<nsIPrompt> prompt = do_GetInterface(docShell);
     903           0 :   if (!prompt) {
     904           0 :     return NS_ERROR_FAILURE;
     905             :   }
     906           0 :   nsCOMPtr<nsIStringBundle> stringBundle;
     907             :   nsCOMPtr<nsIStringBundleService> stringBundleService =
     908           0 :     mozilla::services::GetStringBundleService();
     909           0 :   if (!stringBundleService) {
     910           0 :     return NS_ERROR_FAILURE;
     911             :   }
     912           0 :   rv = stringBundleService->CreateBundle(
     913             :     "chrome://global/locale/browser.properties",
     914           0 :     getter_AddRefs(stringBundle));
     915           0 :   if (NS_FAILED(rv)) {
     916           0 :     return rv;
     917             :   }
     918           0 :   nsAutoString title;
     919           0 :   nsAutoString message;
     920           0 :   nsAutoString cont;
     921           0 :   stringBundle->GetStringFromName(
     922           0 :     u"formPostSecureToInsecureWarning.title", getter_Copies(title));
     923           0 :   stringBundle->GetStringFromName(
     924             :     u"formPostSecureToInsecureWarning.message",
     925           0 :     getter_Copies(message));
     926           0 :   stringBundle->GetStringFromName(
     927             :     u"formPostSecureToInsecureWarning.continue",
     928           0 :     getter_Copies(cont));
     929             :   int32_t buttonPressed;
     930           0 :   bool checkState = false; // this is unused (ConfirmEx requires this parameter)
     931           0 :   rv = prompt->ConfirmEx(title.get(), message.get(),
     932             :                          (nsIPrompt::BUTTON_TITLE_IS_STRING *
     933             :                           nsIPrompt::BUTTON_POS_0) +
     934             :                          (nsIPrompt::BUTTON_TITLE_CANCEL *
     935             :                           nsIPrompt::BUTTON_POS_1),
     936             :                          cont.get(), nullptr, nullptr, nullptr,
     937           0 :                             &checkState, &buttonPressed);
     938           0 :   if (NS_FAILED(rv)) {
     939           0 :     return rv;
     940             :   }
     941           0 :   *aCancelSubmit = (buttonPressed == 1);
     942             :   uint32_t telemetryBucket =
     943           0 :     nsISecurityUITelemetry::WARNING_CONFIRM_POST_TO_INSECURE_FROM_SECURE;
     944             :   mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI,
     945           0 :                                  telemetryBucket);
     946           0 :   if (!*aCancelSubmit) {
     947             :     // The user opted to continue, so note that in the next telemetry bucket.
     948           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI,
     949           0 :                                    telemetryBucket + 1);
     950             :   }
     951           0 :   return NS_OK;
     952             : }
     953             : 
     954             : nsresult
     955           0 : HTMLFormElement::NotifySubmitObservers(nsIURI* aActionURL,
     956             :                                        bool* aCancelSubmit,
     957             :                                        bool    aEarlyNotify)
     958             : {
     959             :   // If this is the first form, bring alive the first form submit
     960             :   // category observers
     961           0 :   if (!gFirstFormSubmitted) {
     962           0 :     gFirstFormSubmitted = true;
     963             :     NS_CreateServicesFromCategory(NS_FIRST_FORMSUBMIT_CATEGORY,
     964             :                                   nullptr,
     965           0 :                                   NS_FIRST_FORMSUBMIT_CATEGORY);
     966             :   }
     967             : 
     968           0 :   if (!aEarlyNotify) {
     969           0 :     nsresult rv = DoSecureToInsecureSubmitCheck(aActionURL, aCancelSubmit);
     970           0 :     if (NS_FAILED(rv)) {
     971           0 :       return rv;
     972             :     }
     973           0 :     if (*aCancelSubmit) {
     974           0 :       return NS_OK;
     975             :     }
     976             :   }
     977             : 
     978             :   // Notify observers that the form is being submitted.
     979             :   nsCOMPtr<nsIObserverService> service =
     980           0 :     mozilla::services::GetObserverService();
     981           0 :   if (!service)
     982           0 :     return NS_ERROR_FAILURE;
     983             : 
     984           0 :   nsCOMPtr<nsISimpleEnumerator> theEnum;
     985           0 :   nsresult rv = service->EnumerateObservers(aEarlyNotify ?
     986             :                                             NS_EARLYFORMSUBMIT_SUBJECT :
     987             :                                             NS_FORMSUBMIT_SUBJECT,
     988           0 :                                             getter_AddRefs(theEnum));
     989           0 :   NS_ENSURE_SUCCESS(rv, rv);
     990             : 
     991           0 :   if (theEnum) {
     992           0 :     nsCOMPtr<nsISupports> inst;
     993           0 :     *aCancelSubmit = false;
     994             : 
     995             :     // XXXbz what do the submit observers actually want?  The window
     996             :     // of the document this is shown in?  Or something else?
     997             :     // sXBL/XBL2 issue
     998           0 :     nsCOMPtr<nsPIDOMWindowOuter> window = OwnerDoc()->GetWindow();
     999             : 
    1000           0 :     bool loop = true;
    1001           0 :     while (NS_SUCCEEDED(theEnum->HasMoreElements(&loop)) && loop) {
    1002           0 :       theEnum->GetNext(getter_AddRefs(inst));
    1003             : 
    1004             :       nsCOMPtr<nsIFormSubmitObserver> formSubmitObserver(
    1005           0 :                       do_QueryInterface(inst));
    1006           0 :       if (formSubmitObserver) {
    1007           0 :         rv = formSubmitObserver->Notify(this,
    1008           0 :                                         window ? window->GetCurrentInnerWindow() : nullptr,
    1009             :                                         aActionURL,
    1010           0 :                                         aCancelSubmit);
    1011           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1012             :       }
    1013           0 :       if (*aCancelSubmit) {
    1014           0 :         return NS_OK;
    1015             :       }
    1016             :     }
    1017             :   }
    1018             : 
    1019           0 :   return rv;
    1020             : }
    1021             : 
    1022             : 
    1023             : nsresult
    1024           0 : HTMLFormElement::WalkFormElements(HTMLFormSubmission* aFormSubmission)
    1025             : {
    1026           0 :   nsTArray<nsGenericHTMLFormElement*> sortedControls;
    1027           0 :   nsresult rv = mControls->GetSortedControls(sortedControls);
    1028           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1029             : 
    1030           0 :   uint32_t len = sortedControls.Length();
    1031             : 
    1032             :   // Hold a reference to the elements so they can't be deleted while
    1033             :   // calling SubmitNamesValues().
    1034           0 :   for (uint32_t i = 0; i < len; ++i) {
    1035           0 :     static_cast<nsGenericHTMLElement*>(sortedControls[i])->AddRef();
    1036             :   }
    1037             : 
    1038             :   //
    1039             :   // Walk the list of nodes and call SubmitNamesValues() on the controls
    1040             :   //
    1041           0 :   for (uint32_t i = 0; i < len; ++i) {
    1042             :     // Tell the control to submit its name/value pairs to the submission
    1043           0 :     sortedControls[i]->SubmitNamesValues(aFormSubmission);
    1044             :   }
    1045             : 
    1046             :   // Release the references.
    1047           0 :   for (uint32_t i = 0; i < len; ++i) {
    1048           0 :     static_cast<nsGenericHTMLElement*>(sortedControls[i])->Release();
    1049             :   }
    1050             : 
    1051           0 :   return NS_OK;
    1052             : }
    1053             : 
    1054             : // nsIForm
    1055             : 
    1056             : NS_IMETHODIMP_(uint32_t)
    1057           0 : HTMLFormElement::GetElementCount() const
    1058             : {
    1059           0 :   uint32_t count = 0;
    1060           0 :   mControls->GetLength(&count);
    1061           0 :   return count;
    1062             : }
    1063             : 
    1064             : Element*
    1065           0 : HTMLFormElement::IndexedGetter(uint32_t aIndex, bool &aFound)
    1066             : {
    1067           0 :   Element* element = mControls->mElements.SafeElementAt(aIndex, nullptr);
    1068           0 :   aFound = element != nullptr;
    1069           0 :   return element;
    1070             : }
    1071             : 
    1072             : NS_IMETHODIMP_(nsIFormControl*)
    1073           0 : HTMLFormElement::GetElementAt(int32_t aIndex) const
    1074             : {
    1075           0 :   return mControls->mElements.SafeElementAt(aIndex, nullptr);
    1076             : }
    1077             : 
    1078             : /**
    1079             :  * Compares the position of aControl1 and aControl2 in the document
    1080             :  * @param aControl1 First control to compare.
    1081             :  * @param aControl2 Second control to compare.
    1082             :  * @param aForm Parent form of the controls.
    1083             :  * @return < 0 if aControl1 is before aControl2,
    1084             :  *         > 0 if aControl1 is after aControl2,
    1085             :  *         0 otherwise
    1086             :  */
    1087             : /* static */ int32_t
    1088           0 : HTMLFormElement::CompareFormControlPosition(Element* aElement1,
    1089             :                                             Element* aElement2,
    1090             :                                             const nsIContent* aForm)
    1091             : {
    1092           0 :   NS_ASSERTION(aElement1 != aElement2, "Comparing a form control to itself");
    1093             : 
    1094             :   // If an element has a @form, we can assume it *might* be able to not have
    1095             :   // a parent and still be in the form.
    1096           0 :   NS_ASSERTION((aElement1->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
    1097             :                 aElement1->GetParent()) &&
    1098             :                (aElement2->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
    1099             :                 aElement2->GetParent()),
    1100             :                "Form controls should always have parents");
    1101             : 
    1102             :   // If we pass aForm, we are assuming both controls are form descendants which
    1103             :   // is not always the case. This function should work but maybe slower.
    1104             :   // However, checking if both elements are form descendants may be slow too...
    1105             :   // TODO: remove the prevent asserts fix, see bug 598468.
    1106             : #ifdef DEBUG
    1107           0 :   nsLayoutUtils::gPreventAssertInCompareTreePosition = true;
    1108           0 :   int32_t rVal = nsLayoutUtils::CompareTreePosition(aElement1, aElement2, aForm);
    1109           0 :   nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
    1110             : 
    1111           0 :   return rVal;
    1112             : #else // DEBUG
    1113             :   return nsLayoutUtils::CompareTreePosition(aElement1, aElement2, aForm);
    1114             : #endif // DEBUG
    1115             : }
    1116             : 
    1117             : #ifdef DEBUG
    1118             : /**
    1119             :  * Checks that all form elements are in document order. Asserts if any pair of
    1120             :  * consecutive elements are not in increasing document order.
    1121             :  *
    1122             :  * @param aControls List of form controls to check.
    1123             :  * @param aForm Parent form of the controls.
    1124             :  */
    1125             : /* static */ void
    1126           0 : HTMLFormElement::AssertDocumentOrder(
    1127             :   const nsTArray<nsGenericHTMLFormElement*>& aControls, nsIContent* aForm)
    1128             : {
    1129             :   // TODO: remove the return statement with bug 598468.
    1130             :   // This is done to prevent asserts in some edge cases.
    1131           0 :   return;
    1132             : 
    1133             :   // Only iterate if aControls is not empty, since otherwise
    1134             :   // |aControls.Length() - 1| will be a very large unsigned number... not what
    1135             :   // we want here.
    1136             :   if (!aControls.IsEmpty()) {
    1137             :     for (uint32_t i = 0; i < aControls.Length() - 1; ++i) {
    1138             :       NS_ASSERTION(CompareFormControlPosition(aControls[i], aControls[i + 1],
    1139             :                                               aForm) < 0,
    1140             :                    "Form controls not ordered correctly");
    1141             :     }
    1142             :   }
    1143             : }
    1144             : #endif
    1145             : 
    1146             : void
    1147           0 : HTMLFormElement::PostPasswordEvent()
    1148             : {
    1149             :   // Don't fire another add event if we have a pending add event.
    1150           0 :   if (mFormPasswordEventDispatcher.get()) {
    1151           0 :     return;
    1152             :   }
    1153             : 
    1154             :   mFormPasswordEventDispatcher =
    1155           0 :     new AsyncEventDispatcher(this, NS_LITERAL_STRING("DOMFormHasPassword"),
    1156           0 :                              true, true);
    1157           0 :   mFormPasswordEventDispatcher->PostDOMEvent();
    1158             : }
    1159             : 
    1160             : namespace {
    1161             : 
    1162             : struct FormComparator
    1163             : {
    1164             :   Element* const mChild;
    1165             :   HTMLFormElement* const mForm;
    1166           0 :   FormComparator(Element* aChild, HTMLFormElement* aForm)
    1167           0 :     : mChild(aChild), mForm(aForm) {}
    1168           0 :   int operator()(Element* aElement) const {
    1169           0 :     return HTMLFormElement::CompareFormControlPosition(mChild, aElement, mForm);
    1170             :   }
    1171             : };
    1172             : 
    1173             : } // namespace
    1174             : 
    1175             : // This function return true if the element, once appended, is the last one in
    1176             : // the array.
    1177             : template<typename ElementType>
    1178             : static bool
    1179           0 : AddElementToList(nsTArray<ElementType*>& aList, ElementType* aChild,
    1180             :                  HTMLFormElement* aForm)
    1181             : {
    1182           0 :   NS_ASSERTION(aList.IndexOf(aChild) == aList.NoIndex,
    1183             :                "aChild already in aList");
    1184             : 
    1185           0 :   const uint32_t count = aList.Length();
    1186             :   ElementType* element;
    1187           0 :   bool lastElement = false;
    1188             : 
    1189             :   // Optimize most common case where we insert at the end.
    1190           0 :   int32_t position = -1;
    1191           0 :   if (count > 0) {
    1192           0 :     element = aList[count - 1];
    1193           0 :     position =
    1194           0 :       HTMLFormElement::CompareFormControlPosition(aChild, element, aForm);
    1195             :   }
    1196             : 
    1197             :   // If this item comes after the last element, or the elements array is
    1198             :   // empty, we append to the end. Otherwise, we do a binary search to
    1199             :   // determine where the element should go.
    1200           0 :   if (position >= 0 || count == 0) {
    1201             :     // WEAK - don't addref
    1202           0 :     aList.AppendElement(aChild);
    1203           0 :     lastElement = true;
    1204             :   }
    1205             :   else {
    1206             :     size_t idx;
    1207           0 :     BinarySearchIf(aList, 0, count, FormComparator(aChild, aForm), &idx);
    1208             : 
    1209             :     // WEAK - don't addref
    1210           0 :     aList.InsertElementAt(idx, aChild);
    1211             :   }
    1212             : 
    1213           0 :   return lastElement;
    1214             : }
    1215             : 
    1216             : nsresult
    1217           0 : HTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild,
    1218             :                             bool aUpdateValidity, bool aNotify)
    1219             : {
    1220             :   // If an element has a @form, we can assume it *might* be able to not have
    1221             :   // a parent and still be in the form.
    1222           0 :   NS_ASSERTION(aChild->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
    1223             :                aChild->GetParent(),
    1224             :                "Form control should have a parent");
    1225             : 
    1226             :   // Determine whether to add the new element to the elements or
    1227             :   // the not-in-elements list.
    1228           0 :   bool childInElements = HTMLFormControlsCollection::ShouldBeInElements(aChild);
    1229             :   nsTArray<nsGenericHTMLFormElement*>& controlList = childInElements ?
    1230           0 :       mControls->mElements : mControls->mNotInElements;
    1231             : 
    1232           0 :   bool lastElement = AddElementToList(controlList, aChild, this);
    1233             : 
    1234             : #ifdef DEBUG
    1235           0 :   AssertDocumentOrder(controlList, this);
    1236             : #endif
    1237             : 
    1238           0 :   int32_t type = aChild->ControlType();
    1239             : 
    1240             :   //
    1241             :   // If it is a password control, and the password manager has not yet been
    1242             :   // initialized, initialize the password manager
    1243             :   //
    1244           0 :   if (type == NS_FORM_INPUT_PASSWORD) {
    1245           0 :     if (!gPasswordManagerInitialized) {
    1246           0 :       gPasswordManagerInitialized = true;
    1247             :       NS_CreateServicesFromCategory(NS_PASSWORDMANAGER_CATEGORY,
    1248             :                                     nullptr,
    1249           0 :                                     NS_PASSWORDMANAGER_CATEGORY);
    1250             :     }
    1251           0 :     PostPasswordEvent();
    1252             :   }
    1253             : 
    1254             :   // Default submit element handling
    1255           0 :   if (aChild->IsSubmitControl()) {
    1256             :     // Update mDefaultSubmitElement, mFirstSubmitInElements,
    1257             :     // mFirstSubmitNotInElements.
    1258             : 
    1259             :     nsGenericHTMLFormElement** firstSubmitSlot =
    1260           0 :       childInElements ? &mFirstSubmitInElements : &mFirstSubmitNotInElements;
    1261             : 
    1262             :     // The new child is the new first submit in its list if the firstSubmitSlot
    1263             :     // is currently empty or if the child is before what's currently in the
    1264             :     // slot.  Note that if we already have a control in firstSubmitSlot and
    1265             :     // we're appending this element can't possibly replace what's currently in
    1266             :     // the slot.  Also note that aChild can't become the mDefaultSubmitElement
    1267             :     // unless it replaces what's in the slot.  If it _does_ replace what's in
    1268             :     // the slot, it becomes the default submit if either the default submit is
    1269             :     // what's in the slot or the child is earlier than the default submit.
    1270           0 :     nsGenericHTMLFormElement* oldDefaultSubmit = mDefaultSubmitElement;
    1271           0 :     if (!*firstSubmitSlot ||
    1272           0 :         (!lastElement &&
    1273           0 :          CompareFormControlPosition(aChild, *firstSubmitSlot, this) < 0)) {
    1274             :       // Update mDefaultSubmitElement if it's currently in a valid state.
    1275             :       // Valid state means either non-null or null because there are in fact
    1276             :       // no submit elements around.
    1277           0 :       if ((mDefaultSubmitElement ||
    1278           0 :            (!mFirstSubmitInElements && !mFirstSubmitNotInElements)) &&
    1279           0 :           (*firstSubmitSlot == mDefaultSubmitElement ||
    1280           0 :            CompareFormControlPosition(aChild,
    1281           0 :                                       mDefaultSubmitElement, this) < 0)) {
    1282           0 :         mDefaultSubmitElement = aChild;
    1283             :       }
    1284           0 :       *firstSubmitSlot = aChild;
    1285             :     }
    1286           0 :     NS_POSTCONDITION(mDefaultSubmitElement == mFirstSubmitInElements ||
    1287             :                      mDefaultSubmitElement == mFirstSubmitNotInElements ||
    1288             :                      !mDefaultSubmitElement,
    1289             :                      "What happened here?");
    1290             : 
    1291             :     // Notify that the state of the previous default submit element has changed
    1292             :     // if the element which is the default submit element has changed.  The new
    1293             :     // default submit element is responsible for its own state update.
    1294           0 :     if (oldDefaultSubmit && oldDefaultSubmit != mDefaultSubmitElement) {
    1295           0 :       oldDefaultSubmit->UpdateState(aNotify);
    1296             :     }
    1297             :   }
    1298             : 
    1299             :   // If the element is subject to constraint validaton and is invalid, we need
    1300             :   // to update our internal counter.
    1301           0 :   if (aUpdateValidity) {
    1302           0 :     nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aChild);
    1303           0 :     if (cvElmt &&
    1304           0 :         cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
    1305           0 :       UpdateValidity(false);
    1306             :     }
    1307             :   }
    1308             : 
    1309             :   // Notify the radio button it's been added to a group
    1310             :   // This has to be done _after_ UpdateValidity() call to prevent the element
    1311             :   // being count twice.
    1312           0 :   if (type == NS_FORM_INPUT_RADIO) {
    1313             :     RefPtr<HTMLInputElement> radio =
    1314           0 :       static_cast<HTMLInputElement*>(aChild);
    1315           0 :     radio->AddedToRadioGroup();
    1316             :   }
    1317             : 
    1318           0 :   return NS_OK;
    1319             : }
    1320             : 
    1321             : nsresult
    1322           0 : HTMLFormElement::AddElementToTable(nsGenericHTMLFormElement* aChild,
    1323             :                                    const nsAString& aName)
    1324             : {
    1325           0 :   return mControls->AddElementToTable(aChild, aName);
    1326             : }
    1327             : 
    1328             : 
    1329             : nsresult
    1330           0 : HTMLFormElement::RemoveElement(nsGenericHTMLFormElement* aChild,
    1331             :                                bool aUpdateValidity)
    1332             : {
    1333           0 :   RemoveElementFromPastNamesMap(aChild);
    1334             : 
    1335             :   //
    1336             :   // Remove it from the radio group if it's a radio button
    1337             :   //
    1338           0 :   nsresult rv = NS_OK;
    1339           0 :   if (aChild->ControlType() == NS_FORM_INPUT_RADIO) {
    1340             :     RefPtr<HTMLInputElement> radio =
    1341           0 :       static_cast<HTMLInputElement*>(aChild);
    1342           0 :     radio->WillRemoveFromRadioGroup();
    1343             :   }
    1344             : 
    1345             :   // Determine whether to remove the child from the elements list
    1346             :   // or the not in elements list.
    1347           0 :   bool childInElements = HTMLFormControlsCollection::ShouldBeInElements(aChild);
    1348             :   nsTArray<nsGenericHTMLFormElement*>& controls = childInElements ?
    1349           0 :       mControls->mElements :  mControls->mNotInElements;
    1350             : 
    1351             :   // Find the index of the child. This will be used later if necessary
    1352             :   // to find the default submit.
    1353           0 :   size_t index = controls.IndexOf(aChild);
    1354           0 :   NS_ENSURE_STATE(index != controls.NoIndex);
    1355             : 
    1356           0 :   controls.RemoveElementAt(index);
    1357             : 
    1358             :   // Update our mFirstSubmit* values.
    1359             :   nsGenericHTMLFormElement** firstSubmitSlot =
    1360           0 :     childInElements ? &mFirstSubmitInElements : &mFirstSubmitNotInElements;
    1361           0 :   if (aChild == *firstSubmitSlot) {
    1362           0 :     *firstSubmitSlot = nullptr;
    1363             : 
    1364             :     // We are removing the first submit in this list, find the new first submit
    1365           0 :     uint32_t length = controls.Length();
    1366           0 :     for (uint32_t i = index; i < length; ++i) {
    1367           0 :       nsGenericHTMLFormElement* currentControl = controls[i];
    1368           0 :       if (currentControl->IsSubmitControl()) {
    1369           0 :         *firstSubmitSlot = currentControl;
    1370           0 :         break;
    1371             :       }
    1372             :     }
    1373             :   }
    1374             : 
    1375           0 :   if (aChild == mDefaultSubmitElement) {
    1376             :     // Need to reset mDefaultSubmitElement.  Do this asynchronously so
    1377             :     // that we're not doing it while the DOM is in flux.
    1378           0 :     mDefaultSubmitElement = nullptr;
    1379           0 :     nsContentUtils::AddScriptRunner(new RemoveElementRunnable(this));
    1380             : 
    1381             :     // Note that we don't need to notify on the old default submit (which is
    1382             :     // being removed) because it's either being removed from the DOM or
    1383             :     // changing attributes in a way that makes it responsible for sending its
    1384             :     // own notifications.
    1385             :   }
    1386             : 
    1387             :   // If the element was subject to constraint validaton and is invalid, we need
    1388             :   // to update our internal counter.
    1389           0 :   if (aUpdateValidity) {
    1390           0 :     nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(aChild);
    1391           0 :     if (cvElmt &&
    1392           0 :         cvElmt->IsCandidateForConstraintValidation() && !cvElmt->IsValid()) {
    1393           0 :       UpdateValidity(true);
    1394             :     }
    1395             :   }
    1396             : 
    1397           0 :   return rv;
    1398             : }
    1399             : 
    1400             : void
    1401           0 : HTMLFormElement::HandleDefaultSubmitRemoval()
    1402             : {
    1403           0 :   if (mDefaultSubmitElement) {
    1404             :     // Already got reset somehow; nothing else to do here
    1405           0 :     return;
    1406             :   }
    1407             : 
    1408           0 :   if (!mFirstSubmitNotInElements) {
    1409           0 :     mDefaultSubmitElement = mFirstSubmitInElements;
    1410           0 :   } else if (!mFirstSubmitInElements) {
    1411           0 :     mDefaultSubmitElement = mFirstSubmitNotInElements;
    1412             :   } else {
    1413           0 :     NS_ASSERTION(mFirstSubmitInElements != mFirstSubmitNotInElements,
    1414             :                  "How did that happen?");
    1415             :     // Have both; use the earlier one
    1416           0 :     mDefaultSubmitElement =
    1417           0 :       CompareFormControlPosition(mFirstSubmitInElements,
    1418           0 :                                  mFirstSubmitNotInElements, this) < 0 ?
    1419             :       mFirstSubmitInElements : mFirstSubmitNotInElements;
    1420             :   }
    1421             : 
    1422           0 :   NS_POSTCONDITION(mDefaultSubmitElement == mFirstSubmitInElements ||
    1423             :                    mDefaultSubmitElement == mFirstSubmitNotInElements,
    1424             :                    "What happened here?");
    1425             : 
    1426             :   // Notify about change if needed.
    1427           0 :   if (mDefaultSubmitElement) {
    1428           0 :     mDefaultSubmitElement->UpdateState(true);
    1429             :   }
    1430             : }
    1431             : 
    1432             : nsresult
    1433           0 : HTMLFormElement::RemoveElementFromTableInternal(
    1434             :   nsInterfaceHashtable<nsStringHashKey,nsISupports>& aTable,
    1435             :   nsIContent* aChild, const nsAString& aName)
    1436             : {
    1437           0 :   auto entry = aTable.Lookup(aName);
    1438           0 :   if (!entry) {
    1439           0 :     return NS_OK;
    1440             :   }
    1441             :   // Single element in the hash, just remove it if it's the one
    1442             :   // we're trying to remove...
    1443           0 :   if (entry.Data() == aChild) {
    1444           0 :     entry.Remove();
    1445           0 :     ++mExpandoAndGeneration.generation;
    1446           0 :     return NS_OK;
    1447             :   }
    1448             : 
    1449           0 :   nsCOMPtr<nsIContent> content(do_QueryInterface(entry.Data()));
    1450           0 :   if (content) {
    1451           0 :     return NS_OK;
    1452             :   }
    1453             : 
    1454             :   // If it's not a content node then it must be a RadioNodeList.
    1455           0 :   MOZ_ASSERT(nsCOMPtr<RadioNodeList>(do_QueryInterface(entry.Data())));
    1456           0 :   auto* list = static_cast<RadioNodeList*>(entry.Data().get());
    1457             : 
    1458           0 :   list->RemoveElement(aChild);
    1459             : 
    1460           0 :   uint32_t length = 0;
    1461           0 :   list->GetLength(&length);
    1462             : 
    1463           0 :   if (!length) {
    1464             :     // If the list is empty we remove if from our hash, this shouldn't
    1465             :     // happen tho
    1466           0 :     entry.Remove();
    1467           0 :     ++mExpandoAndGeneration.generation;
    1468           0 :   } else if (length == 1) {
    1469             :     // Only one element left, replace the list in the hash with the
    1470             :     // single element.
    1471           0 :     nsIContent* node = list->Item(0);
    1472           0 :     if (node) {
    1473           0 :       entry.Data() = node;
    1474             :     }
    1475             :   }
    1476             : 
    1477           0 :   return NS_OK;
    1478             : }
    1479             : 
    1480             : nsresult
    1481           0 : HTMLFormElement::RemoveElementFromTable(nsGenericHTMLFormElement* aElement,
    1482             :                                         const nsAString& aName)
    1483             : {
    1484           0 :   return mControls->RemoveElementFromTable(aElement, aName);
    1485             : }
    1486             : 
    1487             : already_AddRefed<nsISupports>
    1488           0 : HTMLFormElement::NamedGetter(const nsAString& aName, bool &aFound)
    1489             : {
    1490           0 :   aFound = true;
    1491             : 
    1492           0 :   nsCOMPtr<nsISupports> result = DoResolveName(aName, true);
    1493           0 :   if (result) {
    1494           0 :     AddToPastNamesMap(aName, result);
    1495           0 :     return result.forget();
    1496             :   }
    1497             : 
    1498           0 :   result = mImageNameLookupTable.GetWeak(aName);
    1499           0 :   if (result) {
    1500           0 :     AddToPastNamesMap(aName, result);
    1501           0 :     return result.forget();
    1502             :   }
    1503             : 
    1504           0 :   result = mPastNameLookupTable.GetWeak(aName);
    1505           0 :   if (result) {
    1506           0 :     return result.forget();
    1507             :   }
    1508             : 
    1509           0 :   aFound = false;
    1510           0 :   return nullptr;
    1511             : }
    1512             : 
    1513             : void
    1514           0 : HTMLFormElement::GetSupportedNames(nsTArray<nsString >& aRetval)
    1515             : {
    1516             :   // TODO https://github.com/whatwg/html/issues/1731
    1517           0 : }
    1518             : 
    1519             : already_AddRefed<nsISupports>
    1520           0 : HTMLFormElement::FindNamedItem(const nsAString& aName,
    1521             :                                nsWrapperCache** aCache)
    1522             : {
    1523             :   // FIXME Get the wrapper cache from DoResolveName.
    1524             : 
    1525             :   bool found;
    1526           0 :   nsCOMPtr<nsISupports> result = NamedGetter(aName, found);
    1527           0 :   if (result) {
    1528           0 :     *aCache = nullptr;
    1529           0 :     return result.forget();
    1530             :   }
    1531             : 
    1532           0 :   return nullptr;
    1533             : }
    1534             : 
    1535             : already_AddRefed<nsISupports>
    1536           0 : HTMLFormElement::DoResolveName(const nsAString& aName,
    1537             :                                bool aFlushContent)
    1538             : {
    1539             :   nsCOMPtr<nsISupports> result =
    1540           0 :     mControls->NamedItemInternal(aName, aFlushContent);
    1541           0 :   return result.forget();
    1542             : }
    1543             : 
    1544             : void
    1545           0 : HTMLFormElement::OnSubmitClickBegin(nsIContent* aOriginatingElement)
    1546             : {
    1547           0 :   mDeferSubmission = true;
    1548             : 
    1549             :   // Prepare to run NotifySubmitObservers early before the
    1550             :   // scripts on the page get to modify the form data, possibly
    1551             :   // throwing off any password manager. (bug 257781)
    1552           0 :   nsCOMPtr<nsIURI> actionURI;
    1553             :   nsresult rv;
    1554             : 
    1555           0 :   rv = GetActionURL(getter_AddRefs(actionURI), aOriginatingElement);
    1556           0 :   if (NS_FAILED(rv) || !actionURI)
    1557           0 :     return;
    1558             : 
    1559             :   // Notify observers of submit if the form is valid.
    1560             :   // TODO: checking for mInvalidElementsCount is a temporary fix that should be
    1561             :   // removed with bug 610402.
    1562           0 :   if (mInvalidElementsCount == 0) {
    1563           0 :     bool cancelSubmit = false;
    1564           0 :     rv = NotifySubmitObservers(actionURI, &cancelSubmit, true);
    1565           0 :     if (NS_SUCCEEDED(rv)) {
    1566           0 :       mNotifiedObservers = true;
    1567           0 :       mNotifiedObserversResult = cancelSubmit;
    1568             :     }
    1569             :   }
    1570             : }
    1571             : 
    1572             : void
    1573           0 : HTMLFormElement::OnSubmitClickEnd()
    1574             : {
    1575           0 :   mDeferSubmission = false;
    1576           0 : }
    1577             : 
    1578             : void
    1579           0 : HTMLFormElement::FlushPendingSubmission()
    1580             : {
    1581           0 :   if (mPendingSubmission) {
    1582             :     // Transfer owning reference so that the submissioin doesn't get deleted
    1583             :     // if we reenter
    1584           0 :     nsAutoPtr<HTMLFormSubmission> submission = Move(mPendingSubmission);
    1585             : 
    1586           0 :     SubmitSubmission(submission);
    1587             :   }
    1588           0 : }
    1589             : 
    1590             : nsresult
    1591           0 : HTMLFormElement::GetActionURL(nsIURI** aActionURL,
    1592             :                               nsIContent* aOriginatingElement)
    1593             : {
    1594           0 :   nsresult rv = NS_OK;
    1595             : 
    1596           0 :   *aActionURL = nullptr;
    1597             : 
    1598             :   //
    1599             :   // Grab the URL string
    1600             :   //
    1601             :   // If the originating element is a submit control and has the formaction
    1602             :   // attribute specified, it should be used. Otherwise, the action attribute
    1603             :   // from the form element should be used.
    1604             :   //
    1605           0 :   nsAutoString action;
    1606             : 
    1607           0 :   if (aOriginatingElement &&
    1608           0 :       aOriginatingElement->HasAttr(kNameSpaceID_None, nsGkAtoms::formaction)) {
    1609             : #ifdef DEBUG
    1610           0 :     nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(aOriginatingElement);
    1611           0 :     NS_ASSERTION(formControl && formControl->IsSubmitControl(),
    1612             :                  "The originating element must be a submit form control!");
    1613             : #endif // DEBUG
    1614             : 
    1615           0 :     nsCOMPtr<nsIDOMHTMLInputElement> inputElement = do_QueryInterface(aOriginatingElement);
    1616           0 :     if (inputElement) {
    1617           0 :       inputElement->GetFormAction(action);
    1618             :     } else {
    1619           0 :       nsCOMPtr<nsIDOMHTMLButtonElement> buttonElement = do_QueryInterface(aOriginatingElement);
    1620           0 :       if (buttonElement) {
    1621           0 :         buttonElement->GetFormAction(action);
    1622             :       } else {
    1623           0 :         NS_ERROR("Originating element must be an input or button element!");
    1624           0 :         return NS_ERROR_UNEXPECTED;
    1625             :       }
    1626             :     }
    1627             :   } else {
    1628           0 :     GetAction(action);
    1629             :   }
    1630             : 
    1631             :   //
    1632             :   // Form the full action URL
    1633             :   //
    1634             : 
    1635             :   // Get the document to form the URL.
    1636             :   // We'll also need it later to get the DOM window when notifying form submit
    1637             :   // observers (bug 33203)
    1638           0 :   if (!IsInUncomposedDoc()) {
    1639           0 :     return NS_OK; // No doc means don't submit, see Bug 28988
    1640             :   }
    1641             : 
    1642             :   // Get base URL
    1643           0 :   nsIDocument *document = OwnerDoc();
    1644           0 :   nsIURI *docURI = document->GetDocumentURI();
    1645           0 :   NS_ENSURE_TRUE(docURI, NS_ERROR_UNEXPECTED);
    1646             : 
    1647             :   // If an action is not specified and we are inside
    1648             :   // a HTML document then reload the URL. This makes us
    1649             :   // compatible with 4.x browsers.
    1650             :   // If we are in some other type of document such as XML or
    1651             :   // XUL, do nothing. This prevents undesirable reloading of
    1652             :   // a document inside XUL.
    1653             : 
    1654           0 :   nsCOMPtr<nsIURI> actionURL;
    1655           0 :   if (action.IsEmpty()) {
    1656           0 :     nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(document));
    1657           0 :     if (!htmlDoc) {
    1658             :       // Must be a XML, XUL or other non-HTML document type
    1659             :       // so do nothing.
    1660           0 :       return NS_OK;
    1661             :     }
    1662             : 
    1663           0 :     rv = docURI->Clone(getter_AddRefs(actionURL));
    1664           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1665             :   } else {
    1666           0 :     nsCOMPtr<nsIURI> baseURL = GetBaseURI();
    1667           0 :     NS_ASSERTION(baseURL, "No Base URL found in Form Submit!\n");
    1668           0 :     if (!baseURL) {
    1669           0 :       return NS_OK; // No base URL -> exit early, see Bug 30721
    1670             :     }
    1671           0 :     rv = NS_NewURI(getter_AddRefs(actionURL), action, nullptr, baseURL);
    1672           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1673             :   }
    1674             : 
    1675             :   //
    1676             :   // Verify the URL should be reached
    1677             :   //
    1678             :   // Get security manager, check to see if access to action URI is allowed.
    1679             :   //
    1680             :   nsIScriptSecurityManager *securityManager =
    1681           0 :       nsContentUtils::GetSecurityManager();
    1682             :   rv = securityManager->
    1683           0 :     CheckLoadURIWithPrincipal(NodePrincipal(), actionURL,
    1684           0 :                               nsIScriptSecurityManager::STANDARD);
    1685           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1686             : 
    1687             :   // Check if CSP allows this form-action
    1688           0 :   nsCOMPtr<nsIContentSecurityPolicy> csp;
    1689           0 :   rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
    1690           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1691           0 :   if (csp) {
    1692           0 :     bool permitsFormAction = true;
    1693             : 
    1694             :     // form-action is only enforced if explicitly defined in the
    1695             :     // policy - do *not* consult default-src, see:
    1696             :     // http://www.w3.org/TR/CSP2/#directive-default-src
    1697           0 :     rv = csp->Permits(actionURL, nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE,
    1698           0 :                       true, &permitsFormAction);
    1699           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1700           0 :     if (!permitsFormAction) {
    1701           0 :       return NS_ERROR_CSP_FORM_ACTION_VIOLATION;
    1702             :     }
    1703             :   }
    1704             : 
    1705             :   // Potentially the page uses the CSP directive 'upgrade-insecure-requests'. In
    1706             :   // such a case we have to upgrade the action url from http:// to https://.
    1707             :   // If the actionURL is not http, then there is nothing to do.
    1708           0 :   bool isHttpScheme = false;
    1709           0 :   rv = actionURL->SchemeIs("http", &isHttpScheme);
    1710           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1711           0 :   if (isHttpScheme && document->GetUpgradeInsecureRequests(false)) {
    1712             :     // let's use the old specification before the upgrade for logging
    1713           0 :     nsAutoCString spec;
    1714           0 :     rv = actionURL->GetSpec(spec);
    1715           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1716           0 :     NS_ConvertUTF8toUTF16 reportSpec(spec);
    1717             : 
    1718             :     // upgrade the actionURL from http:// to use https://
    1719           0 :     nsCOMPtr<nsIURI> upgradedActionURL;
    1720           0 :     rv = NS_GetSecureUpgradedURI(actionURL, getter_AddRefs(upgradedActionURL));
    1721           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1722           0 :     actionURL = upgradedActionURL.forget();
    1723             : 
    1724             :     // let's log a message to the console that we are upgrading a request
    1725           0 :     nsAutoCString scheme;
    1726           0 :     rv = actionURL->GetScheme(scheme);
    1727           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1728           0 :     NS_ConvertUTF8toUTF16 reportScheme(scheme);
    1729             : 
    1730           0 :     const char16_t* params[] = { reportSpec.get(), reportScheme.get() };
    1731           0 :     CSP_LogLocalizedStr(u"upgradeInsecureRequest",
    1732           0 :                         params, ArrayLength(params),
    1733           0 :                         EmptyString(), // aSourceFile
    1734           0 :                         EmptyString(), // aScriptSample
    1735             :                         0, // aLineNumber
    1736             :                         0, // aColumnNumber
    1737             :                         nsIScriptError::warningFlag, "CSP",
    1738           0 :                         document->InnerWindowID());
    1739             :   }
    1740             : 
    1741             :   //
    1742             :   // Assign to the output
    1743             :   //
    1744           0 :   actionURL.forget(aActionURL);
    1745             : 
    1746           0 :   return rv;
    1747             : }
    1748             : 
    1749             : NS_IMETHODIMP_(nsIFormControl*)
    1750           0 : HTMLFormElement::GetDefaultSubmitElement() const
    1751             : {
    1752           0 :   NS_PRECONDITION(mDefaultSubmitElement == mFirstSubmitInElements ||
    1753             :                   mDefaultSubmitElement == mFirstSubmitNotInElements,
    1754             :                   "What happened here?");
    1755             : 
    1756           0 :   return mDefaultSubmitElement;
    1757             : }
    1758             : 
    1759             : bool
    1760           0 : HTMLFormElement::IsDefaultSubmitElement(const nsIFormControl* aControl) const
    1761             : {
    1762           0 :   NS_PRECONDITION(aControl, "Unexpected call");
    1763             : 
    1764           0 :   if (aControl == mDefaultSubmitElement) {
    1765             :     // Yes, it is
    1766           0 :     return true;
    1767             :   }
    1768             : 
    1769           0 :   if (mDefaultSubmitElement ||
    1770           0 :       (aControl != mFirstSubmitInElements &&
    1771           0 :        aControl != mFirstSubmitNotInElements)) {
    1772             :     // It isn't
    1773           0 :     return false;
    1774             :   }
    1775             : 
    1776             :   // mDefaultSubmitElement is null, but we have a non-null submit around
    1777             :   // (aControl, in fact).  figure out whether it's in fact the default submit
    1778             :   // and just hasn't been set that way yet.  Note that we can't just call
    1779             :   // HandleDefaultSubmitRemoval because we might need to notify to handle that
    1780             :   // correctly and we don't know whether that's safe right here.
    1781           0 :   if (!mFirstSubmitInElements || !mFirstSubmitNotInElements) {
    1782             :     // We only have one first submit; aControl has to be it
    1783           0 :     return true;
    1784             :   }
    1785             : 
    1786             :   // We have both kinds of submits.  Check which comes first.
    1787             :   nsIFormControl* defaultSubmit =
    1788           0 :     CompareFormControlPosition(mFirstSubmitInElements,
    1789           0 :                                mFirstSubmitNotInElements, this) < 0 ?
    1790           0 :       mFirstSubmitInElements : mFirstSubmitNotInElements;
    1791           0 :   return aControl == defaultSubmit;
    1792             : }
    1793             : 
    1794             : bool
    1795           0 : HTMLFormElement::ImplicitSubmissionIsDisabled() const
    1796             : {
    1797             :   // Input text controls are always in the elements list.
    1798           0 :   uint32_t numDisablingControlsFound = 0;
    1799           0 :   uint32_t length = mControls->mElements.Length();
    1800           0 :   for (uint32_t i = 0; i < length && numDisablingControlsFound < 2; ++i) {
    1801           0 :     if (mControls->mElements[i]->IsSingleLineTextControl(false) ||
    1802           0 :         mControls->mElements[i]->ControlType() == NS_FORM_INPUT_NUMBER) {
    1803           0 :       numDisablingControlsFound++;
    1804             :     }
    1805             :   }
    1806           0 :   return numDisablingControlsFound != 1;
    1807             : }
    1808             : 
    1809             : NS_IMETHODIMP
    1810           0 : HTMLFormElement::GetEncoding(nsAString& aEncoding)
    1811             : {
    1812           0 :   return GetEnctype(aEncoding);
    1813             : }
    1814             : 
    1815             : NS_IMETHODIMP
    1816           0 : HTMLFormElement::SetEncoding(const nsAString& aEncoding)
    1817             : {
    1818           0 :   return SetEnctype(aEncoding);
    1819             : }
    1820             : 
    1821             : int32_t
    1822           0 : HTMLFormElement::Length()
    1823             : {
    1824           0 :   return mControls->Length();
    1825             : }
    1826             : 
    1827             : NS_IMETHODIMP
    1828           0 : HTMLFormElement::GetLength(int32_t* aLength)
    1829             : {
    1830           0 :   *aLength = Length();
    1831           0 :   return NS_OK;
    1832             : }
    1833             : 
    1834             : void
    1835           0 : HTMLFormElement::ForgetCurrentSubmission()
    1836             : {
    1837           0 :   mNotifiedObservers = false;
    1838           0 :   mIsSubmitting = false;
    1839           0 :   mSubmittingRequest = nullptr;
    1840           0 :   nsCOMPtr<nsIWebProgress> webProgress = do_QueryReferent(mWebProgress);
    1841           0 :   if (webProgress) {
    1842           0 :     webProgress->RemoveProgressListener(this);
    1843             :   }
    1844           0 :   mWebProgress = nullptr;
    1845           0 : }
    1846             : 
    1847             : bool
    1848           0 : HTMLFormElement::CheckFormValidity(nsIMutableArray* aInvalidElements) const
    1849             : {
    1850           0 :   bool ret = true;
    1851             : 
    1852           0 :   nsTArray<nsGenericHTMLFormElement*> sortedControls;
    1853           0 :   if (NS_FAILED(mControls->GetSortedControls(sortedControls))) {
    1854           0 :     return false;
    1855             :   }
    1856             : 
    1857           0 :   uint32_t len = sortedControls.Length();
    1858             : 
    1859             :   // Hold a reference to the elements so they can't be deleted while calling
    1860             :   // the invalid events.
    1861           0 :   for (uint32_t i = 0; i < len; ++i) {
    1862           0 :     sortedControls[i]->AddRef();
    1863             :   }
    1864             : 
    1865           0 :   for (uint32_t i = 0; i < len; ++i) {
    1866           0 :     nsCOMPtr<nsIConstraintValidation> cvElmt = do_QueryObject(sortedControls[i]);
    1867           0 :     if (cvElmt && cvElmt->IsCandidateForConstraintValidation() &&
    1868           0 :         !cvElmt->IsValid()) {
    1869           0 :       ret = false;
    1870           0 :       bool defaultAction = true;
    1871           0 :       nsContentUtils::DispatchTrustedEvent(sortedControls[i]->OwnerDoc(),
    1872           0 :                                            static_cast<nsIContent*>(sortedControls[i]),
    1873           0 :                                            NS_LITERAL_STRING("invalid"),
    1874           0 :                                            false, true, &defaultAction);
    1875             : 
    1876             :       // Add all unhandled invalid controls to aInvalidElements if the caller
    1877             :       // requested them.
    1878           0 :       if (defaultAction && aInvalidElements) {
    1879           0 :         aInvalidElements->AppendElement(ToSupports(sortedControls[i]),
    1880           0 :                                         false);
    1881             :       }
    1882             :     }
    1883             :   }
    1884             : 
    1885             :   // Release the references.
    1886           0 :   for (uint32_t i = 0; i < len; ++i) {
    1887           0 :     static_cast<nsGenericHTMLElement*>(sortedControls[i])->Release();
    1888             :   }
    1889             : 
    1890           0 :   return ret;
    1891             : }
    1892             : 
    1893             : bool
    1894           0 : HTMLFormElement::CheckValidFormSubmission()
    1895             : {
    1896             :   /**
    1897             :    * Check for form validity: do not submit a form if there are unhandled
    1898             :    * invalid controls in the form.
    1899             :    * This should not be done if the form has been submitted with .submit().
    1900             :    *
    1901             :    * NOTE: for the moment, we are also checking that there is an observer for
    1902             :    * NS_INVALIDFORMSUBMIT_SUBJECT so it will prevent blocking form submission
    1903             :    * if the browser does not have implemented a UI yet.
    1904             :    *
    1905             :    * TODO: the check for observer should be removed later when HTML5 Forms will
    1906             :    * be spread enough and authors will assume forms can't be submitted when
    1907             :    * invalid. See bug 587671.
    1908             :    */
    1909             : 
    1910           0 :   NS_ASSERTION(!HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate),
    1911             :                "We shouldn't be there if novalidate is set!");
    1912             : 
    1913             :   // When .submit() is called aEvent = nullptr so we can rely on that to know if
    1914             :   // we have to check the validity of the form.
    1915             :   nsCOMPtr<nsIObserverService> service =
    1916           0 :     mozilla::services::GetObserverService();
    1917           0 :   if (!service) {
    1918           0 :     NS_WARNING("No observer service available!");
    1919           0 :     return true;
    1920             :   }
    1921             : 
    1922           0 :   nsCOMPtr<nsISimpleEnumerator> theEnum;
    1923           0 :   nsresult rv = service->EnumerateObservers(NS_INVALIDFORMSUBMIT_SUBJECT,
    1924           0 :                                             getter_AddRefs(theEnum));
    1925             :   // Return true on error here because that's what we always did
    1926           0 :   NS_ENSURE_SUCCESS(rv, true);
    1927             : 
    1928           0 :   bool hasObserver = false;
    1929           0 :   rv = theEnum->HasMoreElements(&hasObserver);
    1930             : 
    1931             :   // Do not check form validity if there is no observer for
    1932             :   // NS_INVALIDFORMSUBMIT_SUBJECT.
    1933           0 :   if (NS_SUCCEEDED(rv) && hasObserver) {
    1934             :     nsCOMPtr<nsIMutableArray> invalidElements =
    1935           0 :       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
    1936             :     // Return true on error here because that's what we always did
    1937           0 :     NS_ENSURE_SUCCESS(rv, true);
    1938             : 
    1939           0 :     if (!CheckFormValidity(invalidElements.get())) {
    1940             :       // For the first invalid submission, we should update element states.
    1941             :       // We have to do that _before_ calling the observers so we are sure they
    1942             :       // will not interfere (like focusing the element).
    1943           0 :       if (!mEverTriedInvalidSubmit) {
    1944           0 :         mEverTriedInvalidSubmit = true;
    1945             : 
    1946             :         /*
    1947             :          * We are going to call update states assuming elements want to
    1948             :          * be notified because we can't know.
    1949             :          * Submissions shouldn't happen during parsing so it _should_ be safe.
    1950             :          */
    1951             : 
    1952           0 :         nsAutoScriptBlocker scriptBlocker;
    1953             : 
    1954           0 :         for (uint32_t i = 0, length = mControls->mElements.Length();
    1955           0 :              i < length; ++i) {
    1956             :           // Input elements can trigger a form submission and we want to
    1957             :           // update the style in that case.
    1958           0 :           if (mControls->mElements[i]->IsHTMLElement(nsGkAtoms::input) &&
    1959           0 :               nsContentUtils::IsFocusedContent(mControls->mElements[i])) {
    1960           0 :             static_cast<HTMLInputElement*>(mControls->mElements[i])
    1961           0 :               ->UpdateValidityUIBits(true);
    1962             :           }
    1963             : 
    1964           0 :           mControls->mElements[i]->UpdateState(true);
    1965             :         }
    1966             : 
    1967             :         // Because of backward compatibility, <input type='image'> is not in
    1968             :         // elements but can be invalid.
    1969             :         // TODO: should probably be removed when bug 606491 will be fixed.
    1970           0 :         for (uint32_t i = 0, length = mControls->mNotInElements.Length();
    1971           0 :              i < length; ++i) {
    1972           0 :           mControls->mNotInElements[i]->UpdateState(true);
    1973             :         }
    1974             :       }
    1975             : 
    1976           0 :       nsCOMPtr<nsISupports> inst;
    1977           0 :       nsCOMPtr<nsIFormSubmitObserver> observer;
    1978           0 :       bool more = true;
    1979           0 :       while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
    1980           0 :         theEnum->GetNext(getter_AddRefs(inst));
    1981           0 :         observer = do_QueryInterface(inst);
    1982             : 
    1983           0 :         if (observer) {
    1984           0 :           observer->NotifyInvalidSubmit(this,
    1985           0 :                                         static_cast<nsIArray*>(invalidElements));
    1986             :         }
    1987             :       }
    1988             : 
    1989             :       // The form is invalid. Observers have been alerted. Do not submit.
    1990           0 :       return false;
    1991             :     }
    1992             :   } else {
    1993             :     NS_WARNING("There is no observer for \"invalidformsubmit\". \
    1994           0 : One should be implemented!");
    1995             :   }
    1996             : 
    1997           0 :   return true;
    1998             : }
    1999             : 
    2000             : bool
    2001           0 : HTMLFormElement::SubmissionCanProceed(Element* aSubmitter)
    2002             : {
    2003             : #ifdef DEBUG
    2004           0 :   if (aSubmitter) {
    2005           0 :     nsCOMPtr<nsIFormControl> fc = do_QueryInterface(aSubmitter);
    2006           0 :     MOZ_ASSERT(fc);
    2007             : 
    2008           0 :     uint32_t type = fc->ControlType();
    2009           0 :     MOZ_ASSERT(type == NS_FORM_INPUT_SUBMIT ||
    2010             :                type == NS_FORM_INPUT_IMAGE ||
    2011             :                type == NS_FORM_BUTTON_SUBMIT,
    2012             :                "aSubmitter is not a submit control?");
    2013             :   }
    2014             : #endif
    2015             : 
    2016             :   // Modified step 2 of
    2017             :   // https://html.spec.whatwg.org/multipage/forms.html#concept-form-submit --
    2018             :   // we're not checking whether the node document is disconnected yet...
    2019           0 :   if (OwnerDoc()->GetSandboxFlags() & SANDBOXED_FORMS) {
    2020           0 :     return false;
    2021             :   }
    2022             : 
    2023           0 :   if (HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) {
    2024           0 :     return true;
    2025             :   }
    2026             : 
    2027           0 :   if (aSubmitter &&
    2028           0 :       aSubmitter->HasAttr(kNameSpaceID_None, nsGkAtoms::formnovalidate)) {
    2029           0 :     return true;
    2030             :   }
    2031             : 
    2032           0 :   return CheckValidFormSubmission();
    2033             : }
    2034             : 
    2035             : void
    2036           0 : HTMLFormElement::UpdateValidity(bool aElementValidity)
    2037             : {
    2038           0 :   if (aElementValidity) {
    2039           0 :     --mInvalidElementsCount;
    2040             :   } else {
    2041           0 :     ++mInvalidElementsCount;
    2042             :   }
    2043             : 
    2044           0 :   NS_ASSERTION(mInvalidElementsCount >= 0, "Something went seriously wrong!");
    2045             : 
    2046             :   // The form validity has just changed if:
    2047             :   // - there are no more invalid elements ;
    2048             :   // - or there is one invalid elmement and an element just became invalid.
    2049             :   // If we have invalid elements and we used to before as well, do nothing.
    2050           0 :   if (mInvalidElementsCount &&
    2051           0 :       (mInvalidElementsCount != 1 || aElementValidity)) {
    2052           0 :     return;
    2053             :   }
    2054             : 
    2055             :   /*
    2056             :    * We are going to update states assuming submit controls want to
    2057             :    * be notified because we can't know.
    2058             :    * UpdateValidity shouldn't be called so much during parsing so it _should_
    2059             :    * be safe.
    2060             :    */
    2061             : 
    2062           0 :   nsAutoScriptBlocker scriptBlocker;
    2063             : 
    2064             :   // Inform submit controls that the form validity has changed.
    2065           0 :   for (uint32_t i = 0, length = mControls->mElements.Length();
    2066           0 :        i < length; ++i) {
    2067           0 :     if (mControls->mElements[i]->IsSubmitControl()) {
    2068           0 :       mControls->mElements[i]->UpdateState(true);
    2069             :     }
    2070             :   }
    2071             : 
    2072             :   // Because of backward compatibility, <input type='image'> is not in elements
    2073             :   // so we have to check for controls not in elements too.
    2074           0 :   uint32_t length = mControls->mNotInElements.Length();
    2075           0 :   for (uint32_t i = 0; i < length; ++i) {
    2076           0 :     if (mControls->mNotInElements[i]->IsSubmitControl()) {
    2077           0 :       mControls->mNotInElements[i]->UpdateState(true);
    2078             :     }
    2079             :   }
    2080             : 
    2081           0 :   UpdateState(true);
    2082             : }
    2083             : 
    2084             : // nsIWebProgressListener
    2085             : NS_IMETHODIMP
    2086           0 : HTMLFormElement::OnStateChange(nsIWebProgress* aWebProgress,
    2087             :                                nsIRequest* aRequest,
    2088             :                                uint32_t aStateFlags,
    2089             :                                nsresult aStatus)
    2090             : {
    2091             :   // If STATE_STOP is never fired for any reason (redirect?  Failed state
    2092             :   // change?) the form element will leak.  It will be kept around by the
    2093             :   // nsIWebProgressListener (assuming it keeps a strong pointer).  We will
    2094             :   // consequently leak the request.
    2095           0 :   if (aRequest == mSubmittingRequest &&
    2096           0 :       aStateFlags & nsIWebProgressListener::STATE_STOP) {
    2097           0 :     ForgetCurrentSubmission();
    2098             :   }
    2099             : 
    2100           0 :   return NS_OK;
    2101             : }
    2102             : 
    2103             : NS_IMETHODIMP
    2104           0 : HTMLFormElement::OnProgressChange(nsIWebProgress* aWebProgress,
    2105             :                                   nsIRequest* aRequest,
    2106             :                                   int32_t aCurSelfProgress,
    2107             :                                   int32_t aMaxSelfProgress,
    2108             :                                   int32_t aCurTotalProgress,
    2109             :                                   int32_t aMaxTotalProgress)
    2110             : {
    2111           0 :   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
    2112           0 :   return NS_OK;
    2113             : }
    2114             : 
    2115             : NS_IMETHODIMP
    2116           0 : HTMLFormElement::OnLocationChange(nsIWebProgress* aWebProgress,
    2117             :                                   nsIRequest* aRequest,
    2118             :                                   nsIURI* location,
    2119             :                                   uint32_t aFlags)
    2120             : {
    2121           0 :   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
    2122           0 :   return NS_OK;
    2123             : }
    2124             : 
    2125             : NS_IMETHODIMP
    2126           0 : HTMLFormElement::OnStatusChange(nsIWebProgress* aWebProgress,
    2127             :                                 nsIRequest* aRequest,
    2128             :                                 nsresult aStatus,
    2129             :                                 const char16_t* aMessage)
    2130             : {
    2131           0 :   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
    2132           0 :   return NS_OK;
    2133             : }
    2134             : 
    2135             : NS_IMETHODIMP
    2136           0 : HTMLFormElement::OnSecurityChange(nsIWebProgress* aWebProgress,
    2137             :                                   nsIRequest* aRequest,
    2138             :                                   uint32_t state)
    2139             : {
    2140           0 :   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
    2141           0 :   return NS_OK;
    2142             : }
    2143             : 
    2144             : NS_IMETHODIMP_(int32_t)
    2145           0 : HTMLFormElement::IndexOfControl(nsIFormControl* aControl)
    2146             : {
    2147           0 :   int32_t index = 0;
    2148           0 :   return mControls->IndexOfControl(aControl, &index) == NS_OK ? index : 0;
    2149             : }
    2150             : 
    2151             : void
    2152           0 : HTMLFormElement::SetCurrentRadioButton(const nsAString& aName,
    2153             :                                        HTMLInputElement* aRadio)
    2154             : {
    2155           0 :   mSelectedRadioButtons.Put(aName, aRadio);
    2156           0 : }
    2157             : 
    2158             : HTMLInputElement*
    2159           0 : HTMLFormElement::GetCurrentRadioButton(const nsAString& aName)
    2160             : {
    2161           0 :   return mSelectedRadioButtons.GetWeak(aName);
    2162             : }
    2163             : 
    2164             : NS_IMETHODIMP
    2165           0 : HTMLFormElement::GetNextRadioButton(const nsAString& aName,
    2166             :                                     const bool aPrevious,
    2167             :                                     HTMLInputElement* aFocusedRadio,
    2168             :                                     HTMLInputElement** aRadioOut)
    2169             : {
    2170             :   // Return the radio button relative to the focused radio button.
    2171             :   // If no radio is focused, get the radio relative to the selected one.
    2172           0 :   *aRadioOut = nullptr;
    2173             : 
    2174           0 :   RefPtr<HTMLInputElement> currentRadio;
    2175           0 :   if (aFocusedRadio) {
    2176           0 :     currentRadio = aFocusedRadio;
    2177             :   }
    2178             :   else {
    2179           0 :     mSelectedRadioButtons.Get(aName, getter_AddRefs(currentRadio));
    2180             :   }
    2181             : 
    2182           0 :   nsCOMPtr<nsISupports> itemWithName = DoResolveName(aName, true);
    2183           0 :   nsCOMPtr<nsINodeList> radioGroup(do_QueryInterface(itemWithName));
    2184             : 
    2185           0 :   if (!radioGroup) {
    2186           0 :     return NS_ERROR_FAILURE;
    2187             :   }
    2188             : 
    2189           0 :   int32_t index = radioGroup->IndexOf(currentRadio);
    2190           0 :   if (index < 0) {
    2191           0 :     return NS_ERROR_FAILURE;
    2192             :   }
    2193             : 
    2194             :   uint32_t numRadios;
    2195           0 :   radioGroup->GetLength(&numRadios);
    2196           0 :   RefPtr<HTMLInputElement> radio;
    2197             : 
    2198           0 :   bool isRadio = false;
    2199           0 :   do {
    2200           0 :     if (aPrevious) {
    2201           0 :       if (--index < 0) {
    2202           0 :         index = numRadios -1;
    2203             :       }
    2204             :     }
    2205           0 :     else if (++index >= (int32_t)numRadios) {
    2206           0 :       index = 0;
    2207             :     }
    2208           0 :     radio = HTMLInputElement::FromContentOrNull(radioGroup->Item(index));
    2209           0 :     isRadio = radio && radio->ControlType() == NS_FORM_INPUT_RADIO;
    2210           0 :     if (!isRadio) {
    2211           0 :       continue;
    2212             :     }
    2213             : 
    2214           0 :     nsAutoString name;
    2215           0 :     radio->GetName(name);
    2216           0 :     isRadio = aName.Equals(name);
    2217           0 :   } while (!isRadio || (radio->Disabled() && radio != currentRadio));
    2218             : 
    2219           0 :   NS_IF_ADDREF(*aRadioOut = radio);
    2220           0 :   return NS_OK;
    2221             : }
    2222             : 
    2223             : NS_IMETHODIMP
    2224           0 : HTMLFormElement::WalkRadioGroup(const nsAString& aName,
    2225             :                                 nsIRadioVisitor* aVisitor,
    2226             :                                 bool aFlushContent)
    2227             : {
    2228           0 :   if (aName.IsEmpty()) {
    2229             :     //
    2230             :     // XXX If the name is empty, it's not stored in the control list.  There
    2231             :     // *must* be a more efficient way to do this.
    2232             :     //
    2233           0 :     nsCOMPtr<nsIFormControl> control;
    2234           0 :     uint32_t len = GetElementCount();
    2235           0 :     for (uint32_t i = 0; i < len; i++) {
    2236           0 :       control = GetElementAt(i);
    2237           0 :       if (control->ControlType() == NS_FORM_INPUT_RADIO) {
    2238           0 :         nsCOMPtr<nsIContent> controlContent = do_QueryInterface(control);
    2239           0 :         if (controlContent &&
    2240           0 :             controlContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
    2241           0 :                                         EmptyString(), eCaseMatters) &&
    2242           0 :             !aVisitor->Visit(control)) {
    2243           0 :           break;
    2244             :         }
    2245             :       }
    2246             :     }
    2247           0 :     return NS_OK;
    2248             :   }
    2249             : 
    2250             :   // Get the control / list of controls from the form using form["name"]
    2251           0 :   nsCOMPtr<nsISupports> item = DoResolveName(aName, aFlushContent);
    2252           0 :   if (!item) {
    2253           0 :     return NS_ERROR_FAILURE;
    2254             :   }
    2255             : 
    2256             :   // If it's just a lone radio button, then select it.
    2257           0 :   nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(item);
    2258           0 :   if (formControl) {
    2259           0 :     if (formControl->ControlType() == NS_FORM_INPUT_RADIO) {
    2260           0 :       aVisitor->Visit(formControl);
    2261             :     }
    2262           0 :     return NS_OK;
    2263             :   }
    2264             : 
    2265           0 :   nsCOMPtr<nsIDOMNodeList> nodeList = do_QueryInterface(item);
    2266           0 :   if (!nodeList) {
    2267           0 :     return NS_OK;
    2268             :   }
    2269           0 :   uint32_t length = 0;
    2270           0 :   nodeList->GetLength(&length);
    2271           0 :   for (uint32_t i = 0; i < length; i++) {
    2272           0 :     nsCOMPtr<nsIDOMNode> node;
    2273           0 :     nodeList->Item(i, getter_AddRefs(node));
    2274           0 :     nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(node);
    2275           0 :     if (formControl && formControl->ControlType() == NS_FORM_INPUT_RADIO &&
    2276           0 :         !aVisitor->Visit(formControl)) {
    2277           0 :       break;
    2278             :     }
    2279             :   }
    2280           0 :   return NS_OK;
    2281             : }
    2282             : 
    2283             : void
    2284           0 : HTMLFormElement::AddToRadioGroup(const nsAString& aName,
    2285             :                                  nsIFormControl* aRadio)
    2286             : {
    2287           0 :   nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
    2288           0 :   NS_ASSERTION(element, "radio controls have to be content elements!");
    2289             : 
    2290           0 :   if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
    2291           0 :     auto entry = mRequiredRadioButtonCounts.LookupForAdd(aName);
    2292           0 :     if (!entry) {
    2293           0 :       entry.OrInsert([]() { return 1; });
    2294             :     } else {
    2295           0 :       ++entry.Data();
    2296             :     }
    2297             :   }
    2298           0 : }
    2299             : 
    2300             : void
    2301           0 : HTMLFormElement::RemoveFromRadioGroup(const nsAString& aName,
    2302             :                                       nsIFormControl* aRadio)
    2303             : {
    2304           0 :   nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
    2305           0 :   NS_ASSERTION(element, "radio controls have to be content elements!");
    2306             : 
    2307           0 :   if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
    2308           0 :     auto entry = mRequiredRadioButtonCounts.Lookup(aName);
    2309           0 :     if (!entry) {
    2310           0 :       MOZ_ASSERT_UNREACHABLE("At least one radio button has to be required!");
    2311             :     } else {
    2312           0 :       MOZ_ASSERT(entry.Data() >= 1,
    2313             :                  "At least one radio button has to be required!");
    2314           0 :       if (entry.Data() <= 1) {
    2315           0 :         entry.Remove();
    2316             :       } else {
    2317           0 :         --entry.Data();
    2318             :       }
    2319             :     }
    2320             :   }
    2321           0 : }
    2322             : 
    2323             : uint32_t
    2324           0 : HTMLFormElement::GetRequiredRadioCount(const nsAString& aName) const
    2325             : {
    2326           0 :   return mRequiredRadioButtonCounts.Get(aName);
    2327             : }
    2328             : 
    2329             : void
    2330           0 : HTMLFormElement::RadioRequiredWillChange(const nsAString& aName,
    2331             :                                          bool aRequiredAdded)
    2332             : {
    2333           0 :   if (aRequiredAdded) {
    2334           0 :     mRequiredRadioButtonCounts.Put(aName,
    2335           0 :                                    mRequiredRadioButtonCounts.Get(aName)+1);
    2336             :   } else {
    2337           0 :     uint32_t requiredNb = mRequiredRadioButtonCounts.Get(aName);
    2338           0 :     NS_ASSERTION(requiredNb >= 1,
    2339             :                  "At least one radio button has to be required!");
    2340           0 :     if (requiredNb == 1) {
    2341           0 :       mRequiredRadioButtonCounts.Remove(aName);
    2342             :     } else {
    2343           0 :       mRequiredRadioButtonCounts.Put(aName, requiredNb-1);
    2344             :     }
    2345             :   }
    2346           0 : }
    2347             : 
    2348             : bool
    2349           0 : HTMLFormElement::GetValueMissingState(const nsAString& aName) const
    2350             : {
    2351           0 :   return mValueMissingRadioGroups.Get(aName);
    2352             : }
    2353             : 
    2354             : void
    2355           0 : HTMLFormElement::SetValueMissingState(const nsAString& aName, bool aValue)
    2356             : {
    2357           0 :   mValueMissingRadioGroups.Put(aName, aValue);
    2358           0 : }
    2359             : 
    2360             : EventStates
    2361           0 : HTMLFormElement::IntrinsicState() const
    2362             : {
    2363           0 :   EventStates state = nsGenericHTMLElement::IntrinsicState();
    2364             : 
    2365           0 :   if (mInvalidElementsCount) {
    2366           0 :     state |= NS_EVENT_STATE_INVALID;
    2367             :   } else {
    2368           0 :       state |= NS_EVENT_STATE_VALID;
    2369             :   }
    2370             : 
    2371           0 :   return state;
    2372             : }
    2373             : 
    2374             : void
    2375           0 : HTMLFormElement::Clear()
    2376             : {
    2377           0 :   for (int32_t i = mImageElements.Length() - 1; i >= 0; i--) {
    2378           0 :     mImageElements[i]->ClearForm(false);
    2379             :   }
    2380           0 :   mImageElements.Clear();
    2381           0 :   mImageNameLookupTable.Clear();
    2382           0 :   mPastNameLookupTable.Clear();
    2383           0 : }
    2384             : 
    2385             : namespace {
    2386             : 
    2387             : struct PositionComparator
    2388             : {
    2389             :   nsIContent* const mElement;
    2390           0 :   explicit PositionComparator(nsIContent* const aElement) : mElement(aElement) {}
    2391             : 
    2392           0 :   int operator()(nsIContent* aElement) const {
    2393           0 :     if (mElement == aElement) {
    2394           0 :       return 0;
    2395             :     }
    2396           0 :     if (nsContentUtils::PositionIsBefore(mElement, aElement)) {
    2397           0 :       return -1;
    2398             :     }
    2399           0 :     return 1;
    2400             :   }
    2401             : };
    2402             : 
    2403             : struct RadioNodeListAdaptor
    2404             : {
    2405             :   RadioNodeList* const mList;
    2406           0 :   explicit RadioNodeListAdaptor(RadioNodeList* aList) : mList(aList) {}
    2407           0 :   nsIContent* operator[](size_t aIdx) const {
    2408           0 :     return mList->Item(aIdx);
    2409             :   }
    2410             : };
    2411             : 
    2412             : } // namespace
    2413             : 
    2414             : nsresult
    2415           0 : HTMLFormElement::AddElementToTableInternal(
    2416             :   nsInterfaceHashtable<nsStringHashKey,nsISupports>& aTable,
    2417             :   nsIContent* aChild, const nsAString& aName)
    2418             : {
    2419           0 :   auto entry = aTable.LookupForAdd(aName);
    2420           0 :   if (!entry) {
    2421             :     // No entry found, add the element
    2422           0 :     entry.OrInsert([&aChild]() { return aChild; });
    2423           0 :     ++mExpandoAndGeneration.generation;
    2424             :   } else {
    2425             :     // Found something in the hash, check its type
    2426           0 :     nsCOMPtr<nsIContent> content = do_QueryInterface(entry.Data());
    2427             : 
    2428           0 :     if (content) {
    2429             :       // Check if the new content is the same as the one we found in the
    2430             :       // hash, if it is then we leave it in the hash as it is, this will
    2431             :       // happen if a form control has both a name and an id with the same
    2432             :       // value
    2433           0 :       if (content == aChild) {
    2434           0 :         return NS_OK;
    2435             :       }
    2436             : 
    2437             :       // Found an element, create a list, add the element to the list and put
    2438             :       // the list in the hash
    2439           0 :       RadioNodeList *list = new RadioNodeList(this);
    2440             : 
    2441             :       // If an element has a @form, we can assume it *might* be able to not have
    2442             :       // a parent and still be in the form.
    2443           0 :       NS_ASSERTION(content->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
    2444             :                    content->GetParent(), "Item in list without parent");
    2445             : 
    2446             :       // Determine the ordering between the new and old element.
    2447           0 :       bool newFirst = nsContentUtils::PositionIsBefore(aChild, content);
    2448             : 
    2449           0 :       list->AppendElement(newFirst ? aChild : content.get());
    2450           0 :       list->AppendElement(newFirst ? content.get() : aChild);
    2451             : 
    2452             : 
    2453           0 :       nsCOMPtr<nsISupports> listSupports = do_QueryObject(list);
    2454             : 
    2455             :       // Replace the element with the list.
    2456           0 :       entry.Data() = listSupports;
    2457             :     } else {
    2458             :       // There's already a list in the hash, add the child to the list.
    2459           0 :       MOZ_ASSERT(nsCOMPtr<RadioNodeList>(do_QueryInterface(entry.Data())));
    2460           0 :       auto* list = static_cast<RadioNodeList*>(entry.Data().get());
    2461             : 
    2462           0 :       NS_ASSERTION(list->Length() > 1,
    2463             :                    "List should have been converted back to a single element");
    2464             : 
    2465             :       // Fast-path appends; this check is ok even if the child is
    2466             :       // already in the list, since if it tests true the child would
    2467             :       // have come at the end of the list, and the PositionIsBefore
    2468             :       // will test false.
    2469           0 :       if (nsContentUtils::PositionIsBefore(list->Item(list->Length() - 1), aChild)) {
    2470           0 :         list->AppendElement(aChild);
    2471           0 :         return NS_OK;
    2472             :       }
    2473             : 
    2474             :       // If a control has a name equal to its id, it could be in the
    2475             :       // list already.
    2476           0 :       if (list->IndexOf(aChild) != -1) {
    2477           0 :         return NS_OK;
    2478             :       }
    2479             : 
    2480             :       size_t idx;
    2481           0 :       DebugOnly<bool> found = BinarySearchIf(RadioNodeListAdaptor(list), 0, list->Length(),
    2482           0 :                                              PositionComparator(aChild), &idx);
    2483           0 :       MOZ_ASSERT(!found, "should not have found an element");
    2484             : 
    2485           0 :       list->InsertElementAt(aChild, idx);
    2486             :     }
    2487             :   }
    2488             : 
    2489           0 :   return NS_OK;
    2490             : }
    2491             : 
    2492             : nsresult
    2493           0 : HTMLFormElement::AddImageElement(HTMLImageElement* aChild)
    2494             : {
    2495           0 :   AddElementToList(mImageElements, aChild, this);
    2496           0 :   return NS_OK;
    2497             : }
    2498             : 
    2499             : nsresult
    2500           0 : HTMLFormElement::AddImageElementToTable(HTMLImageElement* aChild,
    2501             :                                         const nsAString& aName)
    2502             : {
    2503           0 :   return AddElementToTableInternal(mImageNameLookupTable, aChild, aName);
    2504             : }
    2505             : 
    2506             : nsresult
    2507           0 : HTMLFormElement::RemoveImageElement(HTMLImageElement* aChild)
    2508             : {
    2509           0 :   RemoveElementFromPastNamesMap(aChild);
    2510             : 
    2511           0 :   size_t index = mImageElements.IndexOf(aChild);
    2512           0 :   NS_ENSURE_STATE(index != mImageElements.NoIndex);
    2513             : 
    2514           0 :   mImageElements.RemoveElementAt(index);
    2515           0 :   return NS_OK;
    2516             : }
    2517             : 
    2518             : nsresult
    2519           0 : HTMLFormElement::RemoveImageElementFromTable(HTMLImageElement* aElement,
    2520             :                                              const nsAString& aName)
    2521             : {
    2522           0 :   return RemoveElementFromTableInternal(mImageNameLookupTable, aElement, aName);
    2523             : }
    2524             : 
    2525             : void
    2526           0 : HTMLFormElement::AddToPastNamesMap(const nsAString& aName,
    2527             :                                    nsISupports* aChild)
    2528             : {
    2529             :   // If candidates contains exactly one node. Add a mapping from name to the
    2530             :   // node in candidates in the form element's past names map, replacing the
    2531             :   // previous entry with the same name, if any.
    2532           0 :   nsCOMPtr<nsIContent> node = do_QueryInterface(aChild);
    2533           0 :   if (node) {
    2534           0 :     mPastNameLookupTable.Put(aName, node);
    2535           0 :     node->SetFlags(MAY_BE_IN_PAST_NAMES_MAP);
    2536             :   }
    2537           0 : }
    2538             : 
    2539             : void
    2540           0 : HTMLFormElement::RemoveElementFromPastNamesMap(Element* aElement)
    2541             : {
    2542           0 :   if (!aElement->HasFlag(MAY_BE_IN_PAST_NAMES_MAP)) {
    2543           0 :     return;
    2544             :   }
    2545             : 
    2546           0 :   aElement->UnsetFlags(MAY_BE_IN_PAST_NAMES_MAP);
    2547             : 
    2548           0 :   uint32_t oldCount = mPastNameLookupTable.Count();
    2549           0 :   for (auto iter = mPastNameLookupTable.Iter(); !iter.Done(); iter.Next()) {
    2550           0 :     if (aElement == iter.Data()) {
    2551           0 :       iter.Remove();
    2552             :     }
    2553             :   }
    2554           0 :   if (oldCount != mPastNameLookupTable.Count()) {
    2555           0 :     ++mExpandoAndGeneration.generation;
    2556             :   }
    2557             : }
    2558             : 
    2559             : JSObject*
    2560           0 : HTMLFormElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
    2561             : {
    2562           0 :   return HTMLFormElementBinding::Wrap(aCx, this, aGivenProto);
    2563             : }
    2564             : 
    2565             : } // namespace dom
    2566             : } // namespace mozilla

Generated by: LCOV version 1.13