зеркало из https://github.com/mozilla/pjs.git
Bug 339501, r=bz, sr=sicking, a=1.9+
This commit is contained in:
Родитель
44d62b28b7
Коммит
93dbdbf87f
|
@ -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 <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<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 <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<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
|
Загрузка…
Ссылка в новой задаче