123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #ifndef mozilla_dom_HTMLSelectElement_h
- #define mozilla_dom_HTMLSelectElement_h
- #include "mozilla/Attributes.h"
- #include "nsGenericHTMLElement.h"
- #include "nsIDOMHTMLSelectElement.h"
- #include "nsIConstraintValidation.h"
- #include "mozilla/dom/BindingDeclarations.h"
- #include "mozilla/dom/HTMLOptionsCollection.h"
- #include "mozilla/ErrorResult.h"
- #include "nsCheapSets.h"
- #include "nsCOMPtr.h"
- #include "nsError.h"
- #include "mozilla/dom/HTMLFormElement.h"
- #include "nsContentUtils.h"
- class nsContentList;
- class nsIDOMHTMLOptionElement;
- class nsIHTMLCollection;
- class nsISelectControlFrame;
- class nsPresState;
- namespace mozilla {
- class EventChainPostVisitor;
- class EventChainPreVisitor;
- namespace dom {
- class HTMLFormSubmission;
- class HTMLSelectElement;
- #define NS_SELECT_STATE_IID \
- { /* 4db54c7c-d159-455f-9d8e-f60ee466dbf3 */ \
- 0x4db54c7c, \
- 0xd159, \
- 0x455f, \
- {0x9d, 0x8e, 0xf6, 0x0e, 0xe4, 0x66, 0xdb, 0xf3} \
- }
- /**
- * The restore state used by select
- */
- class SelectState : public nsISupports
- {
- public:
- SelectState()
- {
- }
- NS_DECLARE_STATIC_IID_ACCESSOR(NS_SELECT_STATE_IID)
- NS_DECL_ISUPPORTS
- void PutOption(int32_t aIndex, const nsAString& aValue)
- {
- // If the option is empty, store the index. If not, store the value.
- if (aValue.IsEmpty()) {
- mIndices.Put(aIndex);
- } else {
- mValues.Put(aValue);
- }
- }
- bool ContainsOption(int32_t aIndex, const nsAString& aValue)
- {
- return mValues.Contains(aValue) || mIndices.Contains(aIndex);
- }
- private:
- virtual ~SelectState()
- {
- }
- nsCheapSet<nsStringHashKey> mValues;
- nsCheapSet<nsUint32HashKey> mIndices;
- };
- NS_DEFINE_STATIC_IID_ACCESSOR(SelectState, NS_SELECT_STATE_IID)
- class MOZ_STACK_CLASS SafeOptionListMutation
- {
- public:
- /**
- * @param aSelect The select element which option list is being mutated.
- * Can be null.
- * @param aParent The content object which is being mutated.
- * @param aKid If not null, a new child element is being inserted to
- * aParent. Otherwise a child element will be removed.
- * @param aIndex The index of the content object in the parent.
- */
- SafeOptionListMutation(nsIContent* aSelect, nsIContent* aParent,
- nsIContent* aKid, uint32_t aIndex, bool aNotify);
- ~SafeOptionListMutation();
- void MutationFailed() { mNeedsRebuild = true; }
- private:
- static void* operator new(size_t) CPP_THROW_NEW { return 0; }
- static void operator delete(void*, size_t) {}
- /** The select element which option list is being mutated. */
- RefPtr<HTMLSelectElement> mSelect;
- /** true if the current mutation is the first one in the stack. */
- bool mTopLevelMutation;
- /** true if it is known that the option list must be recreated. */
- bool mNeedsRebuild;
- /** Option list must be recreated if more than one mutation is detected. */
- nsMutationGuard mGuard;
- };
- /**
- * Implementation of <select>
- */
- class HTMLSelectElement final : public nsGenericHTMLFormElementWithState,
- public nsIDOMHTMLSelectElement,
- public nsIConstraintValidation
- {
- public:
- /**
- * IS_SELECTED whether to set the option(s) to true or false
- *
- * CLEAR_ALL whether to clear all other options (for example, if you
- * are normal-clicking on the current option)
- *
- * SET_DISABLED whether it is permissible to set disabled options
- * (for JavaScript)
- *
- * NOTIFY whether to notify frames and such
- */
- enum OptionType {
- IS_SELECTED = 1 << 0,
- CLEAR_ALL = 1 << 1,
- SET_DISABLED = 1 << 2,
- NOTIFY = 1 << 3
- };
- using nsIConstraintValidation::GetValidationMessage;
- explicit HTMLSelectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
- FromParser aFromParser = NOT_FROM_PARSER);
- NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLSelectElement, select)
- // nsISupports
- NS_DECL_ISUPPORTS_INHERITED
- virtual int32_t TabIndexDefault() override;
- // Element
- virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override
- {
- return true;
- }
- // nsIDOMHTMLSelectElement
- NS_DECL_NSIDOMHTMLSELECTELEMENT
- // WebIdl HTMLSelectElement
- bool Autofocus() const
- {
- return GetBoolAttr(nsGkAtoms::autofocus);
- }
- void SetAutofocus(bool aVal, ErrorResult& aRv)
- {
- SetHTMLBoolAttr(nsGkAtoms::autofocus, aVal, aRv);
- }
- void GetAutocomplete(DOMString& aValue);
- void SetAutocomplete(const nsAString& aValue, ErrorResult& aRv)
- {
- SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv);
- }
- bool Disabled() const
- {
- return GetBoolAttr(nsGkAtoms::disabled);
- }
- void SetDisabled(bool aVal, ErrorResult& aRv)
- {
- SetHTMLBoolAttr(nsGkAtoms::disabled, aVal, aRv);
- }
- HTMLFormElement* GetForm() const
- {
- return nsGenericHTMLFormElementWithState::GetForm();
- }
- bool Multiple() const
- {
- return GetBoolAttr(nsGkAtoms::multiple);
- }
- void SetMultiple(bool aVal, ErrorResult& aRv)
- {
- SetHTMLBoolAttr(nsGkAtoms::multiple, aVal, aRv);
- }
- // Uses XPCOM GetName.
- void SetName(const nsAString& aName, ErrorResult& aRv)
- {
- SetHTMLAttr(nsGkAtoms::name, aName, aRv);
- }
- bool Required() const
- {
- return GetBoolAttr(nsGkAtoms::required);
- }
- void SetRequired(bool aVal, ErrorResult& aRv)
- {
- SetHTMLBoolAttr(nsGkAtoms::required, aVal, aRv);
- }
- uint32_t Size() const
- {
- return GetUnsignedIntAttr(nsGkAtoms::size, 0);
- }
- void SetSize(uint32_t aSize, ErrorResult& aRv)
- {
- SetUnsignedIntAttr(nsGkAtoms::size, aSize, 0, aRv);
- }
- // Uses XPCOM GetType.
- HTMLOptionsCollection* Options() const
- {
- return mOptions;
- }
- uint32_t Length() const
- {
- return mOptions->Length();
- }
- void SetLength(uint32_t aLength, ErrorResult& aRv);
- Element* IndexedGetter(uint32_t aIdx, bool& aFound) const
- {
- return mOptions->IndexedGetter(aIdx, aFound);
- }
- HTMLOptionElement* Item(uint32_t aIdx) const
- {
- return mOptions->ItemAsOption(aIdx);
- }
- HTMLOptionElement* NamedItem(const nsAString& aName) const
- {
- return mOptions->GetNamedItem(aName);
- }
- void Add(const HTMLOptionElementOrHTMLOptGroupElement& aElement,
- const Nullable<HTMLElementOrLong>& aBefore,
- ErrorResult& aRv);
- // Uses XPCOM Remove.
- void IndexedSetter(uint32_t aIndex, HTMLOptionElement* aOption,
- ErrorResult& aRv)
- {
- mOptions->IndexedSetter(aIndex, aOption, aRv);
- }
- static bool MatchSelectedOptions(Element* aElement, int32_t, nsIAtom*,
- void*);
- nsIHTMLCollection* SelectedOptions();
- int32_t SelectedIndex() const
- {
- return mSelectedIndex;
- }
- void SetSelectedIndex(int32_t aIdx, ErrorResult& aRv)
- {
- aRv = SetSelectedIndexInternal(aIdx, true);
- }
- void GetValue(DOMString& aValue);
- // Uses XPCOM SetValue.
- // nsIConstraintValidation::WillValidate is fine.
- // nsIConstraintValidation::Validity() is fine.
- // nsIConstraintValidation::GetValidationMessage() is fine.
- // nsIConstraintValidation::CheckValidity() is fine.
- using nsIConstraintValidation::CheckValidity;
- using nsIConstraintValidation::ReportValidity;
- // nsIConstraintValidation::SetCustomValidity() is fine.
- using nsINode::Remove;
- // nsINode
- virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
- // nsIContent
- virtual nsresult GetEventTargetParent(
- EventChainPreVisitor& aVisitor) override;
- virtual nsresult PostHandleEvent(
- EventChainPostVisitor& aVisitor) override;
- virtual bool IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, int32_t* aTabIndex) override;
- virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
- bool aNotify) override;
- virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override;
- // Overriden nsIFormControl methods
- NS_IMETHOD_(uint32_t) GetType() const override { return NS_FORM_SELECT; }
- NS_IMETHOD Reset() override;
- NS_IMETHOD SubmitNamesValues(HTMLFormSubmission* aFormSubmission) override;
- NS_IMETHOD SaveState() override;
- virtual bool RestoreState(nsPresState* aState) override;
- virtual bool IsDisabledForEvents(WidgetEvent* aEvent) override;
- virtual void FieldSetDisabledChanged(bool aNotify) override;
- EventStates IntrinsicState() const override;
- /**
- * To be called when stuff is added under a child of the select--but *before*
- * they are actually added.
- *
- * @param aOptions the content that was added (usually just an option, but
- * could be an optgroup node with many child options)
- * @param aParent the parent the options were added to (could be an optgroup)
- * @param aContentIndex the index where the options are being added within the
- * parent (if the parent is an optgroup, the index within the optgroup)
- */
- NS_IMETHOD WillAddOptions(nsIContent* aOptions,
- nsIContent* aParent,
- int32_t aContentIndex,
- bool aNotify);
- /**
- * To be called when stuff is removed under a child of the select--but
- * *before* they are actually removed.
- *
- * @param aParent the parent the option(s) are being removed from
- * @param aContentIndex the index of the option(s) within the parent (if the
- * parent is an optgroup, the index within the optgroup)
- */
- NS_IMETHOD WillRemoveOptions(nsIContent* aParent,
- int32_t aContentIndex,
- bool aNotify);
- /**
- * Checks whether an option is disabled (even if it's part of an optgroup)
- *
- * @param aIndex the index of the option to check
- * @return whether the option is disabled
- */
- NS_IMETHOD IsOptionDisabled(int32_t aIndex,
- bool* aIsDisabled);
- bool IsOptionDisabled(HTMLOptionElement* aOption);
- /**
- * Sets multiple options (or just sets startIndex if select is single)
- * and handles notifications and cleanup and everything under the sun.
- * When this method exits, the select will be in a consistent state. i.e.
- * if you set the last option to false, it will select an option anyway.
- *
- * @param aStartIndex the first index to set
- * @param aEndIndex the last index to set (set same as first index for one
- * option)
- * @param aOptionsMask determines whether to set, clear all or disable
- * options and whether frames are to be notified of such.
- * @return whether any options were actually changed
- */
- bool SetOptionsSelectedByIndex(int32_t aStartIndex,
- int32_t aEndIndex,
- uint32_t aOptionsMask);
- /**
- * Finds the index of a given option element
- *
- * @param aOption the option to get the index of
- * @param aStartIndex the index to start looking at
- * @param aForward TRUE to look forward, FALSE to look backward
- * @return the option index
- */
- NS_IMETHOD GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
- int32_t aStartIndex,
- bool aForward,
- int32_t* aIndex);
- /**
- * Called when an attribute is about to be changed
- */
- virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
- nsIContent* aBindingParent,
- bool aCompileEventHandlers) override;
- virtual void UnbindFromTree(bool aDeep, bool aNullParent) override;
- virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
- const nsAttrValueOrString* aValue,
- bool aNotify) override;
- virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
- const nsAttrValue* aValue,
- const nsAttrValue* aOldValue,
- bool aNotify) override;
- virtual void DoneAddingChildren(bool aHaveNotified) override;
- virtual bool IsDoneAddingChildren() override {
- return mIsDoneAddingChildren;
- }
- virtual bool ParseAttribute(int32_t aNamespaceID,
- nsIAtom* aAttribute,
- const nsAString& aValue,
- nsAttrValue& aResult) override;
- virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override;
- virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
- int32_t aModType) const override;
- NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override;
- virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override;
- NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLSelectElement,
- nsGenericHTMLFormElementWithState)
- HTMLOptionsCollection* GetOptions()
- {
- return mOptions;
- }
- // nsIConstraintValidation
- nsresult GetValidationMessage(nsAString& aValidationMessage,
- ValidityStateType aType) override;
- void UpdateValueMissingValidityState();
- /**
- * Insert aElement before the node given by aBefore
- */
- void Add(nsGenericHTMLElement& aElement, nsGenericHTMLElement* aBefore,
- ErrorResult& aError);
- void Add(nsGenericHTMLElement& aElement, int32_t aIndex, ErrorResult& aError)
- {
- // If item index is out of range, insert to last.
- // (since beforeElement becomes null, it is inserted to last)
- nsIContent* beforeContent = mOptions->GetElementAt(aIndex);
- return Add(aElement, nsGenericHTMLElement::FromContentOrNull(beforeContent),
- aError);
- }
- /**
- * Is this a combobox?
- */
- bool IsCombobox() const
- {
- return !Multiple() && Size() <= 1;
- }
- bool OpenInParentProcess();
- void SetOpenInParentProcess(bool aVal);
- protected:
- virtual ~HTMLSelectElement() = default;
- friend class SafeOptionListMutation;
- // Helper Methods
- /**
- * Check whether the option specified by the index is selected
- * @param aIndex the index
- * @return whether the option at the index is selected
- */
- bool IsOptionSelectedByIndex(int32_t aIndex);
- /**
- * Starting with (and including) aStartIndex, find the first selected index
- * and set mSelectedIndex to it.
- * @param aStartIndex the index to start with
- */
- void FindSelectedIndex(int32_t aStartIndex, bool aNotify);
- /**
- * Select some option if possible (generally the first non-disabled option).
- * @return true if something was selected, false otherwise
- */
- bool SelectSomething(bool aNotify);
- /**
- * Call SelectSomething(), but only if nothing is selected
- * @see SelectSomething()
- * @return true if something was selected, false otherwise
- */
- bool CheckSelectSomething(bool aNotify);
- /**
- * Called to trigger notifications of frames and fixing selected index
- *
- * @param aSelectFrame the frame for this content (could be null)
- * @param aIndex the index that was selected or deselected
- * @param aSelected whether the index was selected or deselected
- * @param aChangeOptionState if false, don't do anything to the
- * HTMLOptionElement at aIndex. If true, change
- * its selected state to aSelected.
- * @param aNotify whether to notify the style system and such
- */
- void OnOptionSelected(nsISelectControlFrame* aSelectFrame,
- int32_t aIndex,
- bool aSelected,
- bool aChangeOptionState,
- bool aNotify);
- /**
- * Restore state to a particular state string (representing the options)
- * @param aNewSelected the state string to restore to
- */
- void RestoreStateTo(SelectState* aNewSelected);
- // Adding options
- /**
- * Insert option(s) into the options[] array and perform notifications
- * @param aOptions the option or optgroup being added
- * @param aListIndex the index to start adding options into the list at
- * @param aDepth the depth of aOptions (1=direct child of select ...)
- */
- void InsertOptionsIntoList(nsIContent* aOptions,
- int32_t aListIndex,
- int32_t aDepth,
- bool aNotify);
- /**
- * Remove option(s) from the options[] array
- * @param aOptions the option or optgroup being added
- * @param aListIndex the index to start removing options from the list at
- * @param aDepth the depth of aOptions (1=direct child of select ...)
- */
- nsresult RemoveOptionsFromList(nsIContent* aOptions,
- int32_t aListIndex,
- int32_t aDepth,
- bool aNotify);
- // nsIConstraintValidation
- void UpdateBarredFromConstraintValidation();
- bool IsValueMissing();
- /**
- * Get the index of the first option at, under or following the content in
- * the select, or length of options[] if none are found
- * @param aOptions the content
- * @return the index of the first option
- */
- int32_t GetOptionIndexAt(nsIContent* aOptions);
- /**
- * Get the next option following the content in question (not at or under)
- * (this could include siblings of the current content or siblings of the
- * parent or children of siblings of the parent).
- * @param aOptions the content
- * @return the index of the next option after the content
- */
- int32_t GetOptionIndexAfter(nsIContent* aOptions);
- /**
- * Get the first option index at or under the content in question.
- * @param aOptions the content
- * @return the index of the first option at or under the content
- */
- int32_t GetFirstOptionIndex(nsIContent* aOptions);
- /**
- * Get the first option index under the content in question, within the
- * range specified.
- * @param aOptions the content
- * @param aStartIndex the first child to look at
- * @param aEndIndex the child *after* the last child to look at
- * @return the index of the first option at or under the content
- */
- int32_t GetFirstChildOptionIndex(nsIContent* aOptions,
- int32_t aStartIndex,
- int32_t aEndIndex);
- /**
- * Get the frame as an nsISelectControlFrame (MAY RETURN nullptr)
- * @return the select frame, or null
- */
- nsISelectControlFrame* GetSelectFrame();
- /**
- * Helper method for dispatching ContentReset notifications to list
- * and combo box frames.
- */
- void DispatchContentReset();
- /**
- * Rebuilds the options array from scratch as a fallback in error cases.
- */
- void RebuildOptionsArray(bool aNotify);
- #ifdef DEBUG
- void VerifyOptionsArray();
- #endif
- nsresult SetSelectedIndexInternal(int32_t aIndex, bool aNotify);
- void SetSelectionChanged(bool aValue, bool aNotify);
- /**
- * Marks the selectedOptions list as dirty, so that it'll populate itself
- * again.
- */
- void UpdateSelectedOptions();
- /**
- * Return whether an element should have a validity UI.
- * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
- *
- * @return Whether the element should have a validity UI.
- */
- bool ShouldShowValidityUI() const {
- /**
- * Always show the validity UI if the form has already tried to be submitted
- * but was invalid.
- *
- * Otherwise, show the validity UI if the selection has been changed.
- */
- if (mForm && mForm->HasEverTriedInvalidSubmit()) {
- return true;
- }
- return mSelectionHasChanged;
- }
- /** The options[] array */
- RefPtr<HTMLOptionsCollection> mOptions;
- nsContentUtils::AutocompleteAttrState mAutocompleteAttrState;
- /** false if the parser is in the middle of adding children. */
- bool mIsDoneAddingChildren;
- /** true if our disabled state has changed from the default **/
- bool mDisabledChanged;
- /** true if child nodes are being added or removed.
- * Used by SafeOptionListMutation.
- */
- bool mMutating;
- /**
- * True if DoneAddingChildren will get called but shouldn't restore state.
- */
- bool mInhibitStateRestoration;
- /**
- * True if the selection has changed since the element's creation.
- */
- bool mSelectionHasChanged;
- /**
- * True if the default selected option has been set.
- */
- bool mDefaultSelectionSet;
- /**
- * True if :-moz-ui-invalid can be shown.
- */
- bool mCanShowInvalidUI;
- /**
- * True if :-moz-ui-valid can be shown.
- */
- bool mCanShowValidUI;
- /** The number of non-options as children of the select */
- uint32_t mNonOptionChildren;
- /** The number of optgroups anywhere under the select */
- uint32_t mOptGroupCount;
- /**
- * The current selected index for selectedIndex (will be the first selected
- * index if multiple are selected)
- */
- int32_t mSelectedIndex;
- /**
- * The temporary restore state in case we try to restore before parser is
- * done adding options
- */
- nsCOMPtr<SelectState> mRestoreState;
- /**
- * The live list of selected options.
- */
- RefPtr<nsContentList> mSelectedOptions;
- private:
- static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
- nsRuleData* aData);
- };
- } // namespace dom
- } // namespace mozilla
- #endif // mozilla_dom_HTMLSelectElement_h
|