summaryrefslogtreecommitdiffstats
path: root/layout/forms/nsComboboxControlFrame.h
blob: 5f190814ad0cdbaeba741f6c79a43ac2a9297b7f (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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsIComboboxControlFrame.h"
#include "nsIAnonymousContentCreator.h"
#include "nsISelectControlFrame.h"
#include "nsIRollupListener.h"
#include "nsIStatefulFrame.h"
#include "nsThreadUtils.h"

class nsStyleContext;
class nsIListControlFrame;
class nsComboboxDisplayFrame;
class nsIDOMEventListener;
class nsIScrollableFrame;

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

class nsComboboxControlFrame final : public nsBlockFrame,
                                     public nsIFormControlFrame,
                                     public nsIComboboxControlFrame,
                                     public nsIAnonymousContentCreator,
                                     public nsISelectControlFrame,
                                     public nsIRollupListener,
                                     public nsIStatefulFrame
{
  typedef mozilla::gfx::DrawTarget DrawTarget;

public:
  NS_DECL_QUERYFRAME_TARGET(nsComboboxControlFrame)
  friend nsContainerFrame* NS_NewComboboxControlFrame(nsIPresShell* aPresShell,
                                                      nsStyleContext* aContext,
                                                      nsFrameState aFlags);
  friend class nsComboboxDisplayFrame;

  explicit nsComboboxControlFrame(nsStyleContext* aContext);
  ~nsComboboxControlFrame();

  NS_DECL_QUERYFRAME
  NS_DECL_FRAMEARENA_HELPERS

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

  nsIContent* GetDisplayNode() { return mDisplayContent; }
  nsIFrame* CreateFrameForDisplayNode();

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

  virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;

  virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;

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

  virtual nsresult HandleEvent(nsPresContext* aPresContext,
                               mozilla::WidgetGUIEvent* aEvent,
                               nsEventStatus* aEventStatus) override;

  virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                const nsRect&           aDirtyRect,
                                const nsDisplayListSet& aLists) override;

  void PaintFocus(DrawTarget& aDrawTarget, nsPoint aPt);

  // XXXbz this is only needed to prevent the quirk percent height stuff from
  // leaking out of the combobox.  We may be able to get rid of this as more
  // things move to IsFrameOfType.
  virtual nsIAtom* GetType() const override;

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

  virtual nsIScrollableFrame* GetScrollTargetFrame() override {
    return do_QueryFrame(mDropdownFrame);
  }

#ifdef DEBUG_FRAME_DUMP
  virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
  virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
  virtual void SetInitialChildList(ChildListID     aListID,
                                   nsFrameList&    aChildList) override;
  virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
  virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;

  virtual nsContainerFrame* GetContentInsertionFrame() override;

  // nsIFormControlFrame
  virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
  /**
   * 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|.
   */
  virtual void SetFocus(bool aOn, bool aRepaint) override;

  //nsIComboboxControlFrame
  virtual bool IsDroppedDown() override { return mDroppedDown; }
  /**
   * @note This method might destroy |this|.
   */
  virtual void ShowDropDown(bool aDoDropDown) override;
  virtual nsIFrame* GetDropDown() override;
  virtual void SetDropDown(nsIFrame* aDropDownFrame) override;
  /**
   * @note This method might destroy |this|.
   */
  virtual void RollupFromList() override;

  /**
   * 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);
  virtual int32_t GetIndexOfDisplayArea() override;
  /**
   * @note This method might destroy |this|.
   */
  NS_IMETHOD RedisplaySelectedText() override;
  virtual int32_t UpdateRecentIndex(int32_t aIndex) override;
  virtual void OnContentReset() override;


  bool IsOpenInParentProcess() override
  {
    return mIsOpenInParentProcess;
  }

  void SetOpenInParentProcess(bool aVal) override
  {
    mIsOpenInParentProcess = aVal;
  }

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

  //nsIRollupListener
  /**
   * Hide the dropdown menu and stop capturing mouse events.
   * @note This method might destroy |this|.
   */
  virtual bool Rollup(uint32_t aCount, bool aFlush,
                      const nsIntPoint* pos, nsIContent** aLastRolledUp) override;
  virtual void NotifyGeometryChange() override;

  /**
   * A combobox should roll up if a mousewheel event happens outside of
   * the popup area.
   */
  virtual bool ShouldRollupOnMouseWheelEvent() override
    { return true; }

  virtual bool ShouldConsumeOnMouseWheelEvent() override
    { return false; }

  /**
   * A combobox should not roll up if activated by a mouse activate message
   * (eg. X-mouse).
   */
  virtual bool ShouldRollupOnMouseActivate() override
    { return false; }

  virtual uint32_t GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain) override
    { return 0; }

  virtual nsIWidget* GetRollupWidget() override;

  //nsIStatefulFrame
  NS_IMETHOD SaveState(nsPresState** aState) override;
  NS_IMETHOD RestoreState(nsPresState* aState) override;
  NS_IMETHOD GenerateStateKey(nsIContent* aContent,
                              nsIDocument* aDocument,
                              nsACString& aKey) override;

  static bool ToolkitHasNativePopup();

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

  // Utilities
  void ReflowDropdown(nsPresContext*          aPresContext, 
                      const ReflowInput& aReflowInput);

  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();

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

  class RedisplayTextEvent : public mozilla::Runnable {
  public:
    NS_DECL_NSIRUNNABLE
    explicit RedisplayTextEvent(nsComboboxControlFrame *c) : mControlFrame(c) {}
    void Revoke() { mControlFrame = nullptr; }
  private:
    nsComboboxControlFrame *mControlFrame;
  };
  
  /**
   * Show or hide the dropdown list.
   * @note This method might destroy |this|.
   */
  void ShowPopup(bool aShowPopup);

  /**
   * Show or hide the dropdown list.
   * @param aShowList true to show, false to hide the dropdown.
   * @note This method might destroy |this|.
   * @return false if this frame is destroyed, true if still alive.
   */
  bool ShowList(bool aShowList);
  void CheckFireOnChange();
  void FireValueChangeEvent();
  nsresult RedisplayText(int32_t aIndex);
  void HandleRedisplayTextEvent();
  void ActuallyDisplayText(bool aNotify);

private:
  // 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();

protected:
  nsFrameList              mPopupFrames;             // additional named child list
  nsCOMPtr<nsIContent>     mDisplayContent;          // Anonymous content used to display the current selection
  nsCOMPtr<nsIContent>     mButtonContent;           // Anonymous content for the button
  nsContainerFrame*        mDisplayFrame;            // frame to display selection
  nsIFrame*                mButtonFrame;             // button frame
  nsIFrame*                mDropdownFrame;           // dropdown list frame
  nsIListControlFrame *    mListControlFrame;        // ListControl Interface for the dropdown 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;
  
  nsRevocableEventPtr<RedisplayTextEvent> mRedisplayTextEvent;

  int32_t               mRecentSelectedIndex;
  int32_t               mDisplayedIndex;
  nsString              mDisplayedOptionText;

  // make someone to listen to the button. If its programmatically pressed by someone like Accessibility
  // then open or close the combo box.
  nsCOMPtr<nsIDOMEventListener> mButtonListener;

  // The last y-positions used for estimating available space before and
  // after for the dropdown list in GetAvailableDropdownSpace.  These are
  // reset to nscoord_MIN in AbsolutelyPositionDropDown when placing the
  // dropdown at its actual position.  The GetAvailableDropdownSpace call
  // from nsListControlFrame::ReflowAsDropdown use the last position.
  nscoord               mLastDropDownBeforeScreenBCoord;
  nscoord               mLastDropDownAfterScreenBCoord;
  // Current state of the dropdown list, true is dropped down.
  bool                  mDroppedDown;
  // See comment in HandleRedisplayTextEvent().
  bool                  mInRedisplayText;
  // Acting on ShowDropDown(true) is delayed until we're focused.
  bool                  mDelayedShowDropDown;

  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