Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLFormElement.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsCOMPtr.h"
00038 #include "nsIForm.h"
00039 #include "nsIFormControl.h"
00040 #include "nsIFormSubmission.h"
00041 #include "nsIDOMHTMLFormElement.h"
00042 #include "nsIDOMNSHTMLFormElement.h"
00043 #include "nsIHTMLDocument.h"
00044 #include "nsIDOMNSHTMLFormControlList.h"
00045 #include "nsIDOMEventReceiver.h"
00046 #include "nsGenericHTMLElement.h"
00047 #include "nsEventStateManager.h"
00048 #include "nsHTMLAtoms.h"
00049 #include "nsStyleConsts.h"
00050 #include "nsPresContext.h"
00051 #include "nsIDocument.h"
00052 #include "nsIPresShell.h"
00053 #include "nsIFrame.h"
00054 #include "nsIFormControlFrame.h"
00055 #include "nsIScriptGlobalObject.h"
00056 #include "nsDOMError.h"
00057 #include "nsContentUtils.h"
00058 #include "nsInterfaceHashtable.h"
00059 #include "nsContentList.h"
00060 #include "nsGUIEvent.h"
00061 #include "nsSupportsArray.h"
00062 
00063 // form submission
00064 #include "nsIFormSubmitObserver.h"
00065 #include "nsIURI.h"
00066 #include "nsIObserverService.h"
00067 #include "nsICategoryManager.h"
00068 #include "nsCategoryManagerUtils.h"
00069 #include "nsISimpleEnumerator.h"
00070 #include "nsIDOMWindowInternal.h"
00071 #include "nsPIDOMWindow.h"
00072 #include "nsRange.h"
00073 #include "nsIScriptSecurityManager.h"
00074 #include "nsNetUtil.h"
00075 #include "nsIWebProgress.h"
00076 #include "nsIDocShell.h"
00077 #include "nsIWebProgressListener.h"
00078 #include "nsWeakReference.h"
00079 
00080 // radio buttons
00081 #include "nsIDOMHTMLInputElement.h"
00082 #include "nsIRadioControlElement.h"
00083 #include "nsIRadioVisitor.h"
00084 #include "nsIRadioGroupContainer.h"
00085 
00086 #include "nsLayoutUtils.h"
00087 
00088 static const int NS_FORM_CONTROL_LIST_HASHTABLE_SIZE = 16;
00089 
00090 class nsFormControlList;
00091 
00092 // nsHTMLFormElement
00093 
00094 class nsHTMLFormElement : public nsGenericHTMLElement,
00095                           public nsSupportsWeakReference,
00096                           public nsIDOMHTMLFormElement,
00097                           public nsIDOMNSHTMLFormElement,
00098                           public nsIWebProgressListener,
00099                           public nsIForm,
00100                           public nsIRadioGroupContainer
00101 {
00102 public:
00103   nsHTMLFormElement(nsINodeInfo *aNodeInfo);
00104   virtual ~nsHTMLFormElement();
00105 
00106   nsresult Init();
00107 
00108   // nsISupports
00109   NS_DECL_ISUPPORTS_INHERITED
00110 
00111   // nsIDOMNode
00112   NS_FORWARD_NSIDOMNODE_NO_CLONENODE(nsGenericHTMLElement::)
00113 
00114   // nsIDOMElement
00115   NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
00116 
00117   // nsIDOMHTMLElement
00118   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
00119 
00120   // nsIDOMHTMLFormElement
00121   NS_DECL_NSIDOMHTMLFORMELEMENT
00122 
00123   // nsIDOMNSHTMLFormElement
00124   NS_DECL_NSIDOMNSHTMLFORMELEMENT  
00125 
00126   // nsIWebProgressListener
00127   NS_DECL_NSIWEBPROGRESSLISTENER
00128 
00129   // nsIForm
00130   NS_IMETHOD AddElement(nsIFormControl* aElement);
00131   NS_IMETHOD AddElementToTable(nsIFormControl* aChild,
00132                                const nsAString& aName);
00133   NS_IMETHOD GetElementAt(PRInt32 aIndex, nsIFormControl** aElement) const;
00134   NS_IMETHOD GetElementCount(PRUint32* aCount) const;
00135   NS_IMETHOD RemoveElement(nsIFormControl* aElement);
00136   NS_IMETHOD RemoveElementFromTable(nsIFormControl* aElement,
00137                                     const nsAString& aName);
00138   NS_IMETHOD ResolveName(const nsAString& aName,
00139                          nsISupports** aReturn);
00140   NS_IMETHOD IndexOfControl(nsIFormControl* aControl, PRInt32* aIndex);
00141   NS_IMETHOD GetControlEnumerator(nsISimpleEnumerator** aEnumerator);
00142   NS_IMETHOD OnSubmitClickBegin();
00143   NS_IMETHOD OnSubmitClickEnd();
00144   NS_IMETHOD FlushPendingSubmission();
00145   NS_IMETHOD ForgetPendingSubmission();
00146   NS_IMETHOD GetActionURL(nsIURI** aActionURL);
00147 
00148   // nsIRadioGroupContainer
00149   NS_IMETHOD SetCurrentRadioButton(const nsAString& aName,
00150                                    nsIDOMHTMLInputElement* aRadio);
00151   NS_IMETHOD GetCurrentRadioButton(const nsAString& aName,
00152                                    nsIDOMHTMLInputElement** aRadio);
00153   NS_IMETHOD GetPositionInGroup(nsIDOMHTMLInputElement *aRadio,
00154                                 PRInt32 *aPositionIndex,
00155                                 PRInt32 *aItemsInGroup);
00156   NS_IMETHOD GetNextRadioButton(const nsAString& aName,
00157                                 const PRBool aPrevious,
00158                                 nsIDOMHTMLInputElement*  aFocusedRadio,
00159                                 nsIDOMHTMLInputElement** aRadioOut);
00160   NS_IMETHOD WalkRadioGroup(const nsAString& aName, nsIRadioVisitor* aVisitor);
00161   NS_IMETHOD AddToRadioGroup(const nsAString& aName,
00162                              nsIFormControl* aRadio);
00163   NS_IMETHOD RemoveFromRadioGroup(const nsAString& aName,
00164                                   nsIFormControl* aRadio);
00165 
00166   // nsIContent
00167   virtual PRBool ParseAttribute(nsIAtom* aAttribute,
00168                                 const nsAString& aValue,
00169                                 nsAttrValue& aResult);
00170   virtual nsresult HandleDOMEvent(nsPresContext* aPresContext,
00171                                   nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
00172                                   PRUint32 aFlags,
00173                                   nsEventStatus* aEventStatus);
00174   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
00175                               nsIContent* aBindingParent,
00176                               PRBool aCompileEventHandlers);
00177   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
00178                               PRBool aNullParent = PR_TRUE);
00179   nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
00180                    const nsAString& aValue, PRBool aNotify)
00181   {
00182     return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
00183   }
00184   virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
00185                            nsIAtom* aPrefix, const nsAString& aValue,
00186                            PRBool aNotify);
00187 
00192   void ForgetCurrentSubmission();
00193 
00203   static nsresult CompareNodes(nsIDOMNode* a,
00204                                nsIDOMNode* b,
00205                                PRInt32* retval);
00206 
00207 protected:
00208   nsresult DoSubmitOrReset(nsPresContext* aPresContext,
00209                            nsEvent* aEvent,
00210                            PRInt32 aMessage);
00211   nsresult DoReset();
00212 
00213   //
00214   // Submit Helpers
00215   //
00216   //
00224   nsresult DoSubmit(nsPresContext* aPresContext, nsEvent* aEvent);
00225 
00233   nsresult BuildSubmission(nsPresContext* aPresContext, 
00234                            nsCOMPtr<nsIFormSubmission>& aFormSubmission, 
00235                            nsEvent* aEvent);
00242   nsresult SubmitSubmission(nsPresContext* aPresContext, 
00243                             nsIFormSubmission* aFormSubmission);
00251   nsresult WalkFormElements(nsIFormSubmission* aFormSubmission,
00252                             nsIContent* aSubmitElement);
00253 
00261   nsresult NotifySubmitObservers(nsIURI* aActionURL, PRBool* aCancelSubmit,
00262                                  PRBool aEarlyNotify);
00263   //
00264   // Data members
00265   //
00267   nsFormControlList *mControls;
00269   nsInterfaceHashtable<nsStringHashKey,nsIDOMHTMLInputElement> mSelectedRadioButtons;
00271   PRPackedBool mGeneratingSubmit;
00273   PRPackedBool mGeneratingReset;
00275   PRPackedBool mIsSubmitting;
00277   PRPackedBool mDeferSubmission;
00279   PRPackedBool mNotifiedObservers;
00281   PRPackedBool mNotifiedObserversResult;
00283   PopupControlState mSubmitPopupState;
00285   PRBool mSubmitInitiatedFromUserInput;
00286 
00288   nsCOMPtr<nsIFormSubmission> mPendingSubmission;
00290   nsCOMPtr<nsIRequest> mSubmittingRequest;
00292   nsCOMPtr<nsIWebProgress> mWebProgress;
00293 
00294   friend class nsFormControlEnumerator;
00295 
00296 protected:
00298   static PRBool gFirstFormSubmitted;
00300   static PRBool gPasswordManagerInitialized;
00301 };
00302 
00303 PRBool nsHTMLFormElement::gFirstFormSubmitted = PR_FALSE;
00304 PRBool nsHTMLFormElement::gPasswordManagerInitialized = PR_FALSE;
00305 
00306 
00307 // nsFormControlList
00308 class nsFormControlList : public nsIDOMNSHTMLFormControlList,
00309                           public nsIDOMHTMLCollection
00310 {
00311 public:
00312   nsFormControlList(nsIDOMHTMLFormElement* aForm);
00313   virtual ~nsFormControlList();
00314 
00315   nsresult Init();
00316 
00317   void Clear();
00318   void SetForm(nsIDOMHTMLFormElement* aForm);
00319 
00320   NS_DECL_ISUPPORTS
00321 
00322   // nsIDOMHTMLCollection interface
00323   NS_DECL_NSIDOMHTMLCOLLECTION
00324 
00325   // nsIDOMNSHTMLFormControlList interface
00326   NS_DECL_NSIDOMNSHTMLFORMCONTROLLIST
00327 
00328   nsresult GetNamedObject(const nsAString& aName,
00329                           nsISupports **aResult);
00330 
00331   nsresult AddElementToTable(nsIFormControl* aChild,
00332                              const nsAString& aName);
00333   nsresult RemoveElementFromTable(nsIFormControl* aChild,
00334                                   const nsAString& aName);
00335   nsresult IndexOfControl(nsIFormControl* aControl,
00336                           PRInt32* aIndex);
00337 
00338   nsIDOMHTMLFormElement* mForm;  // WEAK - the form owns me
00339 
00340   nsAutoVoidArray mElements;  // Holds WEAK references - bug 36639
00341 
00342   // This array holds on to all form controls that are not contained
00343   // in mElements (form.elements in JS, see ShouldBeInFormControl()).
00344   // This is needed to properly clean up the bi-directional references
00345   // (both weak and strong) between the form and its form controls.
00346 
00347   nsSmallVoidArray mNotInElements; // Holds WEAK references
00348 
00349 protected:
00350   // A map from an ID or NAME attribute to the form control(s), this
00351   // hash holds strong references either to the named form control, or
00352   // to a list of named form controls, in the case where this hash
00353   // holds on to a list of named form controls the list has weak
00354   // references to the form control.
00355 
00356   nsInterfaceHashtable<nsStringHashKey,nsISupports> mNameLookupTable;
00357 };
00358 
00359 
00360 class nsFormControlEnumerator : public nsISimpleEnumerator {
00361 public:
00362   nsFormControlEnumerator(nsHTMLFormElement* aForm);
00363   virtual ~nsFormControlEnumerator() { };
00364 
00365   NS_DECL_ISUPPORTS
00366   NS_DECL_NSISIMPLEENUMERATOR
00367 
00368 private:
00369   nsHTMLFormElement* mForm;
00370   PRUint32 mElementsIndex;
00371   nsSupportsArray mNotInElementsSorted;
00372   PRUint32 mNotInElementsIndex;
00373 };
00374 
00375 
00376 static PRBool
00377 ShouldBeInElements(nsIFormControl* aFormControl)
00378 {
00379   // For backwards compatibility (with 4.x and IE) we must not add
00380   // <input type=image> elements to the list of form controls in a
00381   // form.
00382 
00383   switch (aFormControl->GetType()) {
00384   case NS_FORM_BUTTON_BUTTON :
00385   case NS_FORM_BUTTON_RESET :
00386   case NS_FORM_BUTTON_SUBMIT :
00387   case NS_FORM_INPUT_BUTTON :
00388   case NS_FORM_INPUT_CHECKBOX :
00389   case NS_FORM_INPUT_FILE :
00390   case NS_FORM_INPUT_HIDDEN :
00391   case NS_FORM_INPUT_RESET :
00392   case NS_FORM_INPUT_PASSWORD :
00393   case NS_FORM_INPUT_RADIO :
00394   case NS_FORM_INPUT_SUBMIT :
00395   case NS_FORM_INPUT_TEXT :
00396   case NS_FORM_SELECT :
00397   case NS_FORM_TEXTAREA :
00398   case NS_FORM_FIELDSET :
00399   case NS_FORM_OBJECT :
00400     return PR_TRUE;
00401   }
00402 
00403   // These form control types are not supposed to end up in the
00404   // form.elements array
00405   //
00406   // NS_FORM_INPUT_IMAGE
00407   // NS_FORM_LABEL
00408   // NS_FORM_OPTION
00409   // NS_FORM_OPTGROUP
00410   // NS_FORM_LEGEND
00411 
00412   return PR_FALSE;
00413 }
00414 
00415 // nsHTMLFormElement implementation
00416 
00417 // construction, destruction
00418 nsGenericHTMLElement*
00419 NS_NewHTMLFormElement(nsINodeInfo *aNodeInfo, PRBool aFromParser)
00420 {
00421   nsHTMLFormElement* it = new nsHTMLFormElement(aNodeInfo);
00422   if (!it) {
00423     return nsnull;
00424   }
00425 
00426   nsresult rv = it->Init();
00427 
00428   if (NS_FAILED(rv)) {
00429     delete it;
00430     return nsnull;
00431   }
00432 
00433   return it;
00434 }
00435 
00436 nsHTMLFormElement::nsHTMLFormElement(nsINodeInfo *aNodeInfo)
00437   : nsGenericHTMLElement(aNodeInfo),
00438     mGeneratingSubmit(PR_FALSE),
00439     mGeneratingReset(PR_FALSE),
00440     mIsSubmitting(PR_FALSE),
00441     mDeferSubmission(PR_FALSE),
00442     mNotifiedObservers(PR_FALSE),
00443     mNotifiedObserversResult(PR_FALSE),
00444     mSubmitPopupState(openAbused),
00445     mSubmitInitiatedFromUserInput(PR_FALSE),
00446     mPendingSubmission(nsnull),
00447     mSubmittingRequest(nsnull)
00448 {
00449 }
00450 
00451 nsHTMLFormElement::~nsHTMLFormElement()
00452 {
00453   if (mControls) {
00454     mControls->Clear();
00455     mControls->SetForm(nsnull);
00456 
00457     NS_RELEASE(mControls);
00458   }
00459 }
00460 
00461 nsresult
00462 nsHTMLFormElement::Init()
00463 {
00464   mControls = new nsFormControlList(this);
00465   if (!mControls) {
00466     return NS_ERROR_OUT_OF_MEMORY;
00467   }
00468 
00469   nsresult rv = mControls->Init();
00470   
00471   if (NS_FAILED(rv))
00472   {
00473     delete mControls;
00474     mControls = nsnull;
00475     return rv;
00476   }
00477   
00478   NS_ADDREF(mControls);
00479 
00480   NS_ENSURE_TRUE(mSelectedRadioButtons.Init(4),
00481                  NS_ERROR_OUT_OF_MEMORY);
00482 
00483   return NS_OK;
00484 }
00485 
00486 
00487 // nsISupports
00488 
00489 NS_IMPL_ADDREF_INHERITED(nsHTMLFormElement, nsGenericElement) 
00490 NS_IMPL_RELEASE_INHERITED(nsHTMLFormElement, nsGenericElement) 
00491 
00492 
00493 // QueryInterface implementation for nsHTMLFormElement
00494 NS_HTML_CONTENT_INTERFACE_MAP_BEGIN(nsHTMLFormElement, nsGenericHTMLElement)
00495   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00496   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLFormElement)
00497   NS_INTERFACE_MAP_ENTRY(nsIDOMNSHTMLFormElement)
00498   NS_INTERFACE_MAP_ENTRY(nsIForm)
00499   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
00500   NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer)
00501   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLFormElement)
00502 NS_HTML_CONTENT_INTERFACE_MAP_END
00503 
00504 
00505 // nsIDOMHTMLFormElement
00506 
00507 NS_IMPL_DOM_CLONENODE_WITH_INIT(nsHTMLFormElement)
00508 
00509 NS_IMETHODIMP
00510 nsHTMLFormElement::GetElements(nsIDOMHTMLCollection** aElements)
00511 {
00512   *aElements = mControls;
00513   NS_ADDREF(mControls);
00514   return NS_OK;
00515 }
00516 
00517 nsresult
00518 nsHTMLFormElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
00519                            nsIAtom* aPrefix, const nsAString& aValue,
00520                            PRBool aNotify)
00521 {
00522   if ((aName == nsHTMLAtoms::action || aName == nsHTMLAtoms::target) &&
00523       aNameSpaceID == kNameSpaceID_None) {
00524     if (mPendingSubmission) {
00525       // aha, there is a pending submission that means we're in
00526       // the script and we need to flush it. let's tell it
00527       // that the event was ignored to force the flush.
00528       // the second argument is not playing a role at all.
00529       FlushPendingSubmission();
00530     }
00531     // Don't forget we've notified the password manager already if the
00532     // page sets the action/target in the during submit. (bug 343182)
00533     PRBool notifiedObservers = mNotifiedObservers;
00534     ForgetCurrentSubmission();
00535     mNotifiedObservers = notifiedObservers;
00536   }
00537   return nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
00538                                        aNotify);
00539 }
00540 
00541 NS_IMPL_STRING_ATTR(nsHTMLFormElement, AcceptCharset, acceptcharset)
00542 NS_IMPL_STRING_ATTR(nsHTMLFormElement, Enctype, enctype)
00543 NS_IMPL_STRING_ATTR(nsHTMLFormElement, Method, method)
00544 NS_IMPL_STRING_ATTR(nsHTMLFormElement, Name, name)
00545 
00546 NS_IMETHODIMP
00547 nsHTMLFormElement::GetAction(nsAString& aValue)
00548 {
00549   nsresult rv = GetAttr(kNameSpaceID_None, nsHTMLAtoms::action, aValue);
00550   NS_ENSURE_SUCCESS(rv, rv);
00551   if (aValue.IsEmpty()) {
00552     // Avoid resolving action="" to the base uri, bug 297761.
00553     return NS_OK;
00554   }
00555   return GetURIAttr(nsHTMLAtoms::action, aValue);
00556 }
00557 
00558 NS_IMETHODIMP
00559 nsHTMLFormElement::SetAction(const nsAString& aValue)
00560 {
00561   return SetAttr(kNameSpaceID_None, nsHTMLAtoms::action, aValue, PR_TRUE);
00562 }
00563 
00564 NS_IMETHODIMP
00565 nsHTMLFormElement::GetTarget(nsAString& aValue)
00566 {
00567   aValue.Truncate();
00568   nsresult rv = GetAttr(kNameSpaceID_None, nsHTMLAtoms::target, aValue);
00569   if (rv == NS_CONTENT_ATTR_NOT_THERE) {
00570     GetBaseTarget(aValue);
00571   }
00572   return NS_OK;
00573 }
00574 
00575 NS_IMETHODIMP
00576 nsHTMLFormElement::SetTarget(const nsAString& aValue)
00577 {
00578   return SetAttr(kNameSpaceID_None, nsHTMLAtoms::target, aValue, PR_TRUE);
00579 }
00580 
00581 NS_IMETHODIMP
00582 nsHTMLFormElement::Submit()
00583 {
00584   // Send the submit event
00585   nsresult rv = NS_OK;
00586   nsCOMPtr<nsPresContext> presContext = GetPresContext();
00587   if (presContext) {
00588     if (mPendingSubmission) {
00589       // aha, we have a pending submission that was not flushed
00590       // (this happens when form.submit() is called twice)
00591       // we have to delete it and build a new one since values
00592       // might have changed inbetween (we emulate IE here, that's all)
00593       mPendingSubmission = nsnull;
00594     }
00595 
00596     rv = DoSubmitOrReset(presContext, nsnull, NS_FORM_SUBMIT);
00597   }
00598   return rv;
00599 }
00600 
00601 NS_IMETHODIMP
00602 nsHTMLFormElement::Reset()
00603 {
00604   // Send the reset event
00605   nsresult rv = NS_OK;
00606   nsCOMPtr<nsPresContext> presContext = GetPresContext();
00607   if (presContext) {
00608     // Calling HandleDOMEvent() directly so that reset() will work even if
00609     // the frame does not exist.  This does not have an effect right now, but
00610     // If PresShell::HandleEventWithTarget() ever starts to work for elements
00611     // without frames, that should be called instead.
00612     nsFormEvent event(PR_TRUE, NS_FORM_RESET);
00613     nsEventStatus status  = nsEventStatus_eIgnore;
00614     HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
00615   }
00616   return rv;
00617 }
00618 
00619 static const nsAttrValue::EnumTable kFormMethodTable[] = {
00620   { "get", NS_FORM_METHOD_GET },
00621   { "post", NS_FORM_METHOD_POST },
00622   { 0 }
00623 };
00624 
00625 static const nsAttrValue::EnumTable kFormEnctypeTable[] = {
00626   { "multipart/form-data", NS_FORM_ENCTYPE_MULTIPART },
00627   { "application/x-www-form-urlencoded", NS_FORM_ENCTYPE_URLENCODED },
00628   { "text/plain", NS_FORM_ENCTYPE_TEXTPLAIN },
00629   { 0 }
00630 };
00631 
00632 PRBool
00633 nsHTMLFormElement::ParseAttribute(nsIAtom* aAttribute,
00634                                   const nsAString& aValue,
00635                                   nsAttrValue& aResult)
00636 {
00637   if (aAttribute == nsHTMLAtoms::method) {
00638     return aResult.ParseEnumValue(aValue, kFormMethodTable);
00639   }
00640   if (aAttribute == nsHTMLAtoms::enctype) {
00641     return aResult.ParseEnumValue(aValue, kFormEnctypeTable);
00642   }
00643 
00644   return nsGenericHTMLElement::ParseAttribute(aAttribute, aValue, aResult);
00645 }
00646 
00647 nsresult
00648 nsHTMLFormElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
00649                               nsIContent* aBindingParent,
00650                               PRBool aCompileEventHandlers)
00651 {
00652   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
00653                                                  aBindingParent,
00654                                                  aCompileEventHandlers);
00655   NS_ENSURE_SUCCESS(rv, rv);
00656 
00657   nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(aDocument));
00658   if (htmlDoc) {
00659     htmlDoc->AddedForm();
00660   }
00661 
00662   return rv;
00663 }
00664 
00665 void
00666 nsHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
00667 {
00668   nsCOMPtr<nsIHTMLDocument> oldDocument = do_QueryInterface(GetCurrentDoc());
00669 
00670   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
00671 
00672   if (oldDocument) {
00673     oldDocument->RemovedForm();
00674   }     
00675   ForgetCurrentSubmission();
00676 }
00677 
00678 nsresult
00679 nsHTMLFormElement::HandleDOMEvent(nsPresContext* aPresContext,
00680                                   nsEvent* aEvent,
00681                                   nsIDOMEvent** aDOMEvent,
00682                                   PRUint32 aFlags,
00683                                   nsEventStatus* aEventStatus)
00684 {
00685   NS_ENSURE_ARG_POINTER(aEvent);
00686 
00687   // If this is the bubble stage, there is a nested form below us which received
00688   // a submit event.  We do *not* want to handle the submit event for this form
00689   // too.  So to avert a disaster, we stop the bubbling altogether.
00690   if ((aFlags & NS_EVENT_FLAG_BUBBLE) &&
00691       (aEvent->message == NS_FORM_RESET || aEvent->message == NS_FORM_SUBMIT)) {
00692     return NS_OK;
00693   }
00694 
00695   // Ignore recursive calls to submit and reset
00696   if (aEvent->message == NS_FORM_SUBMIT) {
00697     if (mGeneratingSubmit) {
00698       return NS_OK;
00699     }
00700     mGeneratingSubmit = PR_TRUE;
00701 
00702     // let the form know that it needs to defer the submission,
00703     // that means that if there are scripted submissions, the
00704     // latest one will be deferred until after the exit point of the handler. 
00705     mDeferSubmission = PR_TRUE;
00706   }
00707   else if (aEvent->message == NS_FORM_RESET) {
00708     if (mGeneratingReset) {
00709       return NS_OK;
00710     }
00711     mGeneratingReset = PR_TRUE;
00712   }
00713 
00714 
00715   nsresult rv = nsGenericHTMLElement::HandleDOMEvent(aPresContext, aEvent,
00716                                                      aDOMEvent, aFlags,
00717                                                      aEventStatus); 
00718   if (aEvent->message == NS_FORM_SUBMIT) {
00719     // let the form know not to defer subsequent submissions
00720     mDeferSubmission = PR_FALSE;
00721   }
00722 
00723   if (NS_SUCCEEDED(rv) &&
00724       !(aFlags & NS_EVENT_FLAG_CAPTURE) &&
00725       !(aFlags & NS_EVENT_FLAG_SYSTEM_EVENT)) {
00726 
00727     if (*aEventStatus == nsEventStatus_eIgnore) {
00728       switch (aEvent->message) {
00729         case NS_FORM_RESET:
00730         case NS_FORM_SUBMIT:
00731         {
00732           if (mPendingSubmission && aEvent->message == NS_FORM_SUBMIT) {
00733             // tell the form to forget a possible pending submission.
00734             // the reason is that the script returned true (the event was
00735             // ignored) so if there is a stored submission, it will miss
00736             // the name/value of the submitting element, thus we need
00737             // to forget it and the form element will build a new one
00738             ForgetPendingSubmission();
00739           }
00740           DoSubmitOrReset(aPresContext, aEvent, aEvent->message);
00741         }
00742         break;
00743       }
00744     } else {
00745       if (aEvent->message == NS_FORM_SUBMIT) {
00746         // tell the form to flush a possible pending submission.
00747         // the reason is that the script returned false (the event was
00748         // not ignored) so if there is a stored submission, it needs to
00749         // be submitted immediatelly.
00750         FlushPendingSubmission();
00751       }
00752     }
00753   }
00754 
00755   if (aEvent->message == NS_FORM_SUBMIT) {
00756     mGeneratingSubmit = PR_FALSE;
00757   }
00758   else if (aEvent->message == NS_FORM_RESET) {
00759     mGeneratingReset = PR_FALSE;
00760   }
00761 
00762   return rv;
00763 }
00764 
00765 nsresult
00766 nsHTMLFormElement::DoSubmitOrReset(nsPresContext* aPresContext,
00767                                    nsEvent* aEvent,
00768                                    PRInt32 aMessage)
00769 {
00770   NS_ENSURE_ARG_POINTER(aPresContext);
00771 
00772   // Make sure the presentation is up-to-date
00773   nsIDocument* doc = GetCurrentDoc();
00774   if (doc) {
00775     doc->FlushPendingNotifications(Flush_ContentAndNotify);
00776   }
00777 
00778   // JBK Don't get form frames anymore - bug 34297
00779 
00780   // Submit or Reset the form
00781   nsresult rv = NS_OK;
00782   if (NS_FORM_RESET == aMessage) {
00783     rv = DoReset();
00784   }
00785   else if (NS_FORM_SUBMIT == aMessage) {
00786     rv = DoSubmit(aPresContext, aEvent);
00787   }
00788   return rv;
00789 }
00790 
00791 nsresult
00792 nsHTMLFormElement::DoReset()
00793 {
00794   // JBK walk the elements[] array instead of form frame controls - bug 34297
00795   PRUint32 numElements;
00796   GetElementCount(&numElements);
00797   for (PRUint32 elementX = 0; (elementX < numElements); elementX++) {
00798     nsCOMPtr<nsIFormControl> controlNode;
00799     GetElementAt(elementX, getter_AddRefs(controlNode));
00800     if (controlNode) {
00801       controlNode->Reset();
00802     }
00803   }
00804 
00805   return NS_OK;
00806 }
00807 
00808 #define NS_ENSURE_SUBMIT_SUCCESS(rv)                                          \
00809   if (NS_FAILED(rv)) {                                                        \
00810     ForgetCurrentSubmission();                                                \
00811     return rv;                                                                \
00812   }
00813 
00814 nsresult
00815 nsHTMLFormElement::DoSubmit(nsPresContext* aPresContext, nsEvent* aEvent)
00816 {
00817   NS_ASSERTION(!mIsSubmitting, "Either two people are trying to submit or the "
00818                "previous submit was not properly cancelled by the DocShell");
00819   if (mIsSubmitting) {
00820     // XXX Should this return an error?
00821     return NS_OK;
00822   }
00823 
00824   // Mark us as submitting so that we don't try to submit again
00825   mIsSubmitting = PR_TRUE;
00826   NS_ASSERTION(!mWebProgress && !mSubmittingRequest, "Web progress / submitting request should not exist here!");
00827 
00828   nsCOMPtr<nsIFormSubmission> submission;
00829    
00830   //
00831   // prepare the submission object
00832   //
00833   BuildSubmission(aPresContext, submission, aEvent); 
00834 
00835   // XXXbz if the script global is that for an sXBL/XBL2 doc, it won't
00836   // be a window...
00837   nsCOMPtr<nsPIDOMWindow> window =
00838     do_QueryInterface(GetOwnerDoc()->GetScriptGlobalObject());
00839 
00840   if (window) {
00841     mSubmitPopupState = window->GetPopupControlState();
00842   } else {
00843     mSubmitPopupState = openAbused;
00844   }
00845 
00846   mSubmitInitiatedFromUserInput = nsEventStateManager::IsHandlingUserInput();
00847 
00848   if(mDeferSubmission) { 
00849     // we are in an event handler, JS submitted so we have to
00850     // defer this submission. let's remember it and return
00851     // without submitting
00852     mPendingSubmission = submission;
00853     // ensure reentrancy
00854     mIsSubmitting = PR_FALSE;
00855     return NS_OK; 
00856   } 
00857   
00858   // 
00859   // perform the submission
00860   //
00861   return SubmitSubmission(aPresContext, submission); 
00862 }
00863 
00864 nsresult
00865 nsHTMLFormElement::BuildSubmission(nsPresContext* aPresContext, 
00866                                    nsCOMPtr<nsIFormSubmission>& aFormSubmission, 
00867                                    nsEvent* aEvent)
00868 {
00869   NS_ASSERTION(!mPendingSubmission, "tried to build two submissions!");
00870 
00871   // Get the originating frame (failure is non-fatal)
00872   nsIContent *originatingElement = nsnull;
00873   if (aEvent) {
00874     if (NS_FORM_EVENT == aEvent->eventStructType) {
00875       originatingElement = ((nsFormEvent *)aEvent)->originator;
00876     }
00877   }
00878 
00879   nsresult rv;
00880 
00881   //
00882   // Get the submission object
00883   //
00884   rv = GetSubmissionFromForm(this, aPresContext, getter_AddRefs(aFormSubmission));
00885   NS_ENSURE_SUBMIT_SUCCESS(rv);
00886 
00887   //
00888   // Dump the data into the submission object
00889   //
00890   rv = WalkFormElements(aFormSubmission, originatingElement);
00891   NS_ENSURE_SUBMIT_SUCCESS(rv);
00892 
00893   return NS_OK;
00894 }
00895 
00896 nsresult
00897 nsHTMLFormElement::SubmitSubmission(nsPresContext* aPresContext, 
00898                                     nsIFormSubmission* aFormSubmission)
00899 {
00900   nsresult rv;
00901   //
00902   // Get the action and target
00903   //
00904   nsCOMPtr<nsIURI> actionURI;
00905   rv = GetActionURL(getter_AddRefs(actionURI));
00906   NS_ENSURE_SUBMIT_SUCCESS(rv);
00907 
00908   if (!actionURI) {
00909     mIsSubmitting = PR_FALSE;
00910     return NS_OK;
00911   }
00912 
00913   // If there is no link handler, then we won't actually be able to submit.
00914   if (!aPresContext->GetLinkHandler()) {
00915     mIsSubmitting = PR_FALSE;
00916     return NS_OK;
00917   }
00918 
00919   // javascript URIs are not really submissions; they just call a function.
00920   // Also, they may synchronously call submit(), and we want them to be able to
00921   // do so while still disallowing other double submissions. (Bug 139798)
00922   // Note that any other URI types that are of equivalent type should also be
00923   // added here.
00924   PRBool schemeIsJavaScript = PR_FALSE;
00925   if (NS_SUCCEEDED(actionURI->SchemeIs("javascript", &schemeIsJavaScript)) &&
00926       schemeIsJavaScript) {
00927     mIsSubmitting = PR_FALSE;
00928   }
00929 
00930   nsAutoString target;
00931   rv = GetTarget(target);
00932   NS_ENSURE_SUBMIT_SUCCESS(rv);
00933 
00934   //
00935   // Notify observers of submit
00936   //
00937   PRBool cancelSubmit = PR_FALSE;
00938   if (mNotifiedObservers) {
00939     cancelSubmit = mNotifiedObserversResult;
00940   } else {
00941     rv = NotifySubmitObservers(actionURI, &cancelSubmit, PR_TRUE);
00942     NS_ENSURE_SUBMIT_SUCCESS(rv);
00943   }
00944 
00945   if (cancelSubmit) {
00946     mIsSubmitting = PR_FALSE;
00947     return NS_OK;
00948   }
00949 
00950   cancelSubmit = PR_FALSE;
00951   rv = NotifySubmitObservers(actionURI, &cancelSubmit, PR_FALSE);
00952   NS_ENSURE_SUBMIT_SUCCESS(rv);
00953 
00954   if (cancelSubmit) {
00955     mIsSubmitting = PR_FALSE;
00956     return NS_OK;
00957   }
00958 
00959   //
00960   // Submit
00961   //
00962   nsCOMPtr<nsIDocShell> docShell;
00963 
00964   {
00965     nsAutoPopupStatePusher popupStatePusher(mSubmitPopupState);
00966 
00967     nsAutoHandlingUserInputStatePusher userInpStatePusher(mSubmitInitiatedFromUserInput);
00968 
00969     rv = aFormSubmission->SubmitTo(actionURI, target, this, aPresContext,
00970                                    getter_AddRefs(docShell),
00971                                    getter_AddRefs(mSubmittingRequest));
00972   }
00973 
00974   NS_ENSURE_SUBMIT_SUCCESS(rv);
00975 
00976   // Even if the submit succeeds, it's possible for there to be no docshell
00977   // or request; for example, if it's to a named anchor within the same page
00978   // the submit will not really do anything.
00979   if (docShell) {
00980     // If the channel is pending, we have to listen for web progress.
00981     PRBool pending = PR_FALSE;
00982     mSubmittingRequest->IsPending(&pending);
00983     if (pending) {
00984       mWebProgress = do_GetInterface(docShell);
00985       NS_ASSERTION(mWebProgress, "nsIDocShell not converted to nsIWebProgress!");
00986       rv = mWebProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_ALL);
00987       NS_ENSURE_SUBMIT_SUCCESS(rv);
00988     } else {
00989       ForgetCurrentSubmission();
00990     }
00991   } else {
00992     ForgetCurrentSubmission();
00993   }
00994 
00995   return rv;
00996 }
00997 
00998 nsresult
00999 nsHTMLFormElement::NotifySubmitObservers(nsIURI* aActionURL,
01000                                          PRBool* aCancelSubmit,
01001                                          PRBool  aEarlyNotify)
01002 {
01003   // If this is the first form, bring alive the first form submit
01004   // category observers
01005   if (!gFirstFormSubmitted) {
01006     gFirstFormSubmitted = PR_TRUE;
01007     NS_CreateServicesFromCategory(NS_FIRST_FORMSUBMIT_CATEGORY,
01008                                   nsnull,
01009                                   NS_FIRST_FORMSUBMIT_CATEGORY);
01010   }
01011 
01012   // Notify observers that the form is being submitted.
01013   nsresult rv = NS_OK;
01014   nsCOMPtr<nsIObserverService> service =
01015     do_GetService("@mozilla.org/observer-service;1", &rv);
01016   NS_ENSURE_SUCCESS(rv, rv);
01017 
01018   nsCOMPtr<nsISimpleEnumerator> theEnum;
01019   rv = service->EnumerateObservers(aEarlyNotify ?
01020                                    NS_EARLYFORMSUBMIT_SUBJECT :
01021                                    NS_FORMSUBMIT_SUBJECT,
01022                                    getter_AddRefs(theEnum));
01023   NS_ENSURE_SUCCESS(rv, rv);
01024 
01025   if (theEnum) {
01026     nsCOMPtr<nsISupports> inst;
01027     *aCancelSubmit = PR_FALSE;
01028 
01029     // XXXbz what do the submit observers actually want?  The window
01030     // of the document this is shown in?  Or something else?
01031     // sXBL/XBL2 issue
01032     nsCOMPtr<nsIDOMWindowInternal> window =
01033       do_QueryInterface(GetOwnerDoc()->GetScriptGlobalObject());
01034 
01035     PRBool loop = PR_TRUE;
01036     while (NS_SUCCEEDED(theEnum->HasMoreElements(&loop)) && loop) {
01037       theEnum->GetNext(getter_AddRefs(inst));
01038 
01039       nsCOMPtr<nsIFormSubmitObserver> formSubmitObserver(
01040                       do_QueryInterface(inst));
01041       if (formSubmitObserver) {
01042         rv = formSubmitObserver->Notify(this,
01043                                         window,
01044                                         aActionURL,
01045                                         aCancelSubmit);
01046         NS_ENSURE_SUCCESS(rv, rv);
01047       }
01048       if (*aCancelSubmit) {
01049         return NS_OK;
01050       }
01051     }
01052   }
01053 
01054   return rv;
01055 }
01056 
01057 
01058 // static
01059 nsresult
01060 nsHTMLFormElement::CompareNodes(nsIDOMNode* a, nsIDOMNode* b, PRInt32* retval)
01061 {
01062   nsresult rv;
01063 
01064   nsCOMPtr<nsIDOMNode> parentANode;
01065   PRInt32 indexA;
01066   rv = a->GetParentNode(getter_AddRefs(parentANode));
01067   NS_ENSURE_SUCCESS(rv, rv);
01068   if (!parentANode) {
01069     return NS_ERROR_UNEXPECTED;
01070   }
01071 
01072   {
01073     // To get the index, we must turn them both into contents
01074     // and do IndexOf().  Ick.
01075     nsCOMPtr<nsIContent> parentA(do_QueryInterface(parentANode));
01076     nsCOMPtr<nsIContent> contentA(do_QueryInterface(a));
01077     if (!parentA || !contentA) {
01078       return NS_ERROR_UNEXPECTED;
01079     }
01080     indexA = parentA->IndexOf(contentA);
01081   }
01082 
01083   nsCOMPtr<nsIDOMNode> parentBNode;
01084   PRInt32 indexB;
01085   rv = b->GetParentNode(getter_AddRefs(parentBNode));
01086   NS_ENSURE_SUCCESS(rv, rv);
01087   if (!parentBNode) {
01088     return NS_ERROR_UNEXPECTED;
01089   }
01090 
01091   {
01092     // To get the index, we must turn them both into contents
01093     // and do IndexOf().  Ick.
01094     nsCOMPtr<nsIContent> parentB(do_QueryInterface(parentBNode));
01095     nsCOMPtr<nsIContent> bContent(do_QueryInterface(b));
01096     if (!parentB || !bContent) {
01097       return NS_ERROR_UNEXPECTED;
01098     }
01099     indexB = parentB->IndexOf(bContent);
01100   }
01101 
01102   *retval = nsRange::ComparePoints(parentANode, indexA, parentBNode, indexB);
01103   return NS_OK;
01104 }
01105 
01106 
01107 nsresult
01108 nsHTMLFormElement::WalkFormElements(nsIFormSubmission* aFormSubmission,
01109                                     nsIContent* aSubmitElement)
01110 {
01111   nsCOMPtr<nsISimpleEnumerator> formControls;
01112   nsresult rv = GetControlEnumerator(getter_AddRefs(formControls));
01113   NS_ENSURE_SUCCESS(rv, rv);
01114 
01115   //
01116   // Walk the list of nodes and call SubmitNamesValues() on the controls
01117   //
01118   PRBool hasMoreElements;
01119   nsCOMPtr<nsISupports> controlSupports;
01120   nsCOMPtr<nsIFormControl> control;
01121   while (NS_SUCCEEDED(formControls->HasMoreElements(&hasMoreElements)) &&
01122          hasMoreElements) {
01123     rv = formControls->GetNext(getter_AddRefs(controlSupports));
01124     NS_ENSURE_SUCCESS(rv, rv);
01125     control = do_QueryInterface(controlSupports);
01126 
01127     // Tell the control to submit its name/value pairs to the submission
01128     control->SubmitNamesValues(aFormSubmission, aSubmitElement);
01129   }
01130 
01131   return NS_OK;
01132 }
01133 
01134 // nsIForm
01135 
01136 NS_IMETHODIMP
01137 nsHTMLFormElement::GetElementCount(PRUint32* aCount) const 
01138 {
01139   mControls->GetLength(aCount); 
01140   return NS_OK;
01141 }
01142 
01143 NS_IMETHODIMP 
01144 nsHTMLFormElement::GetElementAt(PRInt32 aIndex,
01145                                 nsIFormControl** aFormControl) const 
01146 {
01147   *aFormControl = NS_STATIC_CAST(nsIFormControl *,
01148                                  mControls->mElements.SafeElementAt(aIndex));
01149   NS_IF_ADDREF(*aFormControl);
01150 
01151   return NS_OK;
01152 }
01153 
01154 // Compares the position of control1 and control2 in the document
01155 //
01156 // returns < 0 if control1 is before control2,
01157 //         > 0 if control1 is after control2,
01158 //         0 otherwise
01159 static PRInt32 CompareFormControlPosition(nsIFormControl *control1, nsIFormControl *control2)
01160 {
01161   nsCOMPtr<nsIContent> content1 = do_QueryInterface(control1);
01162   nsCOMPtr<nsIContent> content2 = do_QueryInterface(control2);
01163 
01164   NS_ASSERTION(content1 && content2, "We should be able to QI to nsIContent here!");
01165 
01166   // We check that the parent of both content nodes is non-null here, because
01167   // newly created form control elements may not be in the parent form
01168   // element's DOM tree yet. This prevents an assertion in CompareTreePosition.
01169   // See Bug 267511 for more information.
01170   if (content1 && content2 && content1->GetParent() && content2->GetParent())
01171     return nsLayoutUtils::CompareTreePosition(content1, content2);
01172   
01173   return 0;
01174 }
01175 
01176 NS_IMETHODIMP
01177 nsHTMLFormElement::AddElement(nsIFormControl* aChild)
01178 {
01179   NS_ENSURE_TRUE(mControls, NS_ERROR_UNEXPECTED);
01180 
01181   if (ShouldBeInElements(aChild)) {
01182     PRUint32 count;
01183     GetElementCount(&count);
01184 
01185     nsCOMPtr<nsIFormControl> element;
01186 
01187     // Optimize most common case where we insert at the end.
01188     PRInt32 position = -1;
01189     if (count > 0) {
01190       GetElementAt(count - 1, getter_AddRefs(element));
01191       position = CompareFormControlPosition(aChild, element);
01192     }
01193 
01194     // If this item comes after the last element, or the elements array is
01195     // empty, we append to the end. Otherwise, we do a binary search to
01196     // determine where the element should go.    
01197     if (position >= 0 || count == 0) {
01198       // WEAK - don't addref
01199       mControls->mElements.AppendElement(aChild);
01200     }
01201     else {
01202       PRInt32 low = 0, mid, high;
01203       high = count - 1;
01204       
01205       while (low <= high) {
01206         mid = (low + high) / 2;
01207         
01208         GetElementAt(mid, getter_AddRefs(element));
01209         position = CompareFormControlPosition(aChild, element);
01210         if (position >= 0)
01211           low = mid + 1;
01212         else
01213           high = mid - 1;
01214       }
01215       
01216       // WEAK - don't addref
01217       mControls->mElements.InsertElementAt(aChild, low);
01218     }
01219   } else {
01220     // WEAK - don't addref
01221     mControls->mNotInElements.AppendElement(aChild);
01222   }
01223 
01224   //
01225   // Notify the radio button it's been added to a group
01226   //
01227   PRInt32 type = aChild->GetType();
01228   if (type == NS_FORM_INPUT_RADIO) {
01229     nsCOMPtr<nsIRadioControlElement> radio = do_QueryInterface(aChild);
01230     nsresult rv = radio->AddedToRadioGroup();
01231     NS_ENSURE_SUCCESS(rv, rv);
01232   }
01233 
01234   //
01235   // If it is a password control, and the password manager has not yet been
01236   // initialized, initialize the password manager
01237   //
01238   if (!gPasswordManagerInitialized && type == NS_FORM_INPUT_PASSWORD) {
01239     // Initialize the password manager category
01240     gPasswordManagerInitialized = PR_TRUE;
01241     NS_CreateServicesFromCategory(NS_PASSWORDMANAGER_CATEGORY,
01242                                   nsnull,
01243                                   NS_PASSWORDMANAGER_CATEGORY);
01244   }
01245 
01246   return NS_OK;
01247 }
01248 
01249 NS_IMETHODIMP
01250 nsHTMLFormElement::AddElementToTable(nsIFormControl* aChild,
01251                                      const nsAString& aName)
01252 {
01253   NS_ENSURE_TRUE(mControls, NS_ERROR_UNEXPECTED);
01254 
01255   return mControls->AddElementToTable(aChild, aName);  
01256 }
01257 
01258 
01259 NS_IMETHODIMP 
01260 nsHTMLFormElement::RemoveElement(nsIFormControl* aChild) 
01261 {
01262   NS_ENSURE_TRUE(mControls, NS_ERROR_UNEXPECTED);
01263 
01264   //
01265   // Remove it from the radio group if it's a radio button
01266   //
01267   if (aChild->GetType() == NS_FORM_INPUT_RADIO) {
01268     nsCOMPtr<nsIRadioControlElement> radio = do_QueryInterface(aChild);
01269     nsresult rv = radio->WillRemoveFromRadioGroup();
01270     NS_ENSURE_SUCCESS(rv, rv);
01271   }
01272 
01273   if (ShouldBeInElements(aChild)) {
01274     mControls->mElements.RemoveElement(aChild);
01275   } else {
01276     mControls->mNotInElements.RemoveElement(aChild);
01277   }
01278 
01279   return NS_OK;
01280 }
01281 
01282 NS_IMETHODIMP
01283 nsHTMLFormElement::RemoveElementFromTable(nsIFormControl* aElement,
01284                                           const nsAString& aName)
01285 {
01286   NS_ENSURE_TRUE(mControls, NS_ERROR_UNEXPECTED);
01287 
01288   return mControls->RemoveElementFromTable(aElement, aName);
01289 }
01290 
01291 NS_IMETHODIMP
01292 nsHTMLFormElement::ResolveName(const nsAString& aName,
01293                                nsISupports **aResult)
01294 {
01295   return mControls->GetNamedObject(aName, aResult);
01296 }
01297 
01298 NS_IMETHODIMP
01299 nsHTMLFormElement::OnSubmitClickBegin()
01300 {
01301   mDeferSubmission = PR_TRUE;
01302 
01303   // Prepare to run NotifySubmitObservers early before the
01304   // scripts on the page get to modify the form data, possibly
01305   // throwing off any password manager. (bug 257781)
01306   nsCOMPtr<nsIURI> actionURI;
01307   nsresult rv;
01308 
01309   rv = GetActionURL(getter_AddRefs(actionURI));
01310   if (NS_FAILED(rv) || !actionURI)
01311     return NS_OK;
01312 
01313   //
01314   // Notify observers of submit
01315   //
01316   PRBool cancelSubmit = PR_FALSE;
01317   rv = NotifySubmitObservers(actionURI, &cancelSubmit, PR_TRUE);
01318   if (NS_SUCCEEDED(rv)) {
01319     mNotifiedObservers = PR_TRUE;
01320     mNotifiedObserversResult = cancelSubmit;
01321   }
01322 
01323   return NS_OK;
01324 }
01325 
01326 NS_IMETHODIMP
01327 nsHTMLFormElement::OnSubmitClickEnd()
01328 {
01329   mDeferSubmission = PR_FALSE;
01330   return NS_OK;
01331 }
01332 
01333 NS_IMETHODIMP
01334 nsHTMLFormElement::FlushPendingSubmission()
01335 {
01336   nsCOMPtr<nsIFormSubmission> kunkFuDeathGrip(mPendingSubmission);
01337 
01338   if (!mPendingSubmission) {
01339     return NS_OK;
01340   }
01341 
01342   //
01343   // perform the submission with the stored pending submission
01344   //
01345   nsCOMPtr<nsPresContext> presContext = GetPresContext();
01346   SubmitSubmission(presContext, mPendingSubmission);
01347 
01348   // now delete the pending submission object
01349   mPendingSubmission = nsnull;
01350   return NS_OK;
01351 }
01352 
01353 NS_IMETHODIMP
01354 nsHTMLFormElement::ForgetPendingSubmission()
01355 {
01356   // just delete the pending submission
01357   mPendingSubmission = nsnull;
01358   return NS_OK;
01359 }
01360 
01361 NS_IMETHODIMP
01362 nsHTMLFormElement::GetActionURL(nsIURI** aActionURL)
01363 {
01364   nsresult rv = NS_OK;
01365 
01366   *aActionURL = nsnull;
01367 
01368   //
01369   // Grab the URL string
01370   //
01371   nsAutoString action;
01372   GetAction(action);
01373 
01374   //
01375   // Form the full action URL
01376   //
01377 
01378   // Get the document to form the URL.
01379   // We'll also need it later to get the DOM window when notifying form submit
01380   // observers (bug 33203)
01381   if (!IsInDoc()) {
01382     return NS_OK; // No doc means don't submit, see Bug 28988
01383   }
01384 
01385   // Get base URL
01386   nsIDocument *document = GetOwnerDoc();
01387   nsIURI *docURI = document->GetDocumentURI();
01388   NS_ENSURE_TRUE(docURI, NS_ERROR_UNEXPECTED);
01389 
01390   // If an action is not specified and we are inside
01391   // a HTML document then reload the URL. This makes us
01392   // compatible with 4.x browsers.
01393   // If we are in some other type of document such as XML or
01394   // XUL, do nothing. This prevents undesirable reloading of
01395   // a document inside XUL.
01396 
01397   nsCOMPtr<nsIURI> actionURL;
01398   if (action.IsEmpty()) {
01399     nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(document));
01400     if (!htmlDoc) {
01401       // Must be a XML, XUL or other non-HTML document type
01402       // so do nothing.
01403       return NS_OK;
01404     }
01405 
01406     rv = docURI->Clone(getter_AddRefs(actionURL));
01407     NS_ENSURE_SUCCESS(rv, rv);
01408   } else {
01409     nsCOMPtr<nsIURI> baseURL = GetBaseURI();
01410     NS_ASSERTION(baseURL, "No Base URL found in Form Submit!\n");
01411     if (!baseURL) {
01412       return NS_OK; // No base URL -> exit early, see Bug 30721
01413     }
01414     rv = NS_NewURI(getter_AddRefs(actionURL), action, nsnull, baseURL);
01415     NS_ENSURE_SUCCESS(rv, rv);
01416   }
01417 
01418   //
01419   // Verify the URL should be reached
01420   //
01421   // Get security manager, check to see if access to action URI is allowed.
01422   //
01423   nsIScriptSecurityManager *securityManager =
01424       nsContentUtils::GetSecurityManager();
01425   rv = securityManager->
01426     CheckLoadURIWithPrincipal(document->GetPrincipal(), actionURL,
01427                               nsIScriptSecurityManager::STANDARD);
01428   NS_ENSURE_SUCCESS(rv, rv);
01429 
01430   //
01431   // Assign to the output
01432   //
01433   *aActionURL = actionURL;
01434   NS_ADDREF(*aActionURL);
01435 
01436   return rv;
01437 }
01438 
01439 NS_IMETHODIMP
01440 nsHTMLFormElement::GetEncoding(nsAString& aEncoding)
01441 {
01442   return GetEnctype(aEncoding);
01443 }
01444  
01445 NS_IMETHODIMP
01446 nsHTMLFormElement::SetEncoding(const nsAString& aEncoding)
01447 {
01448   return SetEnctype(aEncoding);
01449 }
01450  
01451 NS_IMETHODIMP    
01452 nsHTMLFormElement::GetLength(PRInt32* aLength)
01453 {
01454   *aLength = mControls->mElements.Count();
01455   
01456   return NS_OK;
01457 }
01458 
01459 void
01460 nsHTMLFormElement::ForgetCurrentSubmission()
01461 {
01462   mNotifiedObservers = PR_FALSE;
01463   mIsSubmitting = PR_FALSE;
01464   mSubmittingRequest = nsnull;
01465   if (mWebProgress) {
01466     mWebProgress->RemoveProgressListener(this);
01467     mWebProgress = nsnull;
01468   }
01469 }
01470 
01471 // nsIWebProgressListener
01472 NS_IMETHODIMP
01473 nsHTMLFormElement::OnStateChange(nsIWebProgress* aWebProgress,
01474                                  nsIRequest* aRequest,
01475                                  PRUint32 aStateFlags,
01476                                  PRUint32 aStatus)
01477 {
01478   // If STATE_STOP is never fired for any reason (redirect?  Failed state
01479   // change?) the form element will leak.  It will be kept around by the
01480   // nsIWebProgressListener (assuming it keeps a strong pointer).  We will
01481   // consequently leak the request.
01482   if (aRequest == mSubmittingRequest &&
01483       aStateFlags & nsIWebProgressListener::STATE_STOP) {
01484     ForgetCurrentSubmission();
01485   }
01486 
01487   return NS_OK;
01488 }
01489 
01490 NS_IMETHODIMP
01491 nsHTMLFormElement::OnProgressChange(nsIWebProgress* aWebProgress,
01492                                     nsIRequest* aRequest,
01493                                     PRInt32 aCurSelfProgress,
01494                                     PRInt32 aMaxSelfProgress,
01495                                     PRInt32 aCurTotalProgress,
01496                                     PRInt32 aMaxTotalProgress)
01497 {
01498   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
01499   return NS_OK;
01500 }
01501 
01502 NS_IMETHODIMP
01503 nsHTMLFormElement::OnLocationChange(nsIWebProgress* aWebProgress,
01504                                     nsIRequest* aRequest,
01505                                     nsIURI* location)
01506 {
01507   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
01508   return NS_OK;
01509 }
01510 
01511 NS_IMETHODIMP
01512 nsHTMLFormElement::OnStatusChange(nsIWebProgress* aWebProgress,
01513                                   nsIRequest* aRequest,
01514                                   nsresult aStatus,
01515                                   const PRUnichar* aMessage)
01516 {
01517   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
01518   return NS_OK;
01519 }
01520 
01521 NS_IMETHODIMP
01522 nsHTMLFormElement::OnSecurityChange(nsIWebProgress* aWebProgress,
01523                                     nsIRequest* aRequest,
01524                                     PRUint32 state)
01525 {
01526   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
01527   return NS_OK;
01528 }
01529 
01530 NS_IMETHODIMP
01531 nsHTMLFormElement::GetControlEnumerator(nsISimpleEnumerator** aEnum)
01532 {
01533   *aEnum = new nsFormControlEnumerator(this);
01534   NS_ENSURE_TRUE(*aEnum, NS_ERROR_OUT_OF_MEMORY);
01535   NS_ADDREF(*aEnum);
01536   return NS_OK;
01537 }
01538  
01539 NS_IMETHODIMP
01540 nsHTMLFormElement::IndexOfControl(nsIFormControl* aControl, PRInt32* aIndex)
01541 {
01542   NS_ENSURE_TRUE(mControls, NS_ERROR_FAILURE);
01543 
01544   return mControls->IndexOfControl(aControl, aIndex);
01545 }
01546 
01547 NS_IMETHODIMP
01548 nsHTMLFormElement::SetCurrentRadioButton(const nsAString& aName,
01549                                          nsIDOMHTMLInputElement* aRadio)
01550 {
01551   NS_ENSURE_TRUE(mSelectedRadioButtons.Put(aName, aRadio),
01552                  NS_ERROR_OUT_OF_MEMORY);
01553 
01554   return NS_OK;
01555 }
01556 
01557 NS_IMETHODIMP
01558 nsHTMLFormElement::GetCurrentRadioButton(const nsAString& aName,
01559                                          nsIDOMHTMLInputElement** aRadio)
01560 {
01561   mSelectedRadioButtons.Get(aName, aRadio);
01562 
01563   return NS_OK;
01564 }
01565 
01566 NS_IMETHODIMP
01567 nsHTMLFormElement::GetPositionInGroup(nsIDOMHTMLInputElement *aRadio,
01568                                       PRInt32 *aPositionIndex,
01569                                       PRInt32 *aItemsInGroup)
01570 {
01571   *aPositionIndex = 0;
01572   *aItemsInGroup = 1;
01573 
01574   nsAutoString name;
01575   aRadio->GetName(name);
01576   if (name.IsEmpty()) {
01577     return NS_OK;
01578   }
01579 
01580   nsCOMPtr<nsISupports> itemWithName;
01581   nsresult rv = ResolveName(name, getter_AddRefs(itemWithName));
01582   NS_ENSURE_SUCCESS(rv, rv);
01583   nsCOMPtr<nsIDOMNodeList> radioNodeList(do_QueryInterface(itemWithName));
01584 
01585   // XXX If ResolveName could return an nsContentList instead then we 
01586   //     could get an nsContentList instead of using this hacky upcast
01587   nsBaseContentList *radioGroup =
01588     NS_STATIC_CAST(nsBaseContentList *, (nsIDOMNodeList *)radioNodeList);
01589   NS_ASSERTION(radioGroup, "No such radio group in this container");
01590   if (!radioGroup) {
01591     return NS_OK;
01592   }
01593 
01594   nsCOMPtr<nsIContent> currentRadioNode(do_QueryInterface(aRadio));
01595   NS_ASSERTION(currentRadioNode, "No nsIContent for current radio button");
01596   *aPositionIndex = radioGroup->IndexOf(currentRadioNode, PR_TRUE);
01597   NS_ASSERTION(*aPositionIndex >= 0, "Radio button not found in its own group");
01598   PRUint32 itemsInGroup;
01599   radioGroup->GetLength(&itemsInGroup);
01600   *aItemsInGroup = itemsInGroup;
01601 
01602   return NS_OK;
01603 }
01604 
01605 NS_IMETHODIMP
01606 nsHTMLFormElement::GetNextRadioButton(const nsAString& aName,
01607                                       const PRBool aPrevious,
01608                                       nsIDOMHTMLInputElement*  aFocusedRadio,
01609                                       nsIDOMHTMLInputElement** aRadioOut)
01610 {
01611   // Return the radio button relative to the focused radio button.
01612   // If no radio is focused, get the radio relative to the selected one.
01613   *aRadioOut = nsnull;
01614 
01615   nsCOMPtr<nsIDOMHTMLInputElement> currentRadio;
01616   if (aFocusedRadio) {
01617     currentRadio = aFocusedRadio;
01618   }
01619   else {
01620     mSelectedRadioButtons.Get(aName, getter_AddRefs(currentRadio));
01621   }
01622 
01623   nsCOMPtr<nsISupports> itemWithName;
01624   ResolveName(aName, getter_AddRefs(itemWithName));
01625   nsCOMPtr<nsIDOMNodeList> radioNodeList(do_QueryInterface(itemWithName));
01626 
01627   // XXX If ResolveName could return an nsContentList instead then we 
01628   //     could get an nsContentList instead of using this hacky upcast
01629 
01630   nsBaseContentList *radioGroup =
01631     NS_STATIC_CAST(nsBaseContentList *, (nsIDOMNodeList *)radioNodeList);
01632   if (!radioGroup) {
01633     return NS_ERROR_FAILURE;
01634   }
01635 
01636   nsCOMPtr<nsIContent> currentRadioNode(do_QueryInterface(currentRadio));
01637   NS_ASSERTION(currentRadioNode, "No nsIContent for current radio button");
01638   PRInt32 index = radioGroup->IndexOf(currentRadioNode, PR_TRUE);
01639   if (index < 0) {
01640     return NS_ERROR_FAILURE;
01641   }
01642 
01643   PRUint32 numRadios;
01644   radioGroup->GetLength(&numRadios);
01645   PRBool disabled = PR_TRUE;
01646   nsCOMPtr<nsIDOMHTMLInputElement> radio;
01647   nsCOMPtr<nsIDOMNode> radioDOMNode;
01648   nsCOMPtr<nsIFormControl> formControl;
01649 
01650   do {
01651     if (aPrevious) {
01652       if (--index < 0) {
01653         index = numRadios -1;
01654       }
01655     }
01656     else if (++index >= numRadios) {
01657       index = 0;
01658     }
01659     radioGroup->Item(index, getter_AddRefs(radioDOMNode));
01660     radio = do_QueryInterface(radioDOMNode);
01661     if (!radio)
01662       continue;
01663 
01664     formControl = do_QueryInterface(radio);
01665     if (!formControl || formControl->GetType() != NS_FORM_INPUT_RADIO)
01666       continue;
01667 
01668     radio->GetDisabled(&disabled);
01669   } while (disabled && radio != currentRadio);
01670 
01671   NS_IF_ADDREF(*aRadioOut = radio);
01672   return NS_OK;
01673 }
01674 
01675 NS_IMETHODIMP
01676 nsHTMLFormElement::WalkRadioGroup(const nsAString& aName,
01677                                   nsIRadioVisitor* aVisitor)
01678 {
01679   nsresult rv = NS_OK;
01680 
01681   PRBool stopIterating = PR_FALSE;
01682 
01683   if (aName.IsEmpty()) {
01684     //
01685     // XXX If the name is empty, it's not stored in the control list.  There
01686     // *must* be a more efficient way to do this.
01687     //
01688     nsCOMPtr<nsIFormControl> control;
01689     PRUint32 len = 0;
01690     GetElementCount(&len);
01691     for (PRUint32 i=0; i<len; i++) {
01692       GetElementAt(i, getter_AddRefs(control));
01693       if (control->GetType() == NS_FORM_INPUT_RADIO) {
01694         nsCOMPtr<nsIContent> controlContent(do_QueryInterface(control));
01695         if (controlContent) {
01696           //
01697           // XXX This is a particularly frivolous string copy just to determine
01698           // if the string is empty or not
01699           //
01700           nsAutoString name;
01701           if (controlContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::name,
01702                                       name) != NS_CONTENT_ATTR_NOT_THERE &&
01703               name.IsEmpty()) {
01704             aVisitor->Visit(control, &stopIterating);
01705             if (stopIterating) {
01706               break;
01707             }
01708           }
01709         }
01710       }
01711     }
01712   } else {
01713     //
01714     // Get the control / list of controls from the form using form["name"]
01715     //
01716     nsCOMPtr<nsISupports> item;
01717     rv = ResolveName(aName, getter_AddRefs(item));
01718 
01719     if (item) {
01720       //
01721       // If it's just a lone radio button, then select it.
01722       //
01723       nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(item));
01724       if (formControl) {
01725         if (formControl->GetType() == NS_FORM_INPUT_RADIO) {
01726           aVisitor->Visit(formControl, &stopIterating);
01727         }
01728       } else {
01729         nsCOMPtr<nsIDOMNodeList> nodeList(do_QueryInterface(item));
01730         if (nodeList) {
01731           PRUint32 length = 0;
01732           nodeList->GetLength(&length);
01733           for (PRUint32 i=0; i<length; i++) {
01734             nsCOMPtr<nsIDOMNode> node;
01735             nodeList->Item(i, getter_AddRefs(node));
01736             nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(node));
01737             if (formControl) {
01738               if (formControl->GetType() == NS_FORM_INPUT_RADIO) {
01739                 aVisitor->Visit(formControl, &stopIterating);
01740                 if (stopIterating) {
01741                   break;
01742                 }
01743               }
01744             }
01745           }
01746         }
01747       }
01748     }
01749   }
01750 
01751   return rv;
01752 }
01753 
01754 NS_IMETHODIMP
01755 nsHTMLFormElement::AddToRadioGroup(const nsAString& aName,
01756                                    nsIFormControl* aRadio)
01757 {
01758   return NS_OK;
01759 }
01760 
01761 NS_IMETHODIMP
01762 nsHTMLFormElement::RemoveFromRadioGroup(const nsAString& aName,
01763                                         nsIFormControl* aRadio)
01764 {
01765   return NS_OK;
01766 }
01767 
01768 
01769 //----------------------------------------------------------------------
01770 // nsFormControlList implementation, this could go away if there were
01771 // a lightweight collection implementation somewhere
01772 
01773 nsFormControlList::nsFormControlList(nsIDOMHTMLFormElement* aForm) :
01774   mForm(aForm)
01775 {
01776 }
01777 
01778 nsFormControlList::~nsFormControlList()
01779 {
01780   mForm = nsnull;
01781   Clear();
01782 }
01783 
01784 nsresult nsFormControlList::Init()
01785 {
01786   NS_ENSURE_TRUE(
01787     mNameLookupTable.Init(NS_FORM_CONTROL_LIST_HASHTABLE_SIZE),
01788     NS_ERROR_OUT_OF_MEMORY);
01789 
01790   return NS_OK;
01791 }
01792 
01793 void
01794 nsFormControlList::SetForm(nsIDOMHTMLFormElement* aForm)
01795 {
01796   mForm = aForm; // WEAK - the form owns me
01797 }
01798 
01799 void
01800 nsFormControlList::Clear()
01801 {
01802   // Null out childrens' pointer to me.  No refcounting here
01803   PRInt32 i;
01804   for (i = mElements.Count()-1; i >= 0; i--) {
01805     nsIFormControl* f = NS_STATIC_CAST(nsIFormControl *,
01806                                        mElements.ElementAt(i));
01807     if (f) {
01808       f->SetForm(nsnull, PR_FALSE); 
01809     }
01810   }
01811   mElements.Clear();
01812 
01813   for (i = mNotInElements.Count()-1; i >= 0; i--) {
01814     nsIFormControl* f = NS_STATIC_CAST(nsIFormControl*,
01815                                        mNotInElements.ElementAt(i));
01816     if (f) {
01817       f->SetForm(nsnull, PR_FALSE);
01818     }
01819   }
01820   mNotInElements.Clear();
01821 
01822   mNameLookupTable.Clear();
01823 }
01824 
01825 
01826 // XPConnect interface list for nsFormControlList
01827 NS_INTERFACE_MAP_BEGIN(nsFormControlList)
01828   NS_INTERFACE_MAP_ENTRY(nsIDOMNSHTMLFormControlList)
01829   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLCollection)
01830   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMHTMLCollection)
01831   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLFormControlCollection)
01832 NS_INTERFACE_MAP_END
01833 
01834 
01835 NS_IMPL_ADDREF(nsFormControlList)
01836 NS_IMPL_RELEASE(nsFormControlList)
01837 
01838 
01839 // nsIDOMHTMLCollection interface
01840 
01841 NS_IMETHODIMP    
01842 nsFormControlList::GetLength(PRUint32* aLength)
01843 {
01844   *aLength = mElements.Count();
01845   return NS_OK;
01846 }
01847 
01848 NS_IMETHODIMP
01849 nsFormControlList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
01850 {
01851   nsIFormControl *control = NS_STATIC_CAST(nsIFormControl *,
01852                                            mElements.SafeElementAt(aIndex));
01853   if (control) {
01854     return CallQueryInterface(control, aReturn);
01855   }
01856 
01857   *aReturn = nsnull;
01858 
01859   return NS_OK;
01860 }
01861 
01862 nsresult
01863 nsFormControlList::GetNamedObject(const nsAString& aName,
01864                                   nsISupports** aResult)
01865 {
01866   *aResult = nsnull;
01867 
01868   if (!mForm) {
01869     // No form, no named objects
01870     return NS_OK;
01871   }
01872   
01873   // Get the hash entry
01874   mNameLookupTable.Get(aName, aResult);
01875 
01876   return NS_OK;
01877 }
01878 
01879 NS_IMETHODIMP 
01880 nsFormControlList::NamedItem(const nsAString& aName,
01881                              nsIDOMNode** aReturn)
01882 {
01883   *aReturn = nsnull;
01884 
01885   nsresult rv = NS_OK;
01886 
01887   nsCOMPtr<nsISupports> supports;
01888   
01889   if (!mNameLookupTable.Get(aName, getter_AddRefs(supports))) // key not found
01890      return rv;
01891 
01892   if (supports) {
01893     // We found something, check if it's a node
01894     CallQueryInterface(supports, aReturn);
01895 
01896     if (!*aReturn) {
01897       // If not, we check if it's a node list.
01898       nsCOMPtr<nsIDOMNodeList> nodeList(do_QueryInterface(supports));
01899       NS_WARN_IF_FALSE(nodeList, "Huh, what's going one here?");
01900 
01901       if (nodeList) {
01902         // And since we're only asking for one node here, we return the first
01903         // one from the list.
01904         rv = nodeList->Item(0, aReturn);
01905       }
01906     }
01907   }
01908 
01909   return rv;
01910 }
01911 
01912 NS_IMETHODIMP
01913 nsFormControlList::NamedItem(const nsAString& aName,
01914                              nsISupports** aReturn)
01915 {
01916   mNameLookupTable.Get(aName, aReturn);
01917 
01918   return NS_OK;
01919 }
01920 
01921 nsresult
01922 nsFormControlList::AddElementToTable(nsIFormControl* aChild,
01923                                      const nsAString& aName)
01924 {
01925   if (!ShouldBeInElements(aChild)) {
01926     return NS_OK;
01927   }
01928 
01929   nsCOMPtr<nsISupports> supports;
01930   mNameLookupTable.Get(aName, getter_AddRefs(supports));
01931 
01932   if (!supports) {
01933     // No entry found, add the form control
01934     nsCOMPtr<nsISupports> child(do_QueryInterface(aChild));
01935 
01936     NS_ENSURE_TRUE( mNameLookupTable.Put(aName, child), NS_ERROR_FAILURE );
01937   } else {
01938     // Found something in the hash, check its type
01939     nsCOMPtr<nsIContent> content(do_QueryInterface(supports));
01940     nsCOMPtr<nsIContent> newChild(do_QueryInterface(aChild));
01941 
01942     if (content) {
01943       // Check if the new content is the same as the one we found in the
01944       // hash, if it is then we leave it in the hash as it is, this will
01945       // happen if a form control has both a name and an id with the same
01946       // value
01947       if (content == newChild) {
01948         return NS_OK;
01949       }
01950 
01951       // Found an element, create a list, add the element to the list and put
01952       // the list in the hash
01953       nsBaseContentList *list = new nsBaseContentList();
01954       NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
01955 
01956       list->AppendElement(content);
01957 
01958       // Add the new child too
01959       list->AppendElement(newChild);
01960 
01961       nsCOMPtr<nsISupports> listSupports = do_QueryInterface(list);
01962 
01963       // Replace the element with the list.
01964       NS_ENSURE_TRUE(mNameLookupTable.Put(aName, listSupports),
01965                      NS_ERROR_FAILURE);
01966     } else {
01967       // There's already a list in the hash, add the child to the list
01968       nsCOMPtr<nsIDOMNodeList> nodeList(do_QueryInterface(supports));
01969       NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
01970 
01971       // Upcast, uggly, but it works!
01972       nsBaseContentList *list = NS_STATIC_CAST(nsBaseContentList *,
01973                                                (nsIDOMNodeList *)nodeList.get());
01974 
01975       PRInt32 oldIndex = list->IndexOf(newChild, PR_FALSE);
01976       
01977       // Add the new child only if it's not in our list already
01978       if (oldIndex < 0) {
01979         list->AppendElement(newChild);
01980       }
01981     }
01982   }
01983 
01984   return NS_OK;
01985 }
01986 
01987 nsresult
01988 nsFormControlList::IndexOfControl(nsIFormControl* aControl,
01989                                   PRInt32* aIndex)
01990 {
01991   NS_ENSURE_ARG_POINTER(aIndex);
01992 
01993   *aIndex = mElements.IndexOf(aControl);
01994 
01995   return NS_OK;
01996 }
01997 
01998 nsresult
01999 nsFormControlList::RemoveElementFromTable(nsIFormControl* aChild,
02000                                           const nsAString& aName)
02001 {
02002   if (!ShouldBeInElements(aChild)) {
02003     return NS_OK;
02004   }
02005 
02006   nsCOMPtr<nsIContent> content = do_QueryInterface(aChild);  
02007   if (!content) {
02008     return NS_OK;
02009   }
02010 
02011   nsCOMPtr<nsISupports> supports;
02012 
02013   if (!mNameLookupTable.Get(aName, getter_AddRefs(supports)))
02014     return NS_OK;
02015 
02016   nsCOMPtr<nsIFormControl> fctrl(do_QueryInterface(supports));
02017 
02018   if (fctrl) {
02019     // Single element in the hash, just remove it if it's the one
02020     // we're trying to remove...
02021     if (fctrl == aChild) {
02022       mNameLookupTable.Remove(aName);
02023     }
02024 
02025     return NS_OK;
02026   }
02027 
02028   nsCOMPtr<nsIDOMNodeList> nodeList(do_QueryInterface(supports));
02029   NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
02030 
02031   // Upcast, uggly, but it works!
02032   nsBaseContentList *list = NS_STATIC_CAST(nsBaseContentList *,
02033                                            (nsIDOMNodeList *)nodeList.get());
02034 
02035   list->RemoveElement(content);
02036 
02037   PRUint32 length = 0;
02038   list->GetLength(&length);
02039 
02040   if (!length) {
02041     // If the list is empty we remove if from our hash, this shouldn't
02042     // happen tho
02043     mNameLookupTable.Remove(aName);
02044   } else if (length == 1) {
02045     // Only one element left, replace the list in the hash with the
02046     // single element.
02047     nsCOMPtr<nsIDOMNode> node;
02048     list->Item(0, getter_AddRefs(node));
02049 
02050     if (node) {
02051       nsCOMPtr<nsISupports> tmp(do_QueryInterface(node));
02052       NS_ENSURE_TRUE(mNameLookupTable.Put(aName, tmp),NS_ERROR_FAILURE);
02053     }
02054   }
02055 
02056   return NS_OK;
02057 }
02058 
02059 // nsFormControlEnumerator
02060 NS_IMPL_ISUPPORTS1(nsFormControlEnumerator, nsISimpleEnumerator)
02061 
02062 nsFormControlEnumerator::nsFormControlEnumerator(nsHTMLFormElement* aForm)
02063   : mForm(aForm), mElementsIndex(0), mNotInElementsIndex(0)
02064 {
02065 
02066   // Create the sorted mNotInElementsSorted array
02067   PRInt32 len = aForm->mControls->mNotInElements.Count();
02068   for (PRInt32 indexToAdd=0; indexToAdd < len; indexToAdd++) {
02069     // Ref doesn't need to be strong, don't bother making it so
02070     nsIFormControl* controlToAdd = NS_STATIC_CAST(nsIFormControl*,
02071         aForm->mControls->mNotInElements.ElementAt(indexToAdd));
02072 
02073     // Go through the array and insert the element at the first place where
02074     // it is less than the element already in the array
02075     nsCOMPtr<nsIDOMNode> controlToAddNode = do_QueryInterface(controlToAdd);
02076     nsCOMPtr<nsIDOMNode> existingNode;
02077     PRBool inserted = PR_FALSE;
02078     // Loop over all elements backwards (from indexToAdd to 0)
02079     // indexToAdd is equal to the array length because we've been adding to it
02080     // constantly
02081     PRUint32 i = indexToAdd;
02082     while (i > 0) {
02083       i--;
02084       existingNode = do_QueryElementAt(&mNotInElementsSorted, i);
02085       PRInt32 comparison;
02086       if (NS_FAILED(nsHTMLFormElement::CompareNodes(controlToAddNode,
02087                                                     existingNode,
02088                                                     &comparison))) {
02089         break;
02090       }
02091       if (comparison > 0) {
02092         if (mNotInElementsSorted.InsertElementAt(controlToAdd, i+1)) {
02093           inserted = PR_TRUE;
02094         }
02095         break;
02096       }
02097     }
02098 
02099     // If it wasn't inserted yet, it is greater than everything in the array
02100     // and must be appended.
02101     if (!inserted) {
02102       if (!mNotInElementsSorted.InsertElementAt(controlToAdd,0)) {
02103         break;
02104       }
02105     }
02106   }
02107 }
02108 
02109 NS_IMETHODIMP
02110 nsFormControlEnumerator::HasMoreElements(PRBool* aHasMoreElements)
02111 {
02112   PRUint32 len;
02113   mForm->GetElementCount(&len);
02114   if (mElementsIndex < len) {
02115     *aHasMoreElements = PR_TRUE;
02116   } else {
02117     PRUint32 notInElementsLen;
02118     mNotInElementsSorted.Count(&notInElementsLen);
02119     *aHasMoreElements = mNotInElementsIndex < notInElementsLen;
02120   }
02121   return NS_OK;
02122 }
02123 
02124 NS_IMETHODIMP
02125 nsFormControlEnumerator::GetNext(nsISupports** aNext)
02126 {
02127   // Holds the current form control in form.elements
02128   nsCOMPtr<nsIFormControl> formControl;
02129   // First get the form control in form.elements
02130   PRUint32 len;
02131   mForm->GetElementCount(&len);
02132   if (mElementsIndex < len) {
02133     mForm->GetElementAt(mElementsIndex, getter_AddRefs(formControl));
02134   }
02135   // If there are still controls in mNotInElementsSorted, determine whether said
02136   // control is before the current control in the array, and if so, choose it
02137   // instead
02138   PRUint32 notInElementsLen;
02139   mNotInElementsSorted.Count(&notInElementsLen);
02140   if (mNotInElementsIndex < notInElementsLen) {
02141     // Get the not-in-elements control - weak ref
02142     
02143     nsCOMPtr<nsIFormControl> formControl2 =
02144         do_QueryElementAt(&mNotInElementsSorted, mNotInElementsIndex);
02145 
02146     if (formControl) {
02147       // Both form controls are there.  We have to compare them and see which
02148       // one we need to choose right now.
02149       nsCOMPtr<nsIDOMNode> dom1 = do_QueryInterface(formControl);
02150       nsCOMPtr<nsIDOMNode> dom2 = do_QueryInterface(formControl2);
02151       PRInt32 comparison = 0;
02152       nsresult rv = nsHTMLFormElement::CompareNodes(dom1, dom2, &comparison);
02153       NS_ENSURE_SUCCESS(rv, rv);
02154       if (comparison < 0) {
02155         *aNext = formControl;
02156         mElementsIndex++;
02157       } else {
02158         *aNext = formControl2;
02159         mNotInElementsIndex++;
02160       }
02161     } else {
02162       *aNext = formControl2;
02163       mNotInElementsIndex++;
02164     }
02165   } else {
02166     *aNext = formControl;
02167     mElementsIndex++;
02168   }
02169 
02170   NS_IF_ADDREF(*aNext);
02171   return NS_OK;
02172 }