diff --git a/mozilla/content/html/content/src/nsHTMLOptGroupElement.cpp b/mozilla/content/html/content/src/nsHTMLOptGroupElement.cpp
index 2fc65ec37d2..dd4ec6c64d0 100644
--- a/mozilla/content/html/content/src/nsHTMLOptGroupElement.cpp
+++ b/mozilla/content/html/content/src/nsHTMLOptGroupElement.cpp
@@ -48,6 +48,7 @@
#include "nsISelectElement.h"
#include "nsIDOMHTMLSelectElement.h"
#include "nsEventDispatcher.h"
+#include "nsHTMLSelectElement.h"
/**
* The implementation of <optgroup>
@@ -174,23 +175,23 @@ nsHTMLOptGroupElement::InsertChildAt(nsIContent* aKid,
PRUint32 aIndex,
PRBool aNotify)
{
- nsCOMPtr sel = do_QueryInterface(GetSelect());
- if (sel) {
- sel->WillAddOptions(aKid, this, aIndex);
+ nsSafeOptionListMutation safeMutation(GetSelect(), this, aKid, aIndex);
+ nsresult rv = nsGenericHTMLElement::InsertChildAt(aKid, aIndex, aNotify);
+ if (NS_FAILED(rv)) {
+ safeMutation.MutationFailed();
}
-
- return nsGenericHTMLElement::InsertChildAt(aKid, aIndex, aNotify);
+ return rv;
}
nsresult
nsHTMLOptGroupElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
{
- nsCOMPtr sel = do_QueryInterface(GetSelect());
- if (sel) {
- sel->WillRemoveOptions(this, aIndex);
+ nsSafeOptionListMutation safeMutation(GetSelect(), this, nsnull, aIndex);
+ nsresult rv = nsGenericHTMLElement::RemoveChildAt(aIndex, aNotify);
+ if (NS_FAILED(rv)) {
+ safeMutation.MutationFailed();
}
-
- return nsGenericHTMLElement::RemoveChildAt(aIndex, aNotify);
+ return rv;
}
PRInt32
diff --git a/mozilla/content/html/content/src/nsHTMLSelectElement.cpp b/mozilla/content/html/content/src/nsHTMLSelectElement.cpp
index 5c56f1f9cc6..1645757cbf8 100644
--- a/mozilla/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/mozilla/content/html/content/src/nsHTMLSelectElement.cpp
@@ -36,15 +36,10 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
-#include "nsCOMPtr.h"
-#include "nsCOMArray.h"
-#include "nsIDOMHTMLSelectElement.h"
-#include "nsIDOMNSHTMLSelectElement.h"
-#include "nsIDOMNSXBLFormControl.h"
-#include "nsIDOMHTMLFormElement.h"
+
+#include "nsHTMLSelectElement.h"
#include "nsIDOMEventTarget.h"
#include "nsContentCreatorFunctions.h"
-#include "nsGenericHTMLElement.h"
#include "nsGkAtoms.h"
#include "nsStyleConsts.h"
#include "nsPresContext.h"
@@ -52,28 +47,16 @@
#include "nsMappedAttributes.h"
#include "nsIForm.h"
#include "nsIFormSubmission.h"
-#include "nsIDOMHTMLCollection.h"
-#include "nsIDOMHTMLOptionElement.h"
+
#include "nsIDOMHTMLOptGroupElement.h"
#include "nsIOptionElement.h"
#include "nsIEventStateManager.h"
-#include "nsISelectElement.h"
-#include "nsISelectControlFrame.h"
-#include "nsIDOMHTMLOptionsCollection.h"
-#include "nsIDOMNSHTMLOptionCollectn.h"
#include "nsGUIEvent.h"
#include "nsIPrivateDOMEvent.h"
#include "nsIBoxObject.h"
#include "nsIDOMNSDocument.h"
#include "nsIDOMDocumentEvent.h"
-// PresState
-#include "nsXPCOM.h"
-#include "nsPresState.h"
-#include "nsIComponentManager.h"
-#include "nsCheapSets.h"
-#include "nsLayoutErrors.h"
-
// Notify/query select frame for selectedIndex
#include "nsIDocument.h"
#include "nsIPresShell.h"
@@ -86,405 +69,58 @@
#include "nsRuleData.h"
#include "nsEventDispatcher.h"
-class nsHTMLSelectElement;
-
-/**
- * The collection of options in the select (what you get back when you do
- * select.options in DOM)
- */
-class nsHTMLOptionCollection: public nsIDOMHTMLOptionsCollection,
- public nsIDOMNSHTMLOptionCollection,
- public nsIDOMHTMLCollection
-{
-public:
- nsHTMLOptionCollection(nsHTMLSelectElement* aSelect);
- virtual ~nsHTMLOptionCollection();
-
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-
- // nsIDOMHTMLOptionsCollection interface
- NS_DECL_NSIDOMHTMLOPTIONSCOLLECTION
-
- // nsIDOMNSHTMLOptionCollection interface
- NS_DECL_NSIDOMNSHTMLOPTIONCOLLECTION
-
- // nsIDOMHTMLCollection interface, all its methods are defined in
- // nsIDOMHTMLOptionsCollection
-
- NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHTMLOptionCollection,
- nsIDOMNSHTMLOptionCollection)
-
- // Helpers for nsHTMLSelectElement
- /**
- * Insert an option
- * @param aOption the option to insert
- * @param aIndex the index to insert at
- */
- PRBool InsertOptionAt(nsIDOMHTMLOptionElement* aOption, PRInt32 aIndex)
- {
- return mElements.InsertObjectAt(aOption, aIndex);
- }
-
- /**
- * Remove an option
- * @param aIndex the index of the option to remove
- */
- void RemoveOptionAt(PRInt32 aIndex)
- {
- mElements.RemoveObjectAt(aIndex);
- }
-
- /**
- * Get the option at the index
- * @param aIndex the index
- * @param aReturn the option returned [OUT]
- */
- nsIDOMHTMLOptionElement *ItemAsOption(PRInt32 aIndex)
- {
- return mElements.SafeObjectAt(aIndex);
- }
-
- /**
- * Clears out all options
- */
- void Clear()
- {
- mElements.Clear();
- }
-
- /**
- * Append an option to end of array
- */
- PRBool AppendOption(nsIDOMHTMLOptionElement* aOption)
- {
- return mElements.AppendObject(aOption);
- }
-
- /**
- * Drop the reference to the select. Called during select destruction.
- */
- void DropReference();
-
- /**
- * See nsISelectElement.idl for documentation on this method
- */
- nsresult GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
- PRInt32 aStartIndex, PRBool aForward,
- PRInt32* aIndex);
-
-private:
- /** The list of options (holds strong references) */
- nsCOMArray mElements;
- /** The select element that contains this array */
- nsHTMLSelectElement* mSelect;
-};
-
-
-/**
- * The restore state used by select
- */
-class nsSelectState : public nsISupports {
-public:
- nsSelectState()
- {
- }
- virtual ~nsSelectState()
- {
- }
-
- NS_DECL_ISUPPORTS
-
- void PutOption(PRInt32 aIndex, const nsAString& aValue)
- {
- // If the option is empty, store the index. If not, store the value.
- if (aValue.IsEmpty()) {
- mIndices.Put(aIndex);
- } else {
- mValues.Put(aValue);
- }
- }
-
- PRBool ContainsOption(PRInt32 aIndex, const nsAString& aValue)
- {
- return mValues.Contains(aValue) || mIndices.Contains(aIndex);
- }
-
-private:
- nsCheapStringSet mValues;
- nsCheapInt32Set mIndices;
-};
-
NS_IMPL_ISUPPORTS0(nsSelectState)
+//----------------------------------------------------------------------
+//
+// nsSafeOptionListMutation
+//
-/**
- * Implementation of <select>
- */
-class nsHTMLSelectElement : public nsGenericHTMLFormElement,
- public nsIDOMHTMLSelectElement,
- public nsIDOMNSHTMLSelectElement,
- public nsIDOMNSXBLFormControl,
- public nsISelectElement
+nsSafeOptionListMutation::nsSafeOptionListMutation(nsIContent* aSelect,
+ nsIContent* aParent,
+ nsIContent* aKid,
+ PRUint32 aIndex)
+ : mSelect(do_QueryInterface(aSelect)), mTopLevelMutation(PR_FALSE),
+ mNeedsRebuild(PR_FALSE)
{
-public:
- nsHTMLSelectElement(nsINodeInfo *aNodeInfo, PRBool aFromParser = PR_FALSE);
- virtual ~nsHTMLSelectElement();
-
- // nsISupports
- NS_DECL_ISUPPORTS_INHERITED
-
- // nsIDOMNode
- NS_FORWARD_NSIDOMNODE(nsGenericHTMLFormElement::)
-
- // nsIDOMElement
- NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLFormElement::)
-
- // nsIDOMHTMLElement
- NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLFormElement::)
-
- // nsIDOMHTMLSelectElement
- NS_DECL_NSIDOMHTMLSELECTELEMENT
-
- // nsIDOMNSHTMLSelectElement
- NS_DECL_NSIDOMNSHTMLSELECTELEMENT
-
- // nsIDOMNSXBLFormControl
- NS_DECL_NSIDOMNSXBLFORMCONTROL
-
- // nsIContent
- virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
-
- virtual void SetFocus(nsPresContext* aPresContext);
- virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
- virtual nsresult InsertChildAt(nsIContent* aKid, PRUint32 aIndex,
- PRBool aNotify);
- virtual nsresult RemoveChildAt(PRUint32 aIndex, PRBool aNotify);
-
- // Overriden nsIFormControl methods
- NS_IMETHOD_(PRInt32) GetType() const { return NS_FORM_SELECT; }
- NS_IMETHOD Reset();
- NS_IMETHOD SubmitNamesValues(nsIFormSubmission* aFormSubmission,
- nsIContent* aSubmitElement);
- NS_IMETHOD SaveState();
- virtual PRBool RestoreState(nsPresState* aState);
-
- // nsISelectElement
- NS_DECL_NSISELECTELEMENT
-
- /**
- * Called when an attribute is about to be changed
- */
- virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
- const nsAString* aValue, PRBool aNotify);
- virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
- PRBool aNotify);
-
- virtual nsresult DoneAddingChildren(PRBool aHaveNotified);
- virtual PRBool IsDoneAddingChildren();
-
- virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
- nsIAtom* aAttribute,
- const nsAString& aValue,
- nsAttrValue& aResult);
- virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
- virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
- PRInt32 aModType) const;
- NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
-
- virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
-
- NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLSelectElement,
- nsGenericHTMLFormElement)
-
-protected:
- // Helper Methods
- /**
- * Check whether the option specified by the index is selected
- * @param aIndex the index
- * @return whether the option at the index is selected
- */
- PRBool IsOptionSelectedByIndex(PRInt32 aIndex);
- /**
- * Starting with (and including) aStartIndex, find the first selected index
- * and set mSelectedIndex to it.
- * @param aStartIndex the index to start with
- */
- void FindSelectedIndex(PRInt32 aStartIndex);
- /**
- * Select some option if possible (generally the first non-disabled option).
- * @return true if something was selected, false otherwise
- */
- PRBool SelectSomething();
- /**
- * Call SelectSomething(), but only if nothing is selected
- * @see SelectSomething()
- * @return true if something was selected, false otherwise
- */
- PRBool CheckSelectSomething();
- /**
- * Called to trigger notifications of frames and fixing selected index
- *
- * @param aSelectFrame the frame for this content (could be null)
- * @param aPresContext the current pres context
- * @param aIndex the index that was selected or deselected
- * @param aSelected whether the index was selected or deselected
- * @param aNotify whether to notify the style system and such
- */
- void OnOptionSelected(nsISelectControlFrame* aSelectFrame,
- nsPresContext* aPresContext,
- PRInt32 aIndex,
- PRBool aSelected,
- PRBool aNotify);
- /**
- * Restore state to a particular state string (representing the options)
- * @param aNewSelected the state string to restore to
- */
- void RestoreStateTo(nsSelectState* 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
- /**
- * Insert option(s) into the options[] array and perform notifications
- * @param aOptions the option or optgroup being added
- * @param aListIndex the index to start adding options into the list at
- * @param aDepth the depth of aOptions (1=direct child of select ...)
- */
- nsresult InsertOptionsIntoList(nsIContent* aOptions,
- PRInt32 aListIndex,
- PRInt32 aDepth);
- /**
- * Remove option(s) from the options[] array
- * @param aOptions the option or optgroup being added
- * @param aListIndex the index to start removing options from the list at
- * @param aDepth the depth of aOptions (1=direct child of select ...)
- */
- nsresult RemoveOptionsFromList(nsIContent* aOptions,
- PRInt32 aListIndex,
- PRInt32 aDepth);
- /**
- * Insert option(s) into the options[] array (called by InsertOptionsIntoList)
- * @param aOptions the option or optgroup being added
- * @param aInsertIndex the index to start adding options into the list at
- * @param aDepth the depth of aOptions (1=direct child of select ...)
- */
- nsresult InsertOptionsIntoListRecurse(nsIContent* aOptions,
- PRInt32* aInsertIndex,
- PRInt32 aDepth);
- /**
- * Remove option(s) from the options[] array (called by RemoveOptionsFromList)
- * @param aOptions the option or optgroup being added
- * @param aListIndex the index to start removing options from the list at
- * @param aNumRemoved the number removed so far [OUT]
- * @param aDepth the depth of aOptions (1=direct child of select ...)
- */
- nsresult RemoveOptionsFromListRecurse(nsIContent* aOptions,
- PRInt32 aRemoveIndex,
- PRInt32* aNumRemoved,
- PRInt32 aDepth);
- /**
- * Find out how deep this content is from the select (1=direct child)
- * @param aContent the content to check
- * @return the depth
- */
- PRInt32 GetContentDepth(nsIContent* aContent);
- /**
- * Get the index of the first option at, under or following the content in
- * the select, or length of options[] if none are found
- * @param aOptions the content
- * @return the index of the first option
- */
- PRInt32 GetOptionIndexAt(nsIContent* aOptions);
- /**
- * Get the next option following the content in question (not at or under)
- * (this could include siblings of the current content or siblings of the
- * parent or children of siblings of the parent).
- * @param aOptions the content
- * @return the index of the next option after the content
- */
- PRInt32 GetOptionIndexAfter(nsIContent* aOptions);
- /**
- * Get the first option index at or under the content in question.
- * @param aOptions the content
- * @return the index of the first option at or under the content
- */
- PRInt32 GetFirstOptionIndex(nsIContent* aOptions);
- /**
- * Get the first option index under the content in question, within the
- * range specified.
- * @param aOptions the content
- * @param aStartIndex the first child to look at
- * @param aEndIndex the child *after* the last child to look at
- * @return the index of the first option at or under the content
- */
- PRInt32 GetFirstChildOptionIndex(nsIContent* aOptions,
- PRInt32 aStartIndex,
- PRInt32 aEndIndex);
-
- /**
- * Get the frame as an nsISelectControlFrame (MAY RETURN NULL)
- * @return the select frame, or null
- */
- nsISelectControlFrame *GetSelectFrame();
-
- /**
- * Helper method for dispatching custom DOM events to our anonymous subcontent
- * (for XBL form controls)
- * @param aName the name of the event to dispatch
- */
- void DispatchDOMEvent(const nsAString& aName);
-
- /**
- * Is this a combobox?
- */
- PRBool IsCombobox() {
- PRBool isMultiple = PR_TRUE;
- PRInt32 size = 1;
- GetSize(&size);
- GetMultiple(&isMultiple);
- return !isMultiple && size <= 1;
+ nsHTMLSelectElement* select = static_cast(mSelect.get());
+ if (select) {
+ mTopLevelMutation = !select->mMutating;
+ if (mTopLevelMutation) {
+ select->mMutating = PR_TRUE;
+ } else {
+ // This is very unfortunate, but to handle mutation events properly,
+ // option list must be up-to-date before inserting or removing options.
+ // Fortunately this is called only if mutation event listener
+ // adds or removes options.
+ select->RebuildOptionsArray();
+ }
+ nsresult rv;
+ if (aKid) {
+ rv = mSelect->WillAddOptions(aKid, aParent, aIndex);
+ } else {
+ rv = mSelect->WillRemoveOptions(aParent, aIndex);
+ }
+ mNeedsRebuild = NS_FAILED(rv);
}
+}
- /**
- * Helper method for dispatching ContentReset notifications to list
- * and combo box frames.
- */
- void DispatchContentReset();
-
- /**
- * Rebuilds the options array from scratch as a fallback in error cases.
- */
- void RebuildOptionsArray();
-
+nsSafeOptionListMutation::~nsSafeOptionListMutation()
+{
+ if (mSelect) {
+ nsHTMLSelectElement* select =
+ static_cast(mSelect.get());
+ if (mNeedsRebuild || (mTopLevelMutation && mGuard.Mutated(1))) {
+ select->RebuildOptionsArray();
+ }
+ if (mTopLevelMutation) {
+ select->mMutating = PR_FALSE;
+ }
#ifdef DEBUG
- void VerifyOptionsArray();
+ select->VerifyOptionsArray();
#endif
-
- /** The options[] array */
- nsRefPtr mOptions;
- /** false if the parser is in the middle of adding children. */
- PRPackedBool mIsDoneAddingChildren;
- /** true if our disabled state has changed from the default **/
- PRPackedBool mDisabledChanged;
- /** The number of non-options as children of the select */
- PRUint32 mNonOptionChildren;
- /** The number of optgroups anywhere under the select */
- PRUint32 mOptGroupCount;
- /**
- * The current selected index for selectedIndex (will be the first selected
- * index if multiple are selected)
- */
- PRInt32 mSelectedIndex;
- /**
- * The temporary restore state in case we try to restore before parser is
- * done adding options
- */
- nsRefPtr mRestoreState;
-};
-
+ }
+}
//----------------------------------------------------------------------
//
@@ -502,6 +138,7 @@ nsHTMLSelectElement::nsHTMLSelectElement(nsINodeInfo *aNodeInfo,
mOptions(new nsHTMLOptionCollection(this)),
mIsDoneAddingChildren(!aFromParser),
mDisabledChanged(PR_FALSE),
+ mMutating(PR_FALSE),
mNonOptionChildren(0),
mOptGroupCount(0),
mSelectedIndex(-1)
@@ -561,39 +198,23 @@ nsHTMLSelectElement::InsertChildAt(nsIContent* aKid,
PRUint32 aIndex,
PRBool aNotify)
{
- nsresult rv = WillAddOptions(aKid, this, aIndex);
- PRBool rebuild = NS_FAILED(rv);
-
- rv = nsGenericHTMLFormElement::InsertChildAt(aKid, aIndex, aNotify);
- if (rebuild || NS_FAILED(rv)) {
- RebuildOptionsArray();
- return rv;
+ nsSafeOptionListMutation safeMutation(this, this, aKid, aIndex);
+ nsresult rv = nsGenericHTMLFormElement::InsertChildAt(aKid, aIndex, aNotify);
+ if (NS_FAILED(rv)) {
+ safeMutation.MutationFailed();
}
-
-#ifdef DEBUG
- VerifyOptionsArray();
-#endif
-
- return NS_OK;
+ return rv;
}
nsresult
nsHTMLSelectElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
{
- nsresult rv = WillRemoveOptions(this, aIndex);
- PRBool rebuild = NS_FAILED(rv);
-
- rv = nsGenericHTMLFormElement::RemoveChildAt(aIndex, aNotify);
- if (rebuild || NS_FAILED(rv)) {
- RebuildOptionsArray();
- return rv;
+ nsSafeOptionListMutation safeMutation(this, this, nsnull, aIndex);
+ nsresult rv = nsGenericHTMLFormElement::RemoveChildAt(aIndex, aNotify);
+ if (NS_FAILED(rv)) {
+ safeMutation.MutationFailed();
}
-
-#ifdef DEBUG
- VerifyOptionsArray();
-#endif
-
- return NS_OK;
+ return rv;
}
diff --git a/mozilla/content/html/content/src/nsHTMLSelectElement.h b/mozilla/content/html/content/src/nsHTMLSelectElement.h
new file mode 100644
index 00000000000..b7b431007a8
--- /dev/null
+++ b/mozilla/content/html/content/src/nsHTMLSelectElement.h
@@ -0,0 +1,496 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla 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/MPL/
+ *
+ * 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
+ * Mats Palmgren
+ * Olli Pettay
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 MPL, 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 MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#ifndef nsHTMLSelectElement_h___
+#define nsHTMLSelectElement_h___
+
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsGenericHTMLElement.h"
+#include "nsISelectElement.h"
+#include "nsIDOMHTMLSelectElement.h"
+#include "nsIDOMNSHTMLSelectElement.h"
+#include "nsIDOMNSXBLFormControl.h"
+#include "nsIDOMHTMLFormElement.h"
+#include "nsIDOMHTMLOptionElement.h"
+#include "nsIDOMHTMLCollection.h"
+#include "nsIDOMHTMLOptionsCollection.h"
+#include "nsIDOMNSHTMLOptionCollectn.h"
+#include "nsISelectControlFrame.h"
+
+// PresState
+#include "nsXPCOM.h"
+#include "nsPresState.h"
+#include "nsIComponentManager.h"
+#include "nsCheapSets.h"
+#include "nsLayoutErrors.h"
+
+
+class nsHTMLSelectElement;
+
+/**
+ * The collection of options in the select (what you get back when you do
+ * select.options in DOM)
+ */
+class nsHTMLOptionCollection: public nsIDOMHTMLOptionsCollection,
+ public nsIDOMNSHTMLOptionCollection,
+ public nsIDOMHTMLCollection
+{
+public:
+ nsHTMLOptionCollection(nsHTMLSelectElement* aSelect);
+ virtual ~nsHTMLOptionCollection();
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
+ // nsIDOMHTMLOptionsCollection interface
+ NS_DECL_NSIDOMHTMLOPTIONSCOLLECTION
+
+ // nsIDOMNSHTMLOptionCollection interface
+ NS_DECL_NSIDOMNSHTMLOPTIONCOLLECTION
+
+ // nsIDOMHTMLCollection interface, all its methods are defined in
+ // nsIDOMHTMLOptionsCollection
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHTMLOptionCollection,
+ nsIDOMNSHTMLOptionCollection)
+
+ // Helpers for nsHTMLSelectElement
+ /**
+ * Insert an option
+ * @param aOption the option to insert
+ * @param aIndex the index to insert at
+ */
+ PRBool InsertOptionAt(nsIDOMHTMLOptionElement* aOption, PRInt32 aIndex)
+ {
+ return mElements.InsertObjectAt(aOption, aIndex);
+ }
+
+ /**
+ * Remove an option
+ * @param aIndex the index of the option to remove
+ */
+ void RemoveOptionAt(PRInt32 aIndex)
+ {
+ mElements.RemoveObjectAt(aIndex);
+ }
+
+ /**
+ * Get the option at the index
+ * @param aIndex the index
+ * @param aReturn the option returned [OUT]
+ */
+ nsIDOMHTMLOptionElement *ItemAsOption(PRInt32 aIndex)
+ {
+ return mElements.SafeObjectAt(aIndex);
+ }
+
+ /**
+ * Clears out all options
+ */
+ void Clear()
+ {
+ mElements.Clear();
+ }
+
+ /**
+ * Append an option to end of array
+ */
+ PRBool AppendOption(nsIDOMHTMLOptionElement* aOption)
+ {
+ return mElements.AppendObject(aOption);
+ }
+
+ /**
+ * Drop the reference to the select. Called during select destruction.
+ */
+ void DropReference();
+
+ /**
+ * See nsISelectElement.idl for documentation on this method
+ */
+ nsresult GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
+ PRInt32 aStartIndex, PRBool aForward,
+ PRInt32* aIndex);
+
+private:
+ /** The list of options (holds strong references) */
+ nsCOMArray mElements;
+ /** The select element that contains this array */
+ nsHTMLSelectElement* mSelect;
+};
+
+
+/**
+ * The restore state used by select
+ */
+class nsSelectState : public nsISupports {
+public:
+ nsSelectState()
+ {
+ }
+ virtual ~nsSelectState()
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+
+ void PutOption(PRInt32 aIndex, const nsAString& aValue)
+ {
+ // If the option is empty, store the index. If not, store the value.
+ if (aValue.IsEmpty()) {
+ mIndices.Put(aIndex);
+ } else {
+ mValues.Put(aValue);
+ }
+ }
+
+ PRBool ContainsOption(PRInt32 aIndex, const nsAString& aValue)
+ {
+ return mValues.Contains(aValue) || mIndices.Contains(aIndex);
+ }
+
+private:
+ nsCheapStringSet mValues;
+ nsCheapInt32Set mIndices;
+};
+
+class nsSafeOptionListMutation
+{
+public:
+ /**
+ * @param aSelect The select element which option list is being mutated.
+ * Can be null.
+ * @param aParent The content object which is being mutated.
+ * @param aKid If not null, a new child element is being inserted to
+ * aParent. Otherwise a child element will be removed.
+ * @param aIndex The index of the content object in the parent.
+ */
+ nsSafeOptionListMutation(nsIContent* aSelect, nsIContent* aParent,
+ nsIContent* aKid, PRUint32 aIndex);
+ ~nsSafeOptionListMutation();
+ void MutationFailed() { mNeedsRebuild = PR_TRUE; }
+private:
+ static void* operator new(size_t) CPP_THROW_NEW { return 0; }
+ static void operator delete(void*, size_t) {}
+ /** The select element which option list is being mutated. */
+ nsCOMPtr mSelect;
+ /** PR_TRUE if the current mutation is the first one in the stack. */
+ PRBool mTopLevelMutation;
+ /** PR_TRUE if it is known that the option list must be recreated. */
+ PRBool mNeedsRebuild;
+ /** Option list must be recreated if more than one mutation is detected. */
+ nsMutationGuard mGuard;
+};
+
+
+/**
+ * Implementation of <select>
+ */
+class nsHTMLSelectElement : public nsGenericHTMLFormElement,
+ public nsIDOMHTMLSelectElement,
+ public nsIDOMNSHTMLSelectElement,
+ public nsIDOMNSXBLFormControl,
+ public nsISelectElement
+{
+public:
+ nsHTMLSelectElement(nsINodeInfo *aNodeInfo, PRBool aFromParser = PR_FALSE);
+ virtual ~nsHTMLSelectElement();
+
+ // nsISupports
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // nsIDOMNode
+ NS_FORWARD_NSIDOMNODE(nsGenericHTMLFormElement::)
+
+ // nsIDOMElement
+ NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLFormElement::)
+
+ // nsIDOMHTMLElement
+ NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLFormElement::)
+
+ // nsIDOMHTMLSelectElement
+ NS_DECL_NSIDOMHTMLSELECTELEMENT
+
+ // nsIDOMNSHTMLSelectElement
+ NS_DECL_NSIDOMNSHTMLSELECTELEMENT
+
+ // nsIDOMNSXBLFormControl
+ NS_DECL_NSIDOMNSXBLFORMCONTROL
+
+ // nsIContent
+ virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
+
+ virtual void SetFocus(nsPresContext* aPresContext);
+ virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
+ virtual nsresult InsertChildAt(nsIContent* aKid, PRUint32 aIndex,
+ PRBool aNotify);
+ virtual nsresult RemoveChildAt(PRUint32 aIndex, PRBool aNotify);
+
+ // Overriden nsIFormControl methods
+ NS_IMETHOD_(PRInt32) GetType() const { return NS_FORM_SELECT; }
+ NS_IMETHOD Reset();
+ NS_IMETHOD SubmitNamesValues(nsIFormSubmission* aFormSubmission,
+ nsIContent* aSubmitElement);
+ NS_IMETHOD SaveState();
+ virtual PRBool RestoreState(nsPresState* aState);
+
+ // nsISelectElement
+ NS_DECL_NSISELECTELEMENT
+
+ /**
+ * Called when an attribute is about to be changed
+ */
+ virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+ const nsAString* aValue, PRBool aNotify);
+ virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
+ PRBool aNotify);
+
+ virtual nsresult DoneAddingChildren(PRBool aHaveNotified);
+ virtual PRBool IsDoneAddingChildren();
+
+ virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
+ nsIAtom* aAttribute,
+ const nsAString& aValue,
+ nsAttrValue& aResult);
+ virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
+ virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
+ PRInt32 aModType) const;
+ NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
+
+ virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLSelectElement,
+ nsGenericHTMLFormElement)
+
+protected:
+ friend class nsSafeOptionListMutation;
+
+ // Helper Methods
+ /**
+ * Check whether the option specified by the index is selected
+ * @param aIndex the index
+ * @return whether the option at the index is selected
+ */
+ PRBool IsOptionSelectedByIndex(PRInt32 aIndex);
+ /**
+ * Starting with (and including) aStartIndex, find the first selected index
+ * and set mSelectedIndex to it.
+ * @param aStartIndex the index to start with
+ */
+ void FindSelectedIndex(PRInt32 aStartIndex);
+ /**
+ * Select some option if possible (generally the first non-disabled option).
+ * @return true if something was selected, false otherwise
+ */
+ PRBool SelectSomething();
+ /**
+ * Call SelectSomething(), but only if nothing is selected
+ * @see SelectSomething()
+ * @return true if something was selected, false otherwise
+ */
+ PRBool CheckSelectSomething();
+ /**
+ * Called to trigger notifications of frames and fixing selected index
+ *
+ * @param aSelectFrame the frame for this content (could be null)
+ * @param aPresContext the current pres context
+ * @param aIndex the index that was selected or deselected
+ * @param aSelected whether the index was selected or deselected
+ * @param aNotify whether to notify the style system and such
+ */
+ void OnOptionSelected(nsISelectControlFrame* aSelectFrame,
+ nsPresContext* aPresContext,
+ PRInt32 aIndex,
+ PRBool aSelected,
+ PRBool aNotify);
+ /**
+ * Restore state to a particular state string (representing the options)
+ * @param aNewSelected the state string to restore to
+ */
+ void RestoreStateTo(nsSelectState* 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
+ /**
+ * Insert option(s) into the options[] array and perform notifications
+ * @param aOptions the option or optgroup being added
+ * @param aListIndex the index to start adding options into the list at
+ * @param aDepth the depth of aOptions (1=direct child of select ...)
+ */
+ nsresult InsertOptionsIntoList(nsIContent* aOptions,
+ PRInt32 aListIndex,
+ PRInt32 aDepth);
+ /**
+ * Remove option(s) from the options[] array
+ * @param aOptions the option or optgroup being added
+ * @param aListIndex the index to start removing options from the list at
+ * @param aDepth the depth of aOptions (1=direct child of select ...)
+ */
+ nsresult RemoveOptionsFromList(nsIContent* aOptions,
+ PRInt32 aListIndex,
+ PRInt32 aDepth);
+ /**
+ * Insert option(s) into the options[] array (called by InsertOptionsIntoList)
+ * @param aOptions the option or optgroup being added
+ * @param aInsertIndex the index to start adding options into the list at
+ * @param aDepth the depth of aOptions (1=direct child of select ...)
+ */
+ nsresult InsertOptionsIntoListRecurse(nsIContent* aOptions,
+ PRInt32* aInsertIndex,
+ PRInt32 aDepth);
+ /**
+ * Remove option(s) from the options[] array (called by RemoveOptionsFromList)
+ * @param aOptions the option or optgroup being added
+ * @param aListIndex the index to start removing options from the list at
+ * @param aNumRemoved the number removed so far [OUT]
+ * @param aDepth the depth of aOptions (1=direct child of select ...)
+ */
+ nsresult RemoveOptionsFromListRecurse(nsIContent* aOptions,
+ PRInt32 aRemoveIndex,
+ PRInt32* aNumRemoved,
+ PRInt32 aDepth);
+ /**
+ * Find out how deep this content is from the select (1=direct child)
+ * @param aContent the content to check
+ * @return the depth
+ */
+ PRInt32 GetContentDepth(nsIContent* aContent);
+ /**
+ * Get the index of the first option at, under or following the content in
+ * the select, or length of options[] if none are found
+ * @param aOptions the content
+ * @return the index of the first option
+ */
+ PRInt32 GetOptionIndexAt(nsIContent* aOptions);
+ /**
+ * Get the next option following the content in question (not at or under)
+ * (this could include siblings of the current content or siblings of the
+ * parent or children of siblings of the parent).
+ * @param aOptions the content
+ * @return the index of the next option after the content
+ */
+ PRInt32 GetOptionIndexAfter(nsIContent* aOptions);
+ /**
+ * Get the first option index at or under the content in question.
+ * @param aOptions the content
+ * @return the index of the first option at or under the content
+ */
+ PRInt32 GetFirstOptionIndex(nsIContent* aOptions);
+ /**
+ * Get the first option index under the content in question, within the
+ * range specified.
+ * @param aOptions the content
+ * @param aStartIndex the first child to look at
+ * @param aEndIndex the child *after* the last child to look at
+ * @return the index of the first option at or under the content
+ */
+ PRInt32 GetFirstChildOptionIndex(nsIContent* aOptions,
+ PRInt32 aStartIndex,
+ PRInt32 aEndIndex);
+
+ /**
+ * Get the frame as an nsISelectControlFrame (MAY RETURN NULL)
+ * @return the select frame, or null
+ */
+ nsISelectControlFrame *GetSelectFrame();
+
+ /**
+ * Helper method for dispatching custom DOM events to our anonymous subcontent
+ * (for XBL form controls)
+ * @param aName the name of the event to dispatch
+ */
+ void DispatchDOMEvent(const nsAString& aName);
+
+ /**
+ * Is this a combobox?
+ */
+ PRBool IsCombobox() {
+ PRBool isMultiple = PR_TRUE;
+ PRInt32 size = 1;
+ GetSize(&size);
+ GetMultiple(&isMultiple);
+ return !isMultiple && size <= 1;
+ }
+
+ /**
+ * Helper method for dispatching ContentReset notifications to list
+ * and combo box frames.
+ */
+ void DispatchContentReset();
+
+ /**
+ * Rebuilds the options array from scratch as a fallback in error cases.
+ */
+ void RebuildOptionsArray();
+
+#ifdef DEBUG
+ void VerifyOptionsArray();
+#endif
+
+ /** The options[] array */
+ nsRefPtr mOptions;
+ /** false if the parser is in the middle of adding children. */
+ PRPackedBool mIsDoneAddingChildren;
+ /** true if our disabled state has changed from the default **/
+ PRPackedBool mDisabledChanged;
+ /** true if child nodes are being added or removed.
+ * Used by nsSafeOptionListMutation.
+ */
+ PRPackedBool mMutating;
+ /** The number of non-options as children of the select */
+ PRUint32 mNonOptionChildren;
+ /** The number of optgroups anywhere under the select */
+ PRUint32 mOptGroupCount;
+ /**
+ * The current selected index for selectedIndex (will be the first selected
+ * index if multiple are selected)
+ */
+ PRInt32 mSelectedIndex;
+ /**
+ * The temporary restore state in case we try to restore before parser is
+ * done adding options
+ */
+ nsRefPtr mRestoreState;
+};
+
+#endif