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 #ifndef mozilla_dom_HTMLSelectElement_h
7 #define mozilla_dom_HTMLSelectElement_h
9 #include "mozilla/Attributes.h"
10 #include "nsGenericHTMLElement.h"
11 #include "nsIConstraintValidation.h"
13 #include "mozilla/dom/BindingDeclarations.h"
14 #include "mozilla/dom/UnionTypes.h"
15 #include "mozilla/dom/HTMLOptionsCollection.h"
16 #include "mozilla/ErrorResult.h"
17 #include "nsCheapSets.h"
20 #include "mozilla/dom/HTMLFormElement.h"
21 #include "nsContentUtils.h"
24 class nsIDOMHTMLOptionElement
;
25 class nsIHTMLCollection
;
26 class nsISelectControlFrame
;
30 class EventChainPostVisitor
;
31 class EventChainPreVisitor
;
32 class SelectContentData
;
37 class HTMLFormSubmission
;
38 class HTMLSelectElement
;
40 class MOZ_STACK_CLASS SafeOptionListMutation
44 * @param aSelect The select element which option list is being mutated.
46 * @param aParent The content object which is being mutated.
47 * @param aKid If not null, a new child element is being inserted to
48 * aParent. Otherwise a child element will be removed.
49 * @param aIndex The index of the content object in the parent.
51 SafeOptionListMutation(nsIContent
* aSelect
, nsIContent
* aParent
,
52 nsIContent
* aKid
, uint32_t aIndex
, bool aNotify
);
53 ~SafeOptionListMutation();
54 void MutationFailed() { mNeedsRebuild
= true; }
56 static void* operator new(size_t) CPP_THROW_NEW
{ return 0; }
57 static void operator delete(void*, size_t) {}
58 /** The select element which option list is being mutated. */
59 RefPtr
<HTMLSelectElement
> mSelect
;
60 /** true if the current mutation is the first one in the stack. */
61 bool mTopLevelMutation
;
62 /** true if it is known that the option list must be recreated. */
64 /** Whether we should be notifying when we make various method calls on
67 /** The selected index at mutation start. */
68 int32_t mInitialSelectedIndex
;
69 /** Option list must be recreated if more than one mutation is detected. */
70 nsMutationGuard mGuard
;
75 * Implementation of <select>
77 class HTMLSelectElement final
: public nsGenericHTMLFormElementWithState
,
78 public nsIConstraintValidation
82 * IS_SELECTED whether to set the option(s) to true or false
84 * CLEAR_ALL whether to clear all other options (for example, if you
85 * are normal-clicking on the current option)
87 * SET_DISABLED whether it is permissible to set disabled options
90 * NOTIFY whether to notify frames and such
92 * NO_RESELECT no need to select something after an option is deselected
98 SET_DISABLED
= 1 << 2,
103 using nsIConstraintValidation::GetValidationMessage
;
105 explicit HTMLSelectElement(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
,
106 FromParser aFromParser
= NOT_FROM_PARSER
);
108 NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLSelectElement
, select
)
111 NS_DECL_ISUPPORTS_INHERITED
113 virtual int32_t TabIndexDefault() override
;
116 virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex
) const override
121 // WebIdl HTMLSelectElement
122 bool Autofocus() const
124 return GetBoolAttr(nsGkAtoms::autofocus
);
126 void SetAutofocus(bool aVal
, ErrorResult
& aRv
)
128 SetHTMLBoolAttr(nsGkAtoms::autofocus
, aVal
, aRv
);
130 void GetAutocomplete(DOMString
& aValue
);
131 void SetAutocomplete(const nsAString
& aValue
, ErrorResult
& aRv
)
133 SetHTMLAttr(nsGkAtoms::autocomplete
, aValue
, aRv
);
136 void GetAutocompleteInfo(AutocompleteInfo
& aInfo
);
138 bool Disabled() const
140 return GetBoolAttr(nsGkAtoms::disabled
);
142 void SetDisabled(bool aVal
, ErrorResult
& aRv
)
144 SetHTMLBoolAttr(nsGkAtoms::disabled
, aVal
, aRv
);
146 HTMLFormElement
* GetForm() const
148 return nsGenericHTMLFormElementWithState::GetForm();
150 bool Multiple() const
152 return GetBoolAttr(nsGkAtoms::multiple
);
154 void SetMultiple(bool aVal
, ErrorResult
& aRv
)
156 SetHTMLBoolAttr(nsGkAtoms::multiple
, aVal
, aRv
);
159 void GetName(DOMString
& aValue
)
161 GetHTMLAttr(nsGkAtoms::name
, aValue
);
163 void SetName(const nsAString
& aName
, ErrorResult
& aRv
)
165 SetHTMLAttr(nsGkAtoms::name
, aName
, aRv
);
167 bool Required() const
169 return State().HasState(NS_EVENT_STATE_REQUIRED
);
171 void SetRequired(bool aVal
, ErrorResult
& aRv
)
173 SetHTMLBoolAttr(nsGkAtoms::required
, aVal
, aRv
);
175 uint32_t Size() const
177 return GetUnsignedIntAttr(nsGkAtoms::size
, 0);
179 void SetSize(uint32_t aSize
, ErrorResult
& aRv
)
181 SetUnsignedIntAttr(nsGkAtoms::size
, aSize
, 0, aRv
);
184 void GetType(nsAString
& aValue
);
186 HTMLOptionsCollection
* Options() const
190 uint32_t Length() const
192 return mOptions
->Length();
194 void SetLength(uint32_t aLength
, ErrorResult
& aRv
);
195 Element
* IndexedGetter(uint32_t aIdx
, bool& aFound
) const
197 return mOptions
->IndexedGetter(aIdx
, aFound
);
199 HTMLOptionElement
* Item(uint32_t aIdx
) const
201 return mOptions
->ItemAsOption(aIdx
);
203 HTMLOptionElement
* NamedItem(const nsAString
& aName
) const
205 return mOptions
->GetNamedItem(aName
);
207 void Add(const HTMLOptionElementOrHTMLOptGroupElement
& aElement
,
208 const Nullable
<HTMLElementOrLong
>& aBefore
,
210 void Remove(int32_t aIndex
);
211 void IndexedSetter(uint32_t aIndex
, HTMLOptionElement
* aOption
,
214 mOptions
->IndexedSetter(aIndex
, aOption
, aRv
);
217 static bool MatchSelectedOptions(Element
* aElement
, int32_t, nsAtom
*,
220 nsIHTMLCollection
* SelectedOptions();
222 int32_t SelectedIndex() const
224 return mSelectedIndex
;
226 void SetSelectedIndex(int32_t aIdx
, ErrorResult
& aRv
)
228 aRv
= SetSelectedIndexInternal(aIdx
, true);
230 void GetValue(DOMString
& aValue
);
231 void SetValue(const nsAString
& aValue
);
233 // Override SetCustomValidity so we update our state properly when it's called
235 void SetCustomValidity(const nsAString
& aError
);
237 using nsINode::Remove
;
240 virtual JSObject
* WrapNode(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) override
;
243 void GetEventTargetParent(EventChainPreVisitor
& aVisitor
) override
;
244 virtual nsresult
PostHandleEvent(
245 EventChainPostVisitor
& aVisitor
) override
;
247 virtual bool IsHTMLFocusable(bool aWithMouse
, bool* aIsFocusable
, int32_t* aTabIndex
) override
;
248 virtual nsresult
InsertChildBefore(nsIContent
* aKid
, nsIContent
* aBeforeThis
,
249 bool aNotify
) override
;
250 virtual void RemoveChildNode(nsIContent
* aKid
, bool aNotify
) override
;
252 // Overriden nsIFormControl methods
253 NS_IMETHOD
Reset() override
;
254 NS_IMETHOD
SubmitNamesValues(HTMLFormSubmission
* aFormSubmission
) override
;
255 NS_IMETHOD
SaveState() override
;
256 virtual bool RestoreState(PresState
* aState
) override
;
257 virtual bool IsDisabledForEvents(EventMessage aMessage
) override
;
259 virtual void FieldSetDisabledChanged(bool aNotify
) override
;
261 EventStates
IntrinsicState() const override
;
264 * To be called when stuff is added under a child of the select--but *before*
265 * they are actually added.
267 * @param aOptions the content that was added (usually just an option, but
268 * could be an optgroup node with many child options)
269 * @param aParent the parent the options were added to (could be an optgroup)
270 * @param aContentIndex the index where the options are being added within the
271 * parent (if the parent is an optgroup, the index within the optgroup)
273 NS_IMETHOD
WillAddOptions(nsIContent
* aOptions
,
275 int32_t aContentIndex
,
279 * To be called when stuff is removed under a child of the select--but
280 * *before* they are actually removed.
282 * @param aParent the parent the option(s) are being removed from
283 * @param aContentIndex the index of the option(s) within the parent (if the
284 * parent is an optgroup, the index within the optgroup)
286 NS_IMETHOD
WillRemoveOptions(nsIContent
* aParent
,
287 int32_t aContentIndex
,
291 * Checks whether an option is disabled (even if it's part of an optgroup)
293 * @param aIndex the index of the option to check
294 * @return whether the option is disabled
296 NS_IMETHOD
IsOptionDisabled(int32_t aIndex
,
298 bool IsOptionDisabled(HTMLOptionElement
* aOption
) const;
301 * Sets multiple options (or just sets startIndex if select is single)
302 * and handles notifications and cleanup and everything under the sun.
303 * When this method exits, the select will be in a consistent state. i.e.
304 * if you set the last option to false, it will select an option anyway.
306 * @param aStartIndex the first index to set
307 * @param aEndIndex the last index to set (set same as first index for one
309 * @param aOptionsMask determines whether to set, clear all or disable
310 * options and whether frames are to be notified of such.
311 * @return whether any options were actually changed
313 bool SetOptionsSelectedByIndex(int32_t aStartIndex
,
315 uint32_t aOptionsMask
);
318 * Called when an attribute is about to be changed
320 virtual nsresult
BindToTree(nsIDocument
* aDocument
, nsIContent
* aParent
,
321 nsIContent
* aBindingParent
) override
;
322 virtual void UnbindFromTree(bool aDeep
, bool aNullParent
) override
;
323 virtual nsresult
BeforeSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
324 const nsAttrValueOrString
* aValue
,
325 bool aNotify
) override
;
326 virtual nsresult
AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
327 const nsAttrValue
* aValue
,
328 const nsAttrValue
* aOldValue
,
329 nsIPrincipal
* aSubjectPrincipal
,
330 bool aNotify
) override
;
332 virtual void DoneAddingChildren(bool aHaveNotified
) override
;
333 virtual bool IsDoneAddingChildren() override
{
334 return mIsDoneAddingChildren
;
337 virtual bool ParseAttribute(int32_t aNamespaceID
,
339 const nsAString
& aValue
,
340 nsIPrincipal
* aMaybeScriptedPrincipal
,
341 nsAttrValue
& aResult
) override
;
342 virtual nsMapRuleToAttributesFunc
GetAttributeMappingFunction() const override
;
343 virtual nsChangeHint
GetAttributeChangeHint(const nsAtom
* aAttribute
,
344 int32_t aModType
) const override
;
345 NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom
* aAttribute
) const override
;
347 virtual nsresult
Clone(dom::NodeInfo
*, nsINode
** aResult
) const override
;
349 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLSelectElement
,
350 nsGenericHTMLFormElementWithState
)
352 HTMLOptionsCollection
* GetOptions()
357 // nsIConstraintValidation
358 nsresult
GetValidationMessage(nsAString
& aValidationMessage
,
359 ValidityStateType aType
) override
;
361 void UpdateValueMissingValidityState();
363 * Insert aElement before the node given by aBefore
365 void Add(nsGenericHTMLElement
& aElement
, nsGenericHTMLElement
* aBefore
,
366 ErrorResult
& aError
);
367 void Add(nsGenericHTMLElement
& aElement
, int32_t aIndex
, ErrorResult
& aError
)
369 // If item index is out of range, insert to last.
370 // (since beforeElement becomes null, it is inserted to last)
371 nsIContent
* beforeContent
= mOptions
->GetElementAt(aIndex
);
372 return Add(aElement
, nsGenericHTMLElement::FromNodeOrNull(beforeContent
),
377 * Is this a combobox?
379 bool IsCombobox() const
381 return !Multiple() && Size() <= 1;
384 bool OpenInParentProcess();
385 void SetOpenInParentProcess(bool aVal
);
387 void GetPreviewValue(nsAString
& aValue
)
389 aValue
= mPreviewValue
;
391 void SetPreviewValue(const nsAString
& aValue
);
394 virtual ~HTMLSelectElement();
396 friend class SafeOptionListMutation
;
400 * Check whether the option specified by the index is selected
401 * @param aIndex the index
402 * @return whether the option at the index is selected
404 bool IsOptionSelectedByIndex(int32_t aIndex
);
406 * Starting with (and including) aStartIndex, find the first selected index
407 * and set mSelectedIndex to it.
408 * @param aStartIndex the index to start with
410 void FindSelectedIndex(int32_t aStartIndex
, bool aNotify
);
412 * Select some option if possible (generally the first non-disabled option).
413 * @return true if something was selected, false otherwise
415 bool SelectSomething(bool aNotify
);
417 * Call SelectSomething(), but only if nothing is selected
418 * @see SelectSomething()
419 * @return true if something was selected, false otherwise
421 bool CheckSelectSomething(bool aNotify
);
423 * Called to trigger notifications of frames and fixing selected index
425 * @param aSelectFrame the frame for this content (could be null)
426 * @param aIndex the index that was selected or deselected
427 * @param aSelected whether the index was selected or deselected
428 * @param aChangeOptionState if false, don't do anything to the
429 * HTMLOptionElement at aIndex. If true, change
430 * its selected state to aSelected.
431 * @param aNotify whether to notify the style system and such
433 void OnOptionSelected(nsISelectControlFrame
* aSelectFrame
,
436 bool aChangeOptionState
,
439 * Restore state to a particular state string (representing the options)
440 * @param aNewSelected the state string to restore to
442 void RestoreStateTo(const SelectContentData
& aNewSelected
);
446 * Insert option(s) into the options[] array and perform notifications
447 * @param aOptions the option or optgroup being added
448 * @param aListIndex the index to start adding options into the list at
449 * @param aDepth the depth of aOptions (1=direct child of select ...)
451 void InsertOptionsIntoList(nsIContent
* aOptions
,
456 * Remove option(s) from the options[] array
457 * @param aOptions the option or optgroup being added
458 * @param aListIndex the index to start removing options from the list at
459 * @param aDepth the depth of aOptions (1=direct child of select ...)
461 nsresult
RemoveOptionsFromList(nsIContent
* aOptions
,
466 // nsIConstraintValidation
467 void UpdateBarredFromConstraintValidation();
468 bool IsValueMissing() const;
471 * Get the index of the first option at, under or following the content in
472 * the select, or length of options[] if none are found
473 * @param aOptions the content
474 * @return the index of the first option
476 int32_t GetOptionIndexAt(nsIContent
* aOptions
);
478 * Get the next option following the content in question (not at or under)
479 * (this could include siblings of the current content or siblings of the
480 * parent or children of siblings of the parent).
481 * @param aOptions the content
482 * @return the index of the next option after the content
484 int32_t GetOptionIndexAfter(nsIContent
* aOptions
);
486 * Get the first option index at or under the content in question.
487 * @param aOptions the content
488 * @return the index of the first option at or under the content
490 int32_t GetFirstOptionIndex(nsIContent
* aOptions
);
492 * Get the first option index under the content in question, within the
494 * @param aOptions the content
495 * @param aStartIndex the first child to look at
496 * @param aEndIndex the child *after* the last child to look at
497 * @return the index of the first option at or under the content
499 int32_t GetFirstChildOptionIndex(nsIContent
* aOptions
,
504 * Get the frame as an nsISelectControlFrame (MAY RETURN nullptr)
505 * @return the select frame, or null
507 nsISelectControlFrame
* GetSelectFrame();
510 * Helper method for dispatching ContentReset notifications to list
511 * and combo box frames.
513 void DispatchContentReset();
516 * Rebuilds the options array from scratch as a fallback in error cases.
518 void RebuildOptionsArray(bool aNotify
);
521 void VerifyOptionsArray();
524 nsresult
SetSelectedIndexInternal(int32_t aIndex
, bool aNotify
);
526 void SetSelectionChanged(bool aValue
, bool aNotify
);
529 * Marks the selectedOptions list as dirty, so that it'll populate itself
532 void UpdateSelectedOptions();
535 * Return whether an element should have a validity UI.
536 * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
538 * @return Whether the element should have a validity UI.
540 bool ShouldShowValidityUI() const {
542 * Always show the validity UI if the form has already tried to be submitted
545 * Otherwise, show the validity UI if the selection has been changed.
547 if (mForm
&& mForm
->HasEverTriedInvalidSubmit()) {
551 return mSelectionHasChanged
;
554 /** The options[] array */
555 RefPtr
<HTMLOptionsCollection
> mOptions
;
556 nsContentUtils::AutocompleteAttrState mAutocompleteAttrState
;
557 nsContentUtils::AutocompleteAttrState mAutocompleteInfoState
;
558 /** false if the parser is in the middle of adding children. */
559 bool mIsDoneAddingChildren
;
560 /** true if our disabled state has changed from the default **/
561 bool mDisabledChanged
;
562 /** true if child nodes are being added or removed.
563 * Used by SafeOptionListMutation.
567 * True if DoneAddingChildren will get called but shouldn't restore state.
569 bool mInhibitStateRestoration
;
571 * True if the selection has changed since the element's creation.
573 bool mSelectionHasChanged
;
575 * True if the default selected option has been set.
577 bool mDefaultSelectionSet
;
579 * True if :-moz-ui-invalid can be shown.
581 bool mCanShowInvalidUI
;
583 * True if :-moz-ui-valid can be shown.
585 bool mCanShowValidUI
;
587 /** The number of non-options as children of the select */
588 uint32_t mNonOptionChildren
;
589 /** The number of optgroups anywhere under the select */
590 uint32_t mOptGroupCount
;
592 * The current selected index for selectedIndex (will be the first selected
593 * index if multiple are selected)
595 int32_t mSelectedIndex
;
597 * The temporary restore state in case we try to restore before parser is
598 * done adding options
600 UniquePtr
<SelectContentData
> mRestoreState
;
603 * The live list of selected options.
605 RefPtr
<nsContentList
> mSelectedOptions
;
608 * The current displayed preview text.
610 nsString mPreviewValue
;
613 static void MapAttributesIntoRule(const nsMappedAttributes
* aAttributes
,
614 MappedDeclarations
&);
618 } // namespace mozilla
620 #endif // mozilla_dom_HTMLSelectElement_h