LCOV - code coverage report
Current view: top level - dom/html - HTMLSelectElement.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 853 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 112 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/dom/HTMLSelectElement.h"
       8             : 
       9             : #include "mozAutoDocUpdate.h"
      10             : #include "mozilla/Attributes.h"
      11             : #include "mozilla/BasicEvents.h"
      12             : #include "mozilla/EventDispatcher.h"
      13             : #include "mozilla/EventStates.h"
      14             : #include "mozilla/dom/Element.h"
      15             : #include "mozilla/dom/HTMLFormSubmission.h"
      16             : #include "mozilla/dom/HTMLOptGroupElement.h"
      17             : #include "mozilla/dom/HTMLOptionElement.h"
      18             : #include "mozilla/dom/HTMLSelectElementBinding.h"
      19             : #include "mozilla/dom/UnionTypes.h"
      20             : #include "mozilla/GenericSpecifiedValuesInlines.h"
      21             : #include "nsContentCreatorFunctions.h"
      22             : #include "nsContentList.h"
      23             : #include "nsError.h"
      24             : #include "nsGkAtoms.h"
      25             : #include "nsIComboboxControlFrame.h"
      26             : #include "nsIDocument.h"
      27             : #include "nsIFormControlFrame.h"
      28             : #include "nsIForm.h"
      29             : #include "nsIFormProcessor.h"
      30             : #include "nsIFrame.h"
      31             : #include "nsIListControlFrame.h"
      32             : #include "nsISelectControlFrame.h"
      33             : #include "nsLayoutUtils.h"
      34             : #include "nsMappedAttributes.h"
      35             : #include "nsPresState.h"
      36             : #include "nsServiceManagerUtils.h"
      37             : #include "nsStyleConsts.h"
      38             : #include "nsTextNode.h"
      39             : 
      40           0 : NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Select)
      41             : 
      42             : namespace mozilla {
      43             : namespace dom {
      44             : 
      45           0 : NS_IMPL_ISUPPORTS(SelectState, SelectState)
      46             : 
      47             : //----------------------------------------------------------------------
      48             : //
      49             : // SafeOptionListMutation
      50             : //
      51             : 
      52           0 : SafeOptionListMutation::SafeOptionListMutation(nsIContent* aSelect,
      53             :                                                nsIContent* aParent,
      54             :                                                nsIContent* aKid,
      55             :                                                uint32_t aIndex,
      56           0 :                                                bool aNotify)
      57             :   : mSelect(HTMLSelectElement::FromContentOrNull(aSelect))
      58             :   , mTopLevelMutation(false)
      59             :   , mNeedsRebuild(false)
      60             :   , mNotify(aNotify)
      61           0 :   , mInitialSelectedIndex(-1)
      62             : {
      63           0 :   if (mSelect) {
      64           0 :     mInitialSelectedIndex = mSelect->SelectedIndex();
      65           0 :     mTopLevelMutation = !mSelect->mMutating;
      66           0 :     if (mTopLevelMutation) {
      67           0 :       mSelect->mMutating = true;
      68             :     } else {
      69             :       // This is very unfortunate, but to handle mutation events properly,
      70             :       // option list must be up-to-date before inserting or removing options.
      71             :       // Fortunately this is called only if mutation event listener
      72             :       // adds or removes options.
      73           0 :       mSelect->RebuildOptionsArray(mNotify);
      74             :     }
      75             :     nsresult rv;
      76           0 :     if (aKid) {
      77           0 :       rv = mSelect->WillAddOptions(aKid, aParent, aIndex, mNotify);
      78             :     } else {
      79           0 :       rv = mSelect->WillRemoveOptions(aParent, aIndex, mNotify);
      80             :     }
      81           0 :     mNeedsRebuild = NS_FAILED(rv);
      82             :   }
      83           0 : }
      84             : 
      85           0 : SafeOptionListMutation::~SafeOptionListMutation()
      86             : {
      87           0 :   if (mSelect) {
      88           0 :     if (mNeedsRebuild || (mTopLevelMutation && mGuard.Mutated(1))) {
      89           0 :       mSelect->RebuildOptionsArray(true);
      90             :     }
      91           0 :     if (mTopLevelMutation) {
      92           0 :       mSelect->mMutating = false;
      93             :     }
      94           0 :     if (mSelect->SelectedIndex() != mInitialSelectedIndex) {
      95             :       // We must have triggered the SelectSomething() codepath, which can cause
      96             :       // our validity to change.  Unfortunately, our attempt to update validity
      97             :       // in that case may not have worked correctly, because we actually call it
      98             :       // before we have inserted the new <option>s into the DOM!  Go ahead and
      99             :       // update validity here as needed, because by now we know our <option>s
     100             :       // are where they should be.
     101           0 :       mSelect->UpdateValueMissingValidityState();
     102           0 :       mSelect->UpdateState(mNotify);
     103             :     }
     104             : #ifdef DEBUG
     105           0 :     mSelect->VerifyOptionsArray();
     106             : #endif
     107             :   }
     108           0 : }
     109             : 
     110             : //----------------------------------------------------------------------
     111             : //
     112             : // HTMLSelectElement
     113             : //
     114             : 
     115             : // construction, destruction
     116             : 
     117             : 
     118           0 : HTMLSelectElement::HTMLSelectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
     119           0 :                                      FromParser aFromParser)
     120             :   : nsGenericHTMLFormElementWithState(aNodeInfo, NS_FORM_SELECT),
     121           0 :     mOptions(new HTMLOptionsCollection(this)),
     122             :     mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown),
     123             :     mAutocompleteInfoState(nsContentUtils::eAutocompleteAttrState_Unknown),
     124           0 :     mIsDoneAddingChildren(!aFromParser),
     125             :     mDisabledChanged(false),
     126             :     mMutating(false),
     127           0 :     mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
     128             :     mSelectionHasChanged(false),
     129             :     mDefaultSelectionSet(false),
     130             :     mCanShowInvalidUI(true),
     131             :     mCanShowValidUI(true),
     132             :     mNonOptionChildren(0),
     133             :     mOptGroupCount(0),
     134           0 :     mSelectedIndex(-1)
     135             : {
     136           0 :   SetHasWeirdParserInsertionMode();
     137             : 
     138             :   // DoneAddingChildren() will be called later if it's from the parser,
     139             :   // otherwise it is
     140             : 
     141             :   // Set up our default state: enabled, optional, and valid.
     142           0 :   AddStatesSilently(NS_EVENT_STATE_ENABLED |
     143           0 :                     NS_EVENT_STATE_OPTIONAL |
     144           0 :                     NS_EVENT_STATE_VALID);
     145           0 : }
     146             : 
     147           0 : HTMLSelectElement::~HTMLSelectElement()
     148             : {
     149           0 :   mOptions->DropReference();
     150           0 : }
     151             : 
     152             : // ISupports
     153             : 
     154             : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLSelectElement)
     155             : 
     156           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLSelectElement,
     157             :                                                   nsGenericHTMLFormElementWithState)
     158           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
     159           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOptions)
     160           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedOptions)
     161           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     162           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLSelectElement,
     163             :                                                 nsGenericHTMLFormElementWithState)
     164           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
     165           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedOptions)
     166           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     167             : 
     168           0 : NS_IMPL_ADDREF_INHERITED(HTMLSelectElement, Element)
     169           0 : NS_IMPL_RELEASE_INHERITED(HTMLSelectElement, Element)
     170             : 
     171             : // QueryInterface implementation for HTMLSelectElement
     172           0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLSelectElement)
     173           0 :   NS_INTERFACE_TABLE_INHERITED(HTMLSelectElement,
     174             :                                nsIDOMHTMLSelectElement,
     175             :                                nsIConstraintValidation)
     176           0 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElementWithState)
     177             : 
     178             : 
     179             : // nsIDOMHTMLSelectElement
     180             : 
     181             : 
     182           0 : NS_IMPL_ELEMENT_CLONE(HTMLSelectElement)
     183             : 
     184             : // nsIConstraintValidation
     185           0 : NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(HTMLSelectElement)
     186             : 
     187             : NS_IMETHODIMP
     188           0 : HTMLSelectElement::SetCustomValidity(const nsAString& aError)
     189             : {
     190           0 :   nsIConstraintValidation::SetCustomValidity(aError);
     191             : 
     192           0 :   UpdateState(true);
     193             : 
     194           0 :   return NS_OK;
     195             : }
     196             : 
     197             : void
     198           0 : HTMLSelectElement::GetAutocomplete(DOMString& aValue)
     199             : {
     200           0 :   const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
     201             : 
     202           0 :   mAutocompleteAttrState =
     203           0 :     nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aValue,
     204           0 :                                                    mAutocompleteAttrState);
     205           0 : }
     206             : 
     207             : void
     208           0 : HTMLSelectElement::GetAutocompleteInfo(AutocompleteInfo& aInfo)
     209             : {
     210           0 :   const nsAttrValue* attributeVal = GetParsedAttr(nsGkAtoms::autocomplete);
     211           0 :   mAutocompleteInfoState =
     212           0 :     nsContentUtils::SerializeAutocompleteAttribute(attributeVal, aInfo,
     213           0 :                                                    mAutocompleteInfoState,
     214             :                                                    true);
     215           0 : }
     216             : 
     217             : NS_IMETHODIMP
     218           0 : HTMLSelectElement::GetForm(nsIDOMHTMLFormElement** aForm)
     219             : {
     220           0 :   return nsGenericHTMLFormElementWithState::GetForm(aForm);
     221             : }
     222             : 
     223             : nsresult
     224           0 : HTMLSelectElement::InsertChildAt(nsIContent* aKid,
     225             :                                  uint32_t aIndex,
     226             :                                  bool aNotify)
     227             : {
     228           0 :   SafeOptionListMutation safeMutation(this, this, aKid, aIndex, aNotify);
     229           0 :   nsresult rv = nsGenericHTMLFormElementWithState::InsertChildAt(aKid, aIndex,
     230           0 :                                                                  aNotify);
     231           0 :   if (NS_FAILED(rv)) {
     232           0 :     safeMutation.MutationFailed();
     233             :   }
     234           0 :   return rv;
     235             : }
     236             : 
     237             : void
     238           0 : HTMLSelectElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
     239             : {
     240           0 :   SafeOptionListMutation safeMutation(this, this, nullptr, aIndex, aNotify);
     241           0 :   nsGenericHTMLFormElementWithState::RemoveChildAt(aIndex, aNotify);
     242           0 : }
     243             : 
     244             : 
     245             : 
     246             : void
     247           0 : HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions,
     248             :                                          int32_t aListIndex,
     249             :                                          int32_t aDepth,
     250             :                                          bool aNotify)
     251             : {
     252           0 :   MOZ_ASSERT(aDepth == 0 || aDepth == 1);
     253           0 :   int32_t insertIndex = aListIndex;
     254             : 
     255           0 :   HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
     256           0 :   if (optElement) {
     257           0 :     mOptions->InsertOptionAt(optElement, insertIndex);
     258           0 :     insertIndex++;
     259           0 :   } else if (aDepth == 0) {
     260             :     // If it's at the top level, then we just found out there are non-options
     261             :     // at the top level, which will throw off the insert count
     262           0 :     mNonOptionChildren++;
     263             : 
     264             :     // Deal with optgroups
     265           0 :     if (aOptions->IsHTMLElement(nsGkAtoms::optgroup)) {
     266           0 :       mOptGroupCount++;
     267             : 
     268           0 :       for (nsIContent* child = aOptions->GetFirstChild();
     269           0 :            child;
     270           0 :            child = child->GetNextSibling()) {
     271           0 :         optElement = HTMLOptionElement::FromContent(child);
     272           0 :         if (optElement) {
     273           0 :           mOptions->InsertOptionAt(optElement, insertIndex);
     274           0 :           insertIndex++;
     275             :         }
     276             :       }
     277             :     }
     278             :   } // else ignore even if optgroup; we want to ignore nested optgroups.
     279             : 
     280             :   // Deal with the selected list
     281           0 :   if (insertIndex - aListIndex) {
     282             :     // Fix the currently selected index
     283           0 :     if (aListIndex <= mSelectedIndex) {
     284           0 :       mSelectedIndex += (insertIndex - aListIndex);
     285           0 :       SetSelectionChanged(true, aNotify);
     286             :     }
     287             : 
     288             :     // Get the frame stuff for notification. No need to flush here
     289             :     // since if there's no frame for the select yet the select will
     290             :     // get into the right state once it's created.
     291           0 :     nsISelectControlFrame* selectFrame = nullptr;
     292           0 :     AutoWeakFrame weakSelectFrame;
     293           0 :     bool didGetFrame = false;
     294             : 
     295             :     // Actually select the options if the added options warrant it
     296           0 :     for (int32_t i = aListIndex; i < insertIndex; i++) {
     297             :       // Notify the frame that the option is added
     298           0 :       if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
     299           0 :         selectFrame = GetSelectFrame();
     300           0 :         weakSelectFrame = do_QueryFrame(selectFrame);
     301           0 :         didGetFrame = true;
     302             :       }
     303             : 
     304           0 :       if (selectFrame) {
     305           0 :         selectFrame->AddOption(i);
     306             :       }
     307             : 
     308           0 :       RefPtr<HTMLOptionElement> option = Item(i);
     309           0 :       if (option && option->Selected()) {
     310             :         // Clear all other options
     311           0 :         if (!HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
     312           0 :           uint32_t mask = IS_SELECTED | CLEAR_ALL | SET_DISABLED | NOTIFY;
     313           0 :           SetOptionsSelectedByIndex(i, i, mask);
     314             :         }
     315             : 
     316             :         // This is sort of a hack ... we need to notify that the option was
     317             :         // set and change selectedIndex even though we didn't really change
     318             :         // its value.
     319           0 :         OnOptionSelected(selectFrame, i, true, false, false);
     320             :       }
     321             :     }
     322             : 
     323           0 :     CheckSelectSomething(aNotify);
     324             :   }
     325           0 : }
     326             : 
     327             : nsresult
     328           0 : HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
     329             :                                          int32_t aListIndex,
     330             :                                          int32_t aDepth,
     331             :                                          bool aNotify)
     332             : {
     333           0 :   MOZ_ASSERT(aDepth == 0 || aDepth == 1);
     334           0 :   int32_t numRemoved = 0;
     335             : 
     336           0 :   HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
     337           0 :   if (optElement) {
     338           0 :     if (mOptions->ItemAsOption(aListIndex) != optElement) {
     339           0 :       NS_ERROR("wrong option at index");
     340           0 :       return NS_ERROR_UNEXPECTED;
     341             :     }
     342           0 :     mOptions->RemoveOptionAt(aListIndex);
     343           0 :     numRemoved++;
     344           0 :   } else if (aDepth == 0) {
     345             :     // Yay, one less artifact at the top level.
     346           0 :     mNonOptionChildren--;
     347             : 
     348             :     // Recurse down deeper for options
     349           0 :     if (mOptGroupCount && aOptions->IsHTMLElement(nsGkAtoms::optgroup)) {
     350           0 :       mOptGroupCount--;
     351             : 
     352           0 :       for (nsIContent* child = aOptions->GetFirstChild();
     353           0 :           child;
     354           0 :           child = child->GetNextSibling()) {
     355           0 :         optElement = HTMLOptionElement::FromContent(child);
     356           0 :         if (optElement) {
     357           0 :           if (mOptions->ItemAsOption(aListIndex) != optElement) {
     358           0 :             NS_ERROR("wrong option at index");
     359           0 :             return NS_ERROR_UNEXPECTED;
     360             :           }
     361           0 :           mOptions->RemoveOptionAt(aListIndex);
     362           0 :           numRemoved++;
     363             :         }
     364             :       }
     365             :     }
     366             :   } // else don't check for an optgroup; we want to ignore nested optgroups
     367             : 
     368           0 :   if (numRemoved) {
     369             :     // Tell the widget we removed the options
     370           0 :     nsISelectControlFrame* selectFrame = GetSelectFrame();
     371           0 :     if (selectFrame) {
     372           0 :       nsAutoScriptBlocker scriptBlocker;
     373           0 :       for (int32_t i = aListIndex; i < aListIndex + numRemoved; ++i) {
     374           0 :         selectFrame->RemoveOption(i);
     375             :       }
     376             :     }
     377             : 
     378             :     // Fix the selected index
     379           0 :     if (aListIndex <= mSelectedIndex) {
     380           0 :       if (mSelectedIndex < (aListIndex+numRemoved)) {
     381             :         // aListIndex <= mSelectedIndex < aListIndex+numRemoved
     382             :         // Find a new selected index if it was one of the ones removed.
     383           0 :         FindSelectedIndex(aListIndex, aNotify);
     384             :       } else {
     385             :         // Shift the selected index if something in front of it was removed
     386             :         // aListIndex+numRemoved <= mSelectedIndex
     387           0 :         mSelectedIndex -= numRemoved;
     388           0 :         SetSelectionChanged(true, aNotify);
     389             :       }
     390             :     }
     391             : 
     392             :     // Select something in case we removed the selected option on a
     393             :     // single select
     394           0 :     if (!CheckSelectSomething(aNotify) && mSelectedIndex == -1) {
     395             :       // Update the validity state in case of we've just removed the last
     396             :       // option.
     397           0 :       UpdateValueMissingValidityState();
     398             : 
     399           0 :       UpdateState(aNotify);
     400             :     }
     401             :   }
     402             : 
     403           0 :   return NS_OK;
     404             : }
     405             : 
     406             : // XXXldb Doing the processing before the content nodes have been added
     407             : // to the document (as the name of this function seems to require, and
     408             : // as the callers do), is highly unusual.  Passing around unparented
     409             : // content to other parts of the app can make those things think the
     410             : // options are the root content node.
     411             : NS_IMETHODIMP
     412           0 : HTMLSelectElement::WillAddOptions(nsIContent* aOptions,
     413             :                                   nsIContent* aParent,
     414             :                                   int32_t aContentIndex,
     415             :                                   bool aNotify)
     416             : {
     417           0 :   if (this != aParent && this != aParent->GetParent()) {
     418           0 :     return NS_OK;
     419             :   }
     420           0 :   int32_t level = aParent == this ? 0 : 1;
     421             : 
     422             :   // Get the index where the options will be inserted
     423           0 :   int32_t ind = -1;
     424           0 :   if (!mNonOptionChildren) {
     425             :     // If there are no artifacts, aContentIndex == ind
     426           0 :     ind = aContentIndex;
     427             :   } else {
     428             :     // If there are artifacts, we have to get the index of the option the
     429             :     // hard way
     430           0 :     int32_t children = aParent->GetChildCount();
     431             : 
     432           0 :     if (aContentIndex >= children) {
     433             :       // If the content insert is after the end of the parent, then we want to get
     434             :       // the next index *after* the parent and insert there.
     435           0 :       ind = GetOptionIndexAfter(aParent);
     436             :     } else {
     437             :       // If the content insert is somewhere in the middle of the container, then
     438             :       // we want to get the option currently at the index and insert in front of
     439             :       // that.
     440           0 :       nsIContent* currentKid = aParent->GetChildAt(aContentIndex);
     441           0 :       NS_ASSERTION(currentKid, "Child not found!");
     442           0 :       if (currentKid) {
     443           0 :         ind = GetOptionIndexAt(currentKid);
     444             :       } else {
     445           0 :         ind = -1;
     446             :       }
     447             :     }
     448             :   }
     449             : 
     450           0 :   InsertOptionsIntoList(aOptions, ind, level, aNotify);
     451           0 :   return NS_OK;
     452             : }
     453             : 
     454             : NS_IMETHODIMP
     455           0 : HTMLSelectElement::WillRemoveOptions(nsIContent* aParent,
     456             :                                      int32_t aContentIndex,
     457             :                                      bool aNotify)
     458             : {
     459           0 :   if (this != aParent && this != aParent->GetParent()) {
     460           0 :     return NS_OK;
     461             :   }
     462           0 :   int32_t level = this == aParent ? 0 : 1;
     463             : 
     464             :   // Get the index where the options will be removed
     465           0 :   nsIContent* currentKid = aParent->GetChildAt(aContentIndex);
     466           0 :   if (currentKid) {
     467             :     int32_t ind;
     468           0 :     if (!mNonOptionChildren) {
     469             :       // If there are no artifacts, aContentIndex == ind
     470           0 :       ind = aContentIndex;
     471             :     } else {
     472             :       // If there are artifacts, we have to get the index of the option the
     473             :       // hard way
     474           0 :       ind = GetFirstOptionIndex(currentKid);
     475             :     }
     476           0 :     if (ind != -1) {
     477           0 :       nsresult rv = RemoveOptionsFromList(currentKid, ind, level, aNotify);
     478           0 :       NS_ENSURE_SUCCESS(rv, rv);
     479             :     }
     480             :   }
     481             : 
     482           0 :   return NS_OK;
     483             : }
     484             : 
     485             : int32_t
     486           0 : HTMLSelectElement::GetOptionIndexAt(nsIContent* aOptions)
     487             : {
     488             :   // Search this node and below.
     489             :   // If not found, find the first one *after* this node.
     490           0 :   int32_t retval = GetFirstOptionIndex(aOptions);
     491           0 :   if (retval == -1) {
     492           0 :     retval = GetOptionIndexAfter(aOptions);
     493             :   }
     494             : 
     495           0 :   return retval;
     496             : }
     497             : 
     498             : int32_t
     499           0 : HTMLSelectElement::GetOptionIndexAfter(nsIContent* aOptions)
     500             : {
     501             :   // - If this is the select, the next option is the last.
     502             :   // - If not, search all the options after aOptions and up to the last option
     503             :   //   in the parent.
     504             :   // - If it's not there, search for the first option after the parent.
     505           0 :   if (aOptions == this) {
     506           0 :     return Length();
     507             :   }
     508             : 
     509           0 :   int32_t retval = -1;
     510             : 
     511           0 :   nsCOMPtr<nsIContent> parent = aOptions->GetParent();
     512             : 
     513           0 :   if (parent) {
     514           0 :     int32_t index = parent->IndexOf(aOptions);
     515           0 :     int32_t count = parent->GetChildCount();
     516             : 
     517           0 :     retval = GetFirstChildOptionIndex(parent, index+1, count);
     518             : 
     519           0 :     if (retval == -1) {
     520           0 :       retval = GetOptionIndexAfter(parent);
     521             :     }
     522             :   }
     523             : 
     524           0 :   return retval;
     525             : }
     526             : 
     527             : int32_t
     528           0 : HTMLSelectElement::GetFirstOptionIndex(nsIContent* aOptions)
     529             : {
     530           0 :   int32_t listIndex = -1;
     531           0 :   HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
     532           0 :   if (optElement) {
     533           0 :     GetOptionIndex(optElement, 0, true, &listIndex);
     534           0 :     return listIndex;
     535             :   }
     536             : 
     537           0 :   listIndex = GetFirstChildOptionIndex(aOptions, 0, aOptions->GetChildCount());
     538             : 
     539           0 :   return listIndex;
     540             : }
     541             : 
     542             : int32_t
     543           0 : HTMLSelectElement::GetFirstChildOptionIndex(nsIContent* aOptions,
     544             :                                             int32_t aStartIndex,
     545             :                                             int32_t aEndIndex)
     546             : {
     547           0 :   int32_t retval = -1;
     548             : 
     549           0 :   for (int32_t i = aStartIndex; i < aEndIndex; ++i) {
     550           0 :     retval = GetFirstOptionIndex(aOptions->GetChildAt(i));
     551           0 :     if (retval != -1) {
     552           0 :       break;
     553             :     }
     554             :   }
     555             : 
     556           0 :   return retval;
     557             : }
     558             : 
     559             : nsISelectControlFrame*
     560           0 : HTMLSelectElement::GetSelectFrame()
     561             : {
     562           0 :   nsIFormControlFrame* form_control_frame = GetFormControlFrame(false);
     563             : 
     564           0 :   nsISelectControlFrame* select_frame = nullptr;
     565             : 
     566           0 :   if (form_control_frame) {
     567           0 :     select_frame = do_QueryFrame(form_control_frame);
     568             :   }
     569             : 
     570           0 :   return select_frame;
     571             : }
     572             : 
     573             : void
     574           0 : HTMLSelectElement::Add(const HTMLOptionElementOrHTMLOptGroupElement& aElement,
     575             :                        const Nullable<HTMLElementOrLong>& aBefore,
     576             :                        ErrorResult& aRv)
     577             : {
     578             :   nsGenericHTMLElement& element =
     579           0 :     aElement.IsHTMLOptionElement() ?
     580           0 :     static_cast<nsGenericHTMLElement&>(aElement.GetAsHTMLOptionElement()) :
     581           0 :     static_cast<nsGenericHTMLElement&>(aElement.GetAsHTMLOptGroupElement());
     582             : 
     583           0 :   if (aBefore.IsNull()) {
     584           0 :     Add(element, static_cast<nsGenericHTMLElement*>(nullptr), aRv);
     585           0 :   } else if (aBefore.Value().IsHTMLElement()) {
     586           0 :     Add(element, &aBefore.Value().GetAsHTMLElement(), aRv);
     587             :   } else {
     588           0 :     Add(element, aBefore.Value().GetAsLong(), aRv);
     589             :   }
     590           0 : }
     591             : 
     592             : void
     593           0 : HTMLSelectElement::Add(nsGenericHTMLElement& aElement,
     594             :                        nsGenericHTMLElement* aBefore,
     595             :                        ErrorResult& aError)
     596             : {
     597           0 :   if (!aBefore) {
     598           0 :     Element::AppendChild(aElement, aError);
     599           0 :     return;
     600             :   }
     601             : 
     602             :   // Just in case we're not the parent, get the parent of the reference
     603             :   // element
     604           0 :   nsCOMPtr<nsINode> parent = aBefore->Element::GetParentNode();
     605           0 :   if (!parent || !nsContentUtils::ContentIsDescendantOf(parent, this)) {
     606             :     // NOT_FOUND_ERR: Raised if before is not a descendant of the SELECT
     607             :     // element.
     608           0 :     aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
     609           0 :     return;
     610             :   }
     611             : 
     612             :   // If the before parameter is not null, we are equivalent to the
     613             :   // insertBefore method on the parent of before.
     614           0 :   nsCOMPtr<nsINode> refNode = aBefore;
     615           0 :   parent->InsertBefore(aElement, refNode, aError);
     616             : }
     617             : 
     618             : NS_IMETHODIMP
     619           0 : HTMLSelectElement::Add(nsIDOMHTMLElement* aElement,
     620             :                        nsIVariant* aBefore)
     621             : {
     622             :   uint16_t dataType;
     623           0 :   nsresult rv = aBefore->GetDataType(&dataType);
     624           0 :   NS_ENSURE_SUCCESS(rv, rv);
     625             : 
     626           0 :   nsCOMPtr<nsIContent> element = do_QueryInterface(aElement);
     627             :   nsGenericHTMLElement* htmlElement =
     628           0 :     nsGenericHTMLElement::FromContentOrNull(element);
     629           0 :   if (!htmlElement) {
     630           0 :     return NS_ERROR_NULL_POINTER;
     631             :   }
     632             : 
     633             :   // aBefore is omitted, undefined or null
     634           0 :   if (dataType == nsIDataType::VTYPE_EMPTY ||
     635           0 :       dataType == nsIDataType::VTYPE_VOID) {
     636           0 :     ErrorResult error;
     637           0 :     Add(*htmlElement, (nsGenericHTMLElement*)nullptr, error);
     638           0 :     return error.StealNSResult();
     639             :   }
     640             : 
     641           0 :   nsCOMPtr<nsISupports> supports;
     642             : 
     643             :   // whether aBefore is nsIDOMHTMLElement...
     644           0 :   if (NS_SUCCEEDED(aBefore->GetAsISupports(getter_AddRefs(supports)))) {
     645           0 :     nsCOMPtr<nsIContent> beforeElement = do_QueryInterface(supports);
     646             :     nsGenericHTMLElement* beforeHTMLElement =
     647           0 :       nsGenericHTMLElement::FromContentOrNull(beforeElement);
     648             : 
     649           0 :     NS_ENSURE_TRUE(beforeHTMLElement, NS_ERROR_DOM_SYNTAX_ERR);
     650             : 
     651           0 :     ErrorResult error;
     652           0 :     Add(*htmlElement, beforeHTMLElement, error);
     653           0 :     return error.StealNSResult();
     654             :   }
     655             : 
     656             :   // otherwise, whether aBefore is long
     657             :   int32_t index;
     658           0 :   NS_ENSURE_SUCCESS(aBefore->GetAsInt32(&index), NS_ERROR_DOM_SYNTAX_ERR);
     659             : 
     660           0 :   ErrorResult error;
     661           0 :   Add(*htmlElement, index, error);
     662           0 :   return error.StealNSResult();
     663             : }
     664             : 
     665             : NS_IMETHODIMP
     666           0 : HTMLSelectElement::Remove(int32_t aIndex)
     667             : {
     668           0 :   nsCOMPtr<nsINode> option = Item(static_cast<uint32_t>(aIndex));
     669           0 :   if (!option) {
     670           0 :     return NS_OK;
     671             :   }
     672             : 
     673           0 :   option->Remove();
     674           0 :   return NS_OK;
     675             : }
     676             : 
     677             : NS_IMETHODIMP
     678           0 : HTMLSelectElement::GetOptions(nsIDOMHTMLOptionsCollection** aValue)
     679             : {
     680           0 :   NS_IF_ADDREF(*aValue = GetOptions());
     681             : 
     682           0 :   return NS_OK;
     683             : }
     684             : 
     685             : NS_IMETHODIMP
     686           0 : HTMLSelectElement::GetType(nsAString& aType)
     687             : {
     688           0 :   if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
     689           0 :     aType.AssignLiteral("select-multiple");
     690             :   }
     691             :   else {
     692           0 :     aType.AssignLiteral("select-one");
     693             :   }
     694             : 
     695           0 :   return NS_OK;
     696             : }
     697             : 
     698             : NS_IMETHODIMP
     699           0 : HTMLSelectElement::GetLength(uint32_t* aLength)
     700             : {
     701           0 :   return mOptions->GetLength(aLength);
     702             : }
     703             : 
     704             : #define MAX_DYNAMIC_SELECT_LENGTH 10000
     705             : 
     706             : NS_IMETHODIMP
     707           0 : HTMLSelectElement::SetLength(uint32_t aLength)
     708             : {
     709           0 :   ErrorResult rv;
     710           0 :   SetLength(aLength, rv);
     711           0 :   return rv.StealNSResult();
     712             : }
     713             : 
     714             : void
     715           0 : HTMLSelectElement::SetLength(uint32_t aLength, ErrorResult& aRv)
     716             : {
     717           0 :   uint32_t curlen = Length();
     718             : 
     719           0 :   if (curlen > aLength) { // Remove extra options
     720           0 :     for (uint32_t i = curlen; i > aLength; --i) {
     721           0 :       MOZ_ALWAYS_SUCCEEDS(Remove(i - 1));
     722             :     }
     723           0 :   } else if (aLength > curlen) {
     724           0 :     if (aLength > MAX_DYNAMIC_SELECT_LENGTH) {
     725           0 :       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     726           0 :       return;
     727             :     }
     728             : 
     729           0 :     RefPtr<mozilla::dom::NodeInfo> nodeInfo;
     730             : 
     731           0 :     nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::option,
     732           0 :                                  getter_AddRefs(nodeInfo));
     733             : 
     734           0 :     nsCOMPtr<nsINode> node = NS_NewHTMLOptionElement(nodeInfo.forget());
     735             : 
     736           0 :     RefPtr<nsTextNode> text = new nsTextNode(mNodeInfo->NodeInfoManager());
     737             : 
     738           0 :     aRv = node->AppendChildTo(text, false);
     739           0 :     if (aRv.Failed()) {
     740           0 :       return;
     741             :     }
     742             : 
     743           0 :     for (uint32_t i = curlen; i < aLength; i++) {
     744           0 :       nsINode::AppendChild(*node, aRv);
     745           0 :       if (aRv.Failed()) {
     746           0 :         return;
     747             :       }
     748             : 
     749           0 :       if (i + 1 < aLength) {
     750           0 :         node = node->CloneNode(true, aRv);
     751           0 :         if (aRv.Failed()) {
     752           0 :           return;
     753             :         }
     754           0 :         MOZ_ASSERT(node);
     755             :       }
     756             :     }
     757             :   }
     758             : }
     759             : 
     760             : /* static */
     761             : bool
     762           0 : HTMLSelectElement::MatchSelectedOptions(Element* aElement,
     763             :                                         int32_t /* unused */,
     764             :                                         nsIAtom* /* unused */,
     765             :                                         void* /* unused*/)
     766             : {
     767           0 :   HTMLOptionElement* option = HTMLOptionElement::FromContent(aElement);
     768           0 :   return option && option->Selected();
     769             : }
     770             : 
     771             : nsIHTMLCollection*
     772           0 : HTMLSelectElement::SelectedOptions()
     773             : {
     774           0 :   if (!mSelectedOptions) {
     775             :     mSelectedOptions = new nsContentList(this, MatchSelectedOptions, nullptr,
     776           0 :                                          nullptr, /* deep */ true);
     777             :   }
     778           0 :   return mSelectedOptions;
     779             : }
     780             : 
     781             : NS_IMETHODIMP
     782           0 : HTMLSelectElement::GetSelectedOptions(nsIDOMHTMLCollection** aSelectedOptions)
     783             : {
     784           0 :   NS_ADDREF(*aSelectedOptions = SelectedOptions());
     785           0 :   return NS_OK;
     786             : }
     787             : 
     788             : //NS_IMPL_INT_ATTR(HTMLSelectElement, SelectedIndex, selectedindex)
     789             : 
     790             : NS_IMETHODIMP
     791           0 : HTMLSelectElement::GetSelectedIndex(int32_t* aValue)
     792             : {
     793           0 :   *aValue = SelectedIndex();
     794             : 
     795           0 :   return NS_OK;
     796             : }
     797             : 
     798             : nsresult
     799           0 : HTMLSelectElement::SetSelectedIndexInternal(int32_t aIndex, bool aNotify)
     800             : {
     801           0 :   int32_t oldSelectedIndex = mSelectedIndex;
     802           0 :   uint32_t mask = IS_SELECTED | CLEAR_ALL | SET_DISABLED;
     803           0 :   if (aNotify) {
     804           0 :     mask |= NOTIFY;
     805             :   }
     806             : 
     807           0 :   SetOptionsSelectedByIndex(aIndex, aIndex, mask);
     808             : 
     809           0 :   nsresult rv = NS_OK;
     810           0 :   nsISelectControlFrame* selectFrame = GetSelectFrame();
     811           0 :   if (selectFrame) {
     812           0 :     rv = selectFrame->OnSetSelectedIndex(oldSelectedIndex, mSelectedIndex);
     813             :   }
     814             : 
     815           0 :   SetSelectionChanged(true, aNotify);
     816             : 
     817           0 :   return rv;
     818             : }
     819             : 
     820             : NS_IMETHODIMP
     821           0 : HTMLSelectElement::SetSelectedIndex(int32_t aIndex)
     822             : {
     823           0 :   return SetSelectedIndexInternal(aIndex, true);
     824             : }
     825             : 
     826             : NS_IMETHODIMP
     827           0 : HTMLSelectElement::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
     828             :                                   int32_t aStartIndex, bool aForward,
     829             :                                   int32_t* aIndex)
     830             : {
     831           0 :   nsCOMPtr<nsINode> option = do_QueryInterface(aOption);
     832           0 :   return mOptions->GetOptionIndex(option->AsElement(), aStartIndex, aForward, aIndex);
     833             : }
     834             : 
     835             : bool
     836           0 : HTMLSelectElement::IsOptionSelectedByIndex(int32_t aIndex)
     837             : {
     838           0 :   HTMLOptionElement* option = Item(static_cast<uint32_t>(aIndex));
     839           0 :   return option && option->Selected();
     840             : }
     841             : 
     842             : void
     843           0 : HTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame,
     844             :                                     int32_t aIndex,
     845             :                                     bool aSelected,
     846             :                                     bool aChangeOptionState,
     847             :                                     bool aNotify)
     848             : {
     849             :   // Set the selected index
     850           0 :   if (aSelected && (aIndex < mSelectedIndex || mSelectedIndex < 0)) {
     851           0 :     mSelectedIndex = aIndex;
     852           0 :     SetSelectionChanged(true, aNotify);
     853           0 :   } else if (!aSelected && aIndex == mSelectedIndex) {
     854           0 :     FindSelectedIndex(aIndex + 1, aNotify);
     855             :   }
     856             : 
     857           0 :   if (aChangeOptionState) {
     858             :     // Tell the option to get its bad self selected
     859           0 :     RefPtr<HTMLOptionElement> option = Item(static_cast<uint32_t>(aIndex));
     860           0 :     if (option) {
     861           0 :       option->SetSelectedInternal(aSelected, aNotify);
     862             :     }
     863             :   }
     864             : 
     865             :   // Let the frame know too
     866           0 :   if (aSelectFrame) {
     867           0 :     aSelectFrame->OnOptionSelected(aIndex, aSelected);
     868             :   }
     869             : 
     870           0 :   UpdateSelectedOptions();
     871           0 :   UpdateValueMissingValidityState();
     872           0 :   UpdateState(aNotify);
     873           0 : }
     874             : 
     875             : void
     876           0 : HTMLSelectElement::FindSelectedIndex(int32_t aStartIndex, bool aNotify)
     877             : {
     878           0 :   mSelectedIndex = -1;
     879           0 :   SetSelectionChanged(true, aNotify);
     880           0 :   uint32_t len = Length();
     881           0 :   for (int32_t i = aStartIndex; i < int32_t(len); i++) {
     882           0 :     if (IsOptionSelectedByIndex(i)) {
     883           0 :       mSelectedIndex = i;
     884           0 :       SetSelectionChanged(true, aNotify);
     885           0 :       break;
     886             :     }
     887             :   }
     888           0 : }
     889             : 
     890             : // XXX Consider splitting this into two functions for ease of reading:
     891             : // SelectOptionsByIndex(startIndex, endIndex, clearAll, checkDisabled)
     892             : //   startIndex, endIndex - the range of options to turn on
     893             : //                          (-1, -1) will clear all indices no matter what.
     894             : //   clearAll - will clear all other options unless checkDisabled is on
     895             : //              and all the options attempted to be set are disabled
     896             : //              (note that if it is not multiple, and an option is selected,
     897             : //              everything else will be cleared regardless).
     898             : //   checkDisabled - if this is TRUE, and an option is disabled, it will not be
     899             : //                   changed regardless of whether it is selected or not.
     900             : //                   Generally the UI passes TRUE and JS passes FALSE.
     901             : //                   (setDisabled currently is the opposite)
     902             : // DeselectOptionsByIndex(startIndex, endIndex, checkDisabled)
     903             : //   startIndex, endIndex - the range of options to turn on
     904             : //                          (-1, -1) will clear all indices no matter what.
     905             : //   checkDisabled - if this is TRUE, and an option is disabled, it will not be
     906             : //                   changed regardless of whether it is selected or not.
     907             : //                   Generally the UI passes TRUE and JS passes FALSE.
     908             : //                   (setDisabled currently is the opposite)
     909             : //
     910             : // XXXbz the above comment is pretty confusing.  Maybe we should actually
     911             : // document the args to this function too, in addition to documenting what
     912             : // things might end up looking like?  In particular, pay attention to the
     913             : // setDisabled vs checkDisabled business.
     914             : bool
     915           0 : HTMLSelectElement::SetOptionsSelectedByIndex(int32_t aStartIndex,
     916             :                                              int32_t aEndIndex,
     917             :                                              uint32_t aOptionsMask)
     918             : {
     919             : #if 0
     920             :   printf("SetOption(%d-%d, %c, ClearAll=%c)\n", aStartIndex, aEndIndex,
     921             :                                       (aOptionsMask & IS_SELECTED ? 'Y' : 'N'),
     922             :                                       (aOptionsMask & CLEAR_ALL ? 'Y' : 'N'));
     923             : #endif
     924             :   // Don't bother if the select is disabled
     925           0 :   if (!(aOptionsMask & SET_DISABLED) && IsDisabled()) {
     926           0 :     return false;
     927             :   }
     928             : 
     929             :   // Don't bother if there are no options
     930           0 :   uint32_t numItems = Length();
     931           0 :   if (numItems == 0) {
     932           0 :     return false;
     933             :   }
     934             : 
     935             :   // First, find out whether multiple items can be selected
     936           0 :   bool isMultiple = Multiple();
     937             : 
     938             :   // These variables tell us whether any options were selected
     939             :   // or deselected.
     940           0 :   bool optionsSelected = false;
     941           0 :   bool optionsDeselected = false;
     942             : 
     943           0 :   nsISelectControlFrame* selectFrame = nullptr;
     944           0 :   bool didGetFrame = false;
     945           0 :   AutoWeakFrame weakSelectFrame;
     946             : 
     947           0 :   if (aOptionsMask & IS_SELECTED) {
     948             :     // Setting selectedIndex to an out-of-bounds index means -1. (HTML5)
     949           0 :     if (aStartIndex < 0 || AssertedCast<uint32_t>(aStartIndex) >= numItems ||
     950           0 :         aEndIndex < 0 || AssertedCast<uint32_t>(aEndIndex) >= numItems) {
     951           0 :       aStartIndex = -1;
     952           0 :       aEndIndex = -1;
     953             :     }
     954             : 
     955             :     // Only select the first value if it's not multiple
     956           0 :     if (!isMultiple) {
     957           0 :       aEndIndex = aStartIndex;
     958             :     }
     959             : 
     960             :     // This variable tells whether or not all of the options we attempted to
     961             :     // select are disabled.  If ClearAll is passed in as true, and we do not
     962             :     // select anything because the options are disabled, we will not clear the
     963             :     // other options.  (This is to make the UI work the way one might expect.)
     964           0 :     bool allDisabled = !(aOptionsMask & SET_DISABLED);
     965             : 
     966             :     //
     967             :     // Save a little time when clearing other options
     968             :     //
     969           0 :     int32_t previousSelectedIndex = mSelectedIndex;
     970             : 
     971             :     //
     972             :     // Select the requested indices
     973             :     //
     974             :     // If index is -1, everything will be deselected (bug 28143)
     975           0 :     if (aStartIndex != -1) {
     976           0 :       MOZ_ASSERT(aStartIndex >= 0);
     977           0 :       MOZ_ASSERT(aEndIndex >= 0);
     978             :       // Loop through the options and select them (if they are not disabled and
     979             :       // if they are not already selected).
     980           0 :       for (uint32_t optIndex = AssertedCast<uint32_t>(aStartIndex);
     981           0 :            optIndex <= AssertedCast<uint32_t>(aEndIndex);
     982             :            optIndex++) {
     983           0 :         RefPtr<HTMLOptionElement> option = Item(optIndex);
     984             : 
     985             :         // Ignore disabled options.
     986           0 :         if (!(aOptionsMask & SET_DISABLED)) {
     987           0 :           if (option && IsOptionDisabled(option)) {
     988           0 :             continue;
     989             :           }
     990           0 :           allDisabled = false;
     991             :         }
     992             : 
     993             :         // If the index is already selected, ignore it.
     994           0 :         if (option && !option->Selected()) {
     995             :           // To notify the frame if anything gets changed. No need
     996             :           // to flush here, if there's no frame yet we don't need to
     997             :           // force it to be created just to notify it about a change
     998             :           // in the select.
     999           0 :           selectFrame = GetSelectFrame();
    1000           0 :           weakSelectFrame = do_QueryFrame(selectFrame);
    1001           0 :           didGetFrame = true;
    1002             : 
    1003           0 :           OnOptionSelected(selectFrame, optIndex, true, true,
    1004           0 :                            aOptionsMask & NOTIFY);
    1005           0 :           optionsSelected = true;
    1006             :         }
    1007             :       }
    1008             :     }
    1009             : 
    1010             :     // Next remove all other options if single select or all is clear
    1011             :     // If index is -1, everything will be deselected (bug 28143)
    1012           0 :     if (((!isMultiple && optionsSelected)
    1013           0 :        || ((aOptionsMask & CLEAR_ALL) && !allDisabled)
    1014           0 :        || aStartIndex == -1)
    1015           0 :        && previousSelectedIndex != -1) {
    1016           0 :       for (uint32_t optIndex = AssertedCast<uint32_t>(previousSelectedIndex);
    1017           0 :            optIndex < numItems;
    1018             :            optIndex++) {
    1019           0 :         if (static_cast<int32_t>(optIndex) < aStartIndex ||
    1020           0 :             static_cast<int32_t>(optIndex) > aEndIndex) {
    1021           0 :           HTMLOptionElement* option = Item(optIndex);
    1022             :           // If the index is already selected, ignore it.
    1023           0 :           if (option && option->Selected()) {
    1024           0 :             if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
    1025             :               // To notify the frame if anything gets changed, don't
    1026             :               // flush, if the frame doesn't exist we don't need to
    1027             :               // create it just to tell it about this change.
    1028           0 :               selectFrame = GetSelectFrame();
    1029           0 :               weakSelectFrame = do_QueryFrame(selectFrame);
    1030             : 
    1031           0 :               didGetFrame = true;
    1032             :             }
    1033             : 
    1034           0 :             OnOptionSelected(selectFrame, optIndex, false, true,
    1035           0 :                              aOptionsMask & NOTIFY);
    1036           0 :             optionsDeselected = true;
    1037             : 
    1038             :             // Only need to deselect one option if not multiple
    1039           0 :             if (!isMultiple) {
    1040           0 :               break;
    1041             :             }
    1042             :           }
    1043             :         }
    1044             :       }
    1045             :     }
    1046             :   } else {
    1047             :     // If we're deselecting, loop through all selected items and deselect
    1048             :     // any that are in the specified range.
    1049           0 :     for (int32_t optIndex = aStartIndex; optIndex <= aEndIndex; optIndex++) {
    1050           0 :       HTMLOptionElement* option = Item(optIndex);
    1051           0 :       if (!(aOptionsMask & SET_DISABLED) && IsOptionDisabled(option)) {
    1052           0 :         continue;
    1053             :       }
    1054             : 
    1055             :       // If the index is already selected, ignore it.
    1056           0 :       if (option && option->Selected()) {
    1057           0 :         if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
    1058             :           // To notify the frame if anything gets changed, don't
    1059             :           // flush, if the frame doesn't exist we don't need to
    1060             :           // create it just to tell it about this change.
    1061           0 :           selectFrame = GetSelectFrame();
    1062           0 :           weakSelectFrame = do_QueryFrame(selectFrame);
    1063             : 
    1064           0 :           didGetFrame = true;
    1065             :         }
    1066             : 
    1067           0 :         OnOptionSelected(selectFrame, optIndex, false, true,
    1068           0 :                          aOptionsMask & NOTIFY);
    1069           0 :         optionsDeselected = true;
    1070             :       }
    1071             :     }
    1072             :   }
    1073             : 
    1074             :   // Make sure something is selected unless we were set to -1 (none)
    1075           0 :   if (optionsDeselected && aStartIndex != -1 && !(aOptionsMask & NO_RESELECT)) {
    1076           0 :     optionsSelected =
    1077           0 :       CheckSelectSomething(aOptionsMask & NOTIFY) || optionsSelected;
    1078             :   }
    1079             : 
    1080             :   // Let the caller know whether anything was changed
    1081           0 :   return optionsSelected || optionsDeselected;
    1082             : }
    1083             : 
    1084             : NS_IMETHODIMP
    1085           0 : HTMLSelectElement::IsOptionDisabled(int32_t aIndex, bool* aIsDisabled)
    1086             : {
    1087           0 :   *aIsDisabled = false;
    1088           0 :   RefPtr<HTMLOptionElement> option = Item(aIndex);
    1089           0 :   NS_ENSURE_TRUE(option, NS_ERROR_FAILURE);
    1090             : 
    1091           0 :   *aIsDisabled = IsOptionDisabled(option);
    1092           0 :   return NS_OK;
    1093             : }
    1094             : 
    1095             : bool
    1096           0 : HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption)
    1097             : {
    1098           0 :   MOZ_ASSERT(aOption);
    1099           0 :   if (aOption->Disabled()) {
    1100           0 :     return true;
    1101             :   }
    1102             : 
    1103             :   // Check for disabled optgroups
    1104             :   // If there are no artifacts, there are no optgroups
    1105           0 :   if (mNonOptionChildren) {
    1106           0 :     for (nsCOMPtr<Element> node = static_cast<nsINode*>(aOption)->GetParentElement();
    1107             :          node;
    1108           0 :          node = node->GetParentElement()) {
    1109             :       // If we reached the select element, we're done
    1110           0 :       if (node->IsHTMLElement(nsGkAtoms::select)) {
    1111           0 :         return false;
    1112             :       }
    1113             : 
    1114             :       RefPtr<HTMLOptGroupElement> optGroupElement =
    1115           0 :         HTMLOptGroupElement::FromContent(node);
    1116             : 
    1117           0 :       if (!optGroupElement) {
    1118             :         // If you put something else between you and the optgroup, you're a
    1119             :         // moron and you deserve not to have optgroup disabling work.
    1120           0 :         return false;
    1121             :       }
    1122             : 
    1123           0 :       if (optGroupElement->Disabled()) {
    1124           0 :         return true;
    1125             :       }
    1126             :     }
    1127             :   }
    1128             : 
    1129           0 :   return false;
    1130             : }
    1131             : 
    1132             : NS_IMETHODIMP
    1133           0 : HTMLSelectElement::GetValue(nsAString& aValue)
    1134             : {
    1135           0 :   DOMString value;
    1136           0 :   GetValue(value);
    1137           0 :   value.ToString(aValue);
    1138           0 :   return NS_OK;
    1139             : }
    1140             : 
    1141             : void
    1142           0 : HTMLSelectElement::GetValue(DOMString& aValue)
    1143             : {
    1144           0 :   int32_t selectedIndex = SelectedIndex();
    1145           0 :   if (selectedIndex < 0) {
    1146           0 :     return;
    1147             :   }
    1148             : 
    1149             :   RefPtr<HTMLOptionElement> option =
    1150           0 :     Item(static_cast<uint32_t>(selectedIndex));
    1151             : 
    1152           0 :   if (!option) {
    1153           0 :     return;
    1154             :   }
    1155             : 
    1156           0 :   DebugOnly<nsresult> rv = option->GetValue(aValue);
    1157           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    1158             : }
    1159             : 
    1160             : NS_IMETHODIMP
    1161           0 : HTMLSelectElement::SetValue(const nsAString& aValue)
    1162             : {
    1163           0 :   uint32_t length = Length();
    1164             : 
    1165           0 :   for (uint32_t i = 0; i < length; i++) {
    1166           0 :     RefPtr<HTMLOptionElement> option = Item(i);
    1167           0 :     if (!option) {
    1168           0 :       continue;
    1169             :     }
    1170             : 
    1171           0 :     nsAutoString optionVal;
    1172           0 :     option->GetValue(optionVal);
    1173           0 :     if (optionVal.Equals(aValue)) {
    1174           0 :       SetSelectedIndexInternal(int32_t(i), true);
    1175           0 :       return NS_OK;
    1176             :     }
    1177             :   }
    1178             :   // No matching option was found.
    1179           0 :   SetSelectedIndexInternal(-1, true);
    1180           0 :   return NS_OK;
    1181             : }
    1182             : 
    1183             : 
    1184           0 : NS_IMPL_BOOL_ATTR(HTMLSelectElement, Autofocus, autofocus)
    1185           0 : NS_IMPL_BOOL_ATTR(HTMLSelectElement, Disabled, disabled)
    1186           0 : NS_IMPL_BOOL_ATTR(HTMLSelectElement, Multiple, multiple)
    1187           0 : NS_IMPL_STRING_ATTR(HTMLSelectElement, Name, name)
    1188           0 : NS_IMPL_BOOL_ATTR(HTMLSelectElement, Required, required)
    1189           0 : NS_IMPL_UINT_ATTR(HTMLSelectElement, Size, size)
    1190             : 
    1191             : int32_t
    1192           0 : HTMLSelectElement::TabIndexDefault()
    1193             : {
    1194           0 :   return 0;
    1195             : }
    1196             : 
    1197             : bool
    1198           0 : HTMLSelectElement::IsHTMLFocusable(bool aWithMouse,
    1199             :                                    bool* aIsFocusable, int32_t* aTabIndex)
    1200             : {
    1201           0 :   if (nsGenericHTMLFormElementWithState::IsHTMLFocusable(aWithMouse, aIsFocusable,
    1202             :       aTabIndex))
    1203             :   {
    1204           0 :     return true;
    1205             :   }
    1206             : 
    1207           0 :   *aIsFocusable = !IsDisabled();
    1208             : 
    1209           0 :   return false;
    1210             : }
    1211             : 
    1212             : NS_IMETHODIMP
    1213           0 : HTMLSelectElement::Item(uint32_t aIndex, nsIDOMNode** aReturn)
    1214             : {
    1215           0 :   return mOptions->Item(aIndex, aReturn);
    1216             : }
    1217             : 
    1218             : NS_IMETHODIMP
    1219           0 : HTMLSelectElement::NamedItem(const nsAString& aName, nsIDOMNode** aReturn)
    1220             : {
    1221           0 :   return mOptions->NamedItem(aName, aReturn);
    1222             : }
    1223             : 
    1224             : bool
    1225           0 : HTMLSelectElement::CheckSelectSomething(bool aNotify)
    1226             : {
    1227           0 :   if (mIsDoneAddingChildren) {
    1228           0 :     if (mSelectedIndex < 0 && IsCombobox()) {
    1229           0 :       return SelectSomething(aNotify);
    1230             :     }
    1231             :   }
    1232           0 :   return false;
    1233             : }
    1234             : 
    1235             : bool
    1236           0 : HTMLSelectElement::SelectSomething(bool aNotify)
    1237             : {
    1238             :   // If we're not done building the select, don't play with this yet.
    1239           0 :   if (!mIsDoneAddingChildren) {
    1240           0 :     return false;
    1241             :   }
    1242             : 
    1243             :   uint32_t count;
    1244           0 :   GetLength(&count);
    1245           0 :   for (uint32_t i = 0; i < count; i++) {
    1246             :     bool disabled;
    1247           0 :     nsresult rv = IsOptionDisabled(i, &disabled);
    1248             : 
    1249           0 :     if (NS_FAILED(rv) || !disabled) {
    1250           0 :       rv = SetSelectedIndexInternal(i, aNotify);
    1251           0 :       NS_ENSURE_SUCCESS(rv, false);
    1252             : 
    1253           0 :       UpdateValueMissingValidityState();
    1254           0 :       UpdateState(aNotify);
    1255             : 
    1256           0 :       return true;
    1257             :     }
    1258             :   }
    1259             : 
    1260           0 :   return false;
    1261             : }
    1262             : 
    1263             : nsresult
    1264           0 : HTMLSelectElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
    1265             :                               nsIContent* aBindingParent,
    1266             :                               bool aCompileEventHandlers)
    1267             : {
    1268           0 :   nsresult rv = nsGenericHTMLFormElementWithState::BindToTree(aDocument, aParent,
    1269             :                                                               aBindingParent,
    1270           0 :                                                               aCompileEventHandlers);
    1271           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1272             : 
    1273             :   // If there is a disabled fieldset in the parent chain, the element is now
    1274             :   // barred from constraint validation.
    1275             :   // XXXbz is this still needed now that fieldset changes always call
    1276             :   // FieldSetDisabledChanged?
    1277           0 :   UpdateBarredFromConstraintValidation();
    1278             : 
    1279             :   // And now make sure our state is up to date
    1280           0 :   UpdateState(false);
    1281             : 
    1282           0 :   return rv;
    1283             : }
    1284             : 
    1285             : void
    1286           0 : HTMLSelectElement::UnbindFromTree(bool aDeep, bool aNullParent)
    1287             : {
    1288           0 :   nsGenericHTMLFormElementWithState::UnbindFromTree(aDeep, aNullParent);
    1289             : 
    1290             :   // We might be no longer disabled because our parent chain changed.
    1291             :   // XXXbz is this still needed now that fieldset changes always call
    1292             :   // FieldSetDisabledChanged?
    1293           0 :   UpdateBarredFromConstraintValidation();
    1294             : 
    1295             :   // And now make sure our state is up to date
    1296           0 :   UpdateState(false);
    1297           0 : }
    1298             : 
    1299             : nsresult
    1300           0 : HTMLSelectElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
    1301             :                                  const nsAttrValueOrString* aValue,
    1302             :                                  bool aNotify)
    1303             : {
    1304           0 :   if (aNameSpaceID == kNameSpaceID_None) {
    1305           0 :     if (aName == nsGkAtoms::disabled) {
    1306           0 :       if (aNotify) {
    1307           0 :         mDisabledChanged = true;
    1308             :       }
    1309           0 :     } else if (aName == nsGkAtoms::multiple) {
    1310           0 :       if (!aValue && aNotify && mSelectedIndex >= 0) {
    1311             :         // We're changing from being a multi-select to a single-select.
    1312             :         // Make sure we only have one option selected before we do that.
    1313             :         // Note that this needs to come before we really unset the attr,
    1314             :         // since SetOptionsSelectedByIndex does some bail-out type
    1315             :         // optimization for cases when the select is not multiple that
    1316             :         // would lead to only a single option getting deselected.
    1317           0 :         SetSelectedIndexInternal(mSelectedIndex, aNotify);
    1318             :       }
    1319             :     }
    1320             :   }
    1321             : 
    1322           0 :   return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
    1323           0 :                                                           aValue, aNotify);
    1324             : }
    1325             : 
    1326             : nsresult
    1327           0 : HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
    1328             :                                 const nsAttrValue* aValue,
    1329             :                                 const nsAttrValue* aOldValue, bool aNotify)
    1330             : {
    1331           0 :   if (aNameSpaceID == kNameSpaceID_None) {
    1332           0 :     if (aName == nsGkAtoms::disabled) {
    1333           0 :       UpdateBarredFromConstraintValidation();
    1334           0 :     } else if (aName == nsGkAtoms::required) {
    1335           0 :       UpdateValueMissingValidityState();
    1336           0 :     } else if (aName == nsGkAtoms::autocomplete) {
    1337             :       // Clear the cached @autocomplete attribute and autocompleteInfo state.
    1338           0 :       mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
    1339           0 :       mAutocompleteInfoState = nsContentUtils::eAutocompleteAttrState_Unknown;
    1340           0 :     } else if (aName == nsGkAtoms::multiple) {
    1341           0 :       if (!aValue && aNotify) {
    1342             :         // We might have become a combobox; make sure _something_ gets
    1343             :         // selected in that case
    1344           0 :         CheckSelectSomething(aNotify);
    1345             :       }
    1346             :     }
    1347             :   }
    1348             : 
    1349           0 :   return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName,
    1350             :                                                          aValue, aOldValue,
    1351           0 :                                                          aNotify);
    1352             : }
    1353             : 
    1354             : void
    1355           0 : HTMLSelectElement::DoneAddingChildren(bool aHaveNotified)
    1356             : {
    1357           0 :   mIsDoneAddingChildren = true;
    1358             : 
    1359           0 :   nsISelectControlFrame* selectFrame = GetSelectFrame();
    1360             : 
    1361             :   // If we foolishly tried to restore before we were done adding
    1362             :   // content, restore the rest of the options proper-like
    1363           0 :   if (mRestoreState) {
    1364           0 :     RestoreStateTo(mRestoreState);
    1365           0 :     mRestoreState = nullptr;
    1366             :   }
    1367             : 
    1368             :   // Notify the frame
    1369           0 :   if (selectFrame) {
    1370           0 :     selectFrame->DoneAddingChildren(true);
    1371             :   }
    1372             : 
    1373           0 :   if (!mInhibitStateRestoration) {
    1374           0 :     nsresult rv = GenerateStateKey();
    1375           0 :     if (NS_SUCCEEDED(rv)) {
    1376           0 :       RestoreFormControlState();
    1377             :     }
    1378             :   }
    1379             : 
    1380             :   // Now that we're done, select something (if it's a single select something
    1381             :   // must be selected)
    1382           0 :   if (!CheckSelectSomething(false)) {
    1383             :     // If an option has @selected set, it will be selected during parsing but
    1384             :     // with an empty value. We have to make sure the select element updates it's
    1385             :     // validity state to take this into account.
    1386           0 :     UpdateValueMissingValidityState();
    1387             : 
    1388             :     // And now make sure we update our content state too
    1389           0 :     UpdateState(aHaveNotified);
    1390             :   }
    1391             : 
    1392           0 :   mDefaultSelectionSet = true;
    1393           0 : }
    1394             : 
    1395             : bool
    1396           0 : HTMLSelectElement::ParseAttribute(int32_t aNamespaceID,
    1397             :                                   nsIAtom* aAttribute,
    1398             :                                   const nsAString& aValue,
    1399             :                                   nsAttrValue& aResult)
    1400             : {
    1401           0 :   if (kNameSpaceID_None == aNamespaceID) {
    1402           0 :     if (aAttribute == nsGkAtoms::size) {
    1403           0 :       return aResult.ParsePositiveIntValue(aValue);
    1404           0 :     } else if (aAttribute == nsGkAtoms::autocomplete) {
    1405           0 :       aResult.ParseAtomArray(aValue);
    1406           0 :       return true;
    1407             :     }
    1408             :   }
    1409           0 :   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
    1410           0 :                                               aResult);
    1411             : }
    1412             : 
    1413             : void
    1414           0 : HTMLSelectElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
    1415             :                                          GenericSpecifiedValues* aData)
    1416             : {
    1417           0 :   nsGenericHTMLFormElementWithState::MapImageAlignAttributeInto(aAttributes, aData);
    1418           0 :   nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes, aData);
    1419           0 : }
    1420             : 
    1421             : nsChangeHint
    1422           0 : HTMLSelectElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
    1423             :                                           int32_t aModType) const
    1424             : {
    1425             :   nsChangeHint retval =
    1426           0 :       nsGenericHTMLFormElementWithState::GetAttributeChangeHint(aAttribute, aModType);
    1427           0 :   if (aAttribute == nsGkAtoms::multiple ||
    1428           0 :       aAttribute == nsGkAtoms::size) {
    1429           0 :     retval |= nsChangeHint_ReconstructFrame;
    1430             :   }
    1431           0 :   return retval;
    1432             : }
    1433             : 
    1434             : NS_IMETHODIMP_(bool)
    1435           0 : HTMLSelectElement::IsAttributeMapped(const nsIAtom* aAttribute) const
    1436             : {
    1437             :   static const MappedAttributeEntry* const map[] = {
    1438             :     sCommonAttributeMap,
    1439             :     sImageAlignAttributeMap
    1440             :   };
    1441             : 
    1442           0 :   return FindAttributeDependence(aAttribute, map);
    1443             : }
    1444             : 
    1445             : nsMapRuleToAttributesFunc
    1446           0 : HTMLSelectElement::GetAttributeMappingFunction() const
    1447             : {
    1448           0 :   return &MapAttributesIntoRule;
    1449             : }
    1450             : 
    1451             : bool
    1452           0 : HTMLSelectElement::IsDisabledForEvents(EventMessage aMessage)
    1453             : {
    1454           0 :   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
    1455           0 :   nsIFrame* formFrame = nullptr;
    1456           0 :   if (formControlFrame) {
    1457           0 :     formFrame = do_QueryFrame(formControlFrame);
    1458             :   }
    1459           0 :   return IsElementDisabledForEvents(aMessage, formFrame);
    1460             : }
    1461             : 
    1462             : nsresult
    1463           0 : HTMLSelectElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
    1464             : {
    1465           0 :   aVisitor.mCanHandle = false;
    1466           0 :   if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) {
    1467           0 :     return NS_OK;
    1468             :   }
    1469             : 
    1470           0 :   return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor);
    1471             : }
    1472             : 
    1473             : nsresult
    1474           0 : HTMLSelectElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
    1475             : {
    1476           0 :   if (aVisitor.mEvent->mMessage == eFocus) {
    1477             :     // If the invalid UI is shown, we should show it while focused and
    1478             :     // update the invalid/valid UI.
    1479           0 :     mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
    1480             : 
    1481             :     // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
    1482             :     // UI while focused.
    1483           0 :     mCanShowValidUI = ShouldShowValidityUI();
    1484             : 
    1485             :     // We don't have to update NS_EVENT_STATE_MOZ_UI_INVALID nor
    1486             :     // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change.
    1487           0 :   } else if (aVisitor.mEvent->mMessage == eBlur) {
    1488           0 :     mCanShowInvalidUI = true;
    1489           0 :     mCanShowValidUI = true;
    1490             : 
    1491           0 :     UpdateState(true);
    1492             :   }
    1493             : 
    1494           0 :   return nsGenericHTMLFormElementWithState::PostHandleEvent(aVisitor);
    1495             : }
    1496             : 
    1497             : EventStates
    1498           0 : HTMLSelectElement::IntrinsicState() const
    1499             : {
    1500           0 :   EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
    1501             : 
    1502           0 :   if (IsCandidateForConstraintValidation()) {
    1503           0 :     if (IsValid()) {
    1504           0 :       state |= NS_EVENT_STATE_VALID;
    1505             :     } else {
    1506           0 :       state |= NS_EVENT_STATE_INVALID;
    1507             : 
    1508           0 :       if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
    1509           0 :           (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
    1510           0 :            (mCanShowInvalidUI && ShouldShowValidityUI()))) {
    1511           0 :         state |= NS_EVENT_STATE_MOZ_UI_INVALID;
    1512             :       }
    1513             :     }
    1514             : 
    1515             :     // :-moz-ui-valid applies if all the following are true:
    1516             :     // 1. The element is not focused, or had either :-moz-ui-valid or
    1517             :     //    :-moz-ui-invalid applying before it was focused ;
    1518             :     // 2. The element is either valid or isn't allowed to have
    1519             :     //    :-moz-ui-invalid applying ;
    1520             :     // 3. The element has no form owner or its form owner doesn't have the
    1521             :     //    novalidate attribute set ;
    1522             :     // 4. The element has already been modified or the user tried to submit the
    1523             :     //    form owner while invalid.
    1524           0 :     if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
    1525           0 :         (mCanShowValidUI && ShouldShowValidityUI() &&
    1526           0 :          (IsValid() || (state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
    1527           0 :                         !mCanShowInvalidUI)))) {
    1528           0 :       state |= NS_EVENT_STATE_MOZ_UI_VALID;
    1529             :     }
    1530             :   }
    1531             : 
    1532           0 :   if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
    1533           0 :     state |= NS_EVENT_STATE_REQUIRED;
    1534             :   } else {
    1535           0 :     state |= NS_EVENT_STATE_OPTIONAL;
    1536             :   }
    1537             : 
    1538           0 :   return state;
    1539             : }
    1540             : 
    1541             : // nsIFormControl
    1542             : 
    1543             : NS_IMETHODIMP
    1544           0 : HTMLSelectElement::SaveState()
    1545             : {
    1546           0 :   RefPtr<SelectState> state = new SelectState();
    1547             : 
    1548           0 :   uint32_t len = Length();
    1549             : 
    1550           0 :   for (uint32_t optIndex = 0; optIndex < len; optIndex++) {
    1551           0 :     HTMLOptionElement* option = Item(optIndex);
    1552           0 :     if (option && option->Selected()) {
    1553           0 :       nsAutoString value;
    1554           0 :       option->GetValue(value);
    1555           0 :       state->PutOption(optIndex, value);
    1556             :     }
    1557             :   }
    1558             : 
    1559           0 :   nsPresState* presState = GetPrimaryPresState();
    1560           0 :   if (presState) {
    1561           0 :     presState->SetStateProperty(state);
    1562             : 
    1563           0 :     if (mDisabledChanged) {
    1564             :       // We do not want to save the real disabled state but the disabled
    1565             :       // attribute.
    1566           0 :       presState->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
    1567             :     }
    1568             :   }
    1569             : 
    1570           0 :   return NS_OK;
    1571             : }
    1572             : 
    1573             : bool
    1574           0 : HTMLSelectElement::RestoreState(nsPresState* aState)
    1575             : {
    1576             :   // Get the presentation state object to retrieve our stuff out of.
    1577             :   nsCOMPtr<SelectState> state(
    1578           0 :     do_QueryInterface(aState->GetStateProperty()));
    1579             : 
    1580           0 :   if (state) {
    1581           0 :     RestoreStateTo(state);
    1582             : 
    1583             :     // Don't flush, if the frame doesn't exist yet it doesn't care if
    1584             :     // we're reset or not.
    1585           0 :     DispatchContentReset();
    1586             :   }
    1587             : 
    1588           0 :   if (aState->IsDisabledSet() && !aState->GetDisabled()) {
    1589           0 :     SetDisabled(false);
    1590             :   }
    1591             : 
    1592           0 :   return false;
    1593             : }
    1594             : 
    1595             : void
    1596           0 : HTMLSelectElement::RestoreStateTo(SelectState* aNewSelected)
    1597             : {
    1598           0 :   if (!mIsDoneAddingChildren) {
    1599           0 :     mRestoreState = aNewSelected;
    1600           0 :     return;
    1601             :   }
    1602             : 
    1603           0 :   uint32_t len = Length();
    1604           0 :   uint32_t mask = IS_SELECTED | CLEAR_ALL | SET_DISABLED | NOTIFY;
    1605             : 
    1606             :   // First clear all
    1607           0 :   SetOptionsSelectedByIndex(-1, -1, mask);
    1608             : 
    1609             :   // Next set the proper ones
    1610           0 :   for (uint32_t i = 0; i < len; i++) {
    1611           0 :     HTMLOptionElement* option = Item(i);
    1612           0 :     if (option) {
    1613           0 :       nsAutoString value;
    1614           0 :       nsresult rv = option->GetValue(value);
    1615           0 :       if (NS_SUCCEEDED(rv) && aNewSelected->ContainsOption(i, value)) {
    1616           0 :         SetOptionsSelectedByIndex(i, i, IS_SELECTED | SET_DISABLED | NOTIFY);
    1617             :       }
    1618             :     }
    1619             :   }
    1620             : }
    1621             : 
    1622             : NS_IMETHODIMP
    1623           0 : HTMLSelectElement::Reset()
    1624             : {
    1625           0 :   uint32_t numSelected = 0;
    1626             : 
    1627             :   //
    1628             :   // Cycle through the options array and reset the options
    1629             :   //
    1630           0 :   uint32_t numOptions = Length();
    1631             : 
    1632           0 :   for (uint32_t i = 0; i < numOptions; i++) {
    1633           0 :     RefPtr<HTMLOptionElement> option = Item(i);
    1634           0 :     if (option) {
    1635             :       //
    1636             :       // Reset the option to its default value
    1637             :       //
    1638             : 
    1639           0 :       uint32_t mask = SET_DISABLED | NOTIFY | NO_RESELECT;
    1640           0 :       if (option->DefaultSelected()) {
    1641           0 :         mask |= IS_SELECTED;
    1642           0 :         numSelected++;
    1643             :       }
    1644             : 
    1645           0 :       SetOptionsSelectedByIndex(i, i, mask);
    1646           0 :       option->SetSelectedChanged(false);
    1647             :     }
    1648             :   }
    1649             : 
    1650             :   //
    1651             :   // If nothing was selected and it's not multiple, select something
    1652             :   //
    1653           0 :   if (numSelected == 0 && IsCombobox()) {
    1654           0 :     SelectSomething(true);
    1655             :   }
    1656             : 
    1657           0 :   SetSelectionChanged(false, true);
    1658             : 
    1659             :   //
    1660             :   // Let the frame know we were reset
    1661             :   //
    1662             :   // Don't flush, if there's no frame yet it won't care about us being
    1663             :   // reset even if we forced it to be created now.
    1664             :   //
    1665           0 :   DispatchContentReset();
    1666             : 
    1667           0 :   return NS_OK;
    1668             : }
    1669             : 
    1670             : static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
    1671             : 
    1672             : NS_IMETHODIMP
    1673           0 : HTMLSelectElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission)
    1674             : {
    1675             :   // Disabled elements don't submit
    1676           0 :   if (IsDisabled()) {
    1677           0 :     return NS_OK;
    1678             :   }
    1679             : 
    1680             :   //
    1681             :   // Get the name (if no name, no submit)
    1682             :   //
    1683           0 :   nsAutoString name;
    1684           0 :   GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
    1685           0 :   if (name.IsEmpty()) {
    1686           0 :     return NS_OK;
    1687             :   }
    1688             : 
    1689             :   //
    1690             :   // Submit
    1691             :   //
    1692           0 :   uint32_t len = Length();
    1693             : 
    1694           0 :   nsAutoString mozType;
    1695           0 :   nsCOMPtr<nsIFormProcessor> keyGenProcessor;
    1696           0 :   if (GetAttr(kNameSpaceID_None, nsGkAtoms::moztype, mozType) &&
    1697           0 :       mozType.EqualsLiteral("-mozilla-keygen")) {
    1698           0 :     keyGenProcessor = do_GetService(kFormProcessorCID);
    1699             :   }
    1700             : 
    1701           0 :   for (uint32_t optIndex = 0; optIndex < len; optIndex++) {
    1702           0 :     HTMLOptionElement* option = Item(optIndex);
    1703             : 
    1704             :     // Don't send disabled options
    1705           0 :     if (!option || IsOptionDisabled(option)) {
    1706           0 :       continue;
    1707             :     }
    1708             : 
    1709           0 :     if (!option->Selected()) {
    1710           0 :       continue;
    1711             :     }
    1712             : 
    1713           0 :     nsString value;
    1714           0 :     MOZ_ALWAYS_SUCCEEDS(option->GetValue(value));
    1715             : 
    1716           0 :     if (keyGenProcessor) {
    1717           0 :       nsString tmp(value);
    1718           0 :       if (NS_SUCCEEDED(keyGenProcessor->ProcessValue(this, name, tmp))) {
    1719           0 :         value = tmp;
    1720             :       }
    1721             :     }
    1722             : 
    1723           0 :     aFormSubmission->AddNameValuePair(name, value);
    1724             :   }
    1725             : 
    1726           0 :   return NS_OK;
    1727             : }
    1728             : 
    1729             : void
    1730           0 : HTMLSelectElement::DispatchContentReset()
    1731             : {
    1732           0 :   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
    1733           0 :   if (formControlFrame) {
    1734             :     // Only dispatch content reset notification if this is a list control
    1735             :     // frame or combo box control frame.
    1736           0 :     if (IsCombobox()) {
    1737           0 :       nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
    1738           0 :       if (comboFrame) {
    1739           0 :         comboFrame->OnContentReset();
    1740             :       }
    1741             :     } else {
    1742           0 :       nsIListControlFrame* listFrame = do_QueryFrame(formControlFrame);
    1743           0 :       if (listFrame) {
    1744           0 :         listFrame->OnContentReset();
    1745             :       }
    1746             :     }
    1747             :   }
    1748           0 : }
    1749             : 
    1750             : static void
    1751           0 : AddOptions(nsIContent* aRoot, HTMLOptionsCollection* aArray)
    1752             : {
    1753           0 :   for (nsIContent* child = aRoot->GetFirstChild();
    1754           0 :        child;
    1755           0 :        child = child->GetNextSibling()) {
    1756           0 :     HTMLOptionElement* opt = HTMLOptionElement::FromContent(child);
    1757           0 :     if (opt) {
    1758           0 :       aArray->AppendOption(opt);
    1759           0 :     } else if (child->IsHTMLElement(nsGkAtoms::optgroup)) {
    1760           0 :       for (nsIContent* grandchild = child->GetFirstChild();
    1761           0 :            grandchild;
    1762           0 :            grandchild = grandchild->GetNextSibling()) {
    1763           0 :         opt = HTMLOptionElement::FromContent(grandchild);
    1764           0 :         if (opt) {
    1765           0 :           aArray->AppendOption(opt);
    1766             :         }
    1767             :       }
    1768             :     }
    1769             :   }
    1770           0 : }
    1771             : 
    1772             : void
    1773           0 : HTMLSelectElement::RebuildOptionsArray(bool aNotify)
    1774             : {
    1775           0 :   mOptions->Clear();
    1776           0 :   AddOptions(this, mOptions);
    1777           0 :   FindSelectedIndex(0, aNotify);
    1778           0 : }
    1779             : 
    1780             : bool
    1781           0 : HTMLSelectElement::IsValueMissing()
    1782             : {
    1783           0 :   if (!Required()) {
    1784           0 :     return false;
    1785             :   }
    1786             : 
    1787           0 :   uint32_t length = Length();
    1788             : 
    1789           0 :   for (uint32_t i = 0; i < length; ++i) {
    1790           0 :     RefPtr<HTMLOptionElement> option = Item(i);
    1791             :     // Check for a placeholder label option, don't count it as a valid value.
    1792           0 :     if (i == 0 && !Multiple() && Size() <= 1 && option->GetParent() == this) {
    1793           0 :       nsAutoString value;
    1794           0 :       MOZ_ALWAYS_SUCCEEDS(option->GetValue(value));
    1795           0 :       if (value.IsEmpty()) {
    1796           0 :         continue;
    1797             :       }
    1798             :     }
    1799             : 
    1800           0 :     if (!option->Selected()) {
    1801           0 :       continue;
    1802             :     }
    1803             : 
    1804           0 :     if (IsOptionDisabled(option)) {
    1805           0 :       continue;
    1806             :     }
    1807             : 
    1808           0 :     return false;
    1809             :   }
    1810             : 
    1811           0 :   return true;
    1812             : }
    1813             : 
    1814             : void
    1815           0 : HTMLSelectElement::UpdateValueMissingValidityState()
    1816             : {
    1817           0 :   SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
    1818           0 : }
    1819             : 
    1820             : nsresult
    1821           0 : HTMLSelectElement::GetValidationMessage(nsAString& aValidationMessage,
    1822             :                                         ValidityStateType aType)
    1823             : {
    1824           0 :   switch (aType) {
    1825             :     case VALIDITY_STATE_VALUE_MISSING: {
    1826           0 :       nsXPIDLString message;
    1827             :       nsresult rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
    1828             :                                                        "FormValidationSelectMissing",
    1829           0 :                                                        message);
    1830           0 :       aValidationMessage = message;
    1831           0 :       return rv;
    1832             :     }
    1833             :     default: {
    1834           0 :       return nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
    1835             :     }
    1836             :   }
    1837             : }
    1838             : 
    1839             : #ifdef DEBUG
    1840             : 
    1841             : void
    1842           0 : HTMLSelectElement::VerifyOptionsArray()
    1843             : {
    1844           0 :   int32_t index = 0;
    1845           0 :   for (nsIContent* child = nsINode::GetFirstChild();
    1846           0 :        child;
    1847           0 :        child = child->GetNextSibling()) {
    1848           0 :     HTMLOptionElement* opt = HTMLOptionElement::FromContent(child);
    1849           0 :     if (opt) {
    1850           0 :       NS_ASSERTION(opt == mOptions->ItemAsOption(index++),
    1851             :                    "Options collection broken");
    1852           0 :     } else if (child->IsHTMLElement(nsGkAtoms::optgroup)) {
    1853           0 :       for (nsIContent* grandchild = child->GetFirstChild();
    1854           0 :            grandchild;
    1855           0 :            grandchild = grandchild->GetNextSibling()) {
    1856           0 :         opt = HTMLOptionElement::FromContent(grandchild);
    1857           0 :         if (opt) {
    1858           0 :           NS_ASSERTION(opt == mOptions->ItemAsOption(index++),
    1859             :                        "Options collection broken");
    1860             :         }
    1861             :       }
    1862             :     }
    1863             :   }
    1864           0 : }
    1865             : 
    1866             : #endif
    1867             : 
    1868             : void
    1869           0 : HTMLSelectElement::UpdateBarredFromConstraintValidation()
    1870             : {
    1871           0 :   SetBarredFromConstraintValidation(IsDisabled());
    1872           0 : }
    1873             : 
    1874             : void
    1875           0 : HTMLSelectElement::FieldSetDisabledChanged(bool aNotify)
    1876             : {
    1877           0 :   UpdateBarredFromConstraintValidation();
    1878             : 
    1879           0 :   nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
    1880           0 : }
    1881             : 
    1882             : void
    1883           0 : HTMLSelectElement::SetSelectionChanged(bool aValue, bool aNotify)
    1884             : {
    1885           0 :   if (!mDefaultSelectionSet) {
    1886           0 :     return;
    1887             :   }
    1888             : 
    1889           0 :   UpdateSelectedOptions();
    1890             : 
    1891           0 :   bool previousSelectionChangedValue = mSelectionHasChanged;
    1892           0 :   mSelectionHasChanged = aValue;
    1893             : 
    1894           0 :   if (mSelectionHasChanged != previousSelectionChangedValue) {
    1895           0 :     UpdateState(aNotify);
    1896             :   }
    1897             : }
    1898             : 
    1899             : void
    1900           0 : HTMLSelectElement::UpdateSelectedOptions()
    1901             : {
    1902           0 :   if (mSelectedOptions) {
    1903           0 :     mSelectedOptions->SetDirty();
    1904             :   }
    1905           0 : }
    1906             : 
    1907             : bool
    1908           0 : HTMLSelectElement::OpenInParentProcess()
    1909             : {
    1910           0 :   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
    1911           0 :   nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
    1912           0 :   if (comboFrame) {
    1913           0 :     return comboFrame->IsOpenInParentProcess();
    1914             :   }
    1915             : 
    1916           0 :   return false;
    1917             : }
    1918             : 
    1919             : void
    1920           0 : HTMLSelectElement::SetOpenInParentProcess(bool aVal)
    1921             : {
    1922           0 :   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
    1923           0 :   nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
    1924           0 :   if (comboFrame) {
    1925           0 :     comboFrame->SetOpenInParentProcess(aVal);
    1926             :   }
    1927           0 : }
    1928             : 
    1929             : void
    1930           0 : HTMLSelectElement::GetPreviewValue(nsAString& aValue)
    1931             : {
    1932           0 :   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
    1933           0 :   nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
    1934           0 :   if (comboFrame) {
    1935           0 :     comboFrame->GetPreviewText(aValue);
    1936             :   }
    1937           0 : }
    1938             : 
    1939             : void
    1940           0 : HTMLSelectElement::SetPreviewValue(const nsAString& aValue)
    1941             : {
    1942           0 :   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
    1943           0 :   nsIComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
    1944           0 :   if (comboFrame) {
    1945           0 :     comboFrame->SetPreviewText(aValue);
    1946             :   }
    1947           0 : }
    1948             : 
    1949             : JSObject*
    1950           0 : HTMLSelectElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
    1951             : {
    1952           0 :   return HTMLSelectElementBinding::Wrap(aCx, this, aGivenProto);
    1953             : }
    1954             : 
    1955             : } // namespace dom
    1956             : } // namespace mozilla

Generated by: LCOV version 1.13