/src/mozilla-central/dom/xul/XULPopupElement.cpp
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 | | #include "nsCOMPtr.h" |
8 | | #include "nsIPresShell.h" |
9 | | #include "nsIContent.h" |
10 | | #include "nsNameSpaceManager.h" |
11 | | #include "nsGkAtoms.h" |
12 | | #include "nsMenuPopupFrame.h" |
13 | | #include "nsView.h" |
14 | | #include "mozilla/AppUnits.h" |
15 | | #include "mozilla/dom/DOMRect.h" |
16 | | #include "mozilla/dom/Element.h" |
17 | | #include "mozilla/dom/Event.h" |
18 | | #include "mozilla/dom/XULPopupElement.h" |
19 | | #include "mozilla/dom/XULPopupElementBinding.h" |
20 | | |
21 | | namespace mozilla { |
22 | | namespace dom { |
23 | | |
24 | | nsXULElement* |
25 | | NS_NewXULPopupElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
26 | 0 | { |
27 | 0 | return new XULPopupElement(std::move(aNodeInfo)); |
28 | 0 | } |
29 | | |
30 | | JSObject* |
31 | | XULPopupElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) |
32 | 0 | { |
33 | 0 | return XULPopupElement_Binding::Wrap(aCx, this, aGivenProto); |
34 | 0 | } |
35 | | |
36 | | nsIFrame* |
37 | | XULPopupElement::GetFrame(bool aFlushLayout) |
38 | 0 | { |
39 | 0 | nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference |
40 | 0 |
|
41 | 0 | nsCOMPtr<nsIDocument> doc = GetUncomposedDoc(); |
42 | 0 | if (doc) { |
43 | 0 | doc->FlushPendingNotifications(aFlushLayout ? FlushType::Layout : FlushType::Frames); |
44 | 0 | } |
45 | 0 |
|
46 | 0 | return GetPrimaryFrame(); |
47 | 0 | } |
48 | | |
49 | | void |
50 | | XULPopupElement::OpenPopup(Element* aAnchorElement, |
51 | | const StringOrOpenPopupOptions& aOptions, |
52 | | int32_t aXPos, int32_t aYPos, |
53 | | bool aIsContextMenu, |
54 | | bool aAttributesOverride, |
55 | | Event* aTriggerEvent) |
56 | 0 | { |
57 | 0 | nsAutoString position; |
58 | 0 | if (aOptions.IsOpenPopupOptions()) { |
59 | 0 | const OpenPopupOptions& options = aOptions.GetAsOpenPopupOptions(); |
60 | 0 | position = options.mPosition; |
61 | 0 | aXPos = options.mX; |
62 | 0 | aYPos = options.mY; |
63 | 0 | aIsContextMenu = options.mIsContextMenu; |
64 | 0 | aAttributesOverride = options.mAttributesOverride; |
65 | 0 | aTriggerEvent = options.mTriggerEvent; |
66 | 0 | } |
67 | 0 | else { |
68 | 0 | position = aOptions.GetAsString(); |
69 | 0 | } |
70 | 0 |
|
71 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
72 | 0 | if (pm) { |
73 | 0 | // As a special case for popups that are menus when no anchor or position are |
74 | 0 | // specified, open the popup with ShowMenu instead of ShowPopup so that the |
75 | 0 | // popup is aligned with the menu. |
76 | 0 | if (!aAnchorElement && position.IsEmpty() && GetPrimaryFrame()) { |
77 | 0 | nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame()->GetParent()); |
78 | 0 | if (menu) { |
79 | 0 | pm->ShowMenu(menu->GetContent(), false, false); |
80 | 0 | return; |
81 | 0 | } |
82 | 0 | } |
83 | 0 | |
84 | 0 | pm->ShowPopup(this, aAnchorElement, position, aXPos, aYPos, |
85 | 0 | aIsContextMenu, aAttributesOverride, false, aTriggerEvent); |
86 | 0 | } |
87 | 0 | } |
88 | | |
89 | | void |
90 | | XULPopupElement::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos, |
91 | | bool aIsContextMenu, |
92 | | Event* aTriggerEvent) |
93 | 0 | { |
94 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
95 | 0 | if (pm) { |
96 | 0 | pm->ShowPopupAtScreen(this, aXPos, aYPos, aIsContextMenu, aTriggerEvent); |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | | void |
101 | | XULPopupElement::OpenPopupAtScreenRect(const nsAString& aPosition, |
102 | | int32_t aXPos, int32_t aYPos, |
103 | | int32_t aWidth, int32_t aHeight, |
104 | | bool aIsContextMenu, |
105 | | bool aAttributesOverride, |
106 | | Event* aTriggerEvent) |
107 | 0 | { |
108 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
109 | 0 | if (pm) { |
110 | 0 | pm->ShowPopupAtScreenRect(this, aPosition, |
111 | 0 | nsIntRect(aXPos, aYPos, aWidth, aHeight), |
112 | 0 | aIsContextMenu, aAttributesOverride, aTriggerEvent); |
113 | 0 | } |
114 | 0 | } |
115 | | |
116 | | void |
117 | | XULPopupElement::HidePopup(bool aCancel) |
118 | 0 | { |
119 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
120 | 0 | if (pm) { |
121 | 0 | pm->HidePopup(this, false, true, false, aCancel); |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | | void |
126 | | XULPopupElement::MoveTo(int32_t aLeft, int32_t aTop) |
127 | 0 | { |
128 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame()); |
129 | 0 | if (menuPopupFrame) { |
130 | 0 | menuPopupFrame->MoveTo(CSSIntPoint(aLeft, aTop), true); |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | | void |
135 | | XULPopupElement::MoveToAnchor(Element* aAnchorElement, |
136 | | const nsAString& aPosition, |
137 | | int32_t aXPos, int32_t aYPos, |
138 | | bool aAttributesOverride) |
139 | 0 | { |
140 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame()); |
141 | 0 | if (menuPopupFrame && menuPopupFrame->IsVisible()) { |
142 | 0 | menuPopupFrame->MoveToAnchor(aAnchorElement, aPosition, aXPos, aYPos, aAttributesOverride); |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | | void |
147 | | XULPopupElement::SizeTo(int32_t aWidth, int32_t aHeight) |
148 | 0 | { |
149 | 0 | nsAutoString width, height; |
150 | 0 | width.AppendInt(aWidth); |
151 | 0 | height.AppendInt(aHeight); |
152 | 0 |
|
153 | 0 | nsCOMPtr<nsIContent> kungFuDeathGrip = this; // keep a reference |
154 | 0 |
|
155 | 0 | // We only want to pass aNotify=true to SetAttr once, but must make sure |
156 | 0 | // we pass it when a value is being changed. Thus, we check if the height |
157 | 0 | // is the same and if so, pass true when setting the width. |
158 | 0 | bool heightSame = AttrValueIs(kNameSpaceID_None, nsGkAtoms::height, height, eCaseMatters); |
159 | 0 |
|
160 | 0 | SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, heightSame); |
161 | 0 | SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true); |
162 | 0 |
|
163 | 0 | // If the popup is open, force a reposition of the popup after resizing it |
164 | 0 | // with notifications set to true so that the popuppositioned event is fired. |
165 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame()); |
166 | 0 | if (menuPopupFrame && menuPopupFrame->PopupState() == ePopupShown) { |
167 | 0 | menuPopupFrame->SetPopupPosition(nullptr, false, false, true); |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | | bool |
172 | | XULPopupElement::AutoPosition() |
173 | 0 | { |
174 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame()); |
175 | 0 | if (menuPopupFrame) { |
176 | 0 | return menuPopupFrame->GetAutoPosition(); |
177 | 0 | } |
178 | 0 | return true; |
179 | 0 | } |
180 | | |
181 | | void |
182 | | XULPopupElement::SetAutoPosition(bool aShouldAutoPosition) |
183 | 0 | { |
184 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame()); |
185 | 0 | if (menuPopupFrame) { |
186 | 0 | menuPopupFrame->SetAutoPosition(aShouldAutoPosition); |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | | void |
191 | | XULPopupElement::GetState(nsString& aState) |
192 | 0 | { |
193 | 0 | // set this here in case there's no frame for the popup |
194 | 0 | aState.AssignLiteral("closed"); |
195 | 0 |
|
196 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame()); |
197 | 0 | if (menuPopupFrame) { |
198 | 0 | switch (menuPopupFrame->PopupState()) { |
199 | 0 | case ePopupShown: |
200 | 0 | aState.AssignLiteral("open"); |
201 | 0 | break; |
202 | 0 | case ePopupShowing: |
203 | 0 | case ePopupPositioning: |
204 | 0 | case ePopupOpening: |
205 | 0 | case ePopupVisible: |
206 | 0 | aState.AssignLiteral("showing"); |
207 | 0 | break; |
208 | 0 | case ePopupHiding: |
209 | 0 | case ePopupInvisible: |
210 | 0 | aState.AssignLiteral("hiding"); |
211 | 0 | break; |
212 | 0 | case ePopupClosed: |
213 | 0 | break; |
214 | 0 | default: |
215 | 0 | MOZ_ASSERT_UNREACHABLE("Bad popup state"); |
216 | 0 | break; |
217 | 0 | } |
218 | 0 | } |
219 | 0 | } |
220 | | |
221 | | nsINode* |
222 | | XULPopupElement::GetTriggerNode() const |
223 | 0 | { |
224 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetPrimaryFrame()); |
225 | 0 | return nsMenuPopupFrame::GetTriggerContent(menuPopupFrame); |
226 | 0 | } |
227 | | |
228 | | // FIXME(emilio): should probably be renamed to GetAnchorElement? |
229 | | Element* |
230 | | XULPopupElement::GetAnchorNode() const |
231 | 0 | { |
232 | 0 | nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetPrimaryFrame()); |
233 | 0 | if (!menuPopupFrame) { |
234 | 0 | return nullptr; |
235 | 0 | } |
236 | 0 | |
237 | 0 | return Element::FromNodeOrNull(menuPopupFrame->GetAnchor()); |
238 | 0 | } |
239 | | |
240 | | already_AddRefed<DOMRect> |
241 | | XULPopupElement::GetOuterScreenRect() |
242 | 0 | { |
243 | 0 | RefPtr<DOMRect> rect = new DOMRect(ToSupports(this)); |
244 | 0 |
|
245 | 0 | // Return an empty rectangle if the popup is not open. |
246 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); |
247 | 0 | if (!menuPopupFrame || !menuPopupFrame->IsOpen()) { |
248 | 0 | return rect.forget(); |
249 | 0 | } |
250 | 0 | |
251 | 0 | nsView* view = menuPopupFrame->GetView(); |
252 | 0 | if (view) { |
253 | 0 | nsIWidget* widget = view->GetWidget(); |
254 | 0 | if (widget) { |
255 | 0 | LayoutDeviceIntRect screenRect = widget->GetScreenBounds(); |
256 | 0 |
|
257 | 0 | int32_t pp = menuPopupFrame->PresContext()->AppUnitsPerDevPixel(); |
258 | 0 | rect->SetLayoutRect(LayoutDeviceIntRect::ToAppUnits(screenRect, pp)); |
259 | 0 | } |
260 | 0 | } |
261 | 0 | return rect.forget(); |
262 | 0 | } |
263 | | |
264 | | void |
265 | | XULPopupElement::GetAlignmentPosition(nsString& positionStr) |
266 | 0 | { |
267 | 0 | positionStr.Truncate(); |
268 | 0 |
|
269 | 0 | // This needs to flush layout. |
270 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(true)); |
271 | 0 | if (!menuPopupFrame) |
272 | 0 | return; |
273 | 0 | |
274 | 0 | int8_t position = menuPopupFrame->GetAlignmentPosition(); |
275 | 0 | switch (position) { |
276 | 0 | case POPUPPOSITION_AFTERSTART: |
277 | 0 | positionStr.AssignLiteral("after_start"); |
278 | 0 | break; |
279 | 0 | case POPUPPOSITION_AFTEREND: |
280 | 0 | positionStr.AssignLiteral("after_end"); |
281 | 0 | break; |
282 | 0 | case POPUPPOSITION_BEFORESTART: |
283 | 0 | positionStr.AssignLiteral("before_start"); |
284 | 0 | break; |
285 | 0 | case POPUPPOSITION_BEFOREEND: |
286 | 0 | positionStr.AssignLiteral("before_end"); |
287 | 0 | break; |
288 | 0 | case POPUPPOSITION_STARTBEFORE: |
289 | 0 | positionStr.AssignLiteral("start_before"); |
290 | 0 | break; |
291 | 0 | case POPUPPOSITION_ENDBEFORE: |
292 | 0 | positionStr.AssignLiteral("end_before"); |
293 | 0 | break; |
294 | 0 | case POPUPPOSITION_STARTAFTER: |
295 | 0 | positionStr.AssignLiteral("start_after"); |
296 | 0 | break; |
297 | 0 | case POPUPPOSITION_ENDAFTER: |
298 | 0 | positionStr.AssignLiteral("end_after"); |
299 | 0 | break; |
300 | 0 | case POPUPPOSITION_OVERLAP: |
301 | 0 | positionStr.AssignLiteral("overlap"); |
302 | 0 | break; |
303 | 0 | case POPUPPOSITION_AFTERPOINTER: |
304 | 0 | positionStr.AssignLiteral("after_pointer"); |
305 | 0 | break; |
306 | 0 | case POPUPPOSITION_SELECTION: |
307 | 0 | positionStr.AssignLiteral("selection"); |
308 | 0 | break; |
309 | 0 | default: |
310 | 0 | // Leave as an empty string. |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | | int32_t |
316 | | XULPopupElement::AlignmentOffset() |
317 | 0 | { |
318 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); |
319 | 0 | if (!menuPopupFrame) |
320 | 0 | return 0; |
321 | 0 | |
322 | 0 | int32_t pp = mozilla::AppUnitsPerCSSPixel(); |
323 | 0 | // Note that the offset might be along either the X or Y axis, but for the |
324 | 0 | // sake of simplicity we use a point with only the X axis set so we can |
325 | 0 | // use ToNearestPixels(). |
326 | 0 | nsPoint appOffset(menuPopupFrame->GetAlignmentOffset(), 0); |
327 | 0 | nsIntPoint popupOffset = appOffset.ToNearestPixels(pp); |
328 | 0 | return popupOffset.x; |
329 | 0 | } |
330 | | |
331 | | void |
332 | | XULPopupElement::SetConstraintRect(dom::DOMRectReadOnly& aRect) |
333 | 0 | { |
334 | 0 | nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); |
335 | 0 | if (menuPopupFrame) { |
336 | 0 | menuPopupFrame->SetOverrideConstraintRect( |
337 | 0 | LayoutDeviceIntRect::Truncate(aRect.Left(), aRect.Top(), aRect.Width(), aRect.Height())); |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | | } // namespace dom |
342 | | } // namespace mozilla |