Bug 339501, r=bz, sr=sicking, a=1.9+

This commit is contained in:
Olli.Pettay%helsinki.fi 2007-08-28 07:09:32 +00:00
Родитель 44d62b28b7
Коммит 93dbdbf87f
3 изменённых файлов: 565 добавлений и 447 удалений

Просмотреть файл

@ -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<nsISelectElement> 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<nsISelectElement> 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

Просмотреть файл

@ -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<nsIDOMHTMLOptionElement> 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 &lt;select&gt;
*/
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<nsHTMLSelectElement*>(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<nsHTMLSelectElement*>(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<nsHTMLOptionCollection> 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<nsSelectState> 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;
}

Просмотреть файл

@ -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 <pp@ludusdesign.com>
* Mats Palmgren <mats.palmgren@bredband.net>
* Olli Pettay <Olli.Pettay@helsinki.fi>
*
* 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<nsIDOMHTMLOptionElement> 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<nsISelectElement> 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 &lt;select&gt;
*/
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<nsHTMLOptionCollection> 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<nsSelectState> mRestoreState;
};
#endif