Coverage Report

Created: 2018-09-25 14:53

/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