Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/dom/HTMLSelectElement.h
Line
Count
Source (jump to first uncovered line)
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 "nsGenericHTMLElement.h"
11
#include "nsIConstraintValidation.h"
12
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"
18
#include "nsCOMPtr.h"
19
#include "nsError.h"
20
#include "mozilla/dom/HTMLFormElement.h"
21
#include "nsContentUtils.h"
22
23
class nsContentList;
24
class nsIDOMHTMLOptionElement;
25
class nsIHTMLCollection;
26
class nsISelectControlFrame;
27
28
namespace mozilla {
29
30
class EventChainPostVisitor;
31
class EventChainPreVisitor;
32
class SelectContentData;
33
class PresState;
34
35
namespace dom {
36
37
class HTMLFormSubmission;
38
class HTMLSelectElement;
39
40
class MOZ_STACK_CLASS SafeOptionListMutation
41
{
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
0
  void MutationFailed() { mNeedsRebuild = true; }
55
private:
56
0
  static void* operator new(size_t) CPP_THROW_NEW { return 0; }
57
0
  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. */
63
  bool                       mNeedsRebuild;
64
  /** Whether we should be notifying when we make various method calls on
65
      mSelect */
66
  const bool                 mNotify;
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;
71
};
72
73
74
/**
75
 * Implementation of &lt;select&gt;
76
 */
77
class HTMLSelectElement final : public nsGenericHTMLFormElementWithState,
78
                                public nsIConstraintValidation
79
{
80
public:
81
  /**
82
   *  IS_SELECTED   whether to set the option(s) to true or false
83
   *
84
   *  CLEAR_ALL     whether to clear all other options (for example, if you
85
   *                are normal-clicking on the current option)
86
   *
87
   *  SET_DISABLED  whether it is permissible to set disabled options
88
   *                (for JavaScript)
89
   *
90
   *  NOTIFY        whether to notify frames and such
91
   *
92
   *  NO_RESELECT   no need to select something after an option is deselected
93
   *                (for reset)
94
   */
95
  enum OptionType {
96
    IS_SELECTED   = 1 << 0,
97
    CLEAR_ALL     = 1 << 1,
98
    SET_DISABLED  = 1 << 2,
99
    NOTIFY        = 1 << 3,
100
    NO_RESELECT   = 1 << 4
101
  };
102
103
  using nsIConstraintValidation::GetValidationMessage;
104
105
  explicit HTMLSelectElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
106
                             FromParser aFromParser = NOT_FROM_PARSER);
107
108
  NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLSelectElement, select)
109
110
  // nsISupports
111
  NS_DECL_ISUPPORTS_INHERITED
112
113
  virtual int32_t TabIndexDefault() override;
114
115
  // Element
116
  virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override
117
0
  {
118
0
    return true;
119
0
  }
120
121
  // WebIdl HTMLSelectElement
122
  bool Autofocus() const
123
0
  {
124
0
    return GetBoolAttr(nsGkAtoms::autofocus);
125
0
  }
126
  void SetAutofocus(bool aVal, ErrorResult& aRv)
127
0
  {
128
0
    SetHTMLBoolAttr(nsGkAtoms::autofocus, aVal, aRv);
129
0
  }
130
  void GetAutocomplete(DOMString& aValue);
131
  void SetAutocomplete(const nsAString& aValue, ErrorResult& aRv)
132
0
  {
133
0
    SetHTMLAttr(nsGkAtoms::autocomplete, aValue, aRv);
134
0
  }
135
136
  void GetAutocompleteInfo(AutocompleteInfo& aInfo);
137
138
  bool Disabled() const
139
0
  {
140
0
    return GetBoolAttr(nsGkAtoms::disabled);
141
0
  }
142
  void SetDisabled(bool aVal, ErrorResult& aRv)
143
0
  {
144
0
    SetHTMLBoolAttr(nsGkAtoms::disabled, aVal, aRv);
145
0
  }
146
  HTMLFormElement* GetForm() const
147
0
  {
148
0
    return nsGenericHTMLFormElementWithState::GetForm();
149
0
  }
150
  bool Multiple() const
151
0
  {
152
0
    return GetBoolAttr(nsGkAtoms::multiple);
153
0
  }
154
  void SetMultiple(bool aVal, ErrorResult& aRv)
155
0
  {
156
0
    SetHTMLBoolAttr(nsGkAtoms::multiple, aVal, aRv);
157
0
  }
158
159
  void GetName(DOMString& aValue)
160
0
  {
161
0
    GetHTMLAttr(nsGkAtoms::name, aValue);
162
0
  }
163
  void SetName(const nsAString& aName, ErrorResult& aRv)
164
0
  {
165
0
    SetHTMLAttr(nsGkAtoms::name, aName, aRv);
166
0
  }
167
  bool Required() const
168
0
  {
169
0
    return State().HasState(NS_EVENT_STATE_REQUIRED);
170
0
  }
171
  void SetRequired(bool aVal, ErrorResult& aRv)
172
0
  {
173
0
    SetHTMLBoolAttr(nsGkAtoms::required, aVal, aRv);
174
0
  }
175
  uint32_t Size() const
176
0
  {
177
0
    return GetUnsignedIntAttr(nsGkAtoms::size, 0);
178
0
  }
179
  void SetSize(uint32_t aSize, ErrorResult& aRv)
180
0
  {
181
0
    SetUnsignedIntAttr(nsGkAtoms::size, aSize, 0, aRv);
182
0
  }
183
184
  void GetType(nsAString& aValue);
185
186
  HTMLOptionsCollection* Options() const
187
0
  {
188
0
    return mOptions;
189
0
  }
190
  uint32_t Length() const
191
0
  {
192
0
    return mOptions->Length();
193
0
  }
194
  void SetLength(uint32_t aLength, ErrorResult& aRv);
195
  Element* IndexedGetter(uint32_t aIdx, bool& aFound) const
196
0
  {
197
0
    return mOptions->IndexedGetter(aIdx, aFound);
198
0
  }
199
  HTMLOptionElement* Item(uint32_t aIdx) const
200
0
  {
201
0
    return mOptions->ItemAsOption(aIdx);
202
0
  }
203
  HTMLOptionElement* NamedItem(const nsAString& aName) const
204
0
  {
205
0
    return mOptions->GetNamedItem(aName);
206
0
  }
207
  void Add(const HTMLOptionElementOrHTMLOptGroupElement& aElement,
208
           const Nullable<HTMLElementOrLong>& aBefore,
209
           ErrorResult& aRv);
210
  void Remove(int32_t aIndex);
211
  void IndexedSetter(uint32_t aIndex, HTMLOptionElement* aOption,
212
                     ErrorResult& aRv)
213
0
  {
214
0
    mOptions->IndexedSetter(aIndex, aOption, aRv);
215
0
  }
216
217
  static bool MatchSelectedOptions(Element* aElement, int32_t, nsAtom*,
218
                                   void*);
219
220
  nsIHTMLCollection* SelectedOptions();
221
222
  int32_t SelectedIndex() const
223
0
  {
224
0
    return mSelectedIndex;
225
0
  }
226
  void SetSelectedIndex(int32_t aIdx, ErrorResult& aRv)
227
0
  {
228
0
    aRv = SetSelectedIndexInternal(aIdx, true);
229
0
  }
230
  void GetValue(DOMString& aValue);
231
  void SetValue(const nsAString& aValue);
232
233
  // Override SetCustomValidity so we update our state properly when it's called
234
  // via bindings.
235
  void SetCustomValidity(const nsAString& aError);
236
237
  using nsINode::Remove;
238
239
  // nsINode
240
  virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
241
242
  // nsIContent
243
  void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
244
  virtual nsresult PostHandleEvent(
245
                     EventChainPostVisitor& aVisitor) override;
246
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;
251
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;
258
259
  virtual void FieldSetDisabledChanged(bool aNotify) override;
260
261
  EventStates IntrinsicState() const override;
262
263
  /**
264
   * To be called when stuff is added under a child of the select--but *before*
265
   * they are actually added.
266
   *
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)
272
   */
273
  NS_IMETHOD WillAddOptions(nsIContent* aOptions,
274
                            nsIContent* aParent,
275
                            int32_t aContentIndex,
276
                            bool aNotify);
277
278
  /**
279
   * To be called when stuff is removed under a child of the select--but
280
   * *before* they are actually removed.
281
   *
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)
285
   */
286
  NS_IMETHOD WillRemoveOptions(nsIContent* aParent,
287
                               int32_t aContentIndex,
288
                               bool aNotify);
289
290
  /**
291
   * Checks whether an option is disabled (even if it's part of an optgroup)
292
   *
293
   * @param aIndex the index of the option to check
294
   * @return whether the option is disabled
295
   */
296
  NS_IMETHOD IsOptionDisabled(int32_t aIndex,
297
                              bool* aIsDisabled);
298
  bool IsOptionDisabled(HTMLOptionElement* aOption) const;
299
300
  /**
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.
305
   *
306
   * @param aStartIndex the first index to set
307
   * @param aEndIndex the last index to set (set same as first index for one
308
   *        option)
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
312
   */
313
  bool SetOptionsSelectedByIndex(int32_t aStartIndex,
314
                                 int32_t aEndIndex,
315
                                 uint32_t aOptionsMask);
316
317
  /**
318
   * Called when an attribute is about to be changed
319
   */
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;
331
332
  virtual void DoneAddingChildren(bool aHaveNotified) override;
333
0
  virtual bool IsDoneAddingChildren() override {
334
0
    return mIsDoneAddingChildren;
335
0
  }
336
337
  virtual bool ParseAttribute(int32_t aNamespaceID,
338
                                nsAtom* aAttribute,
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;
346
347
  virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
348
349
  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLSelectElement,
350
                                           nsGenericHTMLFormElementWithState)
351
352
  HTMLOptionsCollection* GetOptions()
353
0
  {
354
0
    return mOptions;
355
0
  }
356
357
  // nsIConstraintValidation
358
  nsresult GetValidationMessage(nsAString& aValidationMessage,
359
                                ValidityStateType aType) override;
360
361
  void UpdateValueMissingValidityState();
362
  /**
363
   * Insert aElement before the node given by aBefore
364
   */
365
  void Add(nsGenericHTMLElement& aElement, nsGenericHTMLElement* aBefore,
366
           ErrorResult& aError);
367
  void Add(nsGenericHTMLElement& aElement, int32_t aIndex, ErrorResult& aError)
368
0
  {
369
0
    // If item index is out of range, insert to last.
370
0
    // (since beforeElement becomes null, it is inserted to last)
371
0
    nsIContent* beforeContent = mOptions->GetElementAt(aIndex);
372
0
    return Add(aElement, nsGenericHTMLElement::FromNodeOrNull(beforeContent),
373
0
               aError);
374
0
  }
375
376
  /**
377
   * Is this a combobox?
378
   */
379
  bool IsCombobox() const
380
0
  {
381
0
    return !Multiple() && Size() <= 1;
382
0
  }
383
384
  bool OpenInParentProcess();
385
  void SetOpenInParentProcess(bool aVal);
386
387
  void GetPreviewValue(nsAString& aValue)
388
0
  {
389
0
    aValue = mPreviewValue;
390
0
  }
391
  void SetPreviewValue(const nsAString& aValue);
392
393
protected:
394
  virtual ~HTMLSelectElement();
395
396
  friend class SafeOptionListMutation;
397
398
  // Helper Methods
399
  /**
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
403
   */
404
  bool IsOptionSelectedByIndex(int32_t aIndex);
405
  /**
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
409
   */
410
  void FindSelectedIndex(int32_t aStartIndex, bool aNotify);
411
  /**
412
   * Select some option if possible (generally the first non-disabled option).
413
   * @return true if something was selected, false otherwise
414
   */
415
  bool SelectSomething(bool aNotify);
416
  /**
417
   * Call SelectSomething(), but only if nothing is selected
418
   * @see SelectSomething()
419
   * @return true if something was selected, false otherwise
420
   */
421
  bool CheckSelectSomething(bool aNotify);
422
  /**
423
   * Called to trigger notifications of frames and fixing selected index
424
   *
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
432
   */
433
  void OnOptionSelected(nsISelectControlFrame* aSelectFrame,
434
                        int32_t aIndex,
435
                        bool aSelected,
436
                        bool aChangeOptionState,
437
                        bool aNotify);
438
  /**
439
   * Restore state to a particular state string (representing the options)
440
   * @param aNewSelected the state string to restore to
441
   */
442
  void RestoreStateTo(const SelectContentData& aNewSelected);
443
444
  // Adding options
445
  /**
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 ...)
450
   */
451
  void InsertOptionsIntoList(nsIContent* aOptions,
452
                             int32_t aListIndex,
453
                             int32_t aDepth,
454
                             bool aNotify);
455
  /**
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 ...)
460
   */
461
  nsresult RemoveOptionsFromList(nsIContent* aOptions,
462
                                 int32_t aListIndex,
463
                                 int32_t aDepth,
464
                                 bool aNotify);
465
466
  // nsIConstraintValidation
467
  void UpdateBarredFromConstraintValidation();
468
  bool IsValueMissing() const;
469
470
  /**
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
475
   */
476
  int32_t GetOptionIndexAt(nsIContent* aOptions);
477
  /**
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
483
   */
484
  int32_t GetOptionIndexAfter(nsIContent* aOptions);
485
  /**
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
489
   */
490
  int32_t GetFirstOptionIndex(nsIContent* aOptions);
491
  /**
492
   * Get the first option index under the content in question, within the
493
   * range specified.
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
498
   */
499
  int32_t GetFirstChildOptionIndex(nsIContent* aOptions,
500
                                   int32_t aStartIndex,
501
                                   int32_t aEndIndex);
502
503
  /**
504
   * Get the frame as an nsISelectControlFrame (MAY RETURN nullptr)
505
   * @return the select frame, or null
506
   */
507
  nsISelectControlFrame* GetSelectFrame();
508
509
  /**
510
   * Helper method for dispatching ContentReset notifications to list
511
   * and combo box frames.
512
   */
513
  void DispatchContentReset();
514
515
  /**
516
   * Rebuilds the options array from scratch as a fallback in error cases.
517
   */
518
  void RebuildOptionsArray(bool aNotify);
519
520
#ifdef DEBUG
521
  void VerifyOptionsArray();
522
#endif
523
524
  nsresult SetSelectedIndexInternal(int32_t aIndex, bool aNotify);
525
526
  void SetSelectionChanged(bool aValue, bool aNotify);
527
528
  /**
529
   * Marks the selectedOptions list as dirty, so that it'll populate itself
530
   * again.
531
   */
532
  void UpdateSelectedOptions();
533
534
  /**
535
   * Return whether an element should have a validity UI.
536
   * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
537
   *
538
   * @return Whether the element should have a validity UI.
539
   */
540
0
  bool ShouldShowValidityUI() const {
541
0
    /**
542
0
     * Always show the validity UI if the form has already tried to be submitted
543
0
     * but was invalid.
544
0
     *
545
0
     * Otherwise, show the validity UI if the selection has been changed.
546
0
     */
547
0
    if (mForm && mForm->HasEverTriedInvalidSubmit()) {
548
0
      return true;
549
0
    }
550
0
551
0
    return mSelectionHasChanged;
552
0
  }
553
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.
564
   */
565
  bool            mMutating;
566
  /**
567
   * True if DoneAddingChildren will get called but shouldn't restore state.
568
   */
569
  bool            mInhibitStateRestoration;
570
  /**
571
   * True if the selection has changed since the element's creation.
572
   */
573
  bool            mSelectionHasChanged;
574
  /**
575
   * True if the default selected option has been set.
576
   */
577
  bool            mDefaultSelectionSet;
578
  /**
579
   * True if :-moz-ui-invalid can be shown.
580
   */
581
  bool            mCanShowInvalidUI;
582
  /**
583
   * True if :-moz-ui-valid can be shown.
584
   */
585
  bool            mCanShowValidUI;
586
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;
591
  /**
592
   * The current selected index for selectedIndex (will be the first selected
593
   * index if multiple are selected)
594
   */
595
  int32_t   mSelectedIndex;
596
  /**
597
   * The temporary restore state in case we try to restore before parser is
598
   * done adding options
599
   */
600
  UniquePtr<SelectContentData> mRestoreState;
601
602
  /**
603
   * The live list of selected options.
604
  */
605
  RefPtr<nsContentList> mSelectedOptions;
606
607
  /**
608
   * The current displayed preview text.
609
  */
610
  nsString  mPreviewValue;
611
612
private:
613
  static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
614
                                    MappedDeclarations&);
615
};
616
617
} // namespace dom
618
} // namespace mozilla
619
620
#endif // mozilla_dom_HTMLSelectElement_h