diff --git a/mozilla/content/html/content/src/nsHTMLSelectElement.cpp b/mozilla/content/html/content/src/nsHTMLSelectElement.cpp new file mode 100644 index 00000000000..9ef21b37c25 --- /dev/null +++ b/mozilla/content/html/content/src/nsHTMLSelectElement.cpp @@ -0,0 +1,2267 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Pierre Phaneuf + * + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "nsCOMPtr.h" +#include "nsIDOMHTMLSelectElement.h" +#include "nsIDOMNSHTMLSelectElement.h" +#include "nsIDOMNSXBLFormControl.h" +#include "nsIDOMHTMLFormElement.h" +#include "nsIDOMEventReceiver.h" +#include "nsIHTMLContent.h" +#include "nsITextContent.h" +#include "nsGenericHTMLElement.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLIIDs.h" +#include "nsIStyleContext.h" +#include "nsStyleConsts.h" +#include "nsIPresContext.h" +#include "nsHTMLAttributes.h" +#include "nsIForm.h" +#include "nsIFormSubmission.h" +#include "nsIDOMHTMLCollection.h" +#include "nsIDOMHTMLOptionElement.h" +#include "nsIDOMHTMLOptGroupElement.h" +#include "nsIOptionElement.h" +#include "nsIEventStateManager.h" +#include "nsGenericDOMHTMLCollection.h" +#include "nsISelectElement.h" +#include "nsISelectControlFrame.h" +#include "nsIDOMNSHTMLOptionCollectn.h" +#include "nsGUIEvent.h" +#include "nsIBoxObject.h" +#include "nsIDOMNSDocument.h" +#include "nsIDOMDocumentEvent.h" + +// PresState +#include "nsVoidArray.h" +#include "nsISupportsArray.h" +#include "nsISupportsPrimitives.h" +#include "nsIPresState.h" +#include "nsIComponentManager.h" + +// Notify/query select frame for selectedIndex +#include "nsIDocument.h" +#include "nsIPresShell.h" +#include "nsIFormControlFrame.h" +#include "nsIFrame.h" + +#include "nsRuleNode.h" + + +class nsHTMLSelectElement; + + +// nsHTMLOptionCollection +class nsHTMLOptionCollection: public nsIDOMNSHTMLOptionCollection, + public nsGenericDOMHTMLCollection +{ +public: + nsHTMLOptionCollection(nsHTMLSelectElement* aSelect); + virtual ~nsHTMLOptionCollection(); + + NS_DECL_ISUPPORTS_INHERITED + + // nsIDOMNSHTMLOptionCollection interface, can't use the macro + // NS_DECL_NSIDOMNSHTMLOPTIONLIST here since GetLength() is defined + // in mode than one interface + NS_IMETHOD SetLength(PRUint32 aLength); + NS_IMETHOD GetSelectedIndex(PRInt32* aSelectedIndex); + NS_IMETHOD SetSelectedIndex(PRInt32 aSelectedIndex); + NS_IMETHOD SetOption(PRInt32 aIndex, nsIDOMHTMLOptionElement* aOption); + + // nsIDOMHTMLCollection interface + NS_DECL_NSIDOMHTMLCOLLECTION + + nsresult InsertElementAt(nsIDOMNode* aOption, PRInt32 aIndex); + nsresult RemoveElementAt(PRInt32 aIndex); + nsresult GetOption(PRInt32 aIndex, nsIDOMHTMLOptionElement** aReturn); + PRInt32 IndexOf(nsIContent* aOption); + nsresult ItemAsOption(PRInt32 aIndex, nsIDOMHTMLOptionElement **aReturn); + + void DropReference(); + +private: + nsCOMPtr mElements; + nsHTMLSelectElement* mSelect; +}; + + +class nsHTMLSelectElement : public nsGenericHTMLContainerFormElement, + public nsIDOMHTMLSelectElement, + public nsIDOMNSHTMLSelectElement, + public nsIDOMNSXBLFormControl, + public nsISelectElement +{ +public: + nsHTMLSelectElement(PRBool aFromParser); + virtual ~nsHTMLSelectElement(); + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + // nsIDOMNode + NS_FORWARD_NSIDOMNODE_NO_CLONENODE(nsGenericHTMLContainerElement::) + + // nsIDOMElement + NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLContainerElement::) + + // nsIDOMHTMLElement + NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLContainerElement::) + + // nsIDOMHTMLSelectElement + NS_DECL_NSIDOMHTMLSELECTELEMENT + + // nsIDOMNSHTMLSelectElement + NS_DECL_NSIDOMNSHTMLSELECTELEMENT + + // nsIDOMNSXBLFormControl + NS_DECL_NSIDOMNSXBLFORMCONTROL + + // nsIContent + NS_IMETHOD InsertChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify, + PRBool aDeepSetDocument); + NS_IMETHOD ReplaceChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify, + PRBool aDeepSetDocument); + NS_IMETHOD AppendChildTo(nsIContent* aKid, PRBool aNotify, + PRBool aDeepSetDocument); + NS_IMETHOD RemoveChildAt(PRInt32 aIndex, PRBool aNotify); + + NS_IMETHOD HandleDOMEvent(nsIPresContext* aPresContext, + nsEvent* aEvent, + nsIDOMEvent** aDOMEvent, + PRUint32 aFlags, + nsEventStatus* aEventStatus); + +#ifdef DEBUG + NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const; +#endif + NS_IMETHOD SetFocus(nsIPresContext* aPresContext); + NS_IMETHOD RemoveFocus(nsIPresContext* aPresContext); + + // Overriden nsIFormControl methods + NS_IMETHOD GetType(PRInt32* aType); + NS_IMETHOD Reset(); + NS_IMETHOD SubmitNamesValues(nsIFormSubmission* aFormSubmission, + nsIContent* aSubmitElement); + + // nsISelectElement + NS_DECL_NSISELECTELEMENT + + NS_IMETHOD StringToAttribute(nsIAtom* aAttribute, + const nsAString& aValue, + nsHTMLValue& aResult); + NS_IMETHOD GetAttributeMappingFunction(nsMapRuleToAttributesFunc& aMapRuleFunc) const; + NS_IMETHOD GetMappedAttributeImpact(const nsIAtom* aAttribute, + PRInt32 aModType, PRInt32& aHint) const; + + +protected: + // Helper Methods + nsresult GetOptionIndex(nsIDOMHTMLOptionElement* aOption, + PRInt32 * anIndex); + nsresult IsOptionSelectedByIndex(PRInt32 index, PRBool* aIsSelected); + nsresult FindSelectedIndex(PRInt32 aStartIndex); + nsresult SelectSomething(); + nsresult CheckSelectSomething(); + nsresult OnOptionSelected(nsISelectControlFrame* aSelectFrame, + nsIPresContext* aPresContext, + PRInt32 aIndex, + PRBool aSelected, + PRBool aNotify); + nsresult InitializeOption(nsIDOMHTMLOptionElement* aOption, + PRUint32* aNumOptions); + nsresult RestoreStateTo(nsAString* aNewSelected); + +#ifdef DEBUG_john + // Don't remove these, por favor. They're very useful in debugging + nsresult PrintOptions(nsIContent* aOptions, PRInt32 tabs); +#endif + + // Adding options + nsresult InsertOptionsIntoList(nsIContent* aOptions, + PRInt32 aListIndex, + PRInt32 aLevel); + nsresult RemoveOptionsFromList(nsIContent* aOptions, + PRInt32 aListIndex, + PRInt32 aLevel); + nsresult InsertOptionsIntoListRecurse(nsIContent* aOptions, + PRInt32* aInsertIndex, + PRInt32 aLevel); + nsresult RemoveOptionsFromListRecurse(nsIContent* aOptions, + PRInt32 aRemoveIndex, + PRInt32* aNumRemoved, + PRInt32 aLevel); + nsresult GetContentLevel(nsIContent* aContent, PRInt32* aLevel); + nsresult GetOptionAt(nsIContent* aOptions, PRInt32* aListIndex); + nsresult GetOptionAfter(nsIContent* aOptions, PRInt32* aListIndex); + nsresult GetFirstOptionIndex(nsIContent* aOptions, PRInt32* aListIndex); + nsresult GetFirstChildOptionIndex(nsIContent* aOptions, + PRInt32 aStartIndex, + PRInt32 aEndIndex, + PRInt32* aListIndex); + + nsISelectControlFrame *GetSelectFrame(); + + nsHTMLOptionCollection* mOptions; + PRBool mIsDoneAddingChildren; + PRUint32 mArtifactsAtTopLevel; + PRInt32 mSelectedIndex; + nsString* mRestoreState; +}; + + +//---------------------------------------------------------------------- +// +// nsHTMLSelectElement +// + +// construction, destruction + +nsresult +NS_NewHTMLSelectElement(nsIHTMLContent** aInstancePtrResult, + nsINodeInfo *aNodeInfo, + PRBool aFromParser) +{ + NS_ENSURE_ARG_POINTER(aInstancePtrResult); + + nsHTMLSelectElement* it = new nsHTMLSelectElement(aFromParser); + + if (!it) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult rv = NS_STATIC_CAST(nsGenericHTMLElement *, it)->Init(aNodeInfo); + + if (NS_FAILED(rv)) { + delete it; + + return rv; + } + + *aInstancePtrResult = NS_STATIC_CAST(nsIHTMLContent *, it); + NS_ADDREF(*aInstancePtrResult); + + return NS_OK; +} + + +nsHTMLSelectElement::nsHTMLSelectElement(PRBool aFromParser) +{ + // DoneAddingChildren() will be called later if it's from the parser, + // otherwise it is + mIsDoneAddingChildren = !aFromParser; + mArtifactsAtTopLevel = 0; + + mOptions = new nsHTMLOptionCollection(this); + NS_IF_ADDREF(mOptions); + + mRestoreState = nsnull; + mSelectedIndex = -1; +} + +nsHTMLSelectElement::~nsHTMLSelectElement() +{ + // Null out form's pointer to us - no ref counting here! + SetForm(nsnull); + if (mOptions) { + mOptions->DropReference(); + NS_RELEASE(mOptions); + } + if (mRestoreState) { + delete mRestoreState; + mRestoreState = nsnull; + } +} + +// ISupports + +NS_IMPL_ADDREF_INHERITED(nsHTMLSelectElement, nsGenericElement); +NS_IMPL_RELEASE_INHERITED(nsHTMLSelectElement, nsGenericElement); + + +// QueryInterface implementation for nsHTMLSelectElement +NS_HTML_CONTENT_INTERFACE_MAP_BEGIN(nsHTMLSelectElement, + nsGenericHTMLContainerFormElement) + NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLSelectElement) + NS_INTERFACE_MAP_ENTRY(nsIDOMNSHTMLSelectElement) + NS_INTERFACE_MAP_ENTRY(nsIDOMNSXBLFormControl) + NS_INTERFACE_MAP_ENTRY(nsISelectElement) + NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLSelectElement) +NS_HTML_CONTENT_INTERFACE_MAP_END + + +// nsIDOMHTMLSelectElement + +nsresult +nsHTMLSelectElement::CloneNode(PRBool aDeep, nsIDOMNode** aReturn) +{ + NS_ENSURE_ARG_POINTER(aReturn); + *aReturn = nsnull; + + nsHTMLSelectElement* it = new nsHTMLSelectElement(PR_FALSE); + + if (!it) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsCOMPtr kungFuDeathGrip(it); + + nsresult rv = NS_STATIC_CAST(nsGenericHTMLElement *, it)->Init(mNodeInfo); + + if (NS_FAILED(rv)) + return rv; + + CopyInnerTo(this, it, aDeep); + + *aReturn = NS_STATIC_CAST(nsIDOMNode *, it); + + NS_ADDREF(*aReturn); + + return NS_OK; +} + +NS_IMETHODIMP +nsHTMLSelectElement::GetForm(nsIDOMHTMLFormElement** aForm) +{ + return nsGenericHTMLContainerFormElement::GetForm(aForm); +} + + +// nsIContent +NS_IMETHODIMP +nsHTMLSelectElement::AppendChildTo(nsIContent* aKid, PRBool aNotify, + PRBool aDeepSetDocument) +{ + PRInt32 count = 0; + ChildCount(count); + WillAddOptions(aKid, this, count); + + // Actually perform the append + return nsGenericHTMLContainerFormElement::AppendChildTo(aKid, + aNotify, + aDeepSetDocument); +} + +NS_IMETHODIMP +nsHTMLSelectElement::InsertChildAt(nsIContent* aKid, PRInt32 aIndex, + PRBool aNotify, PRBool aDeepSetDocument) +{ + WillAddOptions(aKid, this, aIndex); + + return nsGenericHTMLContainerFormElement::InsertChildAt(aKid, + aIndex, + aNotify, + aDeepSetDocument); +} + +NS_IMETHODIMP +nsHTMLSelectElement::ReplaceChildAt(nsIContent* aKid, PRInt32 aIndex, + PRBool aNotify, PRBool aDeepSetDocument) +{ + WillRemoveOptions(this, aIndex); + WillAddOptions(aKid, this, aIndex); + + return nsGenericHTMLContainerFormElement::ReplaceChildAt(aKid, + aIndex, + aNotify, + aDeepSetDocument); +} + +NS_IMETHODIMP +nsHTMLSelectElement::RemoveChildAt(PRInt32 aIndex, PRBool aNotify) +{ + WillRemoveOptions(this, aIndex); + + return nsGenericHTMLContainerFormElement::RemoveChildAt(aIndex, + aNotify); +} + + +// SelectElement methods + +nsresult +nsHTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions, + PRInt32 aListIndex, + PRInt32 aLevel) +{ + PRInt32 insertIndex = aListIndex; + InsertOptionsIntoListRecurse(aOptions, &insertIndex, aLevel); + + // Deal with the selected list + if (insertIndex - aListIndex) { + // Fix the currently selected index + if (aListIndex <= mSelectedIndex) { + mSelectedIndex += (insertIndex - aListIndex); + } + + // Get the frame stuff for notification. No need to flush here + // since if there's no frame for the select yet the select will + // get into the right state once it's created. + nsISelectControlFrame* selectFrame = GetSelectFrame(); + + nsCOMPtr presContext; + if (selectFrame) { + GetPresContext(this, getter_AddRefs(presContext)); + } + + // Actually select the options if the added options warrant it + nsCOMPtr optionNode; + nsCOMPtr option; + for (PRInt32 i=aListIndex;iAddOption(presContext, i); + } + + Item(i, getter_AddRefs(optionNode)); + option = do_QueryInterface(optionNode); + if (option) { + PRBool selected; + option->GetSelected(&selected); + if (selected) { + // Clear all other options + PRBool isMultiple; + GetMultiple(&isMultiple); + if (!isMultiple) { + SetOptionsSelectedByIndex(i, i, PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE, nsnull); + } + + // This is sort of a hack ... we need to notify that the option was + // set and change selectedIndex even though we didn't really change + // its value. + OnOptionSelected(selectFrame, presContext, i, PR_TRUE, PR_TRUE); + } + } + } + + CheckSelectSomething(); + } + + return NS_OK; +} + +#ifdef DEBUG_john +nsresult +nsHTMLSelectElement::PrintOptions(nsIContent* aOptions, PRInt32 tabs) +{ + for (PRInt32 i=0;i elem(do_QueryInterface(aOptions)); + if (elem) { + nsAutoString s; + elem->GetTagName(s); + printf("<%s>\n", NS_ConvertUCS2toUTF8(s).get()); + } else { + printf(">>text\n"); + } + + // Recurse down into optgroups + // + // I *would* put a restriction in here to only search under + // optgroups (and not, for example,

), but it really + // doesn't *hurt* to search under other stuff and it's more + // efficient in the normal only-optgroup-and-option case + // (one less QueryInterface). + PRInt32 numChildren; + aOptions->ChildCount(numChildren); + nsCOMPtr child; + for (PRInt32 i=0;iChildAt(i,*getter_AddRefs(child)); + PrintOptions(child, tabs+1); + } + + return NS_OK; +} +#endif + +nsresult +nsHTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions, + PRInt32 aListIndex, + PRInt32 aLevel) +{ + PRInt32 numRemoved = 0; + RemoveOptionsFromListRecurse(aOptions, aListIndex, &numRemoved, aLevel); + if (numRemoved) { + // Tell the widget we removed the options + nsISelectControlFrame* selectFrame = GetSelectFrame(); + if (selectFrame) { + nsCOMPtr presContext; + GetPresContext(this, getter_AddRefs(presContext)); + for (int i=aListIndex;iRemoveOption(presContext, i); + } + } + + // Fix the selected index + if (aListIndex <= mSelectedIndex) { + if (mSelectedIndex < (aListIndex+numRemoved)) { + // aListIndex <= mSelectedIndex < aListIndex+numRemoved + // Find a new selected index if it was one of the ones removed. + FindSelectedIndex(aListIndex); + } else { + // Shift the selected index if something in front of it was removed + // aListIndex+numRemoved <= mSelectedIndex + mSelectedIndex -= numRemoved; + } + } + + // Select something in case we removed the selected option on a + // single select + CheckSelectSomething(); + } + + return NS_OK; +} + +// If the document is such that recursing over these options gets us +// deeper than four levels, there is something terribly wrong with the +// world. +nsresult +nsHTMLSelectElement::InsertOptionsIntoListRecurse(nsIContent* aOptions, + PRInt32* aInsertIndex, + PRInt32 aLevel) +{ + // We *assume* here that someone's brain has not gone horribly + // wrong by putting