Source code

Revision control

Other Tools

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