/src/mozilla-central/layout/xul/nsMenuFrame.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 | | |
7 | | // |
8 | | // nsMenuFrame |
9 | | // |
10 | | |
11 | | #ifndef nsMenuFrame_h__ |
12 | | #define nsMenuFrame_h__ |
13 | | |
14 | | #include "nsAtom.h" |
15 | | #include "nsCOMPtr.h" |
16 | | |
17 | | #include "nsBoxFrame.h" |
18 | | #include "nsFrameList.h" |
19 | | #include "nsGkAtoms.h" |
20 | | #include "nsMenuParent.h" |
21 | | #include "nsXULPopupManager.h" |
22 | | #include "nsINamed.h" |
23 | | #include "nsIReflowCallback.h" |
24 | | #include "nsITimer.h" |
25 | | #include "mozilla/Attributes.h" |
26 | | |
27 | | nsIFrame* NS_NewMenuFrame(nsIPresShell* aPresShell, mozilla::ComputedStyle*); |
28 | | nsIFrame* NS_NewMenuItemFrame(nsIPresShell* aPresShell, mozilla::ComputedStyle*); |
29 | | |
30 | | class nsIContent; |
31 | | |
32 | | namespace mozilla { |
33 | | namespace dom { |
34 | | class Element; |
35 | | } // namespace dom |
36 | | } // namespace mozilla |
37 | | |
38 | 0 | #define NS_STATE_ACCELTEXT_IS_DERIVED NS_STATE_BOX_CHILD_RESERVED |
39 | | |
40 | | // the type of menuitem |
41 | | enum nsMenuType { |
42 | | // a normal menuitem where a command is carried out when activated |
43 | | eMenuType_Normal = 0, |
44 | | // a menuitem with a checkmark that toggles when activated |
45 | | eMenuType_Checkbox = 1, |
46 | | // a radio menuitem where only one of it and its siblings with the same |
47 | | // name attribute can be checked at a time |
48 | | eMenuType_Radio = 2 |
49 | | }; |
50 | | |
51 | | enum nsMenuListType { |
52 | | eNotMenuList, // not a menulist |
53 | | eReadonlyMenuList // <menulist/> |
54 | | }; |
55 | | |
56 | | class nsMenuFrame; |
57 | | |
58 | | /** |
59 | | * nsMenuTimerMediator is a wrapper around an nsMenuFrame which can be safely |
60 | | * passed to timers. The class is reference counted unlike the underlying |
61 | | * nsMenuFrame, so that it will exist as long as the timer holds a reference |
62 | | * to it. The callback is delegated to the contained nsMenuFrame as long as |
63 | | * the contained nsMenuFrame has not been destroyed. |
64 | | */ |
65 | | class nsMenuTimerMediator final : public nsITimerCallback, |
66 | | public nsINamed |
67 | | { |
68 | | public: |
69 | | explicit nsMenuTimerMediator(nsMenuFrame* aFrame); |
70 | | |
71 | | NS_DECL_ISUPPORTS |
72 | | NS_DECL_NSITIMERCALLBACK |
73 | | NS_DECL_NSINAMED |
74 | | |
75 | | void ClearFrame(); |
76 | | |
77 | | private: |
78 | | ~nsMenuTimerMediator(); |
79 | | |
80 | | // Pointer to the wrapped frame. |
81 | | nsMenuFrame* mFrame; |
82 | | }; |
83 | | |
84 | | class nsMenuFrame final : public nsBoxFrame |
85 | | , public nsIReflowCallback |
86 | | { |
87 | | public: |
88 | | explicit nsMenuFrame(ComputedStyle* aStyle); |
89 | | |
90 | | NS_DECL_QUERYFRAME |
91 | | NS_DECL_FRAMEARENA_HELPERS(nsMenuFrame) |
92 | | |
93 | | NS_IMETHOD DoXULLayout(nsBoxLayoutState& aBoxLayoutState) override; |
94 | | virtual nsSize GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) override; |
95 | | virtual nsSize GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) override; |
96 | | |
97 | | virtual void Init(nsIContent* aContent, |
98 | | nsContainerFrame* aParent, |
99 | | nsIFrame* aPrevInFlow) override; |
100 | | |
101 | | // The following methods are all overridden so that the menupopup |
102 | | // can be stored in a separate list, so that it doesn't impact reflow of the |
103 | | // actual menu item at all. |
104 | | virtual const nsFrameList& GetChildList(ChildListID aList) const override; |
105 | | virtual void GetChildLists(nsTArray<ChildList>* aLists) const override; |
106 | | virtual void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) override; |
107 | | |
108 | | // Overridden to prevent events from going to children of the menu. |
109 | | virtual void BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder, |
110 | | const nsDisplayListSet& aLists) override; |
111 | | |
112 | | // this method can destroy the frame |
113 | | virtual nsresult HandleEvent(nsPresContext* aPresContext, |
114 | | mozilla::WidgetGUIEvent* aEvent, |
115 | | nsEventStatus* aEventStatus) override; |
116 | | |
117 | | virtual void SetInitialChildList(ChildListID aListID, |
118 | | nsFrameList& aChildList) override; |
119 | | virtual void AppendFrames(ChildListID aListID, |
120 | | nsFrameList& aFrameList) override; |
121 | | virtual void InsertFrames(ChildListID aListID, |
122 | | nsIFrame* aPrevFrame, |
123 | | nsFrameList& aFrameList) override; |
124 | | virtual void RemoveFrame(ChildListID aListID, |
125 | | nsIFrame* aOldFrame) override; |
126 | | |
127 | | NS_IMETHOD SelectMenu(bool aActivateFlag); |
128 | | |
129 | | virtual nsIScrollableFrame* GetScrollTargetFrame() override; |
130 | | |
131 | | // Retrieve the element that the menu should be anchored to. By default this is |
132 | | // the menu itself. However, the anchor attribute may refer to the value of an |
133 | | // anonid within the menu's binding, or, if not found, the id of an element in |
134 | | // the document. |
135 | | nsIContent* GetAnchor(); |
136 | | |
137 | | /** |
138 | | * NOTE: OpenMenu will open the menu asynchronously. |
139 | | */ |
140 | | void OpenMenu(bool aSelectFirstItem); |
141 | | // CloseMenu closes the menu asynchronously |
142 | | void CloseMenu(bool aDeselectMenu); |
143 | | |
144 | 0 | bool IsChecked() { return mChecked; } |
145 | | |
146 | | NS_IMETHOD GetActiveChild(mozilla::dom::Element** aResult); |
147 | | NS_IMETHOD SetActiveChild(mozilla::dom::Element* aChild); |
148 | | |
149 | | // called when the Enter key is pressed while the menuitem is the current |
150 | | // one in its parent popup. This will carry out the command attached to |
151 | | // the menuitem. If the menu should be opened, this frame will be returned, |
152 | | // otherwise null will be returned. |
153 | | nsMenuFrame* Enter(mozilla::WidgetGUIEvent* aEvent); |
154 | | |
155 | | // Return the nearest menu bar or menupopup ancestor frame. |
156 | | nsMenuParent* GetMenuParent() const; |
157 | | |
158 | 0 | const nsAString& GetRadioGroupName() { return mGroupName; } |
159 | 0 | nsMenuType GetMenuType() { return mType; } |
160 | | nsMenuPopupFrame* GetPopup(); |
161 | | |
162 | | /** |
163 | | * @return true if this frame has a popup child frame. |
164 | | */ |
165 | | bool HasPopup() const |
166 | 0 | { |
167 | 0 | return (GetStateBits() & NS_STATE_MENU_HAS_POPUP_LIST) != 0; |
168 | 0 | } |
169 | | |
170 | | |
171 | | // nsMenuFrame methods |
172 | | |
173 | | bool IsOnMenuBar() const |
174 | 0 | { |
175 | 0 | nsMenuParent* menuParent = GetMenuParent(); |
176 | 0 | return menuParent && menuParent->IsMenuBar(); |
177 | 0 | } |
178 | | bool IsOnActiveMenuBar() const |
179 | 0 | { |
180 | 0 | nsMenuParent* menuParent = GetMenuParent(); |
181 | 0 | return menuParent && menuParent->IsMenuBar() && menuParent->IsActive(); |
182 | 0 | } |
183 | | virtual bool IsOpen(); |
184 | | virtual bool IsMenu(); |
185 | | nsMenuListType GetParentMenuListType(); |
186 | | bool IsDisabled(); |
187 | | void ToggleMenuState(); |
188 | | |
189 | | // indiciate that the menu's popup has just been opened, so that the menu |
190 | | // can update its open state. This method modifies the open attribute on |
191 | | // the menu, so the frames could be gone after this call. |
192 | | void PopupOpened(); |
193 | | // indiciate that the menu's popup has just been closed, so that the menu |
194 | | // can update its open state. The menu should be unhighlighted if |
195 | | // aDeselectedMenu is true. This method modifies the open attribute on |
196 | | // the menu, so the frames could be gone after this call. |
197 | | void PopupClosed(bool aDeselectMenu); |
198 | | |
199 | | // returns true if this is a menu on another menu popup. A menu is a submenu |
200 | | // if it has a parent popup or menupopup. |
201 | | bool IsOnMenu() const |
202 | 0 | { |
203 | 0 | nsMenuParent* menuParent = GetMenuParent(); |
204 | 0 | return menuParent && menuParent->IsMenu(); |
205 | 0 | } |
206 | 0 | void SetIsMenu(bool aIsMenu) { mIsMenu = aIsMenu; } |
207 | | |
208 | | #ifdef DEBUG_FRAME_DUMP |
209 | | virtual nsresult GetFrameName(nsAString& aResult) const override |
210 | | { |
211 | | return MakeFrameName(NS_LITERAL_STRING("Menu"), aResult); |
212 | | } |
213 | | #endif |
214 | | |
215 | | static bool IsSizedToPopup(nsIContent* aContent, bool aRequireAlways); |
216 | | |
217 | | // nsIReflowCallback |
218 | | virtual bool ReflowFinished() override; |
219 | | virtual void ReflowCallbackCanceled() override; |
220 | | |
221 | | protected: |
222 | | friend class nsMenuTimerMediator; |
223 | | friend class nsASyncMenuInitialization; |
224 | | friend class nsMenuAttributeChangedEvent; |
225 | | |
226 | | /** |
227 | | * Initialize the popup list to the first popup frame within |
228 | | * aChildList. Removes the popup, if any, from aChildList. |
229 | | */ |
230 | | void SetPopupFrame(nsFrameList& aChildList); |
231 | | |
232 | | /** |
233 | | * Get the popup frame list from the frame property. |
234 | | * @return the property value if it exists, nullptr otherwise. |
235 | | */ |
236 | | nsFrameList* GetPopupList() const; |
237 | | |
238 | | /** |
239 | | * Destroy the popup list property. The list must exist and be empty. |
240 | | */ |
241 | | void DestroyPopupList(); |
242 | | |
243 | | // Update the menu's type (normal, checkbox, radio). |
244 | | // This method can destroy the frame. |
245 | | void UpdateMenuType(); |
246 | | // Update the checked state of the menu, and for radios, clear any other |
247 | | // checked items. This method can destroy the frame. |
248 | | void UpdateMenuSpecialState(); |
249 | | |
250 | | // Examines the key node and builds the accelerator. |
251 | | void BuildAcceleratorText(bool aNotify); |
252 | | |
253 | | // Called to execute our command handler. This method can destroy the frame. |
254 | | void Execute(mozilla::WidgetGUIEvent *aEvent); |
255 | | |
256 | | // This method can destroy the frame |
257 | | virtual nsresult AttributeChanged(int32_t aNameSpaceID, |
258 | | nsAtom* aAttribute, |
259 | | int32_t aModType) override; |
260 | 0 | virtual ~nsMenuFrame() { } |
261 | | |
262 | | bool SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize); |
263 | | |
264 | | bool ShouldBlink(); |
265 | | void StartBlinking(mozilla::WidgetGUIEvent* aEvent, bool aFlipChecked); |
266 | | void StopBlinking(); |
267 | | void CreateMenuCommandEvent(mozilla::WidgetGUIEvent* aEvent, |
268 | | bool aFlipChecked); |
269 | | void PassMenuCommandEventToPopupManager(); |
270 | | |
271 | | protected: |
272 | | nsresult Notify(nsITimer* aTimer); |
273 | | |
274 | | bool mIsMenu; // Whether or not we can even have children or not. |
275 | | bool mChecked; // are we checked? |
276 | | bool mIgnoreAccelTextChange; // temporarily set while determining the accelerator key |
277 | | bool mReflowCallbackPosted; |
278 | | nsMenuType mType; |
279 | | |
280 | | // Reference to the mediator which wraps this frame. |
281 | | RefPtr<nsMenuTimerMediator> mTimerMediator; |
282 | | |
283 | | nsCOMPtr<nsITimer> mOpenTimer; |
284 | | nsCOMPtr<nsITimer> mBlinkTimer; |
285 | | |
286 | | uint8_t mBlinkState; // 0: not blinking, 1: off, 2: on |
287 | | RefPtr<nsXULMenuCommandEvent> mDelayedMenuCommandEvent; |
288 | | |
289 | | nsString mGroupName; |
290 | | |
291 | | }; // class nsMenuFrame |
292 | | |
293 | | #endif |