summaryrefslogtreecommitdiffstats
path: root/layout/forms/nsComboboxControlFrame.h
blob: a34652bebef589ab8e974a1fe798fc2b3a93c037 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 nsComboboxControlFrame_h___
#define nsComboboxControlFrame_h___

#ifdef DEBUG_evaughan
// #define DEBUG_rods
#endif

#ifdef DEBUG_rods
// #define DO_REFLOW_DEBUG
// #define DO_REFLOW_COUNTER
// #define DO_UNCONSTRAINED_CHECK
// #define DO_PIXELS
// #define DO_NEW_REFLOW
#endif

// Mark used to indicate when onchange has been fired for current combobox item
#define NS_SKIP_NOTIFY_INDEX -2

#include "mozilla/Attributes.h"
#include "nsBlockFrame.h"
#include "nsIFormControlFrame.h"
#include "nsIAnonymousContentCreator.h"
#include "nsISelectControlFrame.h"
#include "nsIRollupListener.h"
#include "nsThreadUtils.h"

class nsComboboxDisplayFrame;
class nsTextNode;

namespace mozilla {
class PresShell;
class HTMLSelectEventListener;
namespace dom {
class HTMLSelectElement;
}

namespace gfx {
class DrawTarget;
}  // namespace gfx
}  // namespace mozilla

class nsComboboxControlFrame final : public nsBlockFrame,
                                     public nsIFormControlFrame,
                                     public nsIAnonymousContentCreator,
                                     public nsISelectControlFrame {
  using DrawTarget = mozilla::gfx::DrawTarget;
  using Element = mozilla::dom::Element;

 public:
  friend nsComboboxControlFrame* NS_NewComboboxControlFrame(
      mozilla::PresShell* aPresShell, ComputedStyle* aStyle,
      nsFrameState aFlags);
  friend class nsComboboxDisplayFrame;

  explicit nsComboboxControlFrame(ComputedStyle* aStyle,
                                  nsPresContext* aPresContext);
  ~nsComboboxControlFrame();

  NS_DECL_QUERYFRAME
  NS_DECL_FRAMEARENA_HELPERS(nsComboboxControlFrame)

  // nsIAnonymousContentCreator
  nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) final;
  void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
                                uint32_t aFilter) final;

  nsIContent* GetDisplayNode() const;
  nsIFrame* CreateFrameForDisplayNode();

#ifdef ACCESSIBILITY
  mozilla::a11y::AccType AccessibleType() final;
#endif

  nscoord GetMinISize(gfxContext* aRenderingContext) final;

  nscoord GetPrefISize(gfxContext* aRenderingContext) final;

  void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize,
              const ReflowInput& aReflowInput, nsReflowStatus& aStatus) final;

  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  nsresult HandleEvent(nsPresContext* aPresContext,
                       mozilla::WidgetGUIEvent* aEvent,
                       nsEventStatus* aEventStatus) final;

  void BuildDisplayList(nsDisplayListBuilder* aBuilder,
                        const nsDisplayListSet& aLists) final;

  void PaintFocus(DrawTarget& aDrawTarget, nsPoint aPt);

  bool IsFrameOfType(uint32_t aFlags) const final {
    return nsBlockFrame::IsFrameOfType(
        aFlags & ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
  }

  void Init(nsIContent* aContent, nsContainerFrame* aParent,
            nsIFrame* aPrevInFlow) final;

#ifdef DEBUG_FRAME_DUMP
  nsresult GetFrameName(nsAString& aResult) const final;
#endif
  void Destroy(DestroyContext&) final;

  void SetInitialChildList(ChildListID aListID, nsFrameList&& aChildList) final;
  const nsFrameList& GetChildList(ChildListID aListID) const final;
  void GetChildLists(nsTArray<ChildList>* aLists) const final;

  nsContainerFrame* GetContentInsertionFrame() final;

  // Return the dropdown and display frame.
  void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) final;

  // nsIFormControlFrame
  nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) final {
    return NS_OK;
  }

  /**
   * Inform the control that it got (or lost) focus.
   * If it lost focus, the dropdown menu will be rolled up if needed,
   * and FireOnChange() will be called.
   * @param aOn true if got focus, false if lost focus.
   * @param aRepaint if true then force repaint (NOTE: we always force repaint
   *        currently)
   * @note This method might destroy |this|.
   */
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  void SetFocus(bool aOn, bool aRepaint) final;

  /**
   * Return the available space before and after this frame for
   * placing the drop-down list, and the current 2D translation.
   * Note that either or both can be less than or equal to zero,
   * if both are then the drop-down should be closed.
   */
  void GetAvailableDropdownSpace(mozilla::WritingMode aWM, nscoord* aBefore,
                                 nscoord* aAfter,
                                 mozilla::LogicalPoint* aTranslation);
  int32_t GetIndexOfDisplayArea();
  /**
   * @note This method might destroy |this|.
   */
  nsresult RedisplaySelectedText();
  int32_t UpdateRecentIndex(int32_t aIndex);

  bool IsDroppedDown() const;

  // nsISelectControlFrame
  NS_IMETHOD AddOption(int32_t index) final;
  NS_IMETHOD RemoveOption(int32_t index) final;
  NS_IMETHOD DoneAddingChildren(bool aIsDone) final;
  NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) final;
  NS_IMETHOD_(void)
  OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) final;

  int32_t CharCountOfLargestOptionForInflation() const;

 protected:
  friend class RedisplayTextEvent;
  friend class nsAsyncResize;
  friend class nsResizeDropdownAtFinalPosition;

  // Return true if we should render a dropdown button.
  bool HasDropDownButton() const;
  nscoord DropDownButtonISize();

  enum DropDownPositionState {
    // can't show the dropdown at its current position
    eDropDownPositionSuppressed,
    // a resize reflow is pending, don't show it yet
    eDropDownPositionPendingResize,
    // the dropdown has its final size and position and can be displayed here
    eDropDownPositionFinal
  };
  DropDownPositionState AbsolutelyPositionDropDown();

  nscoord GetLongestOptionISize(gfxContext*) const;

  // Helper for GetMinISize/GetPrefISize
  nscoord GetIntrinsicISize(gfxContext* aRenderingContext,
                            mozilla::IntrinsicISizeType aType);

  class RedisplayTextEvent : public mozilla::Runnable {
   public:
    NS_DECL_NSIRUNNABLE
    explicit RedisplayTextEvent(nsComboboxControlFrame* c)
        : mozilla::Runnable("nsComboboxControlFrame::RedisplayTextEvent"),
          mControlFrame(c) {}
    void Revoke() { mControlFrame = nullptr; }

   private:
    nsComboboxControlFrame* mControlFrame;
  };

  void CheckFireOnChange();
  void FireValueChangeEvent();
  nsresult RedisplayText();
  void HandleRedisplayTextEvent();
  void ActuallyDisplayText(bool aNotify);

  // If our total transform to the root frame of the root document is only a 2d
  // translation then return that translation, otherwise returns (0,0).
  nsPoint GetCSSTransformTranslation();

  mozilla::dom::HTMLSelectElement& Select() const;
  void GetOptionText(uint32_t aIndex, nsAString& aText) const;

  RefPtr<nsTextNode> mDisplayContent;  // Anonymous content used to display the
                                       // current selection
  RefPtr<Element> mButtonContent;      // Anonymous content for the button
  nsContainerFrame* mDisplayFrame;     // frame to display selection
  nsIFrame* mButtonFrame;              // button frame

  // The inline size of our display area.  Used by that frame's reflow
  // to size to the full inline size except the drop-marker.
  nscoord mDisplayISize;
  // The maximum inline size of our display area, which is the
  // nsComoboxControlFrame's border-box.
  //
  // Going over this would be observable via DOM APIs like client / scrollWidth.
  nscoord mMaxDisplayISize;

  nsRevocableEventPtr<RedisplayTextEvent> mRedisplayTextEvent;

  int32_t mRecentSelectedIndex;
  int32_t mDisplayedIndex;
  nsString mDisplayedOptionTextOrPreview;

  RefPtr<mozilla::HTMLSelectEventListener> mEventListener;

  // See comment in HandleRedisplayTextEvent().
  bool mInRedisplayText;
  bool mIsOpenInParentProcess;

  // static class data member for Bug 32920
  // only one control can be focused at a time
  static nsComboboxControlFrame* sFocused;

#ifdef DO_REFLOW_COUNTER
  int32_t mReflowId;
#endif
};

#endif