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 "nsIAnonymousContentCreator.h"
29 #include "nsISelectControlFrame.h"
30 #include "nsIRollupListener.h"
31 #include "nsThreadUtils.h"
32 
33 class nsComboboxDisplayFrame;
34 class nsTextNode;
35 
36 namespace mozilla {
37 class PresShell;
38 class HTMLSelectEventListener;
39 namespace dom {
40 class HTMLSelectElement;
41 }
42 
43 namespace gfx {
44 class DrawTarget;
45 }  // namespace gfx
46 }  // namespace mozilla
47 
48 class nsComboboxControlFrame final : public nsBlockFrame,
49                                      public nsIFormControlFrame,
50                                      public nsIAnonymousContentCreator,
51                                      public nsISelectControlFrame {
52   using DrawTarget = mozilla::gfx::DrawTarget;
53   using Element = mozilla::dom::Element;
54 
55  public:
56   friend nsComboboxControlFrame* NS_NewComboboxControlFrame(
57       mozilla::PresShell* aPresShell, ComputedStyle* aStyle,
58       nsFrameState aFlags);
59   friend class nsComboboxDisplayFrame;
60 
61   explicit nsComboboxControlFrame(ComputedStyle* aStyle,
62                                   nsPresContext* aPresContext);
63   ~nsComboboxControlFrame();
64 
65   NS_DECL_QUERYFRAME
66   NS_DECL_FRAMEARENA_HELPERS(nsComboboxControlFrame)
67 
68   // nsIAnonymousContentCreator
69   nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) final;
70   void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
71                                 uint32_t aFilter) final;
72 
73   nsIContent* GetDisplayNode() const;
74   nsIFrame* CreateFrameForDisplayNode();
75 
76 #ifdef ACCESSIBILITY
77   mozilla::a11y::AccType AccessibleType() final;
78 #endif
79 
80   nscoord GetMinISize(gfxContext* aRenderingContext) final;
81 
82   nscoord GetPrefISize(gfxContext* aRenderingContext) final;
83 
84   void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize,
85               const ReflowInput& aReflowInput, nsReflowStatus& aStatus) final;
86 
87   MOZ_CAN_RUN_SCRIPT_BOUNDARY
88   nsresult HandleEvent(nsPresContext* aPresContext,
89                        mozilla::WidgetGUIEvent* aEvent,
90                        nsEventStatus* aEventStatus) final;
91 
92   void BuildDisplayList(nsDisplayListBuilder* aBuilder,
93                         const nsDisplayListSet& aLists) final;
94 
95   void PaintFocus(DrawTarget& aDrawTarget, nsPoint aPt);
96 
IsFrameOfType(uint32_t aFlags)97   bool IsFrameOfType(uint32_t aFlags) const final {
98     return nsBlockFrame::IsFrameOfType(
99         aFlags & ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
100   }
101 
102   void Init(nsIContent* aContent, nsContainerFrame* aParent,
103             nsIFrame* aPrevInFlow) final;
104 
105 #ifdef DEBUG_FRAME_DUMP
106   nsresult GetFrameName(nsAString& aResult) const final;
107 #endif
108   void DestroyFrom(nsIFrame* aDestructRoot,
109                    PostDestroyData& aPostDestroyData) final;
110   void SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) final;
111   const nsFrameList& GetChildList(ChildListID aListID) const final;
112   void GetChildLists(nsTArray<ChildList>* aLists) const final;
113 
114   nsContainerFrame* GetContentInsertionFrame() final;
115 
116   // Return the dropdown and display frame.
117   void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) final;
118 
119   // nsIFormControlFrame
SetFormProperty(nsAtom * aName,const nsAString & aValue)120   nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) final {
121     return NS_OK;
122   }
123 
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   MOZ_CAN_RUN_SCRIPT_BOUNDARY
134   void SetFocus(bool aOn, bool aRepaint) final;
135 
136   /**
137    * Return the available space before and after this frame for
138    * placing the drop-down list, and the current 2D translation.
139    * Note that either or both can be less than or equal to zero,
140    * if both are then the drop-down should be closed.
141    */
142   void GetAvailableDropdownSpace(mozilla::WritingMode aWM, nscoord* aBefore,
143                                  nscoord* aAfter,
144                                  mozilla::LogicalPoint* aTranslation);
145   int32_t GetIndexOfDisplayArea();
146   /**
147    * @note This method might destroy |this|.
148    */
149   nsresult RedisplaySelectedText();
150   int32_t UpdateRecentIndex(int32_t aIndex);
151 
152   bool IsDroppedDown() const;
153 
154   // nsISelectControlFrame
155   NS_IMETHOD AddOption(int32_t index) final;
156   NS_IMETHOD RemoveOption(int32_t index) final;
157   NS_IMETHOD DoneAddingChildren(bool aIsDone) final;
158   NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) final;
159   NS_IMETHOD_(void)
160   OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) final;
161 
162   int32_t CharCountOfLargestOptionForInflation() const;
163 
164  protected:
165   friend class RedisplayTextEvent;
166   friend class nsAsyncResize;
167   friend class nsResizeDropdownAtFinalPosition;
168 
169   // Return true if we should render a dropdown button.
170   bool HasDropDownButton() const;
171   nscoord DropDownButtonISize();
172 
173   enum DropDownPositionState {
174     // can't show the dropdown at its current position
175     eDropDownPositionSuppressed,
176     // a resize reflow is pending, don't show it yet
177     eDropDownPositionPendingResize,
178     // the dropdown has its final size and position and can be displayed here
179     eDropDownPositionFinal
180   };
181   DropDownPositionState AbsolutelyPositionDropDown();
182 
183   // Helper for GetMinISize/GetPrefISize
184   nscoord GetIntrinsicISize(gfxContext* aRenderingContext,
185                             mozilla::IntrinsicISizeType aType);
186 
187   class RedisplayTextEvent : public mozilla::Runnable {
188    public:
189     NS_DECL_NSIRUNNABLE
RedisplayTextEvent(nsComboboxControlFrame * c)190     explicit RedisplayTextEvent(nsComboboxControlFrame* c)
191         : mozilla::Runnable("nsComboboxControlFrame::RedisplayTextEvent"),
192           mControlFrame(c) {}
Revoke()193     void Revoke() { mControlFrame = nullptr; }
194 
195    private:
196     nsComboboxControlFrame* mControlFrame;
197   };
198 
199   void CheckFireOnChange();
200   void FireValueChangeEvent();
201   nsresult RedisplayText();
202   void HandleRedisplayTextEvent();
203   void ActuallyDisplayText(bool aNotify);
204 
205   // If our total transform to the root frame of the root document is only a 2d
206   // translation then return that translation, otherwise returns (0,0).
207   nsPoint GetCSSTransformTranslation();
208 
209   mozilla::dom::HTMLSelectElement& Select() const;
210   void GetOptionText(uint32_t aIndex, nsAString& aText) const;
211 
212   nsFrameList mPopupFrames;            // additional named child list
213   RefPtr<nsTextNode> mDisplayContent;  // Anonymous content used to display the
214                                        // current selection
215   RefPtr<Element> mButtonContent;      // Anonymous content for the button
216   nsContainerFrame* mDisplayFrame;     // frame to display selection
217   nsIFrame* mButtonFrame;              // button frame
218 
219   // The inline size of our display area.  Used by that frame's reflow
220   // to size to the full inline size except the drop-marker.
221   nscoord mDisplayISize;
222   // The maximum inline size of our display area, which is the
223   // nsComoboxControlFrame's border-box.
224   //
225   // Going over this would be observable via DOM APIs like client / scrollWidth.
226   nscoord mMaxDisplayISize;
227 
228   nsRevocableEventPtr<RedisplayTextEvent> mRedisplayTextEvent;
229 
230   int32_t mRecentSelectedIndex;
231   int32_t mDisplayedIndex;
232   nsString mDisplayedOptionTextOrPreview;
233 
234   RefPtr<mozilla::HTMLSelectEventListener> mEventListener;
235 
236   // See comment in HandleRedisplayTextEvent().
237   bool mInRedisplayText;
238   bool mIsOpenInParentProcess;
239 
240   // static class data member for Bug 32920
241   // only one control can be focused at a time
242   static nsComboboxControlFrame* sFocused;
243 
244 #ifdef DO_REFLOW_COUNTER
245   int32_t mReflowId;
246 #endif
247 };
248 
249 #endif
250