LCOV - code coverage report
Current view: directory - content/html/content/src - nsHTMLSelectElement.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 927 3 0.3 %
Date: 2012-06-02 Functions: 132 2 1.5 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla Communicator client code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Pierre Phaneuf <pp@ludusdesign.com>
      24                 :  *   Mats Palmgren <mats.palmgren@bredband.net>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "mozilla/Util.h"
      41                 : 
      42                 : #include "nsHTMLSelectElement.h"
      43                 : 
      44                 : #include "nsHTMLOptionElement.h"
      45                 : #include "nsIDOMEventTarget.h"
      46                 : #include "nsContentCreatorFunctions.h"
      47                 : #include "nsGkAtoms.h"
      48                 : #include "nsStyleConsts.h"
      49                 : #include "nsLayoutUtils.h"
      50                 : #include "nsMappedAttributes.h"
      51                 : #include "nsIForm.h"
      52                 : #include "nsFormSubmission.h"
      53                 : #include "nsIFormProcessor.h"
      54                 : #include "nsContentCreatorFunctions.h"
      55                 : 
      56                 : #include "nsIDOMHTMLOptGroupElement.h"
      57                 : #include "nsEventStates.h"
      58                 : #include "nsGUIEvent.h"
      59                 : #include "nsIPrivateDOMEvent.h"
      60                 : 
      61                 : // Notify/query select frame for selectedIndex
      62                 : #include "nsIDocument.h"
      63                 : #include "nsIFormControlFrame.h"
      64                 : #include "nsIComboboxControlFrame.h"
      65                 : #include "nsIListControlFrame.h"
      66                 : #include "nsIFrame.h"
      67                 : 
      68                 : #include "nsDOMError.h"
      69                 : #include "nsServiceManagerUtils.h"
      70                 : #include "nsRuleData.h"
      71                 : #include "nsEventDispatcher.h"
      72                 : #include "mozilla/dom/Element.h"
      73                 : #include "mozAutoDocUpdate.h"
      74                 : #include "dombindings.h"
      75                 : 
      76                 : using namespace mozilla;
      77                 : using namespace mozilla::dom;
      78                 : 
      79               0 : NS_IMPL_ISUPPORTS1(nsSelectState, nsSelectState)
      80                 : NS_DEFINE_STATIC_IID_ACCESSOR(nsSelectState, NS_SELECT_STATE_IID)
      81                 : 
      82                 : //----------------------------------------------------------------------
      83                 : //
      84                 : // nsSafeOptionListMutation
      85                 : //
      86                 : 
      87               0 : nsSafeOptionListMutation::nsSafeOptionListMutation(nsIContent* aSelect,
      88                 :                                                    nsIContent* aParent,
      89                 :                                                    nsIContent* aKid,
      90                 :                                                    PRUint32 aIndex,
      91                 :                                                    bool aNotify)
      92                 :   : mSelect(nsHTMLSelectElement::FromContent(aSelect))
      93                 :   , mTopLevelMutation(false)
      94               0 :   , mNeedsRebuild(false)
      95                 : {
      96               0 :   if (mSelect) {
      97               0 :     mTopLevelMutation = !mSelect->mMutating;
      98               0 :     if (mTopLevelMutation) {
      99               0 :       mSelect->mMutating = true;
     100                 :     } else {
     101                 :       // This is very unfortunate, but to handle mutation events properly,
     102                 :       // option list must be up-to-date before inserting or removing options.
     103                 :       // Fortunately this is called only if mutation event listener
     104                 :       // adds or removes options.
     105               0 :       mSelect->RebuildOptionsArray(aNotify);
     106                 :     }
     107                 :     nsresult rv;
     108               0 :     if (aKid) {
     109               0 :       rv = mSelect->WillAddOptions(aKid, aParent, aIndex, aNotify);
     110                 :     } else {
     111               0 :       rv = mSelect->WillRemoveOptions(aParent, aIndex, aNotify);
     112                 :     }
     113               0 :     mNeedsRebuild = NS_FAILED(rv);
     114                 :   }
     115               0 : }
     116                 : 
     117               0 : nsSafeOptionListMutation::~nsSafeOptionListMutation()
     118                 : {
     119               0 :   if (mSelect) {
     120               0 :     if (mNeedsRebuild || (mTopLevelMutation && mGuard.Mutated(1))) {
     121               0 :       mSelect->RebuildOptionsArray(true);
     122                 :     }
     123               0 :     if (mTopLevelMutation) {
     124               0 :       mSelect->mMutating = false;
     125                 :     }
     126                 : #ifdef DEBUG
     127               0 :     mSelect->VerifyOptionsArray();
     128                 : #endif
     129                 :   }
     130               0 : }
     131                 : 
     132                 : //----------------------------------------------------------------------
     133                 : //
     134                 : // nsHTMLSelectElement
     135                 : //
     136                 : 
     137                 : // construction, destruction
     138                 : 
     139                 : 
     140               0 : NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Select)
     141                 : 
     142               0 : nsHTMLSelectElement::nsHTMLSelectElement(already_AddRefed<nsINodeInfo> aNodeInfo,
     143                 :                                          FromParser aFromParser)
     144                 :   : nsGenericHTMLFormElement(aNodeInfo),
     145               0 :     mOptions(new nsHTMLOptionCollection(this)),
     146               0 :     mIsDoneAddingChildren(!aFromParser),
     147                 :     mDisabledChanged(false),
     148                 :     mMutating(false),
     149               0 :     mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
     150                 :     mSelectionHasChanged(false),
     151                 :     mDefaultSelectionSet(false),
     152                 :     mCanShowInvalidUI(true),
     153                 :     mCanShowValidUI(true),
     154                 :     mNonOptionChildren(0),
     155                 :     mOptGroupCount(0),
     156               0 :     mSelectedIndex(-1)
     157                 : {
     158                 :   // DoneAddingChildren() will be called later if it's from the parser,
     159                 :   // otherwise it is
     160                 : 
     161                 :   // Set up our default state: enabled, optional, and valid.
     162                 :   AddStatesSilently(NS_EVENT_STATE_ENABLED |
     163                 :                     NS_EVENT_STATE_OPTIONAL |
     164               0 :                     NS_EVENT_STATE_VALID);
     165               0 : }
     166                 : 
     167               0 : nsHTMLSelectElement::~nsHTMLSelectElement()
     168                 : {
     169               0 :   mOptions->DropReference();
     170               0 : }
     171                 : 
     172                 : // ISupports
     173                 : 
     174            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLSelectElement)
     175               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLSelectElement,
     176                 :                                                   nsGenericHTMLFormElement)
     177               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mOptions,
     178                 :                                                        nsIDOMHTMLCollection)
     179               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     180                 : 
     181               0 : NS_IMPL_ADDREF_INHERITED(nsHTMLSelectElement, nsGenericElement)
     182               0 : NS_IMPL_RELEASE_INHERITED(nsHTMLSelectElement, nsGenericElement)
     183                 : 
     184                 : 
     185               0 : DOMCI_NODE_DATA(HTMLSelectElement, nsHTMLSelectElement)
     186                 : 
     187                 : // QueryInterface implementation for nsHTMLSelectElement
     188               0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLSelectElement)
     189               0 :   NS_HTML_CONTENT_INTERFACE_TABLE2(nsHTMLSelectElement,
     190                 :                                    nsIDOMHTMLSelectElement,
     191                 :                                    nsIConstraintValidation)
     192               0 :   NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLSelectElement,
     193                 :                                                nsGenericHTMLFormElement)
     194               0 : NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLSelectElement)
     195                 : 
     196                 : 
     197                 : // nsIDOMHTMLSelectElement
     198                 : 
     199                 : 
     200               0 : NS_IMPL_ELEMENT_CLONE(nsHTMLSelectElement)
     201                 : 
     202                 : // nsIConstraintValidation
     203               0 : NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(nsHTMLSelectElement)
     204                 : 
     205                 : NS_IMETHODIMP
     206               0 : nsHTMLSelectElement::SetCustomValidity(const nsAString& aError)
     207                 : {
     208               0 :   nsIConstraintValidation::SetCustomValidity(aError);
     209                 : 
     210               0 :   UpdateState(true);
     211                 : 
     212               0 :   return NS_OK;
     213                 : }
     214                 : 
     215                 : NS_IMETHODIMP
     216               0 : nsHTMLSelectElement::GetForm(nsIDOMHTMLFormElement** aForm)
     217                 : {
     218               0 :   return nsGenericHTMLFormElement::GetForm(aForm);
     219                 : }
     220                 : 
     221                 : nsresult
     222               0 : nsHTMLSelectElement::InsertChildAt(nsIContent* aKid,
     223                 :                                    PRUint32 aIndex,
     224                 :                                    bool aNotify)
     225                 : {
     226               0 :   nsSafeOptionListMutation safeMutation(this, this, aKid, aIndex, aNotify);
     227               0 :   nsresult rv = nsGenericHTMLFormElement::InsertChildAt(aKid, aIndex, aNotify);
     228               0 :   if (NS_FAILED(rv)) {
     229               0 :     safeMutation.MutationFailed();
     230                 :   }
     231               0 :   return rv;
     232                 : }
     233                 : 
     234                 : nsresult
     235               0 : nsHTMLSelectElement::RemoveChildAt(PRUint32 aIndex, bool aNotify)
     236                 : {
     237               0 :   nsSafeOptionListMutation safeMutation(this, this, nsnull, aIndex, aNotify);
     238               0 :   nsresult rv = nsGenericHTMLFormElement::RemoveChildAt(aIndex, aNotify);
     239               0 :   if (NS_FAILED(rv)) {
     240               0 :     safeMutation.MutationFailed();
     241                 :   }
     242               0 :   return rv;
     243                 : }
     244                 : 
     245                 : 
     246                 : // SelectElement methods
     247                 : 
     248                 : nsresult
     249               0 : nsHTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions,
     250                 :                                            PRInt32 aListIndex,
     251                 :                                            PRInt32 aDepth,
     252                 :                                            bool aNotify)
     253                 : {
     254               0 :   PRInt32 insertIndex = aListIndex;
     255               0 :   nsresult rv = InsertOptionsIntoListRecurse(aOptions, &insertIndex, aDepth);
     256               0 :   NS_ENSURE_SUCCESS(rv, rv);
     257                 : 
     258                 :   // Deal with the selected list
     259               0 :   if (insertIndex - aListIndex) {
     260                 :     // Fix the currently selected index
     261               0 :     if (aListIndex <= mSelectedIndex) {
     262               0 :       mSelectedIndex += (insertIndex - aListIndex);
     263               0 :       SetSelectionChanged(true, aNotify);
     264                 :     }
     265                 : 
     266                 :     // Get the frame stuff for notification. No need to flush here
     267                 :     // since if there's no frame for the select yet the select will
     268                 :     // get into the right state once it's created.
     269               0 :     nsISelectControlFrame* selectFrame = nsnull;
     270               0 :     nsWeakFrame weakSelectFrame;
     271               0 :     bool didGetFrame = false;
     272                 : 
     273                 :     // Actually select the options if the added options warrant it
     274               0 :     nsCOMPtr<nsIDOMNode> optionNode;
     275               0 :     nsCOMPtr<nsIDOMHTMLOptionElement> option;
     276               0 :     for (PRInt32 i = aListIndex; i < insertIndex; i++) {
     277                 :       // Notify the frame that the option is added
     278               0 :       if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
     279               0 :         selectFrame = GetSelectFrame();
     280               0 :         weakSelectFrame = do_QueryFrame(selectFrame);
     281               0 :         didGetFrame = true;
     282                 :       }
     283                 : 
     284               0 :       if (selectFrame) {
     285               0 :         selectFrame->AddOption(i);
     286                 :       }
     287                 : 
     288               0 :       Item(i, getter_AddRefs(optionNode));
     289               0 :       option = do_QueryInterface(optionNode);
     290               0 :       if (option) {
     291                 :         bool selected;
     292               0 :         option->GetSelected(&selected);
     293               0 :         if (selected) {
     294                 :           // Clear all other options
     295               0 :           if (!HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
     296               0 :             SetOptionsSelectedByIndex(i, i, true, true, true, true, nsnull);
     297                 :           }
     298                 : 
     299                 :           // This is sort of a hack ... we need to notify that the option was
     300                 :           // set and change selectedIndex even though we didn't really change
     301                 :           // its value.
     302               0 :           OnOptionSelected(selectFrame, i, true, false, false);
     303                 :         }
     304                 :       }
     305                 :     }
     306                 : 
     307               0 :     CheckSelectSomething(aNotify);
     308                 :   }
     309                 : 
     310               0 :   return NS_OK;
     311                 : }
     312                 : 
     313                 : nsresult
     314               0 : nsHTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
     315                 :                                            PRInt32 aListIndex,
     316                 :                                            PRInt32 aDepth,
     317                 :                                            bool aNotify)
     318                 : {
     319               0 :   PRInt32 numRemoved = 0;
     320                 :   nsresult rv = RemoveOptionsFromListRecurse(aOptions, aListIndex, &numRemoved,
     321               0 :                                              aDepth);
     322               0 :   NS_ENSURE_SUCCESS(rv, rv);
     323                 : 
     324               0 :   if (numRemoved) {
     325                 :     // Tell the widget we removed the options
     326               0 :     nsISelectControlFrame* selectFrame = GetSelectFrame();
     327               0 :     if (selectFrame) {
     328               0 :       nsAutoScriptBlocker scriptBlocker;
     329               0 :       for (PRInt32 i = aListIndex; i < aListIndex + numRemoved; ++i) {
     330               0 :         selectFrame->RemoveOption(i);
     331                 :       }
     332                 :     }
     333                 : 
     334                 :     // Fix the selected index
     335               0 :     if (aListIndex <= mSelectedIndex) {
     336               0 :       if (mSelectedIndex < (aListIndex+numRemoved)) {
     337                 :         // aListIndex <= mSelectedIndex < aListIndex+numRemoved
     338                 :         // Find a new selected index if it was one of the ones removed.
     339               0 :         FindSelectedIndex(aListIndex, aNotify);
     340                 :       } else {
     341                 :         // Shift the selected index if something in front of it was removed
     342                 :         // aListIndex+numRemoved <= mSelectedIndex
     343               0 :         mSelectedIndex -= numRemoved;
     344               0 :         SetSelectionChanged(true, aNotify);
     345                 :       }
     346                 :     }
     347                 : 
     348                 :     // Select something in case we removed the selected option on a
     349                 :     // single select
     350               0 :     if (!CheckSelectSomething(aNotify) && mSelectedIndex == -1) {
     351                 :       // Update the validity state in case of we've just removed the last
     352                 :       // option.
     353               0 :       UpdateValueMissingValidityState();
     354                 : 
     355               0 :       UpdateState(aNotify);
     356                 :     }
     357                 :   }
     358                 : 
     359               0 :   return NS_OK;
     360                 : }
     361                 : 
     362                 : // If the document is such that recursing over these options gets us
     363                 : // deeper than four levels, there is something terribly wrong with the
     364                 : // world.
     365                 : nsresult
     366               0 : nsHTMLSelectElement::InsertOptionsIntoListRecurse(nsIContent* aOptions,
     367                 :                                                   PRInt32* aInsertIndex,
     368                 :                                                   PRInt32 aDepth)
     369                 : {
     370                 :   // We *assume* here that someone's brain has not gone horribly
     371                 :   // wrong by putting <option> inside of <option>.  I'm sorry, I'm
     372                 :   // just not going to look for an option inside of an option.
     373                 :   // Sue me.
     374                 : 
     375               0 :   nsHTMLOptionElement *optElement = nsHTMLOptionElement::FromContent(aOptions);
     376               0 :   if (optElement) {
     377               0 :     nsresult rv = mOptions->InsertOptionAt(optElement, *aInsertIndex);
     378               0 :     NS_ENSURE_SUCCESS(rv, rv);
     379               0 :     (*aInsertIndex)++;
     380               0 :     return NS_OK;
     381                 :   }
     382                 : 
     383                 :   // If it's at the top level, then we just found out there are non-options
     384                 :   // at the top level, which will throw off the insert count
     385               0 :   if (aDepth == 0) {
     386               0 :     mNonOptionChildren++;
     387                 :   }
     388                 : 
     389                 :   // Recurse down into optgroups
     390               0 :   if (aOptions->IsHTML(nsGkAtoms::optgroup)) {
     391               0 :     mOptGroupCount++;
     392                 : 
     393               0 :     for (nsIContent* child = aOptions->GetFirstChild();
     394                 :          child;
     395               0 :          child = child->GetNextSibling()) {
     396                 :       nsresult rv = InsertOptionsIntoListRecurse(child,
     397               0 :                                                  aInsertIndex, aDepth + 1);
     398               0 :       NS_ENSURE_SUCCESS(rv, rv);
     399                 :     }
     400                 :   }
     401                 : 
     402               0 :   return NS_OK;
     403                 : }
     404                 : 
     405                 : // If the document is such that recursing over these options gets us deeper than
     406                 : // four levels, there is something terribly wrong with the world.
     407                 : nsresult
     408               0 : nsHTMLSelectElement::RemoveOptionsFromListRecurse(nsIContent* aOptions,
     409                 :                                                   PRInt32 aRemoveIndex,
     410                 :                                                   PRInt32* aNumRemoved,
     411                 :                                                   PRInt32 aDepth)
     412                 : {
     413                 :   // We *assume* here that someone's brain has not gone horribly
     414                 :   // wrong by putting <option> inside of <option>.  I'm sorry, I'm
     415                 :   // just not going to look for an option inside of an option.
     416                 :   // Sue me.
     417                 : 
     418               0 :   nsCOMPtr<nsIDOMHTMLOptionElement> optElement(do_QueryInterface(aOptions));
     419               0 :   if (optElement) {
     420               0 :     if (mOptions->ItemAsOption(aRemoveIndex) != optElement) {
     421               0 :       NS_ERROR("wrong option at index");
     422               0 :       return NS_ERROR_UNEXPECTED;
     423                 :     }
     424               0 :     mOptions->RemoveOptionAt(aRemoveIndex);
     425               0 :     (*aNumRemoved)++;
     426               0 :     return NS_OK;
     427                 :   }
     428                 : 
     429                 :   // Yay, one less artifact at the top level.
     430               0 :   if (aDepth == 0) {
     431               0 :     mNonOptionChildren--;
     432                 :   }
     433                 : 
     434                 :   // Recurse down deeper for options
     435               0 :   if (mOptGroupCount && aOptions->IsHTML(nsGkAtoms::optgroup)) {
     436               0 :     mOptGroupCount--;
     437                 : 
     438               0 :     for (nsIContent* child = aOptions->GetFirstChild();
     439                 :          child;
     440               0 :          child = child->GetNextSibling()) {
     441                 :       nsresult rv = RemoveOptionsFromListRecurse(child,
     442                 :                                                  aRemoveIndex,
     443                 :                                                  aNumRemoved,
     444               0 :                                                  aDepth + 1);
     445               0 :       NS_ENSURE_SUCCESS(rv, rv);
     446                 :     }
     447                 :   }
     448                 : 
     449               0 :   return NS_OK;
     450                 : }
     451                 : 
     452                 : // XXXldb Doing the processing before the content nodes have been added
     453                 : // to the document (as the name of this function seems to require, and
     454                 : // as the callers do), is highly unusual.  Passing around unparented
     455                 : // content to other parts of the app can make those things think the
     456                 : // options are the root content node.
     457                 : NS_IMETHODIMP
     458               0 : nsHTMLSelectElement::WillAddOptions(nsIContent* aOptions,
     459                 :                                     nsIContent* aParent,
     460                 :                                     PRInt32 aContentIndex,
     461                 :                                     bool aNotify)
     462                 : {
     463               0 :   PRInt32 level = GetContentDepth(aParent);
     464               0 :   if (level == -1) {
     465               0 :     return NS_ERROR_FAILURE;
     466                 :   }
     467                 : 
     468                 :   // Get the index where the options will be inserted
     469               0 :   PRInt32 ind = -1;
     470               0 :   if (!mNonOptionChildren) {
     471                 :     // If there are no artifacts, aContentIndex == ind
     472               0 :     ind = aContentIndex;
     473                 :   } else {
     474                 :     // If there are artifacts, we have to get the index of the option the
     475                 :     // hard way
     476               0 :     PRInt32 children = aParent->GetChildCount();
     477                 : 
     478               0 :     if (aContentIndex >= children) {
     479                 :       // If the content insert is after the end of the parent, then we want to get
     480                 :       // the next index *after* the parent and insert there.
     481               0 :       ind = GetOptionIndexAfter(aParent);
     482                 :     } else {
     483                 :       // If the content insert is somewhere in the middle of the container, then
     484                 :       // we want to get the option currently at the index and insert in front of
     485                 :       // that.
     486               0 :       nsIContent *currentKid = aParent->GetChildAt(aContentIndex);
     487               0 :       NS_ASSERTION(currentKid, "Child not found!");
     488               0 :       if (currentKid) {
     489               0 :         ind = GetOptionIndexAt(currentKid);
     490                 :       } else {
     491               0 :         ind = -1;
     492                 :       }
     493                 :     }
     494                 :   }
     495                 : 
     496               0 :   return InsertOptionsIntoList(aOptions, ind, level, aNotify);
     497                 : }
     498                 : 
     499                 : NS_IMETHODIMP
     500               0 : nsHTMLSelectElement::WillRemoveOptions(nsIContent* aParent,
     501                 :                                        PRInt32 aContentIndex,
     502                 :                                        bool aNotify)
     503                 : {
     504               0 :   PRInt32 level = GetContentDepth(aParent);
     505               0 :   NS_ASSERTION(level >= 0, "getting notified by unexpected content");
     506               0 :   if (level == -1) {
     507               0 :     return NS_ERROR_FAILURE;
     508                 :   }
     509                 : 
     510                 :   // Get the index where the options will be removed
     511               0 :   nsIContent *currentKid = aParent->GetChildAt(aContentIndex);
     512               0 :   if (currentKid) {
     513                 :     PRInt32 ind;
     514               0 :     if (!mNonOptionChildren) {
     515                 :       // If there are no artifacts, aContentIndex == ind
     516               0 :       ind = aContentIndex;
     517                 :     } else {
     518                 :       // If there are artifacts, we have to get the index of the option the
     519                 :       // hard way
     520               0 :       ind = GetFirstOptionIndex(currentKid);
     521                 :     }
     522               0 :     if (ind != -1) {
     523               0 :       nsresult rv = RemoveOptionsFromList(currentKid, ind, level, aNotify);
     524               0 :       NS_ENSURE_SUCCESS(rv, rv);
     525                 :     }
     526                 :   }
     527                 : 
     528               0 :   return NS_OK;
     529                 : }
     530                 : 
     531                 : PRInt32
     532               0 : nsHTMLSelectElement::GetContentDepth(nsIContent* aContent)
     533                 : {
     534               0 :   nsIContent* content = aContent;
     535                 : 
     536               0 :   PRInt32 retval = 0;
     537               0 :   while (content != this) {
     538               0 :     retval++;
     539               0 :     content = content->GetParent();
     540               0 :     if (!content) {
     541               0 :       retval = -1;
     542               0 :       break;
     543                 :     }
     544                 :   }
     545                 : 
     546               0 :   return retval;
     547                 : }
     548                 : 
     549                 : PRInt32
     550               0 : nsHTMLSelectElement::GetOptionIndexAt(nsIContent* aOptions)
     551                 : {
     552                 :   // Search this node and below.
     553                 :   // If not found, find the first one *after* this node.
     554               0 :   PRInt32 retval = GetFirstOptionIndex(aOptions);
     555               0 :   if (retval == -1) {
     556               0 :     retval = GetOptionIndexAfter(aOptions);
     557                 :   }
     558                 : 
     559               0 :   return retval;
     560                 : }
     561                 : 
     562                 : PRInt32
     563               0 : nsHTMLSelectElement::GetOptionIndexAfter(nsIContent* aOptions)
     564                 : {
     565                 :   // - If this is the select, the next option is the last.
     566                 :   // - If not, search all the options after aOptions and up to the last option
     567                 :   //   in the parent.
     568                 :   // - If it's not there, search for the first option after the parent.
     569               0 :   if (aOptions == this) {
     570                 :     PRUint32 len;
     571               0 :     GetLength(&len);
     572               0 :     return len;
     573                 :   }
     574                 : 
     575               0 :   PRInt32 retval = -1;
     576                 : 
     577               0 :   nsCOMPtr<nsIContent> parent = aOptions->GetParent();
     578                 : 
     579               0 :   if (parent) {
     580               0 :     PRInt32 index = parent->IndexOf(aOptions);
     581               0 :     PRInt32 count = parent->GetChildCount();
     582                 : 
     583               0 :     retval = GetFirstChildOptionIndex(parent, index+1, count);
     584                 : 
     585               0 :     if (retval == -1) {
     586               0 :       retval = GetOptionIndexAfter(parent);
     587                 :     }
     588                 :   }
     589                 : 
     590               0 :   return retval;
     591                 : }
     592                 : 
     593                 : PRInt32
     594               0 : nsHTMLSelectElement::GetFirstOptionIndex(nsIContent* aOptions)
     595                 : {
     596               0 :   PRInt32 listIndex = -1;
     597               0 :   nsHTMLOptionElement *optElement = nsHTMLOptionElement::FromContent(aOptions);
     598               0 :   if (optElement) {
     599               0 :     GetOptionIndex(optElement, 0, true, &listIndex);
     600                 :     // If you nested stuff under the option, you're just plain
     601                 :     // screwed.  *I'm* not going to aid and abet your evil deed.
     602               0 :     return listIndex;
     603                 :   }
     604                 : 
     605               0 :   listIndex = GetFirstChildOptionIndex(aOptions, 0, aOptions->GetChildCount());
     606                 : 
     607               0 :   return listIndex;
     608                 : }
     609                 : 
     610                 : PRInt32
     611               0 : nsHTMLSelectElement::GetFirstChildOptionIndex(nsIContent* aOptions,
     612                 :                                               PRInt32 aStartIndex,
     613                 :                                               PRInt32 aEndIndex)
     614                 : {
     615               0 :   PRInt32 retval = -1;
     616                 : 
     617               0 :   for (PRInt32 i = aStartIndex; i < aEndIndex; ++i) {
     618               0 :     retval = GetFirstOptionIndex(aOptions->GetChildAt(i));
     619               0 :     if (retval != -1) {
     620               0 :       break;
     621                 :     }
     622                 :   }
     623                 : 
     624               0 :   return retval;
     625                 : }
     626                 : 
     627                 : nsISelectControlFrame *
     628               0 : nsHTMLSelectElement::GetSelectFrame()
     629                 : {
     630               0 :   nsIFormControlFrame* form_control_frame = GetFormControlFrame(false);
     631                 : 
     632               0 :   nsISelectControlFrame *select_frame = nsnull;
     633                 : 
     634               0 :   if (form_control_frame) {
     635               0 :     select_frame = do_QueryFrame(form_control_frame);
     636                 :   }
     637                 : 
     638               0 :   return select_frame;
     639                 : }
     640                 : 
     641                 : nsresult
     642               0 : nsHTMLSelectElement::Add(nsIDOMHTMLElement* aElement,
     643                 :                          nsIDOMHTMLElement* aBefore)
     644                 : {
     645               0 :   nsCOMPtr<nsIDOMNode> added;
     646               0 :   if (!aBefore) {
     647               0 :     return AppendChild(aElement, getter_AddRefs(added));
     648                 :   }
     649                 : 
     650                 :   // Just in case we're not the parent, get the parent of the reference
     651                 :   // element
     652               0 :   nsCOMPtr<nsIDOMNode> parent;
     653               0 :   aBefore->GetParentNode(getter_AddRefs(parent));
     654               0 :   if (!parent) {
     655                 :     // NOT_FOUND_ERR: Raised if before is not a descendant of the SELECT
     656                 :     // element.
     657               0 :     return NS_ERROR_DOM_NOT_FOUND_ERR;
     658                 :   }
     659                 : 
     660               0 :   nsCOMPtr<nsIDOMNode> ancestor(parent);
     661               0 :   nsCOMPtr<nsIDOMNode> temp;
     662               0 :   while (ancestor != static_cast<nsIDOMNode*>(this)) {
     663               0 :     ancestor->GetParentNode(getter_AddRefs(temp));
     664               0 :     if (!temp) {
     665                 :       // NOT_FOUND_ERR: Raised if before is not a descendant of the SELECT
     666                 :       // element.
     667               0 :       return NS_ERROR_DOM_NOT_FOUND_ERR;
     668                 :     }
     669               0 :     temp.swap(ancestor);
     670                 :   }
     671                 : 
     672                 :   // If the before parameter is not null, we are equivalent to the
     673                 :   // insertBefore method on the parent of before.
     674               0 :   return parent->InsertBefore(aElement, aBefore, getter_AddRefs(added));
     675                 : }
     676                 : 
     677                 : NS_IMETHODIMP
     678               0 : nsHTMLSelectElement::Add(nsIDOMHTMLElement* aElement,
     679                 :                          nsIVariant* aBefore)
     680                 : {
     681                 :   PRUint16 dataType;
     682               0 :   nsresult rv = aBefore->GetDataType(&dataType);
     683               0 :   NS_ENSURE_SUCCESS(rv, rv);
     684                 : 
     685                 :   // aBefore is omitted, undefined or null
     686               0 :   if (dataType == nsIDataType::VTYPE_EMPTY ||
     687                 :       dataType == nsIDataType::VTYPE_VOID) {
     688               0 :     return Add(aElement);
     689                 :   }
     690                 : 
     691               0 :   nsCOMPtr<nsISupports> supports;
     692               0 :   nsCOMPtr<nsIDOMHTMLElement> beforeElement;
     693                 : 
     694                 :   // whether aBefore is nsIDOMHTMLElement...
     695               0 :   if (NS_SUCCEEDED(aBefore->GetAsISupports(getter_AddRefs(supports)))) {
     696               0 :     beforeElement = do_QueryInterface(supports);
     697                 : 
     698               0 :     NS_ENSURE_TRUE(beforeElement, NS_ERROR_DOM_SYNTAX_ERR);
     699               0 :     return Add(aElement, beforeElement);
     700                 :   }
     701                 : 
     702                 :   // otherwise, whether aBefore is long
     703                 :   PRInt32 index;
     704               0 :   NS_ENSURE_SUCCESS(aBefore->GetAsInt32(&index), NS_ERROR_DOM_SYNTAX_ERR);
     705                 : 
     706                 :   // If item index is out of range, insert to last.
     707                 :   // (since beforeElement becomes null, it is inserted to last)
     708               0 :   nsCOMPtr<nsIDOMNode> beforeNode;
     709               0 :   if (NS_SUCCEEDED(Item(index, getter_AddRefs(beforeNode)))) {
     710               0 :     beforeElement = do_QueryInterface(beforeNode);
     711                 :   }
     712                 : 
     713               0 :   return Add(aElement, beforeElement);
     714                 : }
     715                 : 
     716                 : NS_IMETHODIMP
     717               0 : nsHTMLSelectElement::Remove(PRInt32 aIndex)
     718                 : {
     719               0 :   nsCOMPtr<nsIDOMNode> option;
     720               0 :   Item(aIndex, getter_AddRefs(option));
     721                 : 
     722               0 :   if (option) {
     723               0 :     nsCOMPtr<nsIDOMNode> parent;
     724                 : 
     725               0 :     option->GetParentNode(getter_AddRefs(parent));
     726               0 :     if (parent) {
     727               0 :       nsCOMPtr<nsIDOMNode> ret;
     728               0 :       parent->RemoveChild(option, getter_AddRefs(ret));
     729                 :     }
     730                 :   }
     731                 : 
     732               0 :   return NS_OK;
     733                 : }
     734                 : 
     735                 : NS_IMETHODIMP
     736               0 : nsHTMLSelectElement::GetOptions(nsIDOMHTMLOptionsCollection** aValue)
     737                 : {
     738               0 :   NS_IF_ADDREF(*aValue = GetOptions());
     739                 : 
     740               0 :   return NS_OK;
     741                 : }
     742                 : 
     743                 : NS_IMETHODIMP
     744               0 : nsHTMLSelectElement::GetType(nsAString& aType)
     745                 : {
     746               0 :   if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
     747               0 :     aType.AssignLiteral("select-multiple");
     748                 :   }
     749                 :   else {
     750               0 :     aType.AssignLiteral("select-one");
     751                 :   }
     752                 : 
     753               0 :   return NS_OK;
     754                 : }
     755                 : 
     756                 : NS_IMETHODIMP
     757               0 : nsHTMLSelectElement::GetLength(PRUint32* aLength)
     758                 : {
     759               0 :   return mOptions->GetLength(aLength);
     760                 : }
     761                 : 
     762                 : #define MAX_DYNAMIC_SELECT_LENGTH 10000
     763                 : 
     764                 : NS_IMETHODIMP
     765               0 : nsHTMLSelectElement::SetLength(PRUint32 aLength)
     766                 : {
     767                 :   PRUint32 curlen;
     768               0 :   nsresult rv = GetLength(&curlen);
     769               0 :   if (NS_FAILED(rv)) {
     770               0 :     curlen = 0;
     771                 :   }
     772                 : 
     773               0 :   if (curlen > aLength) { // Remove extra options
     774               0 :     for (PRUint32 i = curlen; i > aLength && NS_SUCCEEDED(rv); --i) {
     775               0 :       rv = Remove(i - 1);
     776                 :     }
     777               0 :   } else if (aLength > curlen) {
     778               0 :     if (aLength > MAX_DYNAMIC_SELECT_LENGTH) {
     779               0 :       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     780                 :     }
     781                 :     
     782                 :     // This violates the W3C DOM but we do this for backwards compatibility
     783               0 :     nsCOMPtr<nsINodeInfo> nodeInfo;
     784                 : 
     785                 :     nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::option,
     786               0 :                                 getter_AddRefs(nodeInfo));
     787                 : 
     788               0 :     nsCOMPtr<nsIContent> element = NS_NewHTMLOptionElement(nodeInfo.forget());
     789               0 :     if (!element) {
     790               0 :       return NS_ERROR_OUT_OF_MEMORY;
     791                 :     }
     792                 : 
     793               0 :     nsCOMPtr<nsIContent> text;
     794               0 :     rv = NS_NewTextNode(getter_AddRefs(text), mNodeInfo->NodeInfoManager());
     795               0 :     NS_ENSURE_SUCCESS(rv, rv);
     796                 : 
     797               0 :     rv = element->AppendChildTo(text, false);
     798               0 :     NS_ENSURE_SUCCESS(rv, rv);
     799                 : 
     800               0 :     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(element));
     801                 : 
     802               0 :     for (PRUint32 i = curlen; i < aLength; i++) {
     803               0 :       nsCOMPtr<nsIDOMNode> tmpNode;
     804                 : 
     805               0 :       rv = AppendChild(node, getter_AddRefs(tmpNode));
     806               0 :       NS_ENSURE_SUCCESS(rv, rv);
     807                 : 
     808               0 :       if (i + 1 < aLength) {
     809               0 :         nsCOMPtr<nsIDOMNode> newNode;
     810                 : 
     811               0 :         rv = node->CloneNode(true, 1, getter_AddRefs(newNode));
     812               0 :         NS_ENSURE_SUCCESS(rv, rv);
     813                 : 
     814               0 :         node = newNode;
     815                 :       }
     816                 :     }
     817                 :   }
     818                 : 
     819               0 :   return NS_OK;
     820                 : }
     821                 : 
     822                 : //NS_IMPL_INT_ATTR(nsHTMLSelectElement, SelectedIndex, selectedindex)
     823                 : 
     824                 : NS_IMETHODIMP
     825               0 : nsHTMLSelectElement::GetSelectedIndex(PRInt32* aValue)
     826                 : {
     827               0 :   *aValue = mSelectedIndex;
     828                 : 
     829               0 :   return NS_OK;
     830                 : }
     831                 : 
     832                 : nsresult
     833               0 : nsHTMLSelectElement::SetSelectedIndexInternal(PRInt32 aIndex, bool aNotify)
     834                 : {
     835               0 :   PRInt32 oldSelectedIndex = mSelectedIndex;
     836                 : 
     837                 :   nsresult rv = SetOptionsSelectedByIndex(aIndex, aIndex, true,
     838               0 :                                           true, true, aNotify, nsnull);
     839                 : 
     840               0 :   if (NS_SUCCEEDED(rv)) {
     841               0 :     nsISelectControlFrame* selectFrame = GetSelectFrame();
     842               0 :     if (selectFrame) {
     843               0 :       rv = selectFrame->OnSetSelectedIndex(oldSelectedIndex, mSelectedIndex);
     844                 :     }
     845                 :   }
     846                 : 
     847               0 :   SetSelectionChanged(true, aNotify);
     848                 : 
     849               0 :   return rv;
     850                 : }
     851                 : 
     852                 : NS_IMETHODIMP
     853               0 : nsHTMLSelectElement::SetSelectedIndex(PRInt32 aIndex)
     854                 : {
     855               0 :   return SetSelectedIndexInternal(aIndex, true);
     856                 : }
     857                 : 
     858                 : NS_IMETHODIMP
     859               0 : nsHTMLSelectElement::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
     860                 :                                     PRInt32 aStartIndex, bool aForward,
     861                 :                                     PRInt32* aIndex)
     862                 : {
     863               0 :   nsCOMPtr<nsINode> option = do_QueryInterface(aOption);
     864               0 :   return mOptions->GetOptionIndex(option->AsElement(), aStartIndex, aForward, aIndex);
     865                 : }
     866                 : 
     867                 : bool
     868               0 : nsHTMLSelectElement::IsOptionSelectedByIndex(PRInt32 aIndex)
     869                 : {
     870               0 :   nsIDOMHTMLOptionElement *option = mOptions->ItemAsOption(aIndex);
     871               0 :   bool isSelected = false;
     872               0 :   if (option) {
     873               0 :     option->GetSelected(&isSelected);
     874                 :   }
     875               0 :   return isSelected;
     876                 : }
     877                 : 
     878                 : void
     879               0 : nsHTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame,
     880                 :                                       PRInt32 aIndex,
     881                 :                                       bool aSelected,
     882                 :                                       bool aChangeOptionState,
     883                 :                                       bool aNotify)
     884                 : {
     885                 :   // Set the selected index
     886               0 :   if (aSelected && (aIndex < mSelectedIndex || mSelectedIndex < 0)) {
     887               0 :     mSelectedIndex = aIndex;
     888               0 :     SetSelectionChanged(true, aNotify);
     889               0 :   } else if (!aSelected && aIndex == mSelectedIndex) {
     890               0 :     FindSelectedIndex(aIndex + 1, aNotify);
     891                 :   }
     892                 : 
     893               0 :   if (aChangeOptionState) {
     894                 :     // Tell the option to get its bad self selected
     895               0 :     nsCOMPtr<nsIDOMNode> option;
     896               0 :     Item(aIndex, getter_AddRefs(option));
     897               0 :     if (option) {
     898                 :       nsRefPtr<nsHTMLOptionElement> optionElement = 
     899               0 :         static_cast<nsHTMLOptionElement*>(option.get());
     900               0 :       optionElement->SetSelectedInternal(aSelected, aNotify);
     901                 :     }
     902                 :   }
     903                 : 
     904                 :   // Let the frame know too
     905               0 :   if (aSelectFrame) {
     906               0 :     aSelectFrame->OnOptionSelected(aIndex, aSelected);
     907                 :   }
     908                 : 
     909               0 :   UpdateValueMissingValidityState();
     910               0 :   UpdateState(aNotify);
     911               0 : }
     912                 : 
     913                 : void
     914               0 : nsHTMLSelectElement::FindSelectedIndex(PRInt32 aStartIndex, bool aNotify)
     915                 : {
     916               0 :   mSelectedIndex = -1;
     917               0 :   SetSelectionChanged(true, aNotify);
     918                 :   PRUint32 len;
     919               0 :   GetLength(&len);
     920               0 :   for (PRInt32 i = aStartIndex; i < PRInt32(len); i++) {
     921               0 :     if (IsOptionSelectedByIndex(i)) {
     922               0 :       mSelectedIndex = i;
     923               0 :       SetSelectionChanged(true, aNotify);
     924               0 :       break;
     925                 :     }
     926                 :   }
     927               0 : }
     928                 : 
     929                 : // XXX Consider splitting this into two functions for ease of reading:
     930                 : // SelectOptionsByIndex(startIndex, endIndex, clearAll, checkDisabled)
     931                 : //   startIndex, endIndex - the range of options to turn on
     932                 : //                          (-1, -1) will clear all indices no matter what.
     933                 : //   clearAll - will clear all other options unless checkDisabled is on
     934                 : //              and all the options attempted to be set are disabled
     935                 : //              (note that if it is not multiple, and an option is selected,
     936                 : //              everything else will be cleared regardless).
     937                 : //   checkDisabled - if this is TRUE, and an option is disabled, it will not be
     938                 : //                   changed regardless of whether it is selected or not.
     939                 : //                   Generally the UI passes TRUE and JS passes FALSE.
     940                 : //                   (setDisabled currently is the opposite)
     941                 : // DeselectOptionsByIndex(startIndex, endIndex, checkDisabled)
     942                 : //   startIndex, endIndex - the range of options to turn on
     943                 : //                          (-1, -1) will clear all indices no matter what.
     944                 : //   checkDisabled - if this is TRUE, and an option is disabled, it will not be
     945                 : //                   changed regardless of whether it is selected or not.
     946                 : //                   Generally the UI passes TRUE and JS passes FALSE.
     947                 : //                   (setDisabled currently is the opposite)
     948                 : //
     949                 : // XXXbz the above comment is pretty confusing.  Maybe we should actually
     950                 : // document the args to this function too, in addition to documenting what
     951                 : // things might end up looking like?  In particular, pay attention to the
     952                 : // setDisabled vs checkDisabled business.
     953                 : NS_IMETHODIMP
     954               0 : nsHTMLSelectElement::SetOptionsSelectedByIndex(PRInt32 aStartIndex,
     955                 :                                                PRInt32 aEndIndex,
     956                 :                                                bool aIsSelected,
     957                 :                                                bool aClearAll,
     958                 :                                                bool aSetDisabled,
     959                 :                                                bool aNotify,
     960                 :                                                bool* aChangedSomething)
     961                 : {
     962                 : #if 0
     963                 :   printf("SetOption(%d-%d, %c, ClearAll=%c)\n", aStartIndex, aEndIndex,
     964                 :                                        (aIsSelected ? 'Y' : 'N'),
     965                 :                                        (aClearAll ? 'Y' : 'N'));
     966                 : #endif
     967               0 :   if (aChangedSomething) {
     968               0 :     *aChangedSomething = false;
     969                 :   }
     970                 : 
     971                 :   // Don't bother if the select is disabled
     972               0 :   if (!aSetDisabled && IsDisabled()) {
     973               0 :     return NS_OK;
     974                 :   }
     975                 : 
     976                 :   // Don't bother if there are no options
     977               0 :   PRUint32 numItems = 0;
     978               0 :   GetLength(&numItems);
     979               0 :   if (numItems == 0) {
     980               0 :     return NS_OK;
     981                 :   }
     982                 : 
     983                 :   // First, find out whether multiple items can be selected
     984               0 :   bool isMultiple = HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
     985                 : 
     986                 :   // These variables tell us whether any options were selected
     987                 :   // or deselected.
     988               0 :   bool optionsSelected = false;
     989               0 :   bool optionsDeselected = false;
     990                 : 
     991               0 :   nsISelectControlFrame *selectFrame = nsnull;
     992               0 :   bool didGetFrame = false;
     993               0 :   nsWeakFrame weakSelectFrame;
     994                 : 
     995               0 :   if (aIsSelected) {
     996                 :     // Setting selectedIndex to an out-of-bounds index means -1. (HTML5)
     997               0 :     if (aStartIndex >= (PRInt32)numItems || aStartIndex < 0 ||
     998                 :         aEndIndex >= (PRInt32)numItems || aEndIndex < 0) {
     999               0 :       aStartIndex = -1;
    1000               0 :       aEndIndex = -1;
    1001                 :     }
    1002                 : 
    1003                 :     // Only select the first value if it's not multiple
    1004               0 :     if (!isMultiple) {
    1005               0 :       aEndIndex = aStartIndex;
    1006                 :     }
    1007                 : 
    1008                 :     // This variable tells whether or not all of the options we attempted to
    1009                 :     // select are disabled.  If ClearAll is passed in as true, and we do not
    1010                 :     // select anything because the options are disabled, we will not clear the
    1011                 :     // other options.  (This is to make the UI work the way one might expect.)
    1012               0 :     bool allDisabled = !aSetDisabled;
    1013                 : 
    1014                 :     //
    1015                 :     // Save a little time when clearing other options
    1016                 :     //
    1017               0 :     PRInt32 previousSelectedIndex = mSelectedIndex;
    1018                 : 
    1019                 :     //
    1020                 :     // Select the requested indices
    1021                 :     //
    1022                 :     // If index is -1, everything will be deselected (bug 28143)
    1023               0 :     if (aStartIndex != -1) {
    1024                 :       // Loop through the options and select them (if they are not disabled and
    1025                 :       // if they are not already selected).
    1026               0 :       for (PRInt32 optIndex = aStartIndex; optIndex <= aEndIndex; optIndex++) {
    1027                 : 
    1028                 :         // Ignore disabled options.
    1029               0 :         if (!aSetDisabled) {
    1030                 :           bool isDisabled;
    1031               0 :           IsOptionDisabled(optIndex, &isDisabled);
    1032                 : 
    1033               0 :           if (isDisabled) {
    1034               0 :             continue;
    1035                 :           } else {
    1036               0 :             allDisabled = false;
    1037                 :           }
    1038                 :         }
    1039                 : 
    1040               0 :         nsIDOMHTMLOptionElement *option = mOptions->ItemAsOption(optIndex);
    1041               0 :         if (option) {
    1042                 :           // If the index is already selected, ignore it.
    1043               0 :           bool isSelected = false;
    1044               0 :           option->GetSelected(&isSelected);
    1045               0 :           if (!isSelected) {
    1046                 :             // To notify the frame if anything gets changed. No need
    1047                 :             // to flush here, if there's no frame yet we don't need to
    1048                 :             // force it to be created just to notify it about a change
    1049                 :             // in the select.
    1050               0 :             selectFrame = GetSelectFrame();
    1051               0 :             weakSelectFrame = do_QueryFrame(selectFrame);
    1052               0 :             didGetFrame = true;
    1053                 : 
    1054               0 :             OnOptionSelected(selectFrame, optIndex, true, true, aNotify);
    1055               0 :             optionsSelected = true;
    1056                 :           }
    1057                 :         }
    1058                 :       }
    1059                 :     }
    1060                 : 
    1061                 :     // Next remove all other options if single select or all is clear
    1062                 :     // If index is -1, everything will be deselected (bug 28143)
    1063               0 :     if (((!isMultiple && optionsSelected)
    1064               0 :        || (aClearAll && !allDisabled)
    1065                 :        || aStartIndex == -1)
    1066                 :        && previousSelectedIndex != -1) {
    1067               0 :       for (PRInt32 optIndex = previousSelectedIndex;
    1068                 :            optIndex < PRInt32(numItems);
    1069                 :            optIndex++) {
    1070               0 :         if (optIndex < aStartIndex || optIndex > aEndIndex) {
    1071               0 :           nsIDOMHTMLOptionElement *option = mOptions->ItemAsOption(optIndex);
    1072               0 :           if (option) {
    1073                 :             // If the index is already selected, ignore it.
    1074               0 :             bool isSelected = false;
    1075               0 :             option->GetSelected(&isSelected);
    1076               0 :             if (isSelected) {
    1077               0 :               if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
    1078                 :                 // To notify the frame if anything gets changed, don't
    1079                 :                 // flush, if the frame doesn't exist we don't need to
    1080                 :                 // create it just to tell it about this change.
    1081               0 :                 selectFrame = GetSelectFrame();
    1082               0 :                 weakSelectFrame = do_QueryFrame(selectFrame);
    1083                 : 
    1084               0 :                 didGetFrame = true;
    1085                 :               }
    1086                 : 
    1087                 :               OnOptionSelected(selectFrame, optIndex, false, true,
    1088               0 :                                aNotify);
    1089               0 :               optionsDeselected = true;
    1090                 : 
    1091                 :               // Only need to deselect one option if not multiple
    1092               0 :               if (!isMultiple) {
    1093               0 :                 break;
    1094                 :               }
    1095                 :             }
    1096                 :           }
    1097                 :         }
    1098                 :       }
    1099                 :     }
    1100                 : 
    1101                 :   } else {
    1102                 : 
    1103                 :     // If we're deselecting, loop through all selected items and deselect
    1104                 :     // any that are in the specified range.
    1105               0 :     for (PRInt32 optIndex = aStartIndex; optIndex <= aEndIndex; optIndex++) {
    1106               0 :       if (!aSetDisabled) {
    1107                 :         bool isDisabled;
    1108               0 :         IsOptionDisabled(optIndex, &isDisabled);
    1109               0 :         if (isDisabled) {
    1110               0 :           continue;
    1111                 :         }
    1112                 :       }
    1113                 : 
    1114               0 :       nsIDOMHTMLOptionElement *option = mOptions->ItemAsOption(optIndex);
    1115               0 :       if (option) {
    1116                 :         // If the index is already selected, ignore it.
    1117               0 :         bool isSelected = false;
    1118               0 :         option->GetSelected(&isSelected);
    1119               0 :         if (isSelected) {
    1120               0 :           if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
    1121                 :             // To notify the frame if anything gets changed, don't
    1122                 :             // flush, if the frame doesn't exist we don't need to
    1123                 :             // create it just to tell it about this change.
    1124               0 :             selectFrame = GetSelectFrame();
    1125               0 :             weakSelectFrame = do_QueryFrame(selectFrame);
    1126                 : 
    1127               0 :             didGetFrame = true;
    1128                 :           }
    1129                 : 
    1130               0 :           OnOptionSelected(selectFrame, optIndex, false, true, aNotify);
    1131               0 :           optionsDeselected = true;
    1132                 :         }
    1133                 :       }
    1134                 :     }
    1135                 :   }
    1136                 : 
    1137                 :   // Make sure something is selected unless we were set to -1 (none)
    1138               0 :   if (optionsDeselected && aStartIndex != -1) {
    1139               0 :     optionsSelected = CheckSelectSomething(aNotify) || optionsSelected;
    1140                 :   }
    1141                 : 
    1142                 :   // Let the caller know whether anything was changed
    1143               0 :   if (optionsSelected || optionsDeselected) {
    1144               0 :     if (aChangedSomething)
    1145               0 :       *aChangedSomething = true;
    1146                 :   }
    1147                 : 
    1148               0 :   return NS_OK;
    1149                 : }
    1150                 : 
    1151                 : NS_IMETHODIMP
    1152               0 : nsHTMLSelectElement::IsOptionDisabled(PRInt32 aIndex, bool* aIsDisabled)
    1153                 : {
    1154               0 :   *aIsDisabled = false;
    1155               0 :   nsCOMPtr<nsIDOMNode> optionNode;
    1156               0 :   Item(aIndex, getter_AddRefs(optionNode));
    1157               0 :   NS_ENSURE_TRUE(optionNode, NS_ERROR_FAILURE);
    1158                 : 
    1159               0 :   nsCOMPtr<nsIDOMHTMLOptionElement> option = do_QueryInterface(optionNode);
    1160               0 :   if (option) {
    1161                 :     bool isDisabled;
    1162               0 :     option->GetDisabled(&isDisabled);
    1163               0 :     if (isDisabled) {
    1164               0 :       *aIsDisabled = true;
    1165               0 :       return NS_OK;
    1166                 :     }
    1167                 :   }
    1168                 : 
    1169                 :   // Check for disabled optgroups
    1170                 :   // If there are no artifacts, there are no optgroups
    1171               0 :   if (mNonOptionChildren) {
    1172               0 :     nsCOMPtr<nsIDOMNode> parent;
    1173               0 :     while (1) {
    1174               0 :       optionNode->GetParentNode(getter_AddRefs(parent));
    1175                 : 
    1176                 :       // If we reached the top of the doc (scary), we're done
    1177               0 :       if (!parent) {
    1178               0 :         break;
    1179                 :       }
    1180                 : 
    1181                 :       // If we reached the select element, we're done
    1182                 :       nsCOMPtr<nsIDOMHTMLSelectElement> selectElement =
    1183               0 :         do_QueryInterface(parent);
    1184               0 :       if (selectElement) {
    1185                 :         break;
    1186                 :       }
    1187                 : 
    1188                 :       nsCOMPtr<nsIDOMHTMLOptGroupElement> optGroupElement =
    1189               0 :         do_QueryInterface(parent);
    1190                 : 
    1191               0 :       if (optGroupElement) {
    1192                 :         bool isDisabled;
    1193               0 :         optGroupElement->GetDisabled(&isDisabled);
    1194                 : 
    1195               0 :         if (isDisabled) {
    1196               0 :           *aIsDisabled = true;
    1197               0 :           return NS_OK;
    1198                 :         }
    1199                 :       } else {
    1200                 :         // If you put something else between you and the optgroup, you're a
    1201                 :         // moron and you deserve not to have optgroup disabling work.
    1202                 :         break;
    1203                 :       }
    1204                 : 
    1205               0 :       optionNode = parent;
    1206                 :     }
    1207                 :   }
    1208                 : 
    1209               0 :   return NS_OK;
    1210                 : }
    1211                 : 
    1212                 : NS_IMETHODIMP
    1213               0 : nsHTMLSelectElement::GetValue(nsAString& aValue)
    1214                 : {
    1215                 :   PRInt32 selectedIndex;
    1216                 : 
    1217               0 :   nsresult rv = GetSelectedIndex(&selectedIndex);
    1218                 : 
    1219               0 :   if (NS_SUCCEEDED(rv) && selectedIndex > -1) {
    1220               0 :     nsCOMPtr<nsIDOMNode> node;
    1221                 : 
    1222               0 :     rv = Item(selectedIndex, getter_AddRefs(node));
    1223                 : 
    1224               0 :     nsCOMPtr<nsIDOMHTMLOptionElement> option = do_QueryInterface(node);
    1225               0 :     if (NS_SUCCEEDED(rv) && option) {
    1226               0 :       return option->GetValue(aValue);
    1227                 :     }
    1228                 :   }
    1229                 : 
    1230               0 :   aValue.Truncate();
    1231               0 :   return rv;
    1232                 : }
    1233                 : 
    1234                 : NS_IMETHODIMP
    1235               0 : nsHTMLSelectElement::SetValue(const nsAString& aValue)
    1236                 : {
    1237                 :   PRUint32 length;
    1238               0 :   nsresult rv = GetLength(&length);
    1239               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1240                 : 
    1241               0 :   for (PRUint32 i = 0; i < length; i++) {
    1242               0 :     nsCOMPtr<nsIDOMNode> node;
    1243               0 :     rv = Item(i, getter_AddRefs(node));
    1244               0 :     if (NS_FAILED(rv) || !node) {
    1245               0 :       continue;
    1246                 :     }
    1247                 : 
    1248               0 :     nsCOMPtr<nsIDOMHTMLOptionElement> option = do_QueryInterface(node);
    1249               0 :     if (!option) {
    1250               0 :       continue;
    1251                 :     }
    1252               0 :     nsAutoString optionVal;
    1253               0 :     option->GetValue(optionVal);
    1254               0 :     if (optionVal.Equals(aValue)) {
    1255               0 :       SetSelectedIndexInternal(PRInt32(i), true);
    1256                 :       break;
    1257                 :     }
    1258                 :   }
    1259               0 :   return rv;
    1260                 : }
    1261                 : 
    1262                 : 
    1263               0 : NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Autofocus, autofocus)
    1264               0 : NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Disabled, disabled)
    1265               0 : NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Multiple, multiple)
    1266               0 : NS_IMPL_STRING_ATTR(nsHTMLSelectElement, Name, name)
    1267               0 : NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Required, required)
    1268               0 : NS_IMPL_NON_NEGATIVE_INT_ATTR_DEFAULT_VALUE(nsHTMLSelectElement, Size, size, 0)
    1269               0 : NS_IMPL_INT_ATTR(nsHTMLSelectElement, TabIndex, tabindex)
    1270                 : 
    1271                 : bool
    1272               0 : nsHTMLSelectElement::IsHTMLFocusable(bool aWithMouse,
    1273                 :                                      bool *aIsFocusable, PRInt32 *aTabIndex)
    1274                 : {
    1275               0 :   if (nsGenericHTMLFormElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
    1276               0 :     return true;
    1277                 :   }
    1278                 : 
    1279               0 :   *aIsFocusable = !IsDisabled();
    1280                 : 
    1281               0 :   return false;
    1282                 : }
    1283                 : 
    1284                 : NS_IMETHODIMP
    1285               0 : nsHTMLSelectElement::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
    1286                 : {
    1287               0 :   return mOptions->Item(aIndex, aReturn);
    1288                 : }
    1289                 : 
    1290                 : NS_IMETHODIMP
    1291               0 : nsHTMLSelectElement::NamedItem(const nsAString& aName,
    1292                 :                                nsIDOMNode** aReturn)
    1293                 : {
    1294               0 :   return mOptions->NamedItem(aName, aReturn);
    1295                 : }
    1296                 : 
    1297                 : bool
    1298               0 : nsHTMLSelectElement::CheckSelectSomething(bool aNotify)
    1299                 : {
    1300               0 :   if (mIsDoneAddingChildren) {
    1301               0 :     if (mSelectedIndex < 0 && IsCombobox()) {
    1302               0 :       return SelectSomething(aNotify);
    1303                 :     }
    1304                 :   }
    1305               0 :   return false;
    1306                 : }
    1307                 : 
    1308                 : bool
    1309               0 : nsHTMLSelectElement::SelectSomething(bool aNotify)
    1310                 : {
    1311                 :   // If we're not done building the select, don't play with this yet.
    1312               0 :   if (!mIsDoneAddingChildren) {
    1313               0 :     return false;
    1314                 :   }
    1315                 : 
    1316                 :   PRUint32 count;
    1317               0 :   GetLength(&count);
    1318               0 :   for (PRUint32 i = 0; i < count; i++) {
    1319                 :     bool disabled;
    1320               0 :     nsresult rv = IsOptionDisabled(i, &disabled);
    1321                 : 
    1322               0 :     if (NS_FAILED(rv) || !disabled) {
    1323               0 :       rv = SetSelectedIndexInternal(i, aNotify);
    1324               0 :       NS_ENSURE_SUCCESS(rv, false);
    1325                 : 
    1326               0 :       UpdateValueMissingValidityState();
    1327               0 :       UpdateState(aNotify);
    1328                 : 
    1329               0 :       return true;
    1330                 :     }
    1331                 :   }
    1332                 : 
    1333               0 :   return false;
    1334                 : }
    1335                 : 
    1336                 : nsresult
    1337               0 : nsHTMLSelectElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
    1338                 :                                 nsIContent* aBindingParent,
    1339                 :                                 bool aCompileEventHandlers)
    1340                 : {
    1341                 :   nsresult rv = nsGenericHTMLFormElement::BindToTree(aDocument, aParent,
    1342                 :                                                      aBindingParent,
    1343               0 :                                                      aCompileEventHandlers);
    1344               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1345                 : 
    1346                 :   // If there is a disabled fieldset in the parent chain, the element is now
    1347                 :   // barred from constraint validation.
    1348                 :   // XXXbz is this still needed now that fieldset changes always call
    1349                 :   // FieldSetDisabledChanged?
    1350               0 :   UpdateBarredFromConstraintValidation();
    1351                 : 
    1352                 :   // And now make sure our state is up to date
    1353               0 :   UpdateState(false);
    1354                 : 
    1355               0 :   return rv;
    1356                 : }
    1357                 : 
    1358                 : void
    1359               0 : nsHTMLSelectElement::UnbindFromTree(bool aDeep, bool aNullParent)
    1360                 : {
    1361               0 :   nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
    1362                 : 
    1363                 :   // We might be no longer disabled because our parent chain changed.
    1364                 :   // XXXbz is this still needed now that fieldset changes always call
    1365                 :   // FieldSetDisabledChanged?
    1366               0 :   UpdateBarredFromConstraintValidation();
    1367                 : 
    1368                 :   // And now make sure our state is up to date
    1369               0 :   UpdateState(false);
    1370               0 : }
    1371                 : 
    1372                 : nsresult
    1373               0 : nsHTMLSelectElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
    1374                 :                                    const nsAttrValueOrString* aValue,
    1375                 :                                    bool aNotify)
    1376                 : {
    1377               0 :   if (aNotify && aName == nsGkAtoms::disabled &&
    1378                 :       aNameSpaceID == kNameSpaceID_None) {
    1379               0 :     mDisabledChanged = true;
    1380                 :   }
    1381                 : 
    1382                 :   return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
    1383               0 :                                                  aValue, aNotify);
    1384                 : }
    1385                 : 
    1386                 : nsresult
    1387               0 : nsHTMLSelectElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
    1388                 :                                   const nsAttrValue* aValue, bool aNotify)
    1389                 : {
    1390               0 :   if (aNameSpaceID == kNameSpaceID_None) {
    1391               0 :     if (aName == nsGkAtoms::disabled) {
    1392               0 :       UpdateBarredFromConstraintValidation();
    1393               0 :     } else if (aName == nsGkAtoms::required) {
    1394               0 :       UpdateValueMissingValidityState();
    1395                 :     }
    1396                 : 
    1397               0 :     UpdateState(aNotify);
    1398                 :   }
    1399                 : 
    1400                 :   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
    1401               0 :                                                 aValue, aNotify);
    1402                 : }
    1403                 : 
    1404                 : nsresult
    1405               0 : nsHTMLSelectElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
    1406                 :                                bool aNotify)
    1407                 : {
    1408               0 :   if (aNotify && aNameSpaceID == kNameSpaceID_None &&
    1409                 :       aAttribute == nsGkAtoms::multiple) {
    1410                 :     // We're changing from being a multi-select to a single-select.
    1411                 :     // Make sure we only have one option selected before we do that.
    1412                 :     // Note that this needs to come before we really unset the attr,
    1413                 :     // since SetOptionsSelectedByIndex does some bail-out type
    1414                 :     // optimization for cases when the select is not multiple that
    1415                 :     // would lead to only a single option getting deselected.
    1416               0 :     if (mSelectedIndex >= 0) {
    1417               0 :       SetSelectedIndexInternal(mSelectedIndex, aNotify);
    1418                 :     }
    1419                 :   }
    1420                 : 
    1421                 :   nsresult rv = nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID, aAttribute,
    1422               0 :                                                     aNotify);
    1423               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1424                 : 
    1425               0 :   if (aNotify && aNameSpaceID == kNameSpaceID_None &&
    1426                 :       aAttribute == nsGkAtoms::multiple) {
    1427                 :     // We might have become a combobox; make sure _something_ gets
    1428                 :     // selected in that case
    1429               0 :     CheckSelectSomething(aNotify);
    1430                 :   }
    1431                 : 
    1432               0 :   return rv;
    1433                 : }
    1434                 : 
    1435                 : void
    1436               0 : nsHTMLSelectElement::DoneAddingChildren(bool aHaveNotified)
    1437                 : {
    1438               0 :   mIsDoneAddingChildren = true;
    1439                 : 
    1440               0 :   nsISelectControlFrame* selectFrame = GetSelectFrame();
    1441                 : 
    1442                 :   // If we foolishly tried to restore before we were done adding
    1443                 :   // content, restore the rest of the options proper-like
    1444               0 :   if (mRestoreState) {
    1445               0 :     RestoreStateTo(mRestoreState);
    1446               0 :     mRestoreState = nsnull;
    1447                 :   }
    1448                 : 
    1449                 :   // Notify the frame
    1450               0 :   if (selectFrame) {
    1451               0 :     selectFrame->DoneAddingChildren(true);
    1452                 :   }
    1453                 : 
    1454                 :   // Restore state
    1455               0 :   if (!mInhibitStateRestoration) {
    1456               0 :     RestoreFormControlState(this, this);
    1457                 :   }
    1458                 : 
    1459                 :   // Now that we're done, select something (if it's a single select something
    1460                 :   // must be selected)
    1461               0 :   if (!CheckSelectSomething(false)) {
    1462                 :     // If an option has @selected set, it will be selected during parsing but
    1463                 :     // with an empty value. We have to make sure the select element updates it's
    1464                 :     // validity state to take this into account.
    1465               0 :     UpdateValueMissingValidityState();
    1466                 : 
    1467                 :     // And now make sure we update our content state too
    1468               0 :     UpdateState(aHaveNotified);
    1469                 :   }
    1470                 : 
    1471               0 :   mDefaultSelectionSet = true;
    1472               0 : }
    1473                 : 
    1474                 : bool
    1475               0 : nsHTMLSelectElement::ParseAttribute(PRInt32 aNamespaceID,
    1476                 :                                     nsIAtom* aAttribute,
    1477                 :                                     const nsAString& aValue,
    1478                 :                                     nsAttrValue& aResult)
    1479                 : {
    1480               0 :   if (aAttribute == nsGkAtoms::size && kNameSpaceID_None == aNamespaceID) {
    1481               0 :     return aResult.ParsePositiveIntValue(aValue);
    1482                 :   }
    1483                 :   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
    1484               0 :                                               aResult);
    1485                 : }
    1486                 : 
    1487                 : static void
    1488               0 : MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
    1489                 :                       nsRuleData* aData)
    1490                 : {
    1491               0 :   nsGenericHTMLFormElement::MapImageAlignAttributeInto(aAttributes, aData);
    1492               0 :   nsGenericHTMLFormElement::MapCommonAttributesInto(aAttributes, aData);
    1493               0 : }
    1494                 : 
    1495                 : nsChangeHint
    1496               0 : nsHTMLSelectElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
    1497                 :                                             PRInt32 aModType) const
    1498                 : {
    1499                 :   nsChangeHint retval =
    1500               0 :       nsGenericHTMLFormElement::GetAttributeChangeHint(aAttribute, aModType);
    1501               0 :   if (aAttribute == nsGkAtoms::multiple ||
    1502                 :       aAttribute == nsGkAtoms::size) {
    1503               0 :     NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
    1504                 :   }
    1505               0 :   return retval;
    1506                 : }
    1507                 : 
    1508                 : NS_IMETHODIMP_(bool)
    1509               0 : nsHTMLSelectElement::IsAttributeMapped(const nsIAtom* aAttribute) const
    1510                 : {
    1511                 :   static const MappedAttributeEntry* const map[] = {
    1512                 :     sCommonAttributeMap,
    1513                 :     sImageAlignAttributeMap
    1514                 :   };
    1515                 : 
    1516               0 :   return FindAttributeDependence(aAttribute, map);
    1517                 : }
    1518                 : 
    1519                 : nsMapRuleToAttributesFunc
    1520               0 : nsHTMLSelectElement::GetAttributeMappingFunction() const
    1521                 : {
    1522               0 :   return &MapAttributesIntoRule;
    1523                 : }
    1524                 : 
    1525                 : 
    1526                 : nsresult
    1527               0 : nsHTMLSelectElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
    1528                 : {
    1529               0 :   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
    1530               0 :   nsIFrame* formFrame = nsnull;
    1531               0 :   if (formControlFrame) {
    1532               0 :     formFrame = do_QueryFrame(formControlFrame);
    1533                 :   }
    1534                 : 
    1535               0 :   aVisitor.mCanHandle = false;
    1536               0 :   if (IsElementDisabledForEvents(aVisitor.mEvent->message, formFrame)) {
    1537               0 :     return NS_OK;
    1538                 :   }
    1539                 : 
    1540               0 :   return nsGenericHTMLFormElement::PreHandleEvent(aVisitor);
    1541                 : }
    1542                 : 
    1543                 : nsresult
    1544               0 : nsHTMLSelectElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
    1545                 : {
    1546               0 :   if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) {
    1547                 :     // If the invalid UI is shown, we should show it while focused and
    1548                 :     // update the invalid/valid UI.
    1549               0 :     mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
    1550                 : 
    1551                 :     // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
    1552                 :     // UI while focused.
    1553               0 :     mCanShowValidUI = ShouldShowValidityUI();
    1554                 : 
    1555                 :     // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID nor
    1556                 :     // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change.
    1557               0 :   } else if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
    1558               0 :     mCanShowInvalidUI = true;
    1559               0 :     mCanShowValidUI = true;
    1560                 : 
    1561               0 :     UpdateState(true);
    1562                 :   }
    1563                 : 
    1564               0 :   return nsGenericHTMLFormElement::PostHandleEvent(aVisitor);
    1565                 : }
    1566                 : 
    1567                 : nsEventStates
    1568               0 : nsHTMLSelectElement::IntrinsicState() const
    1569                 : {
    1570               0 :   nsEventStates state = nsGenericHTMLFormElement::IntrinsicState();
    1571                 : 
    1572               0 :   if (IsCandidateForConstraintValidation()) {
    1573               0 :     if (IsValid()) {
    1574               0 :       state |= NS_EVENT_STATE_VALID;
    1575                 :     } else {
    1576               0 :       state |= NS_EVENT_STATE_INVALID;
    1577                 : 
    1578               0 :       if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
    1579               0 :           (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
    1580               0 :            (mCanShowInvalidUI && ShouldShowValidityUI()))) {
    1581               0 :         state |= NS_EVENT_STATE_MOZ_UI_INVALID;
    1582                 :       }
    1583                 :     }
    1584                 : 
    1585                 :     // :-moz-ui-valid applies if all the following are true:
    1586                 :     // 1. The element is not focused, or had either :-moz-ui-valid or
    1587                 :     //    :-moz-ui-invalid applying before it was focused ;
    1588                 :     // 2. The element is either valid or isn't allowed to have
    1589                 :     //    :-moz-ui-invalid applying ;
    1590                 :     // 3. The element has no form owner or its form owner doesn't have the
    1591                 :     //    novalidate attribute set ;
    1592                 :     // 4. The element has already been modified or the user tried to submit the
    1593                 :     //    form owner while invalid.
    1594               0 :     if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
    1595               0 :         (mCanShowValidUI && ShouldShowValidityUI() &&
    1596               0 :          (IsValid() || (state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
    1597               0 :                         !mCanShowInvalidUI)))) {
    1598               0 :       state |= NS_EVENT_STATE_MOZ_UI_VALID;
    1599                 :     }
    1600                 :   }
    1601                 : 
    1602               0 :   if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
    1603               0 :     state |= NS_EVENT_STATE_REQUIRED;
    1604                 :   } else {
    1605               0 :     state |= NS_EVENT_STATE_OPTIONAL;
    1606                 :   }
    1607                 : 
    1608                 :   return state;
    1609                 : }
    1610                 : 
    1611                 : // nsIFormControl
    1612                 : 
    1613                 : NS_IMETHODIMP
    1614               0 : nsHTMLSelectElement::SaveState()
    1615                 : {
    1616               0 :   nsRefPtr<nsSelectState> state = new nsSelectState();
    1617                 : 
    1618                 :   PRUint32 len;
    1619               0 :   GetLength(&len);
    1620                 : 
    1621               0 :   for (PRUint32 optIndex = 0; optIndex < len; optIndex++) {
    1622               0 :     nsIDOMHTMLOptionElement *option = mOptions->ItemAsOption(optIndex);
    1623               0 :     if (option) {
    1624                 :       bool isSelected;
    1625               0 :       option->GetSelected(&isSelected);
    1626               0 :       if (isSelected) {
    1627               0 :         nsAutoString value;
    1628               0 :         option->GetValue(value);
    1629               0 :         state->PutOption(optIndex, value);
    1630                 :       }
    1631                 :     }
    1632                 :   }
    1633                 : 
    1634               0 :   nsPresState *presState = nsnull;
    1635               0 :   nsresult rv = GetPrimaryPresState(this, &presState);
    1636               0 :   if (presState) {
    1637               0 :     presState->SetStateProperty(state);
    1638                 : 
    1639               0 :     if (mDisabledChanged) {
    1640                 :       // We do not want to save the real disabled state but the disabled
    1641                 :       // attribute.
    1642               0 :       presState->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
    1643                 :     }
    1644                 :   }
    1645                 : 
    1646               0 :   return rv;
    1647                 : }
    1648                 : 
    1649                 : bool
    1650               0 : nsHTMLSelectElement::RestoreState(nsPresState* aState)
    1651                 : {
    1652                 :   // Get the presentation state object to retrieve our stuff out of.
    1653                 :   nsCOMPtr<nsSelectState> state(
    1654               0 :     do_QueryInterface(aState->GetStateProperty()));
    1655                 : 
    1656               0 :   if (state) {
    1657               0 :     RestoreStateTo(state);
    1658                 : 
    1659                 :     // Don't flush, if the frame doesn't exist yet it doesn't care if
    1660                 :     // we're reset or not.
    1661               0 :     DispatchContentReset();
    1662                 :   }
    1663                 : 
    1664               0 :   if (aState->IsDisabledSet()) {
    1665               0 :     SetDisabled(aState->GetDisabled());
    1666                 :   }
    1667                 : 
    1668               0 :   return false;
    1669                 : }
    1670                 : 
    1671                 : void
    1672               0 : nsHTMLSelectElement::RestoreStateTo(nsSelectState* aNewSelected)
    1673                 : {
    1674               0 :   if (!mIsDoneAddingChildren) {
    1675               0 :     mRestoreState = aNewSelected;
    1676               0 :     return;
    1677                 :   }
    1678                 : 
    1679                 :   PRUint32 len;
    1680               0 :   GetLength(&len);
    1681                 : 
    1682                 :   // First clear all
    1683               0 :   SetOptionsSelectedByIndex(-1, -1, true, true, true, true, nsnull);
    1684                 : 
    1685                 :   // Next set the proper ones
    1686               0 :   for (PRInt32 i = 0; i < PRInt32(len); i++) {
    1687               0 :     nsIDOMHTMLOptionElement *option = mOptions->ItemAsOption(i);
    1688               0 :     if (option) {
    1689               0 :       nsAutoString value;
    1690               0 :       nsresult rv = option->GetValue(value);
    1691               0 :       if (NS_SUCCEEDED(rv) && aNewSelected->ContainsOption(i, value)) {
    1692               0 :         SetOptionsSelectedByIndex(i, i, true, false, true, true, nsnull);
    1693                 :       }
    1694                 :     }
    1695                 :   }
    1696                 : }
    1697                 : 
    1698                 : NS_IMETHODIMP
    1699               0 : nsHTMLSelectElement::Reset()
    1700                 : {
    1701               0 :   PRUint32 numSelected = 0;
    1702                 : 
    1703                 :   //
    1704                 :   // Cycle through the options array and reset the options
    1705                 :   //
    1706                 :   PRUint32 numOptions;
    1707               0 :   nsresult rv = GetLength(&numOptions);
    1708               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1709                 : 
    1710               0 :   for (PRUint32 i = 0; i < numOptions; i++) {
    1711               0 :     nsCOMPtr<nsIDOMNode> node;
    1712               0 :     rv = Item(i, getter_AddRefs(node));
    1713               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1714                 : 
    1715               0 :     nsCOMPtr<nsIDOMHTMLOptionElement> option(do_QueryInterface(node));
    1716                 : 
    1717               0 :     NS_ASSERTION(option, "option not an OptionElement");
    1718               0 :     if (option) {
    1719                 :       //
    1720                 :       // Reset the option to its default value
    1721                 :       //
    1722               0 :       bool selected = false;
    1723               0 :       option->GetDefaultSelected(&selected);
    1724                 :       SetOptionsSelectedByIndex(i, i, selected,
    1725               0 :                                 false, true, true, nsnull);
    1726               0 :       if (selected) {
    1727               0 :         numSelected++;
    1728                 :       }
    1729                 :     }
    1730                 :   }
    1731                 : 
    1732                 :   //
    1733                 :   // If nothing was selected and it's not multiple, select something
    1734                 :   //
    1735               0 :   if (numSelected == 0 && IsCombobox()) {
    1736               0 :     SelectSomething(true);
    1737                 :   }
    1738                 : 
    1739               0 :   SetSelectionChanged(false, true);
    1740                 : 
    1741                 :   //
    1742                 :   // Let the frame know we were reset
    1743                 :   //
    1744                 :   // Don't flush, if there's no frame yet it won't care about us being
    1745                 :   // reset even if we forced it to be created now.
    1746                 :   //
    1747               0 :   DispatchContentReset();
    1748                 : 
    1749               0 :   return NS_OK;
    1750                 : }
    1751                 : 
    1752                 : static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
    1753                 : 
    1754                 : NS_IMETHODIMP
    1755               0 : nsHTMLSelectElement::SubmitNamesValues(nsFormSubmission* aFormSubmission)
    1756                 : {
    1757                 :   // Disabled elements don't submit
    1758               0 :   if (IsDisabled()) {
    1759               0 :     return NS_OK;
    1760                 :   }
    1761                 : 
    1762                 :   //
    1763                 :   // Get the name (if no name, no submit)
    1764                 :   //
    1765               0 :   nsAutoString name;
    1766               0 :   GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
    1767               0 :   if (name.IsEmpty()) {
    1768               0 :     return NS_OK;
    1769                 :   }
    1770                 : 
    1771                 :   //
    1772                 :   // Submit
    1773                 :   //
    1774                 :   PRUint32 len;
    1775               0 :   GetLength(&len);
    1776                 : 
    1777               0 :   nsAutoString mozType;
    1778               0 :   nsCOMPtr<nsIFormProcessor> keyGenProcessor;
    1779               0 :   if (GetAttr(kNameSpaceID_None, nsGkAtoms::_moz_type, mozType) &&
    1780               0 :       mozType.EqualsLiteral("-mozilla-keygen")) {
    1781               0 :     keyGenProcessor = do_GetService(kFormProcessorCID);
    1782                 :   }
    1783                 : 
    1784               0 :   for (PRUint32 optIndex = 0; optIndex < len; optIndex++) {
    1785                 :     // Don't send disabled options
    1786                 :     bool disabled;
    1787               0 :     nsresult rv = IsOptionDisabled(optIndex, &disabled);
    1788               0 :     if (NS_FAILED(rv) || disabled) {
    1789               0 :       continue;
    1790                 :     }
    1791                 : 
    1792               0 :     nsIDOMHTMLOptionElement *option = mOptions->ItemAsOption(optIndex);
    1793               0 :     NS_ENSURE_TRUE(option, NS_ERROR_UNEXPECTED);
    1794                 : 
    1795                 :     bool isSelected;
    1796               0 :     rv = option->GetSelected(&isSelected);
    1797               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1798               0 :     if (!isSelected) {
    1799               0 :       continue;
    1800                 :     }
    1801                 : 
    1802               0 :     nsCOMPtr<nsIDOMHTMLOptionElement> optionElement = do_QueryInterface(option);
    1803               0 :     NS_ENSURE_TRUE(optionElement, NS_ERROR_UNEXPECTED);
    1804                 : 
    1805               0 :     nsAutoString value;
    1806               0 :     rv = optionElement->GetValue(value);
    1807               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1808                 : 
    1809               0 :     if (keyGenProcessor) {
    1810               0 :       nsAutoString tmp(value);
    1811               0 :       rv = keyGenProcessor->ProcessValue(this, name, tmp);
    1812               0 :       if (NS_SUCCEEDED(rv)) {
    1813               0 :         value = tmp;
    1814                 :       }
    1815                 :     }
    1816                 : 
    1817               0 :     rv = aFormSubmission->AddNameValuePair(name, value);
    1818                 :   }
    1819                 : 
    1820               0 :   return NS_OK;
    1821                 : }
    1822                 : 
    1823                 : NS_IMETHODIMP
    1824               0 : nsHTMLSelectElement::GetHasOptGroups(bool* aHasGroups)
    1825                 : {
    1826               0 :   *aHasGroups = (mOptGroupCount > 0);
    1827               0 :   return NS_OK;
    1828                 : }
    1829                 : 
    1830                 : void
    1831               0 : nsHTMLSelectElement::DispatchContentReset()
    1832                 : {
    1833               0 :   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
    1834               0 :   if (formControlFrame) {
    1835                 :     // Only dispatch content reset notification if this is a list control
    1836                 :     // frame or combo box control frame.
    1837               0 :     if (IsCombobox()) {
    1838               0 :       nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
    1839               0 :       if (comboFrame) {
    1840               0 :         comboFrame->OnContentReset();
    1841                 :       }
    1842                 :     } else {
    1843               0 :       nsIListControlFrame* listFrame = do_QueryFrame(formControlFrame);
    1844               0 :       if (listFrame) {
    1845               0 :         listFrame->OnContentReset();
    1846                 :       }
    1847                 :     }
    1848                 :   }
    1849               0 : }
    1850                 : 
    1851                 : static void
    1852               0 : AddOptionsRecurse(nsIContent* aRoot, nsHTMLOptionCollection* aArray)
    1853                 : {
    1854               0 :   for (nsIContent* cur = aRoot->GetFirstChild();
    1855                 :        cur;
    1856               0 :        cur = cur->GetNextSibling()) {
    1857               0 :     nsHTMLOptionElement* opt = nsHTMLOptionElement::FromContent(cur);
    1858               0 :     if (opt) {
    1859                 :       // If we fail here, then at least we've tried our best
    1860               0 :       aArray->AppendOption(opt);
    1861               0 :     } else if (cur->IsHTML(nsGkAtoms::optgroup)) {
    1862               0 :       AddOptionsRecurse(cur, aArray);
    1863                 :     }
    1864                 :   }
    1865               0 : }
    1866                 : 
    1867                 : void
    1868               0 : nsHTMLSelectElement::RebuildOptionsArray(bool aNotify)
    1869                 : {
    1870               0 :   mOptions->Clear();
    1871               0 :   AddOptionsRecurse(this, mOptions);
    1872               0 :   FindSelectedIndex(0, aNotify);
    1873               0 : }
    1874                 : 
    1875                 : bool
    1876               0 : nsHTMLSelectElement::IsValueMissing()
    1877                 : {
    1878               0 :   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
    1879               0 :     return false;
    1880                 :   }
    1881                 : 
    1882                 :   PRUint32 length;
    1883               0 :   mOptions->GetLength(&length);
    1884                 : 
    1885               0 :   for (PRUint32 i = 0; i < length; ++i) {
    1886               0 :     nsIDOMHTMLOptionElement* option = mOptions->ItemAsOption(i);
    1887                 :     bool selected;
    1888               0 :     NS_ENSURE_SUCCESS(option->GetSelected(&selected), false);
    1889                 : 
    1890               0 :     if (!selected) {
    1891               0 :       continue;
    1892                 :     }
    1893                 : 
    1894                 :     bool disabled;
    1895               0 :     IsOptionDisabled(i, &disabled);
    1896               0 :     if (disabled) {
    1897               0 :       continue;
    1898                 :     }
    1899                 : 
    1900               0 :     nsAutoString value;
    1901               0 :     NS_ENSURE_SUCCESS(option->GetValue(value), false);
    1902               0 :     if (!value.IsEmpty()) {
    1903               0 :       return false;
    1904                 :     }
    1905                 :   }
    1906                 : 
    1907               0 :   return true;
    1908                 : }
    1909                 : 
    1910                 : void
    1911               0 : nsHTMLSelectElement::UpdateValueMissingValidityState()
    1912                 : {
    1913               0 :   SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
    1914               0 : }
    1915                 : 
    1916                 : nsresult
    1917               0 : nsHTMLSelectElement::GetValidationMessage(nsAString& aValidationMessage,
    1918                 :                                           ValidityStateType aType)
    1919                 : {
    1920               0 :   switch (aType) {
    1921                 :     case VALIDITY_STATE_VALUE_MISSING: {
    1922               0 :       nsXPIDLString message;
    1923                 :       nsresult rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
    1924                 :                                                        "FormValidationSelectMissing",
    1925               0 :                                                        message);
    1926               0 :       aValidationMessage = message;
    1927               0 :       return rv;
    1928                 :     }
    1929                 :     default: {
    1930               0 :       return nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
    1931                 :     }
    1932                 :   }
    1933                 : }
    1934                 : 
    1935                 : #ifdef DEBUG
    1936                 : 
    1937                 : static void
    1938               0 : VerifyOptionsRecurse(nsIContent* aRoot, PRInt32& aIndex,
    1939                 :                      nsHTMLOptionCollection* aArray)
    1940                 : {
    1941               0 :   for (nsIContent* cur = aRoot->GetFirstChild();
    1942                 :        cur;
    1943               0 :        cur = cur->GetNextSibling()) {
    1944               0 :     nsCOMPtr<nsIDOMHTMLOptionElement> opt = do_QueryInterface(cur);
    1945               0 :     if (opt) {
    1946               0 :       NS_ASSERTION(opt == aArray->ItemAsOption(aIndex++),
    1947                 :                    "Options collection broken");
    1948               0 :     } else if (cur->IsHTML(nsGkAtoms::optgroup)) {
    1949               0 :       VerifyOptionsRecurse(cur, aIndex, aArray);
    1950                 :     }
    1951                 :   }
    1952               0 : }
    1953                 : 
    1954                 : void
    1955               0 : nsHTMLSelectElement::VerifyOptionsArray()
    1956                 : {
    1957               0 :   PRInt32 aIndex = 0;
    1958               0 :   VerifyOptionsRecurse(this, aIndex, mOptions);
    1959               0 : }
    1960                 : 
    1961                 : #endif
    1962                 : 
    1963                 : //----------------------------------------------------------------------
    1964                 : //
    1965                 : // nsHTMLOptionCollection implementation
    1966                 : //
    1967                 : 
    1968               0 : nsHTMLOptionCollection::nsHTMLOptionCollection(nsHTMLSelectElement* aSelect)
    1969                 : {
    1970               0 :   SetIsProxy();
    1971                 : 
    1972                 :   // Do not maintain a reference counted reference. When
    1973                 :   // the select goes away, it will let us know.
    1974               0 :   mSelect = aSelect;
    1975               0 : }
    1976                 : 
    1977               0 : nsHTMLOptionCollection::~nsHTMLOptionCollection()
    1978                 : {
    1979               0 :   DropReference();
    1980               0 : }
    1981                 : 
    1982                 : void
    1983               0 : nsHTMLOptionCollection::DropReference()
    1984                 : {
    1985                 :   // Drop our (non ref-counted) reference
    1986               0 :   mSelect = nsnull;
    1987               0 : }
    1988                 : 
    1989                 : nsresult
    1990               0 : nsHTMLOptionCollection::GetOptionIndex(mozilla::dom::Element* aOption,
    1991                 :                                        PRInt32 aStartIndex,
    1992                 :                                        bool aForward,
    1993                 :                                        PRInt32* aIndex)
    1994                 : {
    1995                 :   // NOTE: aIndex shouldn't be set if the returned value isn't NS_OK.
    1996                 : 
    1997                 :   PRInt32 index;
    1998                 : 
    1999                 :   // Make the common case fast
    2000               0 :   if (aStartIndex == 0 && aForward) {
    2001               0 :     index = mElements.IndexOf(aOption);
    2002               0 :     if (index == -1) {
    2003               0 :       return NS_ERROR_FAILURE;
    2004                 :     }
    2005                 :     
    2006               0 :     *aIndex = index;
    2007               0 :     return NS_OK;
    2008                 :   }
    2009                 : 
    2010               0 :   PRInt32 high = mElements.Length();
    2011               0 :   PRInt32 step = aForward ? 1 : -1;
    2012                 : 
    2013               0 :   for (index = aStartIndex; index < high && index > -1; index += step) {
    2014               0 :     if (mElements[index] == aOption) {
    2015               0 :       *aIndex = index;
    2016               0 :       return NS_OK;
    2017                 :     }
    2018                 :   }
    2019                 : 
    2020               0 :   return NS_ERROR_FAILURE;
    2021                 : }
    2022                 : 
    2023                 : 
    2024            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLOptionCollection)
    2025               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHTMLOptionCollection)
    2026               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mElements)
    2027               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    2028               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    2029               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHTMLOptionCollection)
    2030                 :   {
    2031                 :     PRUint32 i;
    2032               0 :     for (i = 0; i < tmp->mElements.Length(); ++i) {
    2033               0 :       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mElements[i]");
    2034               0 :       cb.NoteXPCOMChild(static_cast<Element*>(tmp->mElements[i]));
    2035                 :     }
    2036                 :   }
    2037               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
    2038               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    2039               0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsHTMLOptionCollection)
    2040               0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    2041               0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
    2042                 : 
    2043                 : // nsISupports
    2044                 : 
    2045                 : DOMCI_DATA(HTMLOptionsCollection, nsHTMLOptionCollection)
    2046                 : 
    2047                 : // QueryInterface implementation for nsHTMLOptionCollection
    2048               0 : NS_INTERFACE_TABLE_HEAD(nsHTMLOptionCollection)
    2049               0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    2050               0 :   NS_INTERFACE_TABLE3(nsHTMLOptionCollection,
    2051                 :                       nsIHTMLCollection,
    2052                 :                       nsIDOMHTMLOptionsCollection,
    2053                 :                       nsIDOMHTMLCollection)
    2054               0 :   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHTMLOptionCollection)
    2055               0 :   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(HTMLOptionsCollection)
    2056               0 : NS_INTERFACE_MAP_END
    2057                 : 
    2058                 : 
    2059               0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHTMLOptionCollection)
    2060               0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHTMLOptionCollection)
    2061                 : 
    2062                 : 
    2063                 : JSObject*
    2064               0 : nsHTMLOptionCollection::WrapObject(JSContext *cx, XPCWrappedNativeScope *scope,
    2065                 :                                    bool *triedToWrap)
    2066                 : {
    2067                 :   return mozilla::dom::binding::HTMLOptionsCollection::create(cx, scope, this,
    2068               0 :                                                               triedToWrap);
    2069                 : }
    2070                 : 
    2071                 : NS_IMETHODIMP
    2072               0 : nsHTMLOptionCollection::GetLength(PRUint32* aLength)
    2073                 : {
    2074               0 :   *aLength = mElements.Length();
    2075                 : 
    2076               0 :   return NS_OK;
    2077                 : }
    2078                 : 
    2079                 : NS_IMETHODIMP
    2080               0 : nsHTMLOptionCollection::SetLength(PRUint32 aLength)
    2081                 : {
    2082               0 :   if (!mSelect) {
    2083               0 :     return NS_ERROR_UNEXPECTED;
    2084                 :   }
    2085                 : 
    2086               0 :   return mSelect->SetLength(aLength);
    2087                 : }
    2088                 : 
    2089                 : NS_IMETHODIMP
    2090               0 : nsHTMLOptionCollection::SetOption(PRUint32 aIndex,
    2091                 :                                   nsIDOMHTMLOptionElement *aOption)
    2092                 : {
    2093               0 :   if (!mSelect) {
    2094               0 :     return NS_OK;
    2095                 :   }
    2096                 : 
    2097                 :   // if the new option is null, just remove this option.  Note that it's safe
    2098                 :   // to pass a too-large aIndex in here.
    2099               0 :   if (!aOption) {
    2100               0 :     mSelect->Remove(aIndex);
    2101                 : 
    2102                 :     // We're done.
    2103               0 :     return NS_OK;
    2104                 :   }
    2105                 : 
    2106               0 :   nsresult rv = NS_OK;
    2107                 : 
    2108               0 :   PRUint32 index = PRUint32(aIndex);
    2109                 : 
    2110                 :   // Now we're going to be setting an option in our collection
    2111               0 :   if (index > mElements.Length()) {
    2112                 :     // Fill our array with blank options up to (but not including, since we're
    2113                 :     // about to change it) aIndex, for compat with other browsers.
    2114               0 :     rv = SetLength(index);
    2115               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2116                 :   }
    2117                 : 
    2118               0 :   NS_ASSERTION(index <= mElements.Length(), "SetLength lied");
    2119                 :   
    2120               0 :   nsCOMPtr<nsIDOMNode> ret;
    2121               0 :   if (index == mElements.Length()) {
    2122               0 :     rv = mSelect->AppendChild(aOption, getter_AddRefs(ret));
    2123                 :   } else {
    2124                 :     // Find the option they're talking about and replace it
    2125                 :     // hold a strong reference to follow COM rules.
    2126               0 :     nsCOMPtr<nsIDOMHTMLOptionElement> refChild = ItemAsOption(index);
    2127               0 :     NS_ENSURE_TRUE(refChild, NS_ERROR_UNEXPECTED);
    2128                 : 
    2129               0 :     nsCOMPtr<nsIDOMNode> parent;
    2130               0 :     refChild->GetParentNode(getter_AddRefs(parent));
    2131               0 :     if (parent) {
    2132               0 :       rv = parent->ReplaceChild(aOption, refChild, getter_AddRefs(ret));
    2133                 :     }
    2134                 :   }
    2135                 : 
    2136               0 :   return rv;
    2137                 : }
    2138                 : 
    2139                 : NS_IMETHODIMP
    2140               0 : nsHTMLOptionCollection::GetSelectedIndex(PRInt32 *aSelectedIndex)
    2141                 : {
    2142               0 :   NS_ENSURE_TRUE(mSelect, NS_ERROR_UNEXPECTED);
    2143                 : 
    2144               0 :   return mSelect->GetSelectedIndex(aSelectedIndex);
    2145                 : }
    2146                 : 
    2147                 : NS_IMETHODIMP
    2148               0 : nsHTMLOptionCollection::SetSelectedIndex(PRInt32 aSelectedIndex)
    2149                 : {
    2150               0 :   NS_ENSURE_TRUE(mSelect, NS_ERROR_UNEXPECTED);
    2151                 : 
    2152               0 :   return mSelect->SetSelectedIndex(aSelectedIndex);
    2153                 : }
    2154                 : 
    2155                 : NS_IMETHODIMP
    2156               0 : nsHTMLOptionCollection::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
    2157                 : {
    2158               0 :   nsISupports* item = GetNodeAt(aIndex);
    2159               0 :   if (!item) {
    2160               0 :     *aReturn = nsnull;
    2161                 : 
    2162               0 :     return NS_OK;
    2163                 :   }
    2164                 : 
    2165               0 :   return CallQueryInterface(item, aReturn);
    2166                 : }
    2167                 : 
    2168                 : nsIContent*
    2169               0 : nsHTMLOptionCollection::GetNodeAt(PRUint32 aIndex)
    2170                 : {
    2171               0 :   return static_cast<nsIContent*>(ItemAsOption(aIndex));
    2172                 : }
    2173                 : 
    2174                 : static nsHTMLOptionElement*
    2175               0 : GetNamedItemHelper(nsTArray<nsRefPtr<nsHTMLOptionElement> > &aElements,
    2176                 :                    const nsAString& aName)
    2177                 : {
    2178               0 :   PRUint32 count = aElements.Length();
    2179               0 :   for (PRUint32 i = 0; i < count; i++) {
    2180               0 :     nsHTMLOptionElement *content = aElements.ElementAt(i);
    2181               0 :     if (content &&
    2182                 :         (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, aName,
    2183               0 :                               eCaseMatters) ||
    2184                 :          content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, aName,
    2185               0 :                               eCaseMatters))) {
    2186               0 :       return content;
    2187                 :     }
    2188                 :   }
    2189                 : 
    2190               0 :   return nsnull;
    2191                 : }
    2192                 : 
    2193                 : nsISupports*
    2194               0 : nsHTMLOptionCollection::GetNamedItem(const nsAString& aName,
    2195                 :                                      nsWrapperCache **aCache)
    2196                 : {
    2197               0 :   nsINode *item = GetNamedItemHelper(mElements, aName);
    2198               0 :   *aCache = item;
    2199               0 :   return item;
    2200                 : }
    2201                 : 
    2202                 : nsINode*
    2203               0 : nsHTMLOptionCollection::GetParentObject()
    2204                 : {
    2205               0 :     return mSelect;
    2206                 : }
    2207                 : 
    2208                 : NS_IMETHODIMP
    2209               0 : nsHTMLOptionCollection::NamedItem(const nsAString& aName,
    2210                 :                                   nsIDOMNode** aReturn)
    2211                 : {
    2212               0 :   NS_IF_ADDREF(*aReturn = GetNamedItemHelper(mElements, aName));
    2213                 : 
    2214               0 :   return NS_OK;
    2215                 : }
    2216                 : 
    2217                 : NS_IMETHODIMP
    2218               0 : nsHTMLOptionCollection::GetSelect(nsIDOMHTMLSelectElement **aReturn)
    2219                 : {
    2220               0 :   NS_IF_ADDREF(*aReturn = mSelect);
    2221               0 :   return NS_OK;
    2222                 : }
    2223                 : 
    2224                 : NS_IMETHODIMP
    2225               0 : nsHTMLOptionCollection::Add(nsIDOMHTMLOptionElement *aOption,
    2226                 :                             nsIVariant *aBefore)
    2227                 : {
    2228               0 :   if (!aOption) {
    2229               0 :     return NS_ERROR_INVALID_ARG;
    2230                 :   }
    2231                 : 
    2232               0 :   if (!mSelect) {
    2233               0 :     return NS_ERROR_NOT_INITIALIZED;
    2234                 :   }
    2235                 : 
    2236               0 :   return mSelect->Add(aOption, aBefore);
    2237                 : }
    2238                 : 
    2239                 : NS_IMETHODIMP
    2240               0 : nsHTMLOptionCollection::Remove(PRInt32 aIndex)
    2241                 : {
    2242               0 :   NS_ENSURE_TRUE(mSelect, NS_ERROR_UNEXPECTED);
    2243                 : 
    2244               0 :   PRUint32 len = 0;
    2245               0 :   mSelect->GetLength(&len);
    2246               0 :   if (aIndex < 0 || (PRUint32)aIndex >= len)
    2247               0 :     aIndex = 0;
    2248                 : 
    2249               0 :   return mSelect->Remove(aIndex);
    2250                 : }
    2251                 : 
    2252                 : void
    2253               0 : nsHTMLSelectElement::UpdateBarredFromConstraintValidation()
    2254                 : {
    2255               0 :   SetBarredFromConstraintValidation(IsDisabled());
    2256               0 : }
    2257                 : 
    2258                 : void
    2259               0 : nsHTMLSelectElement::FieldSetDisabledChanged(bool aNotify)
    2260                 : {
    2261               0 :   UpdateBarredFromConstraintValidation();
    2262                 : 
    2263               0 :   nsGenericHTMLFormElement::FieldSetDisabledChanged(aNotify);
    2264               0 : }
    2265                 : 
    2266                 : void
    2267               0 : nsHTMLSelectElement::SetSelectionChanged(bool aValue, bool aNotify)
    2268                 : {
    2269               0 :   if (!mDefaultSelectionSet) {
    2270               0 :     return;
    2271                 :   }
    2272                 : 
    2273               0 :   bool previousSelectionChangedValue = mSelectionHasChanged;
    2274               0 :   mSelectionHasChanged = aValue;
    2275                 : 
    2276               0 :   if (mSelectionHasChanged != previousSelectionChangedValue) {
    2277               0 :     UpdateState(aNotify);
    2278                 :   }
    2279            4392 : }

Generated by: LCOV version 1.7