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 
7 #ifndef nsComboboxControlFrame_h___
8 #define nsComboboxControlFrame_h___
9 
10 #ifdef DEBUG_evaughan
11 //#define DEBUG_rods
12 #endif
13 
14 #ifdef DEBUG_rods
15 //#define DO_REFLOW_DEBUG
16 //#define DO_REFLOW_COUNTER
17 //#define DO_UNCONSTRAINED_CHECK
18 //#define DO_PIXELS
19 //#define DO_NEW_REFLOW
20 #endif
21 
22 // Mark used to indicate when onchange has been fired for current combobox item
23 #define NS_SKIP_NOTIFY_INDEX -2
24 
25 #include "mozilla/Attributes.h"
26 #include "nsBlockFrame.h"
27 #include "nsIFormControlFrame.h"
28 #include "nsIComboboxControlFrame.h"
29 #include "nsIAnonymousContentCreator.h"
30 #include "nsISelectControlFrame.h"
31 #include "nsIRollupListener.h"
32 #include "nsIStatefulFrame.h"
33 #include "nsThreadUtils.h"
34 
35 class nsStyleContext;
36 class nsIListControlFrame;
37 class nsComboboxDisplayFrame;
38 class nsIDOMEventListener;
39 class nsIScrollableFrame;
40 
41 namespace mozilla {
42 namespace gfx {
43 class DrawTarget;
44 }  // namespace gfx
45 }  // namespace mozilla
46 
47 class nsComboboxControlFrame final : public nsBlockFrame,
48                                      public nsIFormControlFrame,
49                                      public nsIComboboxControlFrame,
50                                      public nsIAnonymousContentCreator,
51                                      public nsISelectControlFrame,
52                                      public nsIRollupListener,
53                                      public nsIStatefulFrame {
54   typedef mozilla::gfx::DrawTarget DrawTarget;
55 
56  public:
57   friend nsComboboxControlFrame* NS_NewComboboxControlFrame(
58       nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags);
59   friend class nsComboboxDisplayFrame;
60 
61   explicit nsComboboxControlFrame(nsStyleContext* aContext);
62   ~nsComboboxControlFrame();
63 
64   NS_DECL_QUERYFRAME
65   NS_DECL_FRAMEARENA_HELPERS(nsComboboxControlFrame)
66 
67   // nsIAnonymousContentCreator
68   virtual nsresult CreateAnonymousContent(
69       nsTArray<ContentInfo>& aElements) override;
70   virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
71                                         uint32_t aFilter) override;
72 
GetDisplayNode()73   nsIContent* GetDisplayNode() { return mDisplayContent; }
74   nsIFrame* CreateFrameForDisplayNode();
75 
76 #ifdef ACCESSIBILITY
77   virtual mozilla::a11y::AccType AccessibleType() override;
78 #endif
79 
80   virtual nscoord GetMinISize(gfxContext* aRenderingContext) override;
81 
82   virtual nscoord GetPrefISize(gfxContext* aRenderingContext) override;
83 
84   virtual void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize,
85                       const ReflowInput& aReflowInput,
86                       nsReflowStatus& aStatus) override;
87 
88   virtual nsresult HandleEvent(nsPresContext* aPresContext,
89                                mozilla::WidgetGUIEvent* aEvent,
90                                nsEventStatus* aEventStatus) override;
91 
92   virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
93                                 const nsDisplayListSet& aLists) override;
94 
95   void PaintFocus(DrawTarget& aDrawTarget, nsPoint aPt);
96 
IsFrameOfType(uint32_t aFlags)97   virtual bool IsFrameOfType(uint32_t aFlags) const override {
98     return nsBlockFrame::IsFrameOfType(
99         aFlags & ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
100   }
101 
GetScrollTargetFrame()102   virtual nsIScrollableFrame* GetScrollTargetFrame() override {
103     return do_QueryFrame(mDropdownFrame);
104   }
105 
106 #ifdef DEBUG_FRAME_DUMP
107   virtual nsresult GetFrameName(nsAString& aResult) const override;
108 #endif
109   virtual void DestroyFrom(nsIFrame* aDestructRoot,
110                            PostDestroyData& aPostDestroyData) override;
111   virtual void SetInitialChildList(ChildListID aListID,
112                                    nsFrameList& aChildList) override;
113   virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
114   virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
115 
116   virtual nsContainerFrame* GetContentInsertionFrame() override;
117 
118   // Return the dropdown and display frame.
119   void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override;
120 
121   // nsIFormControlFrame
122   virtual nsresult SetFormProperty(nsAtom* aName,
123                                    const nsAString& aValue) override;
124   /**
125    * Inform the control that it got (or lost) focus.
126    * If it lost focus, the dropdown menu will be rolled up if needed,
127    * and FireOnChange() will be called.
128    * @param aOn true if got focus, false if lost focus.
129    * @param aRepaint if true then force repaint (NOTE: we always force repaint
130    * currently)
131    * @note This method might destroy |this|.
132    */
133   virtual void SetFocus(bool aOn, bool aRepaint) override;
134 
135   // nsIComboboxControlFrame
IsDroppedDown()136   virtual bool IsDroppedDown() override { return mDroppedDown; }
137   /**
138    * @note This method might destroy |this|.
139    */
140   virtual void ShowDropDown(bool aDoDropDown) override;
141   virtual nsIFrame* GetDropDown() override;
142   virtual void SetDropDown(nsIFrame* aDropDownFrame) override;
143   /**
144    * @note This method might destroy |this|.
145    */
146   virtual void RollupFromList() override;
147 
148   /**
149    * Return the available space before and after this frame for
150    * placing the drop-down list, and the current 2D translation.
151    * Note that either or both can be less than or equal to zero,
152    * if both are then the drop-down should be closed.
153    */
154   void GetAvailableDropdownSpace(mozilla::WritingMode aWM, nscoord* aBefore,
155                                  nscoord* aAfter,
156                                  mozilla::LogicalPoint* aTranslation);
157   virtual int32_t GetIndexOfDisplayArea() override;
158   /**
159    * @note This method might destroy |this|.
160    */
161   NS_IMETHOD RedisplaySelectedText() override;
162   virtual int32_t UpdateRecentIndex(int32_t aIndex) override;
163   virtual void OnContentReset() override;
164 
IsOpenInParentProcess()165   bool IsOpenInParentProcess() override { return mIsOpenInParentProcess; }
166 
SetOpenInParentProcess(bool aVal)167   void SetOpenInParentProcess(bool aVal) override {
168     mIsOpenInParentProcess = aVal;
169   }
170 
171   // nsISelectControlFrame
172   NS_IMETHOD AddOption(int32_t index) override;
173   NS_IMETHOD RemoveOption(int32_t index) override;
174   NS_IMETHOD DoneAddingChildren(bool aIsDone) override;
175   NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) override;
176   NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) override;
177 
178   // nsIRollupListener
179   /**
180    * Hide the dropdown menu and stop capturing mouse events.
181    * @note This method might destroy |this|.
182    */
183   virtual bool Rollup(uint32_t aCount, bool aFlush, const nsIntPoint* pos,
184                       nsIContent** aLastRolledUp) override;
185   virtual void NotifyGeometryChange() override;
186 
187   /**
188    * A combobox should roll up if a mousewheel event happens outside of
189    * the popup area.
190    */
ShouldRollupOnMouseWheelEvent()191   virtual bool ShouldRollupOnMouseWheelEvent() override { return true; }
192 
ShouldConsumeOnMouseWheelEvent()193   virtual bool ShouldConsumeOnMouseWheelEvent() override { return false; }
194 
195   /**
196    * A combobox should not roll up if activated by a mouse activate message
197    * (eg. X-mouse).
198    */
ShouldRollupOnMouseActivate()199   virtual bool ShouldRollupOnMouseActivate() override { return false; }
200 
GetSubmenuWidgetChain(nsTArray<nsIWidget * > * aWidgetChain)201   virtual uint32_t GetSubmenuWidgetChain(
202       nsTArray<nsIWidget*>* aWidgetChain) override {
203     return 0;
204   }
205 
206   virtual nsIWidget* GetRollupWidget() override;
207 
208   // nsIStatefulFrame
209   NS_IMETHOD SaveState(nsPresState** aState) override;
210   NS_IMETHOD RestoreState(nsPresState* aState) override;
211   NS_IMETHOD GenerateStateKey(nsIContent* aContent, nsIDocument* aDocument,
212                               nsACString& aKey) override;
213 
214   static bool ToolkitHasNativePopup();
215 
216  protected:
217   friend class RedisplayTextEvent;
218   friend class nsAsyncResize;
219   friend class nsResizeDropdownAtFinalPosition;
220 
221   // Utilities
222   void ReflowDropdown(nsPresContext* aPresContext,
223                       const ReflowInput& aReflowInput);
224 
225   // Return true if we should render a dropdown button.
226   bool HasDropDownButton() const;
227 
228   enum DropDownPositionState {
229     // can't show the dropdown at its current position
230     eDropDownPositionSuppressed,
231     // a resize reflow is pending, don't show it yet
232     eDropDownPositionPendingResize,
233     // the dropdown has its final size and position and can be displayed here
234     eDropDownPositionFinal
235   };
236   DropDownPositionState AbsolutelyPositionDropDown();
237 
238   // Helper for GetMinISize/GetPrefISize
239   nscoord GetIntrinsicISize(gfxContext* aRenderingContext,
240                             nsLayoutUtils::IntrinsicISizeType aType);
241 
242   class RedisplayTextEvent : public mozilla::Runnable {
243    public:
244     NS_DECL_NSIRUNNABLE
RedisplayTextEvent(nsComboboxControlFrame * c)245     explicit RedisplayTextEvent(nsComboboxControlFrame* c)
246         : mozilla::Runnable("nsComboboxControlFrame::RedisplayTextEvent"),
247           mControlFrame(c) {}
Revoke()248     void Revoke() { mControlFrame = nullptr; }
249 
250    private:
251     nsComboboxControlFrame* mControlFrame;
252   };
253 
254   /**
255    * Show or hide the dropdown list.
256    * @note This method might destroy |this|.
257    */
258   void ShowPopup(bool aShowPopup);
259 
260   /**
261    * Show or hide the dropdown list.
262    * @param aShowList true to show, false to hide the dropdown.
263    * @note This method might destroy |this|.
264    * @return false if this frame is destroyed, true if still alive.
265    */
266   bool ShowList(bool aShowList);
267   void CheckFireOnChange();
268   void FireValueChangeEvent();
269   nsresult RedisplayText();
270   void HandleRedisplayTextEvent();
271   void ActuallyDisplayText(bool aNotify);
272 
273  private:
274   // If our total transform to the root frame of the root document is only a 2d
275   // translation then return that translation, otherwise returns (0,0).
276   nsPoint GetCSSTransformTranslation();
277 
278  protected:
279   nsFrameList mPopupFrames;              // additional named child list
280   nsCOMPtr<nsIContent> mDisplayContent;  // Anonymous content used to display
281                                          // the current selection
282   RefPtr<Element> mButtonContent;        // Anonymous content for the button
283   nsContainerFrame* mDisplayFrame;       // frame to display selection
284   nsIFrame* mButtonFrame;                // button frame
285   nsIFrame* mDropdownFrame;              // dropdown list frame
286   nsIListControlFrame*
287       mListControlFrame;  // ListControl Interface for the dropdown frame
288 
289   // The inline size of our display area.  Used by that frame's reflow
290   // to size to the full inline size except the drop-marker.
291   nscoord mDisplayISize;
292 
293   nsRevocableEventPtr<RedisplayTextEvent> mRedisplayTextEvent;
294 
295   int32_t mRecentSelectedIndex;
296   int32_t mDisplayedIndex;
297   nsString mDisplayedOptionTextOrPreview;
298 
299   // make someone to listen to the button. If its programmatically pressed by
300   // someone like Accessibility then open or close the combo box.
301   nsCOMPtr<nsIDOMEventListener> mButtonListener;
302 
303   // The last y-positions used for estimating available space before and
304   // after for the dropdown list in GetAvailableDropdownSpace.  These are
305   // reset to nscoord_MIN in AbsolutelyPositionDropDown when placing the
306   // dropdown at its actual position.  The GetAvailableDropdownSpace call
307   // from nsListControlFrame::ReflowAsDropdown use the last position.
308   nscoord mLastDropDownBeforeScreenBCoord;
309   nscoord mLastDropDownAfterScreenBCoord;
310   // Current state of the dropdown list, true is dropped down.
311   bool mDroppedDown;
312   // See comment in HandleRedisplayTextEvent().
313   bool mInRedisplayText;
314   // Acting on ShowDropDown(true) is delayed until we're focused.
315   bool mDelayedShowDropDown;
316 
317   bool mIsOpenInParentProcess;
318 
319   // static class data member for Bug 32920
320   // only one control can be focused at a time
321   static nsComboboxControlFrame* sFocused;
322 
323 #ifdef DO_REFLOW_COUNTER
324   int32_t mReflowId;
325 #endif
326 };
327 
328 #endif
329