Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/forms/nsListControlFrame.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 nsListControlFrame_h___
7
#define nsListControlFrame_h___
8
9
#ifdef DEBUG_evaughan
10
//#define DEBUG_rods
11
#endif
12
13
#ifdef DEBUG_rods
14
//#define DO_REFLOW_DEBUG
15
//#define DO_REFLOW_COUNTER
16
//#define DO_UNCONSTRAINED_CHECK
17
//#define DO_PIXELS
18
#endif
19
20
#include "mozilla/Attributes.h"
21
#include "nsGfxScrollFrame.h"
22
#include "nsIFormControlFrame.h"
23
#include "nsIListControlFrame.h"
24
#include "nsISelectControlFrame.h"
25
#include "nsSelectsAreaFrame.h"
26
27
// X.h defines KeyPress
28
#ifdef KeyPress
29
#undef KeyPress
30
#endif
31
32
class nsIComboboxControlFrame;
33
class nsPresContext;
34
class nsListEventListener;
35
36
namespace mozilla {
37
namespace dom {
38
class Event;
39
class HTMLOptionElement;
40
class HTMLOptionsCollection;
41
} // namespace dom
42
} // namespace mozilla
43
44
/**
45
 * Frame-based listbox.
46
 */
47
48
class nsListControlFrame final : public nsHTMLScrollFrame,
49
                                 public nsIFormControlFrame,
50
                                 public nsIListControlFrame,
51
                                 public nsISelectControlFrame
52
{
53
public:
54
  typedef mozilla::dom::HTMLOptionElement HTMLOptionElement;
55
56
  friend nsContainerFrame* NS_NewListControlFrame(nsIPresShell* aPresShell,
57
                                                  ComputedStyle* aStyle);
58
59
  NS_DECL_QUERYFRAME
60
  NS_DECL_FRAMEARENA_HELPERS(nsListControlFrame)
61
62
    // nsIFrame
63
  virtual nsresult HandleEvent(nsPresContext* aPresContext,
64
                               mozilla::WidgetGUIEvent* aEvent,
65
                               nsEventStatus* aEventStatus) override;
66
67
  virtual void SetInitialChildList(ChildListID     aListID,
68
                                   nsFrameList&    aChildList) override;
69
70
  virtual nscoord GetPrefISize(gfxContext *aRenderingContext) override;
71
  virtual nscoord GetMinISize(gfxContext *aRenderingContext) override;
72
73
  virtual void Reflow(nsPresContext*           aCX,
74
                      ReflowOutput&     aDesiredSize,
75
                      const ReflowInput& aReflowInput,
76
                      nsReflowStatus&          aStatus) override;
77
78
  virtual void Init(nsIContent*       aContent,
79
                    nsContainerFrame* aParent,
80
                    nsIFrame*         aPrevInFlow) override;
81
82
  virtual void DidReflow(nsPresContext*            aPresContext,
83
                         const ReflowInput*  aReflowInput) override;
84
  virtual void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) override;
85
86
  virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
87
                                const nsDisplayListSet& aLists) override;
88
89
  virtual nsContainerFrame* GetContentInsertionFrame() override;
90
91
  virtual bool IsFrameOfType(uint32_t aFlags) const override
92
0
  {
93
0
    return nsHTMLScrollFrame::IsFrameOfType(aFlags &
94
0
      ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
95
0
  }
96
97
#ifdef DEBUG_FRAME_DUMP
98
  virtual nsresult GetFrameName(nsAString& aResult) const override;
99
#endif
100
101
    // nsIFormControlFrame
102
  virtual nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) override;
103
  virtual void SetFocus(bool aOn = true, bool aRepaint = false) override;
104
105
  virtual mozilla::ScrollStyles GetScrollStyles() const override;
106
  virtual bool ShouldPropagateComputedBSizeToScrolledContent() const override;
107
108
    // for accessibility purposes
109
#ifdef ACCESSIBILITY
110
  virtual mozilla::a11y::AccType AccessibleType() override;
111
#endif
112
113
    // nsIListControlFrame
114
  virtual void SetComboboxFrame(nsIFrame* aComboboxFrame) override;
115
  virtual int32_t GetSelectedIndex() override;
116
  virtual HTMLOptionElement* GetCurrentOption() override;
117
118
  /**
119
   * Gets the text of the currently selected item.
120
   * If the there are zero items then an empty string is returned
121
   * If there is nothing selected, then the 0th item's text is returned.
122
   */
123
  virtual void GetOptionText(uint32_t aIndex, nsAString& aStr) override;
124
125
  virtual void CaptureMouseEvents(bool aGrabMouseEvents) override;
126
  virtual nscoord GetBSizeOfARow() override;
127
  virtual uint32_t GetNumberOfOptions() override;
128
  virtual void AboutToDropDown() override;
129
130
  /**
131
   * @note This method might destroy the frame, pres shell and other objects.
132
   */
133
  virtual void AboutToRollup() override;
134
135
  /**
136
   * Dispatch a DOM oninput and onchange event synchroniously.
137
   * @note This method might destroy the frame, pres shell and other objects.
138
   */
139
  virtual void FireOnInputAndOnChange() override;
140
141
  /**
142
   * Makes aIndex the selected option of a combobox list.
143
   * @note This method might destroy the frame, pres shell and other objects.
144
   */
145
  virtual void ComboboxFinish(int32_t aIndex) override;
146
  virtual void OnContentReset() override;
147
148
  // nsISelectControlFrame
149
  NS_IMETHOD AddOption(int32_t index) override;
150
  NS_IMETHOD RemoveOption(int32_t index) override;
151
  NS_IMETHOD DoneAddingChildren(bool aIsDone) override;
152
153
  /**
154
   * Gets the content (an option) by index and then set it as
155
   * being selected or not selected.
156
   */
157
  NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) override;
158
  NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) override;
159
160
  /**
161
   * Mouse event listeners.
162
   * @note These methods might destroy the frame, pres shell and other objects.
163
   */
164
  nsresult MouseDown(mozilla::dom::Event* aMouseEvent);
165
  nsresult MouseUp(mozilla::dom::Event* aMouseEvent);
166
  nsresult MouseMove(mozilla::dom::Event* aMouseEvent);
167
  nsresult DragMove(mozilla::dom::Event* aMouseEvent);
168
  nsresult KeyDown(mozilla::dom::Event* aKeyEvent);
169
  nsresult KeyPress(mozilla::dom::Event* aKeyEvent);
170
171
  /**
172
   * Returns the options collection for mContent, if any.
173
   */
174
  mozilla::dom::HTMLOptionsCollection* GetOptions() const;
175
  /**
176
   * Returns the HTMLOptionElement for a given index in mContent's collection.
177
   */
178
  HTMLOptionElement* GetOption(uint32_t aIndex) const;
179
180
  static void ComboboxFocusSet();
181
182
  // Helper
183
0
  bool IsFocused() { return this == mFocused; }
184
185
  /**
186
   * Function to paint the focus rect when our nsSelectsAreaFrame is painting.
187
   * @param aPt the offset of this frame, relative to the rendering reference
188
   * frame
189
   */
190
  void PaintFocus(mozilla::gfx::DrawTarget* aDrawTarget, nsPoint aPt);
191
192
  /**
193
   * If this frame IsFocused(), invalidates an area that includes anything
194
   * that PaintFocus will or could have painted --- basically the whole
195
   * GetOptionsContainer, plus some extra stuff if there are no options. This
196
   * must be called every time mEndSelectionIndex changes.
197
   */
198
  void InvalidateFocus();
199
200
  /**
201
   * Function to calculate the block size of a row, for use with the
202
   * "size" attribute.
203
   * Can't be const because GetNumberOfOptions() isn't const.
204
   */
205
  nscoord CalcBSizeOfARow();
206
207
  /**
208
   * Function to ask whether we're currently in what might be the
209
   * first pass of a two-pass reflow.
210
   */
211
0
  bool MightNeedSecondPass() const {
212
0
    return mMightNeedSecondPass;
213
0
  }
214
215
0
  void SetSuppressScrollbarUpdate(bool aSuppress) {
216
0
    nsHTMLScrollFrame::SetSuppressScrollbarUpdate(aSuppress);
217
0
  }
218
219
  /**
220
   * Return whether the list is in dropdown mode.
221
   */
222
  bool IsInDropDownMode() const;
223
224
  /**
225
   * Return the number of displayed rows in the list.
226
   */
227
0
  uint32_t GetNumDisplayRows() const { return mNumDisplayRows; }
228
229
  /**
230
   * Return true if the drop-down list can display more rows.
231
   * (always false if not in drop-down mode)
232
   */
233
0
  bool GetDropdownCanGrow() const { return mDropdownCanGrow; }
234
235
  /**
236
   * Frees statics owned by this class.
237
   */
238
  static void Shutdown();
239
240
#ifdef ACCESSIBILITY
241
  /**
242
   * Post a custom DOM event for the change, so that accessibility can
243
   * fire a native focus event for accessibility
244
   * (Some 3rd party products need to track our focus)
245
   */
246
  void FireMenuItemActiveEvent(); // Inform assistive tech what got focused
247
#endif
248
249
protected:
250
  /**
251
   * Return the first non-disabled option starting at aFromIndex (inclusive).
252
   * @param aFoundIndex if non-null, set to the index of the returned option
253
   */
254
  HTMLOptionElement* GetNonDisabledOptionFrom(int32_t aFromIndex,
255
                                              int32_t* aFoundIndex = nullptr);
256
257
  /**
258
   * Updates the selected text in a combobox and then calls FireOnChange().
259
   * @note This method might destroy the frame, pres shell and other objects.
260
   * Returns false if calling it destroyed |this|.
261
   */
262
  bool UpdateSelection();
263
264
  /**
265
   * Returns whether mContent supports multiple selection.
266
   */
267
0
  bool GetMultiple() const {
268
0
    return mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
269
0
  }
270
271
272
  /**
273
   * Toggles (show/hide) the combobox dropdown menu.
274
   * @note This method might destroy the frame, pres shell and other objects.
275
   */
276
  void DropDownToggleKey(mozilla::dom::Event* aKeyEvent);
277
278
  nsresult   IsOptionDisabled(int32_t anIndex, bool &aIsDisabled);
279
  /**
280
   * @note This method might destroy the frame, pres shell and other objects.
281
   */
282
  void ScrollToFrame(HTMLOptionElement& aOptElement);
283
  /**
284
   * @note This method might destroy the frame, pres shell and other objects.
285
   */
286
  void ScrollToIndex(int32_t anIndex);
287
288
  /**
289
   * When the user clicks on the comboboxframe to show the dropdown
290
   * listbox, they then have to move the mouse into the list. We don't
291
   * want to process those mouse events as selection events (i.e., to
292
   * scroll list items into view). So we ignore the events until
293
   * the mouse moves below our border-inner-edge, when
294
   * mItemSelectionStarted is set.
295
   *
296
   * @param aPoint relative to this frame
297
   */
298
  bool       IgnoreMouseEventForSelection(mozilla::dom::Event* aEvent);
299
300
  /**
301
   * If the dropdown is showing and the mouse has moved below our
302
   * border-inner-edge, then set mItemSelectionStarted.
303
   */
304
  void       UpdateInListState(mozilla::dom::Event* aEvent);
305
  void       AdjustIndexForDisabledOpt(int32_t aStartIndex, int32_t &anNewIndex,
306
                                       int32_t aNumOptions, int32_t aDoAdjustInc, int32_t aDoAdjustIncNext);
307
308
  /**
309
   * Resets the select back to it's original default values;
310
   * those values as determined by the original HTML
311
   */
312
  virtual void ResetList(bool aAllowScrolling);
313
314
  explicit nsListControlFrame(ComputedStyle* aStyle);
315
  virtual ~nsListControlFrame();
316
317
  /**
318
   * Sets the mSelectedIndex and mOldSelectedIndex from figuring out what
319
   * item was selected using content
320
   * @param aPoint the event point, in listcontrolframe coordinates
321
   * @return NS_OK if it successfully found the selection
322
   */
323
  nsresult GetIndexFromDOMEvent(mozilla::dom::Event* aMouseEvent,
324
                                int32_t& aCurIndex);
325
326
  bool     CheckIfAllFramesHere();
327
  bool     IsLeftButton(mozilla::dom::Event* aMouseEvent);
328
329
  // guess at a row block size based on our own style.
330
  nscoord  CalcFallbackRowBSize(float aFontSizeInflation);
331
332
  // CalcIntrinsicBSize computes our intrinsic block size (taking the
333
  // "size" attribute into account).  This should only be called in
334
  // non-dropdown mode.
335
  nscoord CalcIntrinsicBSize(nscoord aBSizeOfARow, int32_t aNumberOfOptions);
336
337
  // Dropped down stuff
338
  void     SetComboboxItem(int32_t aIndex);
339
340
  /**
341
   * Method to reflow ourselves as a dropdown list.  This differs from
342
   * reflow as a listbox because the criteria for needing a second
343
   * pass are different.  This will be called from Reflow() as needed.
344
   */
345
  void ReflowAsDropdown(nsPresContext*           aPresContext,
346
                        ReflowOutput&     aDesiredSize,
347
                        const ReflowInput& aReflowInput,
348
                        nsReflowStatus&          aStatus);
349
350
  // Selection
351
  bool     SetOptionsSelectedFromFrame(int32_t aStartIndex,
352
                                       int32_t aEndIndex,
353
                                       bool aValue,
354
                                       bool aClearAll);
355
  bool     ToggleOptionSelectedFromFrame(int32_t aIndex);
356
  /**
357
   * @note This method might destroy the frame, pres shell and other objects.
358
   */
359
  bool     SingleSelection(int32_t aClickedIndex, bool aDoToggle);
360
  bool     ExtendedSelection(int32_t aStartIndex, int32_t aEndIndex,
361
                             bool aClearAll);
362
  /**
363
   * @note This method might destroy the frame, pres shell and other objects.
364
   */
365
  bool     PerformSelection(int32_t aClickedIndex, bool aIsShift,
366
                            bool aIsControl);
367
  /**
368
   * @note This method might destroy the frame, pres shell and other objects.
369
   */
370
  bool     HandleListSelection(mozilla::dom::Event * aDOMEvent,
371
                               int32_t selectedIndex);
372
  void     InitSelectionRange(int32_t aClickedIndex);
373
  void     PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode,
374
                              bool aIsShift, bool aIsControlOrMeta);
375
376
public:
377
0
  nsSelectsAreaFrame* GetOptionsContainer() const {
378
0
    return static_cast<nsSelectsAreaFrame*>(GetScrolledFrame());
379
0
  }
380
381
protected:
382
0
  nscoord BSizeOfARow() {
383
0
    return GetOptionsContainer()->BSizeOfARow();
384
0
  }
385
386
  /**
387
   * @return how many displayable options/optgroups this frame has.
388
   */
389
  uint32_t GetNumberOfRows();
390
391
0
  nsView* GetViewInternal() const override { return mView; }
392
0
  void SetViewInternal(nsView* aView) override { mView = aView; }
393
394
  // Data Members
395
  int32_t      mStartSelectionIndex;
396
  int32_t      mEndSelectionIndex;
397
398
  nsIComboboxControlFrame* mComboboxFrame;
399
400
  // The view is only created (& non-null) if IsInDropDownMode() is true.
401
  nsView* mView;
402
403
  uint32_t mNumDisplayRows;
404
  bool mChangesSinceDragStart:1;
405
  bool mButtonDown:1;
406
407
  // Has the user selected a visible item since we showed the dropdown?
408
  bool mItemSelectionStarted:1;
409
410
  bool mIsAllContentHere:1;
411
  bool mIsAllFramesHere:1;
412
  bool mHasBeenInitialized:1;
413
  bool mNeedToReset:1;
414
  bool mPostChildrenLoadedReset:1;
415
416
  //bool value for multiple discontiguous selection
417
  bool mControlSelectMode:1;
418
419
  // True if we're in the middle of a reflow and might need a second
420
  // pass.  This only happens for auto heights.
421
  bool mMightNeedSecondPass:1;
422
423
  /**
424
   * Set to aPresContext->HasPendingInterrupt() at the start of Reflow.
425
   * Set to false at the end of DidReflow.
426
   */
427
  bool mHasPendingInterruptAtStartOfReflow:1;
428
429
  // True if the drop-down can show more rows.  Always false if this list
430
  // is not in drop-down mode.
431
  bool mDropdownCanGrow:1;
432
433
  // True if the selection can be set to nothing or disabled options.
434
  bool mForceSelection:1;
435
436
  // The last computed block size we reflowed at if we're a combobox
437
  // dropdown.
438
  // XXXbz should we be using a subclass here?  Or just not worry
439
  // about the extra member on listboxes?
440
  nscoord mLastDropdownComputedBSize;
441
442
  // At the time of our last dropdown, the backstop color to draw in case we
443
  // are translucent.
444
  nscolor mLastDropdownBackstopColor;
445
446
  RefPtr<nsListEventListener> mEventListener;
447
448
  static nsListControlFrame * mFocused;
449
  static nsString * sIncrementalString;
450
451
#ifdef DO_REFLOW_COUNTER
452
  int32_t mReflowId;
453
#endif
454
455
private:
456
  // for incremental typing navigation
457
  static nsAString& GetIncrementalString ();
458
  static DOMTimeStamp gLastKeyTime;
459
460
  class MOZ_RAII AutoIncrementalSearchResetter
461
  {
462
  public:
463
    AutoIncrementalSearchResetter() :
464
      mCancelled(false)
465
0
    {
466
0
    }
467
    ~AutoIncrementalSearchResetter()
468
0
    {
469
0
      if (!mCancelled) {
470
0
        nsListControlFrame::GetIncrementalString().Truncate();
471
0
      }
472
0
    }
473
    void Cancel()
474
0
    {
475
0
      mCancelled = true;
476
0
    }
477
  private:
478
    bool mCancelled;
479
  };
480
};
481
482
#endif /* nsListControlFrame_h___ */
483