HTMLFormElement.cpp 79 KB

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