tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HTMLSelectElement.h (19565B)


      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
      8 
      9 #include "mozilla/Attributes.h"
     10 #include "mozilla/EnumSet.h"
     11 #include "mozilla/dom/BindingDeclarations.h"
     12 #include "mozilla/dom/ConstraintValidation.h"
     13 #include "mozilla/dom/HTMLFormElement.h"
     14 #include "mozilla/dom/HTMLOptionsCollection.h"
     15 #include "nsCOMPtr.h"
     16 #include "nsCheapSets.h"
     17 #include "nsContentUtils.h"
     18 #include "nsError.h"
     19 #include "nsGenericHTMLElement.h"
     20 
     21 class nsContentList;
     22 class nsIDOMHTMLOptionElement;
     23 class nsIHTMLCollection;
     24 class nsISelectControlFrame;
     25 
     26 namespace mozilla {
     27 
     28 class ErrorResult;
     29 class EventChainPostVisitor;
     30 class EventChainPreVisitor;
     31 class SelectContentData;
     32 class PresState;
     33 
     34 namespace dom {
     35 
     36 class FormData;
     37 class HTMLElementOrLong;
     38 class HTMLOptionElementOrHTMLOptGroupElement;
     39 class HTMLSelectElement;
     40 
     41 class MOZ_STACK_CLASS SafeOptionListMutation {
     42 public:
     43  /**
     44   * @param aSelect The select element which option list is being mutated.
     45   *                Can be null.
     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.
     50   */
     51  SafeOptionListMutation(nsIContent* aSelect, nsIContent* aParent,
     52                         nsIContent* aKid, uint32_t aIndex, bool aNotify);
     53  ~SafeOptionListMutation();
     54  void MutationFailed() { mNeedsRebuild = true; }
     55 
     56 private:
     57  static void* operator new(size_t) noexcept(true) { return nullptr; }
     58  static void operator delete(void*, size_t) {}
     59  /** The select element which option list is being mutated. */
     60  RefPtr<HTMLSelectElement> mSelect;
     61  /** true if the current mutation is the first one in the stack. */
     62  bool mTopLevelMutation;
     63  /** true if it is known that the option list must be recreated. */
     64  bool mNeedsRebuild;
     65  /** Whether we should be notifying when we make various method calls on
     66      mSelect */
     67  const bool mNotify;
     68  /** The selected option at mutation start. */
     69  RefPtr<HTMLOptionElement> mInitialSelectedOption;
     70  /** Option list must be recreated if more than one mutation is detected. */
     71  nsMutationGuard mGuard;
     72 };
     73 
     74 /**
     75 * Implementation of &lt;select&gt;
     76 */
     77 class HTMLSelectElement final : public nsGenericHTMLFormControlElementWithState,
     78                                public ConstraintValidation {
     79 public:
     80  /**
     81   *  IsSelected        whether to set the option(s) to true or false
     82   *
     83   *  ClearAll          whether to clear all other options (for example, if you
     84   *                     are normal-clicking on the current option)
     85   *
     86   *  SetDisabled       whether it is permissible to set disabled options
     87   *                     (for JavaScript)
     88   *
     89   *  Notify             whether to notify frames and such
     90   *
     91   *  NoReselect        no need to select something after an option is
     92   * deselected (for reset)
     93   *
     94   *  InsertingOptions  if an option has just been inserted some bailouts can't
     95   * be taken
     96   */
     97  enum class OptionFlag : uint8_t {
     98    IsSelected,
     99    ClearAll,
    100    SetDisabled,
    101    Notify,
    102    NoReselect,
    103    InsertingOptions
    104  };
    105  using OptionFlags = EnumSet<OptionFlag>;
    106 
    107  using ConstraintValidation::GetValidationMessage;
    108 
    109  explicit HTMLSelectElement(
    110      already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
    111      FromParser aFromParser = NOT_FROM_PARSER);
    112 
    113  NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLSelectElement, select)
    114 
    115  // nsISupports
    116  NS_DECL_ISUPPORTS_INHERITED
    117 
    118  int32_t TabIndexDefault() override;
    119 
    120  // Element
    121  bool IsInteractiveHTMLContent() const override { return true; }
    122 
    123  // WebIdl HTMLSelectElement
    124  void GetAutocomplete(DOMString& aValue);
    125  void SetAutocomplete(const nsAString& aValue, ErrorResult& aRv) {
    126    SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv);
    127  }
    128 
    129  void GetAutocompleteInfo(AutocompleteInfo& aInfo);
    130 
    131  // Sets the user interacted flag and fires input/change events if needed.
    132  MOZ_CAN_RUN_SCRIPT void UserFinishedInteracting(bool aChanged);
    133 
    134  bool Disabled() const { return GetBoolAttr(nsGkAtoms::disabled); }
    135  void SetDisabled(bool aVal, ErrorResult& aRv) {
    136    SetHTMLBoolAttr(nsGkAtoms::disabled, aVal, aRv);
    137  }
    138  bool Multiple() const { return GetBoolAttr(nsGkAtoms::multiple); }
    139  void SetMultiple(bool aVal, ErrorResult& aRv) {
    140    SetHTMLBoolAttr(nsGkAtoms::multiple, aVal, aRv);
    141  }
    142 
    143  void GetName(DOMString& aValue) { GetHTMLAttr(nsGkAtoms::name, aValue); }
    144  void SetName(const nsAString& aName, ErrorResult& aRv) {
    145    SetHTMLAttr(nsGkAtoms::name, aName, aRv);
    146  }
    147  bool Required() const { return State().HasState(ElementState::REQUIRED); }
    148  void SetRequired(bool aVal, ErrorResult& aRv) {
    149    SetHTMLBoolAttr(nsGkAtoms::required, aVal, aRv);
    150  }
    151  uint32_t Size() const { return GetUnsignedIntAttr(nsGkAtoms::size, 0); }
    152  void SetSize(uint32_t aSize, ErrorResult& aRv) {
    153    SetUnsignedIntAttr(nsGkAtoms::size, aSize, 0, aRv);
    154  }
    155 
    156  void GetType(nsAString& aValue);
    157 
    158  HTMLOptionsCollection* Options() const { return mOptions; }
    159  uint32_t Length() const { return mOptions->Length(); }
    160  void SetLength(uint32_t aLength, ErrorResult& aRv);
    161  Element* IndexedGetter(uint32_t aIdx, bool& aFound) const {
    162    return mOptions->IndexedGetter(aIdx, aFound);
    163  }
    164  HTMLOptionElement* Item(uint32_t aIdx) const {
    165    return mOptions->ItemAsOption(aIdx);
    166  }
    167  HTMLOptionElement* NamedItem(const nsAString& aName) const {
    168    return mOptions->GetNamedItem(aName);
    169  }
    170  void Add(const HTMLOptionElementOrHTMLOptGroupElement& aElement,
    171           const Nullable<HTMLElementOrLong>& aBefore, ErrorResult& aRv);
    172  void Remove(int32_t aIndex) const;
    173  void IndexedSetter(uint32_t aIndex, HTMLOptionElement* aOption,
    174                     ErrorResult& aRv) {
    175    mOptions->IndexedSetter(aIndex, aOption, aRv);
    176  }
    177 
    178  static bool MatchSelectedOptions(Element* aElement, int32_t, nsAtom*, void*);
    179 
    180  nsIHTMLCollection* SelectedOptions();
    181 
    182  int32_t SelectedIndex() const { return mSelectedIndex; }
    183  void SetSelectedIndex(int32_t aIdx) { SetSelectedIndexInternal(aIdx, true); }
    184  void GetValue(DOMString& aValue) const;
    185  void SetValue(const nsAString& aValue);
    186 
    187  // Override SetCustomValidity so we update our state properly when it's called
    188  // via bindings.
    189  void SetCustomValidity(const nsAString& aError);
    190 
    191  void ShowPicker(ErrorResult& aRv);
    192 
    193  using nsINode::Remove;
    194 
    195  // nsINode
    196  JSObject* WrapNode(JSContext*, JS::Handle<JSObject*> aGivenProto) override;
    197 
    198  // nsIContent
    199  void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
    200 
    201  bool IsHTMLFocusable(IsFocusableFlags, bool* aIsFocusable,
    202                       int32_t* aTabIndex) override;
    203  void InsertChildBefore(
    204      nsIContent* aKid, nsIContent* aBeforeThis, bool aNotify, ErrorResult& aRv,
    205      nsINode* aOldParent = nullptr,
    206      MutationEffectOnScript aMutationEffectOnScript =
    207          MutationEffectOnScript::DropTrustWorthiness) override;
    208  void RemoveChildNode(
    209      nsIContent* aKid, bool aNotify, const BatchRemovalState* aState,
    210      nsINode* aNewParent = nullptr,
    211      MutationEffectOnScript aMutationEffectOnScript =
    212          MutationEffectOnScript::DropTrustWorthiness) override;
    213 
    214  // nsGenericHTMLElement
    215  bool IsDisabledForEvents(WidgetEvent* aEvent) override;
    216 
    217  // nsGenericHTMLFormElement
    218  void SaveState() override;
    219  bool RestoreState(PresState* aState) override;
    220 
    221  // Overriden nsIFormControl methods
    222  NS_IMETHOD Reset() override;
    223  NS_IMETHOD SubmitNamesValues(FormData* aFormData) override;
    224 
    225  void FieldSetDisabledChanged(bool aNotify) override;
    226 
    227  /**
    228   * To be called when stuff is added under a child of the select--but *before*
    229   * they are actually added.
    230   *
    231   * @param aOptions the content that was added (usually just an option, but
    232   *        could be an optgroup node with many child options)
    233   * @param aParent the parent the options were added to (could be an optgroup)
    234   * @param aContentIndex the index where the options are being added within the
    235   *        parent (if the parent is an optgroup, the index within the optgroup)
    236   */
    237  NS_IMETHOD WillAddOptions(nsIContent* aOptions, nsIContent* aParent,
    238                            int32_t aContentIndex, bool aNotify);
    239 
    240  /**
    241   * To be called when stuff is removed under a child of the select--but
    242   * *before* they are actually removed.
    243   *
    244   * @param aParent the parent the option(s) are being removed from
    245   * @param aContentIndex the index of the option(s) within the parent (if the
    246   *        parent is an optgroup, the index within the optgroup)
    247   */
    248  NS_IMETHOD WillRemoveOptions(nsIContent* aParent, int32_t aContentIndex,
    249                               bool aNotify);
    250 
    251  /**
    252   * Checks whether an option is disabled (even if it's part of an optgroup)
    253   *
    254   * @param aIndex the index of the option to check
    255   * @return whether the option is disabled
    256   */
    257  NS_IMETHOD IsOptionDisabled(int32_t aIndex, bool* aIsDisabled);
    258  bool IsOptionDisabled(HTMLOptionElement* aOption) const;
    259 
    260  /**
    261   * Sets multiple options (or just sets startIndex if select is single)
    262   * and handles notifications and cleanup and everything under the sun.
    263   * When this method exits, the select will be in a consistent state.  i.e.
    264   * if you set the last option to false, it will select an option anyway.
    265   *
    266   * @param aStartIndex the first index to set
    267   * @param aEndIndex the last index to set (set same as first index for one
    268   *        option)
    269   * @param aOptionsMask determines whether to set, clear all or disable
    270   *        options and whether frames are to be notified of such.
    271   * @return whether any options were actually changed
    272   */
    273  bool SetOptionsSelectedByIndex(int32_t aStartIndex, int32_t aEndIndex,
    274                                 OptionFlags aOptionsMask);
    275 
    276  /**
    277   * Called when an attribute is about to be changed
    278   */
    279  nsresult BindToTree(BindContext&, nsINode& aParent) override;
    280  void UnbindFromTree(UnbindContext&) override;
    281  void BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
    282                     const nsAttrValue* aValue, bool aNotify) override;
    283  void AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
    284                    const nsAttrValue* aValue, const nsAttrValue* aOldValue,
    285                    nsIPrincipal* aSubjectPrincipal, bool aNotify) override;
    286 
    287  void DoneAddingChildren(bool aHaveNotified) override;
    288  bool IsDoneAddingChildren() const { return mIsDoneAddingChildren; }
    289 
    290  bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
    291                      const nsAString& aValue,
    292                      nsIPrincipal* aMaybeScriptedPrincipal,
    293                      nsAttrValue& aResult) override;
    294  nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override;
    295  nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute,
    296                                      AttrModType aModType) const override;
    297  NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
    298 
    299  nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
    300 
    301  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
    302      HTMLSelectElement, nsGenericHTMLFormControlElementWithState)
    303 
    304  HTMLOptionsCollection* GetOptions() { return mOptions; }
    305 
    306  // ConstraintValidation
    307  nsresult GetValidationMessage(nsAString& aValidationMessage,
    308                                ValidityStateType aType) override;
    309 
    310  void UpdateValueMissingValidityState();
    311  void UpdateValidityElementStates(bool aNotify);
    312  /**
    313   * Insert aElement before the node given by aBefore
    314   */
    315  void Add(nsGenericHTMLElement& aElement, nsGenericHTMLElement* aBefore,
    316           ErrorResult& aError);
    317  void Add(nsGenericHTMLElement& aElement, int32_t aIndex,
    318           ErrorResult& aError) {
    319    // If item index is out of range, insert to last.
    320    // (since beforeElement becomes null, it is inserted to last)
    321    nsIContent* beforeContent = mOptions->GetElementAt(aIndex);
    322    return Add(aElement, nsGenericHTMLElement::FromNodeOrNull(beforeContent),
    323               aError);
    324  }
    325 
    326  /**
    327   * Is this a combobox?
    328   */
    329  bool IsCombobox() const { return !Multiple() && Size() <= 1; }
    330 
    331  bool OpenInParentProcess() const { return mIsOpenInParentProcess; }
    332  void SetOpenInParentProcess(bool aVal) {
    333    mIsOpenInParentProcess = aVal;
    334    SetStates(ElementState::OPEN, aVal);
    335  }
    336 
    337  void GetPreviewValue(nsAString& aValue) { aValue = mPreviewValue; }
    338  void SetPreviewValue(const nsAString& aValue);
    339 
    340  void SetAutofillState(const nsAString& aState) {
    341    SetFormAutofillState(aState);
    342  }
    343  void GetAutofillState(nsAString& aState) { GetFormAutofillState(aState); }
    344 
    345 protected:
    346  virtual ~HTMLSelectElement() = default;
    347 
    348  friend class SafeOptionListMutation;
    349 
    350  // Helper Methods
    351  /**
    352   * Check whether the option specified by the index is selected
    353   * @param aIndex the index
    354   * @return whether the option at the index is selected
    355   */
    356  bool IsOptionSelectedByIndex(int32_t aIndex) const;
    357  /**
    358   * Starting with (and including) aStartIndex, find the first selected index
    359   * and set mSelectedIndex to it.
    360   * @param aStartIndex the index to start with
    361   */
    362  void FindSelectedIndex(int32_t aStartIndex, bool aNotify);
    363  /**
    364   * Select some option if possible (generally the first non-disabled option).
    365   * @return true if something was selected, false otherwise
    366   */
    367  bool SelectSomething(bool aNotify);
    368  /**
    369   * Call SelectSomething(), but only if nothing is selected
    370   * @see SelectSomething()
    371   * @return true if something was selected, false otherwise
    372   */
    373  bool CheckSelectSomething(bool aNotify);
    374  /**
    375   * Called to trigger notifications of frames and fixing selected index
    376   *
    377   * @param aSelectFrame the frame for this content (could be null)
    378   * @param aIndex the index that was selected or deselected
    379   * @param aSelected whether the index was selected or deselected
    380   * @param aChangeOptionState if false, don't do anything to the
    381   *                           HTMLOptionElement at aIndex.  If true, change
    382   *                           its selected state to aSelected.
    383   * @param aNotify whether to notify the style system and such
    384   */
    385  void OnOptionSelected(nsISelectControlFrame* aSelectFrame, int32_t aIndex,
    386                        bool aSelected, bool aChangeOptionState, bool aNotify);
    387  /**
    388   * Restore state to a particular state string (representing the options)
    389   * @param aNewSelected the state string to restore to
    390   */
    391  void RestoreStateTo(const SelectContentData& aNewSelected);
    392 
    393  // Adding options
    394  /**
    395   * Insert option(s) into the options[] array and perform notifications
    396   * @param aOptions the option or optgroup being added
    397   * @param aListIndex the index to start adding options into the list at
    398   * @param aDepth the depth of aOptions (1=direct child of select ...)
    399   */
    400  void InsertOptionsIntoList(nsIContent* aOptions, int32_t aListIndex,
    401                             int32_t aDepth, bool aNotify);
    402  /**
    403   * Remove option(s) from the options[] array
    404   * @param aOptions the option or optgroup being added
    405   * @param aListIndex the index to start removing options from the list at
    406   * @param aDepth the depth of aOptions (1=direct child of select ...)
    407   */
    408  nsresult RemoveOptionsFromList(nsIContent* aOptions, int32_t aListIndex,
    409                                 int32_t aDepth, bool aNotify);
    410 
    411  // nsIConstraintValidation
    412  void UpdateBarredFromConstraintValidation();
    413  bool IsValueMissing() const;
    414 
    415  /**
    416   * Get the index of the first option at, under or following the content in
    417   * the select, or length of options[] if none are found
    418   * @param aOptions the content
    419   * @return the index of the first option
    420   */
    421  int32_t GetOptionIndexAt(nsIContent* aOptions);
    422  /**
    423   * Get the next option following the content in question (not at or under)
    424   * (this could include siblings of the current content or siblings of the
    425   * parent or children of siblings of the parent).
    426   * @param aOptions the content
    427   * @return the index of the next option after the content
    428   */
    429  int32_t GetOptionIndexAfter(nsIContent* aOptions);
    430  /**
    431   * Get the first option index at or under the content in question.
    432   * @param aOptions the content
    433   * @return the index of the first option at or under the content
    434   */
    435  int32_t GetFirstOptionIndex(nsIContent* aOptions);
    436  /**
    437   * Get the first option index under the content in question, within the
    438   * range specified.
    439   * @param aOptions the content
    440   * @param aStartIndex the first child to look at
    441   * @param aEndIndex the child *after* the last child to look at
    442   * @return the index of the first option at or under the content
    443   */
    444  int32_t GetFirstChildOptionIndex(nsIContent* aOptions, int32_t aStartIndex,
    445                                   int32_t aEndIndex);
    446 
    447  /**
    448   * Get the frame as an nsISelectControlFrame (MAY RETURN nullptr)
    449   * @return the select frame, or null
    450   */
    451  nsISelectControlFrame* GetSelectFrame();
    452 
    453  /**
    454   * Helper method for dispatching ContentReset notifications to list box
    455   * frames.
    456   */
    457  void DispatchContentReset();
    458 
    459  /**
    460   * Rebuilds the options array from scratch as a fallback in error cases.
    461   */
    462  void RebuildOptionsArray(bool aNotify);
    463 
    464 #ifdef DEBUG
    465  void VerifyOptionsArray();
    466 #endif
    467 
    468  void SetSelectedIndexInternal(int32_t aIndex, bool aNotify);
    469 
    470  void OnSelectionChanged();
    471 
    472  /**
    473   * Marks the selectedOptions list as dirty, so that it'll populate itself
    474   * again.
    475   */
    476  void UpdateSelectedOptions();
    477 
    478  void SetUserInteracted(bool) final;
    479 
    480  /** The options[] array */
    481  RefPtr<HTMLOptionsCollection> mOptions;
    482  nsContentUtils::AutocompleteAttrState mAutocompleteAttrState;
    483  nsContentUtils::AutocompleteAttrState mAutocompleteInfoState;
    484  /** false if the parser is in the middle of adding children. */
    485  bool mIsDoneAddingChildren : 1;
    486  /** true if our disabled state has changed from the default **/
    487  bool mDisabledChanged : 1;
    488  /** true if child nodes are being added or removed.
    489   *  Used by SafeOptionListMutation.
    490   */
    491  bool mMutating : 1;
    492  /**
    493   * True if DoneAddingChildren will get called but shouldn't restore state.
    494   */
    495  bool mInhibitStateRestoration : 1;
    496  /** https://html.spec.whatwg.org/#user-interacted */
    497  bool mUserInteracted : 1;
    498  /** True if the default selected option has been set. */
    499  bool mDefaultSelectionSet : 1;
    500  /** True if we're open in the parent process */
    501  bool mIsOpenInParentProcess : 1;
    502 
    503  /** The number of non-options as children of the select */
    504  uint32_t mNonOptionChildren;
    505  /** The number of optgroups anywhere under the select */
    506  uint32_t mOptGroupCount;
    507  /**
    508   * The current selected index for selectedIndex (will be the first selected
    509   * index if multiple are selected)
    510   */
    511  int32_t mSelectedIndex;
    512  /**
    513   * The temporary restore state in case we try to restore before parser is
    514   * done adding options
    515   */
    516  UniquePtr<SelectContentData> mRestoreState;
    517 
    518  /**
    519   * The live list of selected options.
    520   */
    521  RefPtr<nsContentList> mSelectedOptions;
    522 
    523  /**
    524   * The current displayed preview text.
    525   */
    526  nsString mPreviewValue;
    527 
    528 private:
    529  static void MapAttributesIntoRule(MappedDeclarationsBuilder&);
    530 };
    531 
    532 }  // namespace dom
    533 }  // namespace mozilla
    534 
    535 #endif  // mozilla_dom_HTMLSelectElement_h