|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 #include "nsCOMPtr.h" |
|
6 #include "nsIPopupBoxObject.h" |
|
7 #include "nsIRootBox.h" |
|
8 #include "nsBoxObject.h" |
|
9 #include "nsIPresShell.h" |
|
10 #include "nsFrameManager.h" |
|
11 #include "nsIContent.h" |
|
12 #include "nsIDOMElement.h" |
|
13 #include "nsNameSpaceManager.h" |
|
14 #include "nsGkAtoms.h" |
|
15 #include "nsMenuPopupFrame.h" |
|
16 #include "nsView.h" |
|
17 #include "mozilla/AppUnits.h" |
|
18 #include "mozilla/dom/DOMRect.h" |
|
19 |
|
20 using namespace mozilla::dom; |
|
21 |
|
22 class nsPopupBoxObject : public nsBoxObject, |
|
23 public nsIPopupBoxObject |
|
24 { |
|
25 public: |
|
26 NS_DECL_ISUPPORTS_INHERITED |
|
27 NS_DECL_NSIPOPUPBOXOBJECT |
|
28 |
|
29 nsPopupBoxObject() {} |
|
30 protected: |
|
31 virtual ~nsPopupBoxObject() {} |
|
32 |
|
33 nsPopupSetFrame* GetPopupSetFrame(); |
|
34 }; |
|
35 |
|
36 NS_IMPL_ISUPPORTS_INHERITED(nsPopupBoxObject, nsBoxObject, nsIPopupBoxObject) |
|
37 |
|
38 nsPopupSetFrame* |
|
39 nsPopupBoxObject::GetPopupSetFrame() |
|
40 { |
|
41 nsIRootBox* rootBox = nsIRootBox::GetRootBox(GetPresShell(false)); |
|
42 if (!rootBox) |
|
43 return nullptr; |
|
44 |
|
45 return rootBox->GetPopupSetFrame(); |
|
46 } |
|
47 |
|
48 NS_IMETHODIMP |
|
49 nsPopupBoxObject::HidePopup() |
|
50 { |
|
51 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
52 if (pm && mContent) |
|
53 pm->HidePopup(mContent, false, true, false, false); |
|
54 |
|
55 return NS_OK; |
|
56 } |
|
57 |
|
58 NS_IMETHODIMP |
|
59 nsPopupBoxObject::ShowPopup(nsIDOMElement* aAnchorElement, |
|
60 nsIDOMElement* aPopupElement, |
|
61 int32_t aXPos, int32_t aYPos, |
|
62 const char16_t *aPopupType, |
|
63 const char16_t *aAnchorAlignment, |
|
64 const char16_t *aPopupAlignment) |
|
65 { |
|
66 NS_ENSURE_TRUE(aPopupElement, NS_ERROR_INVALID_ARG); |
|
67 // srcContent can be null. |
|
68 |
|
69 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
70 if (pm && mContent) { |
|
71 nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement)); |
|
72 nsAutoString popupType(aPopupType); |
|
73 nsAutoString anchor(aAnchorAlignment); |
|
74 nsAutoString align(aPopupAlignment); |
|
75 pm->ShowPopupWithAnchorAlign(mContent, anchorContent, anchor, align, |
|
76 aXPos, aYPos, popupType.EqualsLiteral("context")); |
|
77 } |
|
78 |
|
79 return NS_OK; |
|
80 } |
|
81 |
|
82 NS_IMETHODIMP |
|
83 nsPopupBoxObject::OpenPopup(nsIDOMElement* aAnchorElement, |
|
84 const nsAString& aPosition, |
|
85 int32_t aXPos, int32_t aYPos, |
|
86 bool aIsContextMenu, |
|
87 bool aAttributesOverride, |
|
88 nsIDOMEvent* aTriggerEvent) |
|
89 { |
|
90 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
91 if (pm && mContent) { |
|
92 nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement)); |
|
93 pm->ShowPopup(mContent, anchorContent, aPosition, aXPos, aYPos, |
|
94 aIsContextMenu, aAttributesOverride, false, aTriggerEvent); |
|
95 } |
|
96 |
|
97 return NS_OK; |
|
98 } |
|
99 |
|
100 NS_IMETHODIMP |
|
101 nsPopupBoxObject::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos, |
|
102 bool aIsContextMenu, |
|
103 nsIDOMEvent* aTriggerEvent) |
|
104 { |
|
105 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
106 if (pm && mContent) |
|
107 pm->ShowPopupAtScreen(mContent, aXPos, aYPos, aIsContextMenu, aTriggerEvent); |
|
108 return NS_OK; |
|
109 } |
|
110 |
|
111 NS_IMETHODIMP |
|
112 nsPopupBoxObject::MoveTo(int32_t aLeft, int32_t aTop) |
|
113 { |
|
114 nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; |
|
115 if (menuPopupFrame) { |
|
116 menuPopupFrame->MoveTo(aLeft, aTop, true); |
|
117 } |
|
118 |
|
119 return NS_OK; |
|
120 } |
|
121 |
|
122 NS_IMETHODIMP |
|
123 nsPopupBoxObject::MoveToAnchor(nsIDOMElement* aAnchorElement, |
|
124 const nsAString& aPosition, |
|
125 int32_t aXPos, int32_t aYPos, |
|
126 bool aAttributesOverride) |
|
127 { |
|
128 if (mContent) { |
|
129 nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement)); |
|
130 |
|
131 nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
132 if (menuPopupFrame && menuPopupFrame->PopupState() == ePopupOpenAndVisible) { |
|
133 menuPopupFrame->MoveToAnchor(anchorContent, aPosition, aXPos, aYPos, aAttributesOverride); |
|
134 } |
|
135 } |
|
136 |
|
137 return NS_OK; |
|
138 } |
|
139 |
|
140 NS_IMETHODIMP |
|
141 nsPopupBoxObject::SizeTo(int32_t aWidth, int32_t aHeight) |
|
142 { |
|
143 if (!mContent) |
|
144 return NS_OK; |
|
145 |
|
146 nsAutoString width, height; |
|
147 width.AppendInt(aWidth); |
|
148 height.AppendInt(aHeight); |
|
149 |
|
150 nsCOMPtr<nsIContent> content = mContent; |
|
151 |
|
152 // We only want to pass aNotify=true to SetAttr once, but must make sure |
|
153 // we pass it when a value is being changed. Thus, we check if the height |
|
154 // is the same and if so, pass true when setting the width. |
|
155 bool heightSame = content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::height, height, eCaseMatters); |
|
156 |
|
157 content->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, heightSame); |
|
158 content->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true); |
|
159 |
|
160 return NS_OK; |
|
161 } |
|
162 |
|
163 NS_IMETHODIMP |
|
164 nsPopupBoxObject::GetAutoPosition(bool* aShouldAutoPosition) |
|
165 { |
|
166 *aShouldAutoPosition = true; |
|
167 |
|
168 nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; |
|
169 if (menuPopupFrame) { |
|
170 *aShouldAutoPosition = menuPopupFrame->GetAutoPosition(); |
|
171 } |
|
172 |
|
173 return NS_OK; |
|
174 } |
|
175 |
|
176 NS_IMETHODIMP |
|
177 nsPopupBoxObject::SetAutoPosition(bool aShouldAutoPosition) |
|
178 { |
|
179 nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; |
|
180 if (menuPopupFrame) { |
|
181 menuPopupFrame->SetAutoPosition(aShouldAutoPosition); |
|
182 } |
|
183 |
|
184 return NS_OK; |
|
185 } |
|
186 |
|
187 NS_IMETHODIMP |
|
188 nsPopupBoxObject::EnableRollup(bool aShouldRollup) |
|
189 { |
|
190 // this does nothing now |
|
191 return NS_OK; |
|
192 } |
|
193 |
|
194 NS_IMETHODIMP |
|
195 nsPopupBoxObject::SetConsumeRollupEvent(uint32_t aConsume) |
|
196 { |
|
197 nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); |
|
198 if (menuPopupFrame) { |
|
199 menuPopupFrame->SetConsumeRollupEvent(aConsume); |
|
200 } |
|
201 |
|
202 return NS_OK; |
|
203 } |
|
204 |
|
205 NS_IMETHODIMP |
|
206 nsPopupBoxObject::EnableKeyboardNavigator(bool aEnableKeyboardNavigator) |
|
207 { |
|
208 if (!mContent) |
|
209 return NS_OK; |
|
210 |
|
211 // Use ignorekeys="true" on the popup instead of using this function. |
|
212 if (aEnableKeyboardNavigator) |
|
213 mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, true); |
|
214 else |
|
215 mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, |
|
216 NS_LITERAL_STRING("true"), true); |
|
217 |
|
218 return NS_OK; |
|
219 } |
|
220 |
|
221 NS_IMETHODIMP |
|
222 nsPopupBoxObject::GetPopupState(nsAString& aState) |
|
223 { |
|
224 // set this here in case there's no frame for the popup |
|
225 aState.AssignLiteral("closed"); |
|
226 |
|
227 nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; |
|
228 if (menuPopupFrame) { |
|
229 switch (menuPopupFrame->PopupState()) { |
|
230 case ePopupShowing: |
|
231 case ePopupOpen: |
|
232 aState.AssignLiteral("showing"); |
|
233 break; |
|
234 case ePopupOpenAndVisible: |
|
235 aState.AssignLiteral("open"); |
|
236 break; |
|
237 case ePopupHiding: |
|
238 case ePopupInvisible: |
|
239 aState.AssignLiteral("hiding"); |
|
240 break; |
|
241 case ePopupClosed: |
|
242 break; |
|
243 default: |
|
244 NS_NOTREACHED("Bad popup state"); |
|
245 break; |
|
246 } |
|
247 } |
|
248 |
|
249 return NS_OK; |
|
250 } |
|
251 |
|
252 NS_IMETHODIMP |
|
253 nsPopupBoxObject::GetTriggerNode(nsIDOMNode** aTriggerNode) |
|
254 { |
|
255 *aTriggerNode = nullptr; |
|
256 |
|
257 nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; |
|
258 nsIContent* triggerContent = nsMenuPopupFrame::GetTriggerContent(menuPopupFrame); |
|
259 if (triggerContent) |
|
260 CallQueryInterface(triggerContent, aTriggerNode); |
|
261 |
|
262 return NS_OK; |
|
263 } |
|
264 |
|
265 NS_IMETHODIMP |
|
266 nsPopupBoxObject::GetAnchorNode(nsIDOMElement** aAnchor) |
|
267 { |
|
268 *aAnchor = nullptr; |
|
269 |
|
270 nsMenuPopupFrame *menuPopupFrame = mContent ? do_QueryFrame(mContent->GetPrimaryFrame()) : nullptr; |
|
271 if (!menuPopupFrame) |
|
272 return NS_OK; |
|
273 |
|
274 nsIContent* anchor = menuPopupFrame->GetAnchor(); |
|
275 if (anchor) |
|
276 CallQueryInterface(anchor, aAnchor); |
|
277 |
|
278 return NS_OK; |
|
279 } |
|
280 |
|
281 NS_IMETHODIMP |
|
282 nsPopupBoxObject::GetOuterScreenRect(nsIDOMClientRect** aRect) |
|
283 { |
|
284 DOMRect* rect = new DOMRect(mContent); |
|
285 |
|
286 NS_ADDREF(*aRect = rect); |
|
287 |
|
288 nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); |
|
289 if (!menuPopupFrame) |
|
290 return NS_OK; |
|
291 |
|
292 // Return an empty rectangle if the popup is not open. |
|
293 nsPopupState state = menuPopupFrame->PopupState(); |
|
294 if (state != ePopupOpen && state != ePopupOpenAndVisible) |
|
295 return NS_OK; |
|
296 |
|
297 nsView* view = menuPopupFrame->GetView(); |
|
298 if (view) { |
|
299 nsIWidget* widget = view->GetWidget(); |
|
300 if (widget) { |
|
301 nsIntRect screenRect; |
|
302 widget->GetScreenBounds(screenRect); |
|
303 |
|
304 int32_t pp = menuPopupFrame->PresContext()->AppUnitsPerDevPixel(); |
|
305 rect->SetLayoutRect(screenRect.ToAppUnits(pp)); |
|
306 } |
|
307 } |
|
308 |
|
309 return NS_OK; |
|
310 } |
|
311 |
|
312 NS_IMETHODIMP |
|
313 nsPopupBoxObject::GetAlignmentPosition(nsAString& positionStr) |
|
314 { |
|
315 positionStr.Truncate(); |
|
316 |
|
317 // This needs to flush layout. |
|
318 nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(true)); |
|
319 if (!menuPopupFrame) |
|
320 return NS_OK; |
|
321 |
|
322 int8_t position = menuPopupFrame->GetAlignmentPosition(); |
|
323 switch (position) { |
|
324 case POPUPPOSITION_AFTERSTART: |
|
325 positionStr.AssignLiteral("after_start"); |
|
326 break; |
|
327 case POPUPPOSITION_AFTEREND: |
|
328 positionStr.AssignLiteral("after_end"); |
|
329 break; |
|
330 case POPUPPOSITION_BEFORESTART: |
|
331 positionStr.AssignLiteral("before_start"); |
|
332 break; |
|
333 case POPUPPOSITION_BEFOREEND: |
|
334 positionStr.AssignLiteral("before_end"); |
|
335 break; |
|
336 case POPUPPOSITION_STARTBEFORE: |
|
337 positionStr.AssignLiteral("start_before"); |
|
338 break; |
|
339 case POPUPPOSITION_ENDBEFORE: |
|
340 positionStr.AssignLiteral("end_before"); |
|
341 break; |
|
342 case POPUPPOSITION_STARTAFTER: |
|
343 positionStr.AssignLiteral("start_after"); |
|
344 break; |
|
345 case POPUPPOSITION_ENDAFTER: |
|
346 positionStr.AssignLiteral("end_after"); |
|
347 break; |
|
348 case POPUPPOSITION_OVERLAP: |
|
349 positionStr.AssignLiteral("overlap"); |
|
350 break; |
|
351 case POPUPPOSITION_AFTERPOINTER: |
|
352 positionStr.AssignLiteral("after_pointer"); |
|
353 break; |
|
354 default: |
|
355 // Leave as an empty string. |
|
356 break; |
|
357 } |
|
358 |
|
359 return NS_OK; |
|
360 } |
|
361 |
|
362 NS_IMETHODIMP |
|
363 nsPopupBoxObject::GetAlignmentOffset(int32_t *aAlignmentOffset) |
|
364 { |
|
365 nsMenuPopupFrame *menuPopupFrame = do_QueryFrame(GetFrame(false)); |
|
366 if (!menuPopupFrame) |
|
367 return NS_OK; |
|
368 |
|
369 int32_t pp = mozilla::AppUnitsPerCSSPixel(); |
|
370 // Note that the offset might be along either the X or Y axis, but for the |
|
371 // sake of simplicity we use a point with only the X axis set so we can |
|
372 // use ToNearestPixels(). |
|
373 nsPoint appOffset(menuPopupFrame->GetAlignmentOffset(), 0); |
|
374 nsIntPoint popupOffset = appOffset.ToNearestPixels(pp); |
|
375 *aAlignmentOffset = popupOffset.x; |
|
376 return NS_OK; |
|
377 } |
|
378 |
|
379 // Creation Routine /////////////////////////////////////////////////////////////////////// |
|
380 |
|
381 nsresult |
|
382 NS_NewPopupBoxObject(nsIBoxObject** aResult) |
|
383 { |
|
384 *aResult = new nsPopupBoxObject; |
|
385 if (!*aResult) |
|
386 return NS_ERROR_OUT_OF_MEMORY; |
|
387 NS_ADDREF(*aResult); |
|
388 return NS_OK; |
|
389 } |