Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/view/nsView.cpp
Line
Count
Source (jump to first uncovered line)
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
6
#include "nsView.h"
7
8
#include "mozilla/Attributes.h"
9
#include "mozilla/BasicEvents.h"
10
#include "mozilla/DebugOnly.h"
11
#include "mozilla/IntegerPrintfMacros.h"
12
#include "mozilla/Likely.h"
13
#include "mozilla/Poison.h"
14
#include "nsIWidget.h"
15
#include "nsViewManager.h"
16
#include "nsIFrame.h"
17
#include "nsPresArena.h"
18
#include "nsXULPopupManager.h"
19
#include "nsIWidgetListener.h"
20
#include "nsContentUtils.h" // for nsAutoScriptBlocker
21
#include "mozilla/TimelineConsumers.h"
22
#include "mozilla/CompositeTimelineMarker.h"
23
24
using namespace mozilla;
25
26
static bool sShowPreviousPage = true;
27
28
nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility)
29
  : mViewManager(aViewManager)
30
  , mParent(nullptr)
31
  , mNextSibling(nullptr)
32
  , mFirstChild(nullptr)
33
  , mFrame(nullptr)
34
  , mDirtyRegion(nullptr)
35
  , mZIndex(0)
36
  , mVis(aVisibility)
37
  , mPosX(0)
38
  , mPosY(0)
39
  , mVFlags(0)
40
  , mWidgetIsTopLevel(false)
41
  , mForcedRepaint(false)
42
  , mNeedsWindowPropertiesSync(false)
43
0
{
44
0
  MOZ_COUNT_CTOR(nsView);
45
0
46
0
  // Views should be transparent by default. Not being transparent is
47
0
  // a promise that the view will paint all its pixels opaquely. Views
48
0
  // should make this promise explicitly by calling
49
0
  // SetViewContentTransparency.
50
0
51
0
  static bool sShowPreviousPageInitialized = false;
52
0
  if (!sShowPreviousPageInitialized) {
53
0
    Preferences::AddBoolVarCache(&sShowPreviousPage, "layout.show_previous_page", true);
54
0
    sShowPreviousPageInitialized = true;
55
0
  }
56
0
}
57
58
void nsView::DropMouseGrabbing()
59
0
{
60
0
  nsIPresShell* presShell = mViewManager->GetPresShell();
61
0
  if (presShell)
62
0
    presShell->ClearMouseCaptureOnView(this);
63
0
}
64
65
nsView::~nsView()
66
0
{
67
0
  MOZ_COUNT_DTOR(nsView);
68
0
69
0
  while (GetFirstChild())
70
0
  {
71
0
    nsView* child = GetFirstChild();
72
0
    if (child->GetViewManager() == mViewManager) {
73
0
      child->Destroy();
74
0
    } else {
75
0
      // just unhook it. Someone else will want to destroy this.
76
0
      RemoveChild(child);
77
0
    }
78
0
  }
79
0
80
0
  if (mViewManager)
81
0
  {
82
0
    DropMouseGrabbing();
83
0
84
0
    nsView *rootView = mViewManager->GetRootView();
85
0
86
0
    if (rootView)
87
0
    {
88
0
      // Root views can have parents!
89
0
      if (mParent)
90
0
      {
91
0
        mViewManager->RemoveChild(this);
92
0
      }
93
0
94
0
      if (rootView == this)
95
0
      {
96
0
        // Inform the view manager that the root view has gone away...
97
0
        mViewManager->SetRootView(nullptr);
98
0
      }
99
0
    }
100
0
    else if (mParent)
101
0
    {
102
0
      mParent->RemoveChild(this);
103
0
    }
104
0
105
0
    mViewManager = nullptr;
106
0
  }
107
0
  else if (mParent)
108
0
  {
109
0
    mParent->RemoveChild(this);
110
0
  }
111
0
112
0
  if (mPreviousWindow) {
113
0
    mPreviousWindow->SetPreviouslyAttachedWidgetListener(nullptr);
114
0
  }
115
0
116
0
  // Destroy and release the widget
117
0
  DestroyWidget();
118
0
119
0
  delete mDirtyRegion;
120
0
}
121
122
class DestroyWidgetRunnable : public Runnable {
123
public:
124
  NS_DECL_NSIRUNNABLE
125
126
  explicit DestroyWidgetRunnable(nsIWidget* aWidget)
127
    : mozilla::Runnable("DestroyWidgetRunnable")
128
    , mWidget(aWidget)
129
0
  {
130
0
  }
131
132
private:
133
  nsCOMPtr<nsIWidget> mWidget;
134
};
135
136
NS_IMETHODIMP DestroyWidgetRunnable::Run()
137
0
{
138
0
  mWidget->Destroy();
139
0
  mWidget = nullptr;
140
0
  return NS_OK;
141
0
}
142
143
144
void nsView::DestroyWidget()
145
0
{
146
0
  if (mWindow)
147
0
  {
148
0
    // If we are not attached to a base window, we're going to tear down our
149
0
    // widget here. However, if we're attached to somebody elses widget, we
150
0
    // want to leave the widget alone: don't reset the client data or call
151
0
    // Destroy. Just clear our event view ptr and free our reference to it.
152
0
    if (mWidgetIsTopLevel) {
153
0
      mWindow->SetAttachedWidgetListener(nullptr);
154
0
    }
155
0
    else {
156
0
      mWindow->SetWidgetListener(nullptr);
157
0
158
0
      nsCOMPtr<nsIRunnable> widgetDestroyer =
159
0
        new DestroyWidgetRunnable(mWindow);
160
0
161
0
      // Don't leak if we happen to arrive here after the main thread
162
0
      // has disappeared.
163
0
      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
164
0
      if (mainThread) {
165
0
        mainThread->Dispatch(widgetDestroyer.forget(), NS_DISPATCH_NORMAL);
166
0
      }
167
0
    }
168
0
169
0
    mWindow = nullptr;
170
0
  }
171
0
}
172
173
nsView* nsView::GetViewFor(nsIWidget* aWidget)
174
0
{
175
0
  MOZ_ASSERT(nullptr != aWidget, "null widget ptr");
176
0
177
0
  nsIWidgetListener* listener = aWidget->GetWidgetListener();
178
0
  if (listener) {
179
0
    nsView* view = listener->GetView();
180
0
    if (view)
181
0
      return view;
182
0
  }
183
0
184
0
  listener = aWidget->GetAttachedWidgetListener();
185
0
  return listener ? listener->GetView() : nullptr;
186
0
}
187
188
void nsView::Destroy()
189
0
{
190
0
  this->~nsView();
191
0
  mozWritePoison(this, sizeof(*this));
192
0
  nsView::operator delete(this);
193
0
}
194
195
void nsView::SetPosition(nscoord aX, nscoord aY)
196
0
{
197
0
  mDimBounds.MoveBy(aX - mPosX, aY - mPosY);
198
0
  mPosX = aX;
199
0
  mPosY = aY;
200
0
201
0
  NS_ASSERTION(GetParent() || (aX == 0 && aY == 0),
202
0
               "Don't try to move the root widget to something non-zero");
203
0
204
0
  ResetWidgetBounds(true, false);
205
0
}
206
207
void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync)
208
0
{
209
0
  if (mWindow) {
210
0
    if (!aForceSync) {
211
0
      // Don't change widget geometry synchronously, since that can
212
0
      // cause synchronous painting.
213
0
      mViewManager->PostPendingUpdate();
214
0
    } else {
215
0
      DoResetWidgetBounds(false, true);
216
0
    }
217
0
    return;
218
0
  }
219
0
220
0
  if (aRecurse) {
221
0
    // reposition any widgets under this view
222
0
    for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) {
223
0
      v->ResetWidgetBounds(true, aForceSync);
224
0
    }
225
0
  }
226
0
}
227
228
bool nsView::IsEffectivelyVisible()
229
0
{
230
0
  for (nsView* v = this; v; v = v->mParent) {
231
0
    if (v->GetVisibility() == nsViewVisibility_kHide)
232
0
      return false;
233
0
  }
234
0
  return true;
235
0
}
236
237
LayoutDeviceIntRect nsView::CalcWidgetBounds(nsWindowType aType)
238
0
{
239
0
  int32_t p2a = mViewManager->AppUnitsPerDevPixel();
240
0
241
0
  nsRect viewBounds(mDimBounds);
242
0
243
0
  nsView* parent = GetParent();
244
0
  nsIWidget* parentWidget = nullptr;
245
0
  if (parent) {
246
0
    nsPoint offset;
247
0
    parentWidget = parent->GetNearestWidget(&offset, p2a);
248
0
    // make viewBounds be relative to the parent widget, in appunits
249
0
    viewBounds += offset;
250
0
251
0
    if (parentWidget && aType == eWindowType_popup &&
252
0
        IsEffectivelyVisible()) {
253
0
      // put offset into screen coordinates. (based on client area origin)
254
0
      LayoutDeviceIntPoint screenPoint = parentWidget->WidgetToScreenOffset();
255
0
      viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a),
256
0
                            NSIntPixelsToAppUnits(screenPoint.y, p2a));
257
0
    }
258
0
  }
259
0
260
0
  // Compute widget bounds in device pixels
261
0
  LayoutDeviceIntRect newBounds =
262
0
    LayoutDeviceIntRect::FromUnknownRect(viewBounds.ToNearestPixels(p2a));
263
0
264
0
#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
265
0
  // cocoa and GTK round widget coordinates to the nearest global "display
266
0
  // pixel" integer value. So we avoid fractional display pixel values by
267
0
  // rounding to the nearest value that won't yield a fractional display pixel.
268
0
  nsIWidget* widget = parentWidget ? parentWidget : mWindow.get();
269
0
  uint32_t round;
270
0
  if (aType == eWindowType_popup && widget &&
271
0
      ((round = widget->RoundsWidgetCoordinatesTo()) > 1)) {
272
0
    LayoutDeviceIntSize pixelRoundedSize = newBounds.Size();
273
0
    // round the top left and bottom right to the nearest round pixel
274
0
    newBounds.x = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.x, p2a) / round) * round;
275
0
    newBounds.y = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.y, p2a) / round) * round;
276
0
    newBounds.width =
277
0
      NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.XMost(), p2a) / round) * round - newBounds.x;
278
0
    newBounds.height =
279
0
      NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.YMost(), p2a) / round) * round - newBounds.y;
280
0
    // but if that makes the widget larger then our frame may not paint the
281
0
    // extra pixels, so reduce the size to the nearest round value
282
0
    if (newBounds.width > pixelRoundedSize.width) {
283
0
      newBounds.width -= round;
284
0
    }
285
0
    if (newBounds.height > pixelRoundedSize.height) {
286
0
      newBounds.height -= round;
287
0
    }
288
0
  }
289
0
#endif
290
0
291
0
  // Compute where the top-left of our widget ended up relative to the parent
292
0
  // widget, in appunits.
293
0
  nsPoint roundedOffset(NSIntPixelsToAppUnits(newBounds.X(), p2a),
294
0
                        NSIntPixelsToAppUnits(newBounds.Y(), p2a));
295
0
296
0
  // mViewToWidgetOffset is added to coordinates relative to the view origin
297
0
  // to get coordinates relative to the widget.
298
0
  // The view origin, relative to the parent widget, is at
299
0
  // (mPosX,mPosY) - mDimBounds.TopLeft() + viewBounds.TopLeft().
300
0
  // Our widget, relative to the parent widget, is roundedOffset.
301
0
  mViewToWidgetOffset = nsPoint(mPosX, mPosY)
302
0
    - mDimBounds.TopLeft() + viewBounds.TopLeft() - roundedOffset;
303
0
304
0
  return newBounds;
305
0
}
306
307
void nsView::DoResetWidgetBounds(bool aMoveOnly,
308
0
                                 bool aInvalidateChangedSize) {
309
0
  // The geometry of a root view's widget is controlled externally,
310
0
  // NOT by sizing or positioning the view
311
0
  if (mViewManager->GetRootView() == this) {
312
0
    return;
313
0
  }
314
0
315
0
  MOZ_ASSERT(mWindow, "Why was this called??");
316
0
317
0
  // Hold this ref to make sure it stays alive.
318
0
  nsCOMPtr<nsIWidget> widget = mWindow;
319
0
320
0
  // Stash a copy of these and use them so we can handle this being deleted (say
321
0
  // from sync painting/flushing from Show/Move/Resize on the widget).
322
0
  LayoutDeviceIntRect newBounds;
323
0
  RefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
324
0
325
0
  nsWindowType type = widget->WindowType();
326
0
327
0
  LayoutDeviceIntRect curBounds = widget->GetClientBounds();
328
0
  bool invisiblePopup = type == eWindowType_popup &&
329
0
                        ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) ||
330
0
                         mVis == nsViewVisibility_kHide);
331
0
332
0
  if (invisiblePopup) {
333
0
    // We're going to hit the early exit below, avoid calling CalcWidgetBounds.
334
0
  } else {
335
0
    newBounds = CalcWidgetBounds(type);
336
0
    invisiblePopup = newBounds.IsEmpty();
337
0
  }
338
0
339
0
  bool curVisibility = widget->IsVisible();
340
0
  bool newVisibility = IsEffectivelyVisible();
341
0
  if (curVisibility && !newVisibility) {
342
0
    widget->Show(false);
343
0
  }
344
0
345
0
  if (invisiblePopup) {
346
0
    // Don't manipulate empty or hidden popup widgets. For example there's no
347
0
    // point moving hidden comboboxes around, or doing X server roundtrips
348
0
    // to compute their true screen position. This could mean that WidgetToScreen
349
0
    // operations on these widgets don't return up-to-date values, but popup
350
0
    // positions aren't reliable anyway because of correction to be on or off-screen.
351
0
    return;
352
0
  }
353
0
354
0
  bool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
355
0
  bool changedSize = curBounds.Size() != newBounds.Size();
356
0
357
0
  // Child views are never attached to top level widgets, this is safe.
358
0
359
0
  // Coordinates are converted to desktop pixels for window Move/Resize APIs,
360
0
  // because of the potential for device-pixel coordinate spaces for mixed
361
0
  // hidpi/lodpi screens to overlap each other and result in bad placement
362
0
  // (bug 814434).
363
0
  DesktopToLayoutDeviceScale scale = dx->GetDesktopToDeviceScale();
364
0
365
0
  DesktopRect deskRect = newBounds / scale;
366
0
  if (changedPos) {
367
0
    if (changedSize && !aMoveOnly) {
368
0
      widget->ResizeClient(deskRect.X(), deskRect.Y(),
369
0
                           deskRect.Width(), deskRect.Height(),
370
0
                           aInvalidateChangedSize);
371
0
    } else {
372
0
      widget->MoveClient(deskRect.X(), deskRect.Y());
373
0
    }
374
0
  } else {
375
0
    if (changedSize && !aMoveOnly) {
376
0
      widget->ResizeClient(deskRect.Width(), deskRect.Height(),
377
0
                           aInvalidateChangedSize);
378
0
    } // else do nothing!
379
0
  }
380
0
381
0
  if (!curVisibility && newVisibility) {
382
0
    widget->Show(true);
383
0
  }
384
0
}
385
386
void nsView::SetDimensions(const nsRect& aRect, bool aPaint, bool aResizeWidget)
387
0
{
388
0
  nsRect dims = aRect;
389
0
  dims.MoveBy(mPosX, mPosY);
390
0
391
0
  // Don't use nsRect's operator== here, since it returns true when
392
0
  // both rects are empty even if they have different widths and we
393
0
  // have cases where that sort of thing matters to us.
394
0
  if (mDimBounds.TopLeft() == dims.TopLeft() &&
395
0
      mDimBounds.Size() == dims.Size()) {
396
0
    return;
397
0
  }
398
0
399
0
  mDimBounds = dims;
400
0
401
0
  if (aResizeWidget) {
402
0
    ResetWidgetBounds(false, false);
403
0
  }
404
0
}
405
406
void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible)
407
0
{
408
0
  if (!aEffectivelyVisible)
409
0
  {
410
0
    DropMouseGrabbing();
411
0
  }
412
0
413
0
  SetForcedRepaint(true);
414
0
415
0
  if (nullptr != mWindow)
416
0
  {
417
0
    ResetWidgetBounds(false, false);
418
0
  }
419
0
420
0
  for (nsView* child = mFirstChild; child; child = child->mNextSibling) {
421
0
    if (child->mVis == nsViewVisibility_kHide) {
422
0
      // It was effectively hidden and still is
423
0
      continue;
424
0
    }
425
0
    // Our child is visible if we are
426
0
    child->NotifyEffectiveVisibilityChanged(aEffectivelyVisible);
427
0
  }
428
0
}
429
430
void nsView::SetVisibility(nsViewVisibility aVisibility)
431
0
{
432
0
  mVis = aVisibility;
433
0
  NotifyEffectiveVisibilityChanged(IsEffectivelyVisible());
434
0
}
435
436
void nsView::SetFloating(bool aFloatingView)
437
0
{
438
0
  if (aFloatingView)
439
0
    mVFlags |= NS_VIEW_FLAG_FLOATING;
440
0
  else
441
0
    mVFlags &= ~NS_VIEW_FLAG_FLOATING;
442
0
}
443
444
void nsView::InvalidateHierarchy()
445
0
{
446
0
  if (mViewManager->GetRootView() == this)
447
0
    mViewManager->InvalidateHierarchy();
448
0
449
0
  for (nsView *child = mFirstChild; child; child = child->GetNextSibling())
450
0
    child->InvalidateHierarchy();
451
0
}
452
453
void nsView::InsertChild(nsView *aChild, nsView *aSibling)
454
0
{
455
0
  MOZ_ASSERT(nullptr != aChild, "null ptr");
456
0
457
0
  if (nullptr != aChild)
458
0
  {
459
0
    if (nullptr != aSibling)
460
0
    {
461
#ifdef DEBUG
462
      NS_ASSERTION(aSibling->GetParent() == this, "tried to insert view with invalid sibling");
463
#endif
464
      //insert after sibling
465
0
      aChild->SetNextSibling(aSibling->GetNextSibling());
466
0
      aSibling->SetNextSibling(aChild);
467
0
    }
468
0
    else
469
0
    {
470
0
      aChild->SetNextSibling(mFirstChild);
471
0
      mFirstChild = aChild;
472
0
    }
473
0
    aChild->SetParent(this);
474
0
475
0
    // If we just inserted a root view, then update the RootViewManager
476
0
    // on all view managers in the new subtree.
477
0
478
0
    nsViewManager *vm = aChild->GetViewManager();
479
0
    if (vm->GetRootView() == aChild)
480
0
    {
481
0
      aChild->InvalidateHierarchy();
482
0
    }
483
0
  }
484
0
}
485
486
void nsView::RemoveChild(nsView *child)
487
0
{
488
0
  MOZ_ASSERT(nullptr != child, "null ptr");
489
0
490
0
  if (nullptr != child)
491
0
  {
492
0
    nsView* prevKid = nullptr;
493
0
    nsView* kid = mFirstChild;
494
0
    DebugOnly<bool> found = false;
495
0
    while (nullptr != kid) {
496
0
      if (kid == child) {
497
0
        if (nullptr != prevKid) {
498
0
          prevKid->SetNextSibling(kid->GetNextSibling());
499
0
        } else {
500
0
          mFirstChild = kid->GetNextSibling();
501
0
        }
502
0
        child->SetParent(nullptr);
503
0
        found = true;
504
0
        break;
505
0
      }
506
0
      prevKid = kid;
507
0
      kid = kid->GetNextSibling();
508
0
    }
509
0
    NS_ASSERTION(found, "tried to remove non child");
510
0
511
0
    // If we just removed a root view, then update the RootViewManager
512
0
    // on all view managers in the removed subtree.
513
0
514
0
    nsViewManager *vm = child->GetViewManager();
515
0
    if (vm->GetRootView() == child)
516
0
    {
517
0
      child->InvalidateHierarchy();
518
0
    }
519
0
  }
520
0
}
521
522
// Native widgets ultimately just can't deal with the awesome power of
523
// CSS2 z-index. However, we set the z-index on the widget anyway
524
// because in many simple common cases the widgets do end up in the
525
// right order. We set each widget's z-index to the z-index of the
526
// nearest ancestor that has non-auto z-index.
527
static void UpdateNativeWidgetZIndexes(nsView* aView, int32_t aZIndex)
528
0
{
529
0
  if (aView->HasWidget()) {
530
0
    nsIWidget* widget = aView->GetWidget();
531
0
    if (widget->GetZIndex() != aZIndex) {
532
0
      widget->SetZIndex(aZIndex);
533
0
    }
534
0
  } else {
535
0
    for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
536
0
      if (v->GetZIndexIsAuto()) {
537
0
        UpdateNativeWidgetZIndexes(v, aZIndex);
538
0
      }
539
0
    }
540
0
  }
541
0
}
542
543
static int32_t FindNonAutoZIndex(nsView* aView)
544
0
{
545
0
  while (aView) {
546
0
    if (!aView->GetZIndexIsAuto()) {
547
0
      return aView->GetZIndex();
548
0
    }
549
0
    aView = aView->GetParent();
550
0
  }
551
0
  return 0;
552
0
}
553
554
struct DefaultWidgetInitData : public nsWidgetInitData {
555
  DefaultWidgetInitData() : nsWidgetInitData()
556
0
  {
557
0
    mWindowType = eWindowType_child;
558
0
    clipChildren = true;
559
0
    clipSiblings = true;
560
0
  }
561
};
562
563
nsresult nsView::CreateWidget(nsWidgetInitData *aWidgetInitData,
564
                               bool aEnableDragDrop,
565
                               bool aResetVisibility)
566
0
{
567
0
  AssertNoWindow();
568
0
  MOZ_ASSERT(!aWidgetInitData ||
569
0
             aWidgetInitData->mWindowType != eWindowType_popup,
570
0
             "Use CreateWidgetForPopup");
571
0
572
0
  DefaultWidgetInitData defaultInitData;
573
0
  bool initDataPassedIn = !!aWidgetInitData;
574
0
  aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
575
0
  defaultInitData.mListenForResizes =
576
0
    (!initDataPassedIn && GetParent() &&
577
0
     GetParent()->GetViewManager() != mViewManager);
578
0
579
0
  LayoutDeviceIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
580
0
581
0
  nsIWidget* parentWidget =
582
0
    GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr;
583
0
  if (!parentWidget) {
584
0
    NS_ERROR("nsView::CreateWidget without suitable parent widget??");
585
0
    return NS_ERROR_FAILURE;
586
0
  }
587
0
588
0
  // XXX: using aForceUseIWidgetParent=true to preserve previous
589
0
  // semantics.  It's not clear that it's actually needed.
590
0
  mWindow = parentWidget->CreateChild(trect, aWidgetInitData, true);
591
0
  if (!mWindow) {
592
0
    return NS_ERROR_FAILURE;
593
0
  }
594
0
595
0
  InitializeWindow(aEnableDragDrop, aResetVisibility);
596
0
597
0
  return NS_OK;
598
0
}
599
600
nsresult nsView::CreateWidgetForParent(nsIWidget* aParentWidget,
601
                                        nsWidgetInitData *aWidgetInitData,
602
                                        bool aEnableDragDrop,
603
                                        bool aResetVisibility)
604
0
{
605
0
  AssertNoWindow();
606
0
  MOZ_ASSERT(!aWidgetInitData ||
607
0
             aWidgetInitData->mWindowType != eWindowType_popup,
608
0
             "Use CreateWidgetForPopup");
609
0
  MOZ_ASSERT(aParentWidget, "Parent widget required");
610
0
611
0
  DefaultWidgetInitData defaultInitData;
612
0
  aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData;
613
0
614
0
  LayoutDeviceIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
615
0
616
0
  mWindow = aParentWidget->CreateChild(trect, aWidgetInitData);
617
0
  if (!mWindow) {
618
0
    return NS_ERROR_FAILURE;
619
0
  }
620
0
621
0
  InitializeWindow(aEnableDragDrop, aResetVisibility);
622
0
623
0
  return NS_OK;
624
0
}
625
626
nsresult nsView::CreateWidgetForPopup(nsWidgetInitData *aWidgetInitData,
627
                                       nsIWidget* aParentWidget,
628
                                       bool aEnableDragDrop,
629
                                       bool aResetVisibility)
630
0
{
631
0
  AssertNoWindow();
632
0
  MOZ_ASSERT(aWidgetInitData, "Widget init data required");
633
0
  MOZ_ASSERT(aWidgetInitData->mWindowType == eWindowType_popup,
634
0
             "Use one of the other CreateWidget methods");
635
0
636
0
  LayoutDeviceIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType);
637
0
638
0
  // XXX/cjones: having these two separate creation cases seems ... um
639
0
  // ... unnecessary, but it's the way the old code did it.  Please
640
0
  // unify them by first finding a suitable parent nsIWidget, then
641
0
  // getting rid of aForceUseIWidgetParent.
642
0
  if (aParentWidget) {
643
0
    // XXX: using aForceUseIWidgetParent=true to preserve previous
644
0
    // semantics.  It's not clear that it's actually needed.
645
0
    mWindow = aParentWidget->CreateChild(trect, aWidgetInitData, true);
646
0
  }
647
0
  else {
648
0
    nsIWidget* nearestParent = GetParent() ? GetParent()->GetNearestWidget(nullptr)
649
0
                                           : nullptr;
650
0
    if (!nearestParent) {
651
0
      // Without a parent, we can't make a popup.  This can happen
652
0
      // when printing
653
0
      return NS_ERROR_FAILURE;
654
0
    }
655
0
656
0
    mWindow = nearestParent->CreateChild(trect, aWidgetInitData);
657
0
  }
658
0
  if (!mWindow) {
659
0
    return NS_ERROR_FAILURE;
660
0
  }
661
0
662
0
  InitializeWindow(aEnableDragDrop, aResetVisibility);
663
0
664
0
  return NS_OK;
665
0
}
666
667
void
668
nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility)
669
0
{
670
0
  MOZ_ASSERT(mWindow, "Must have a window to initialize");
671
0
672
0
  mWindow->SetWidgetListener(this);
673
0
674
0
  if (aEnableDragDrop) {
675
0
    mWindow->EnableDragDrop(true);
676
0
  }
677
0
678
0
  // propagate the z-index to the widget.
679
0
  UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
680
0
681
0
  //make sure visibility state is accurate
682
0
683
0
  if (aResetVisibility) {
684
0
    SetVisibility(GetVisibility());
685
0
  }
686
0
}
687
688
void
689
nsView::SetNeedsWindowPropertiesSync()
690
0
{
691
0
  mNeedsWindowPropertiesSync = true;
692
0
  if (mViewManager) {
693
0
    mViewManager->PostPendingUpdate();
694
0
  }
695
0
}
696
697
698
// Attach to a top level widget and start receiving mirrored events.
699
nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget)
700
0
{
701
0
  MOZ_ASSERT(nullptr != aWidget, "null widget ptr");
702
0
703
0
  /// XXXjimm This is a temporary workaround to an issue w/document
704
0
  // viewer (bug 513162).
705
0
  nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener();
706
0
  if (listener) {
707
0
    nsView *oldView = listener->GetView();
708
0
    if (oldView) {
709
0
      oldView->DetachFromTopLevelWidget();
710
0
    }
711
0
  }
712
0
713
0
  // Note, the previous device context will be released. Detaching
714
0
  // will not restore the old one.
715
0
  aWidget->AttachViewToTopLevel(!nsIWidget::UsePuppetWidgets());
716
0
717
0
  mWindow = aWidget;
718
0
719
0
  mWindow->SetAttachedWidgetListener(this);
720
0
  if (mWindow->WindowType() != eWindowType_invisible) {
721
0
    nsresult rv = mWindow->AsyncEnableDragDrop(true);
722
0
    NS_ENSURE_SUCCESS(rv, rv);
723
0
  }
724
0
  mWidgetIsTopLevel = true;
725
0
726
0
  // Refresh the view bounds
727
0
  CalcWidgetBounds(mWindow->WindowType());
728
0
729
0
  return NS_OK;
730
0
}
731
732
// Detach this view from an attached widget.
733
nsresult nsView::DetachFromTopLevelWidget()
734
0
{
735
0
  MOZ_ASSERT(mWidgetIsTopLevel, "Not attached currently!");
736
0
  MOZ_ASSERT(mWindow, "null mWindow for DetachFromTopLevelWidget!");
737
0
738
0
  mWindow->SetAttachedWidgetListener(nullptr);
739
0
  nsIWidgetListener* listener = mWindow->GetPreviouslyAttachedWidgetListener();
740
0
741
0
  if (listener && listener->GetView()) {
742
0
    // Ensure the listener doesn't think it's being used anymore
743
0
    listener->GetView()->SetPreviousWidget(nullptr);
744
0
  }
745
0
746
0
  // If the new view's frame is paint suppressed then the window
747
0
  // will want to use us instead until that's done
748
0
  mWindow->SetPreviouslyAttachedWidgetListener(this);
749
0
750
0
  mPreviousWindow = mWindow;
751
0
  mWindow = nullptr;
752
0
753
0
  mWidgetIsTopLevel = false;
754
0
755
0
  return NS_OK;
756
0
}
757
758
void nsView::SetZIndex(bool aAuto, int32_t aZIndex)
759
0
{
760
0
  bool oldIsAuto = GetZIndexIsAuto();
761
0
  mVFlags = (mVFlags & ~NS_VIEW_FLAG_AUTO_ZINDEX) | (aAuto ? NS_VIEW_FLAG_AUTO_ZINDEX : 0);
762
0
  mZIndex = aZIndex;
763
0
764
0
  if (HasWidget() || !oldIsAuto || !aAuto) {
765
0
    UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this));
766
0
  }
767
0
}
768
769
void nsView::AssertNoWindow()
770
0
{
771
0
  // XXX: it would be nice to make this a strong assert
772
0
  if (MOZ_UNLIKELY(mWindow)) {
773
0
    NS_ERROR("We already have a window for this view? BAD");
774
0
    mWindow->SetWidgetListener(nullptr);
775
0
    mWindow->Destroy();
776
0
    mWindow = nullptr;
777
0
  }
778
0
}
779
780
//
781
// internal window creation functions
782
//
783
void nsView::AttachWidgetEventHandler(nsIWidget* aWidget)
784
0
{
785
#ifdef DEBUG
786
  NS_ASSERTION(!aWidget->GetWidgetListener(), "Already have a widget listener");
787
#endif
788
789
0
  aWidget->SetWidgetListener(this);
790
0
}
791
792
void nsView::DetachWidgetEventHandler(nsIWidget* aWidget)
793
0
{
794
0
  NS_ASSERTION(!aWidget->GetWidgetListener() ||
795
0
               aWidget->GetWidgetListener()->GetView() == this, "Wrong view");
796
0
  aWidget->SetWidgetListener(nullptr);
797
0
}
798
799
#ifdef DEBUG
800
void nsView::List(FILE* out, int32_t aIndent) const
801
{
802
  int32_t i;
803
  for (i = aIndent; --i >= 0; ) fputs("  ", out);
804
  fprintf(out, "%p ", (void*)this);
805
  if (nullptr != mWindow) {
806
    nscoord p2a = mViewManager->AppUnitsPerDevPixel();
807
    LayoutDeviceIntRect rect = mWindow->GetClientBounds();
808
    nsRect windowBounds = LayoutDeviceIntRect::ToAppUnits(rect, p2a);
809
    rect = mWindow->GetBounds();
810
    nsRect nonclientBounds = LayoutDeviceIntRect::ToAppUnits(rect, p2a);
811
    nsrefcnt widgetRefCnt = mWindow.get()->AddRef() - 1;
812
    mWindow.get()->Release();
813
    int32_t Z = mWindow->GetZIndex();
814
    fprintf(out, "(widget=%p[%" PRIuPTR "] z=%d pos={%d,%d,%d,%d}) ",
815
            (void*)mWindow, widgetRefCnt, Z,
816
            nonclientBounds.X(), nonclientBounds.Y(),
817
            windowBounds.Width(), windowBounds.Height());
818
  }
819
  nsRect brect = GetBounds();
820
  fprintf(out, "{%d,%d,%d,%d}",
821
          brect.X(), brect.Y(), brect.Width(), brect.Height());
822
  fprintf(out, " z=%d vis=%d frame=%p <\n",
823
          mZIndex, mVis, static_cast<void*>(mFrame));
824
  for (nsView* kid = mFirstChild; kid; kid = kid->GetNextSibling()) {
825
    NS_ASSERTION(kid->GetParent() == this, "incorrect parent");
826
    kid->List(out, aIndent + 1);
827
  }
828
  for (i = aIndent; --i >= 0; ) fputs("  ", out);
829
  fputs(">\n", out);
830
}
831
#endif // DEBUG
832
833
nsPoint nsView::GetOffsetTo(const nsView* aOther) const
834
0
{
835
0
  return GetOffsetTo(aOther, GetViewManager()->AppUnitsPerDevPixel());
836
0
}
837
838
nsPoint nsView::GetOffsetTo(const nsView* aOther, const int32_t aAPD) const
839
0
{
840
0
  MOZ_ASSERT(GetParent() || !aOther || aOther->GetParent() || this == aOther,
841
0
             "caller of (outer) GetOffsetTo must not pass unrelated views");
842
0
  // We accumulate the final result in offset
843
0
  nsPoint offset(0, 0);
844
0
  // The offset currently accumulated at the current APD
845
0
  nsPoint docOffset(0, 0);
846
0
  const nsView* v = this;
847
0
  nsViewManager* currVM = v->GetViewManager();
848
0
  int32_t currAPD = currVM->AppUnitsPerDevPixel();
849
0
  const nsView* root = nullptr;
850
0
  for ( ; v != aOther && v; root = v, v = v->GetParent()) {
851
0
    nsViewManager* newVM = v->GetViewManager();
852
0
    if (newVM != currVM) {
853
0
      int32_t newAPD = newVM->AppUnitsPerDevPixel();
854
0
      if (newAPD != currAPD) {
855
0
        offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
856
0
        docOffset.x = docOffset.y = 0;
857
0
        currAPD = newAPD;
858
0
      }
859
0
      currVM = newVM;
860
0
    }
861
0
    docOffset += v->GetPosition();
862
0
  }
863
0
  offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
864
0
865
0
  if (v != aOther) {
866
0
    // Looks like aOther wasn't an ancestor of |this|.  So now we have
867
0
    // the root-VM-relative position of |this| in |offset|.  Get the
868
0
    // root-VM-relative position of aOther and subtract it.
869
0
    nsPoint negOffset = aOther->GetOffsetTo(root, aAPD);
870
0
    offset -= negOffset;
871
0
  }
872
0
873
0
  return offset;
874
0
}
875
876
nsPoint nsView::GetOffsetToWidget(nsIWidget* aWidget) const
877
0
{
878
0
  nsPoint pt;
879
0
  // Get the view for widget
880
0
  nsView* widgetView = GetViewFor(aWidget);
881
0
  if (!widgetView) {
882
0
    return pt;
883
0
  }
884
0
885
0
  // Get the offset to the widget view in the widget view's APD
886
0
  // We get the offset in the widget view's APD first and then convert to our
887
0
  // APD afterwards so that we can include the widget view's ViewToWidgetOffset
888
0
  // in the sum in its native APD, and then convert the whole thing to our APD
889
0
  // so that we don't have to convert the APD of the relatively small
890
0
  // ViewToWidgetOffset by itself with a potentially large relative rounding
891
0
  // error.
892
0
  pt = -widgetView->GetOffsetTo(this);
893
0
  // Add in the offset to the widget.
894
0
  pt += widgetView->ViewToWidgetOffset();
895
0
896
0
  // Convert to our appunits.
897
0
  int32_t widgetAPD = widgetView->GetViewManager()->AppUnitsPerDevPixel();
898
0
  int32_t ourAPD = GetViewManager()->AppUnitsPerDevPixel();
899
0
  pt = pt.ScaleToOtherAppUnits(widgetAPD, ourAPD);
900
0
  return pt;
901
0
}
902
903
nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset) const
904
0
{
905
0
  return GetNearestWidget(aOffset, GetViewManager()->AppUnitsPerDevPixel());
906
0
}
907
908
nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset, const int32_t aAPD) const
909
0
{
910
0
  // aOffset is based on the view's position, which ignores any chrome on
911
0
  // attached parent widgets.
912
0
913
0
  // We accumulate the final result in pt
914
0
  nsPoint pt(0, 0);
915
0
  // The offset currently accumulated at the current APD
916
0
  nsPoint docPt(0,0);
917
0
  const nsView* v = this;
918
0
  nsViewManager* currVM = v->GetViewManager();
919
0
  int32_t currAPD = currVM->AppUnitsPerDevPixel();
920
0
  for ( ; v && !v->HasWidget(); v = v->GetParent()) {
921
0
    nsViewManager* newVM = v->GetViewManager();
922
0
    if (newVM != currVM) {
923
0
      int32_t newAPD = newVM->AppUnitsPerDevPixel();
924
0
      if (newAPD != currAPD) {
925
0
        pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
926
0
        docPt.x = docPt.y = 0;
927
0
        currAPD = newAPD;
928
0
      }
929
0
      currVM = newVM;
930
0
    }
931
0
    docPt += v->GetPosition();
932
0
  }
933
0
  if (!v) {
934
0
    if (aOffset) {
935
0
      pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
936
0
      *aOffset = pt;
937
0
    }
938
0
    return nullptr;
939
0
  }
940
0
941
0
  // pt is now the offset from v's origin to this view's origin.
942
0
  // We add the ViewToWidgetOffset to get the offset to the widget.
943
0
  if (aOffset) {
944
0
    docPt += v->ViewToWidgetOffset();
945
0
    pt += docPt.ScaleToOtherAppUnits(currAPD, aAPD);
946
0
    *aOffset = pt;
947
0
  }
948
0
  return v->GetWidget();
949
0
}
950
951
bool nsView::IsRoot() const
952
0
{
953
0
  NS_ASSERTION(mViewManager != nullptr," View manager is null in nsView::IsRoot()");
954
0
  return mViewManager->GetRootView() == this;
955
0
}
956
957
nsRect
958
nsView::GetBoundsInParentUnits() const
959
0
{
960
0
  nsView* parent = GetParent();
961
0
  nsViewManager* VM = GetViewManager();
962
0
  if (this != VM->GetRootView() || !parent) {
963
0
    return mDimBounds;
964
0
  }
965
0
  int32_t ourAPD = VM->AppUnitsPerDevPixel();
966
0
  int32_t parentAPD = parent->GetViewManager()->AppUnitsPerDevPixel();
967
0
  return mDimBounds.ScaleToOtherAppUnitsRoundOut(ourAPD, parentAPD);
968
0
}
969
970
nsPoint
971
nsView::ConvertFromParentCoords(nsPoint aPt) const
972
0
{
973
0
  const nsView* parent = GetParent();
974
0
  if (parent) {
975
0
    aPt = aPt.ScaleToOtherAppUnits(
976
0
      parent->GetViewManager()->AppUnitsPerDevPixel(),
977
0
      GetViewManager()->AppUnitsPerDevPixel());
978
0
  }
979
0
  aPt -= GetPosition();
980
0
  return aPt;
981
0
}
982
983
static bool
984
IsPopupWidget(nsIWidget* aWidget)
985
0
{
986
0
  return (aWidget->WindowType() == eWindowType_popup);
987
0
}
988
989
nsIPresShell*
990
nsView::GetPresShell()
991
0
{
992
0
  return GetViewManager()->GetPresShell();
993
0
}
994
995
bool
996
nsView::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y)
997
0
{
998
0
  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
999
0
  if (pm && IsPopupWidget(aWidget)) {
1000
0
    pm->PopupMoved(mFrame, nsIntPoint(x, y));
1001
0
    return true;
1002
0
  }
1003
0
1004
0
  return false;
1005
0
}
1006
1007
bool
1008
nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight)
1009
0
{
1010
0
  // The root view may not be set if this is the resize associated with
1011
0
  // window creation
1012
0
  SetForcedRepaint(true);
1013
0
  if (this == mViewManager->GetRootView()) {
1014
0
    RefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext();
1015
0
    // ensure DPI is up-to-date, in case of window being opened and sized
1016
0
    // on a non-default-dpi display (bug 829963)
1017
0
    devContext->CheckDPIChange();
1018
0
    int32_t p2a = devContext->AppUnitsPerDevPixel();
1019
0
    mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(aWidth, p2a),
1020
0
                                      NSIntPixelsToAppUnits(aHeight, p2a));
1021
0
1022
0
    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1023
0
    if (pm) {
1024
0
      nsIPresShell* presShell = mViewManager->GetPresShell();
1025
0
      if (presShell && presShell->GetDocument()) {
1026
0
        pm->AdjustPopupsOnWindowChange(presShell);
1027
0
      }
1028
0
    }
1029
0
1030
0
    return true;
1031
0
  }
1032
0
  if (IsPopupWidget(aWidget)) {
1033
0
    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1034
0
    if (pm) {
1035
0
      pm->PopupResized(mFrame, LayoutDeviceIntSize(aWidth, aHeight));
1036
0
      return true;
1037
0
    }
1038
0
  }
1039
0
1040
0
  return false;
1041
0
}
1042
1043
bool
1044
nsView::RequestWindowClose(nsIWidget* aWidget)
1045
0
{
1046
0
  if (mFrame && IsPopupWidget(aWidget) && mFrame->IsMenuPopupFrame()) {
1047
0
    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1048
0
    if (pm) {
1049
0
      pm->HidePopup(mFrame->GetContent(), false, true, false, false);
1050
0
      return true;
1051
0
    }
1052
0
  }
1053
0
1054
0
  return false;
1055
0
}
1056
1057
void
1058
nsView::WillPaintWindow(nsIWidget* aWidget)
1059
0
{
1060
0
  RefPtr<nsViewManager> vm = mViewManager;
1061
0
  vm->WillPaintWindow(aWidget);
1062
0
}
1063
1064
bool
1065
nsView::PaintWindow(nsIWidget* aWidget, LayoutDeviceIntRegion aRegion)
1066
0
{
1067
0
  NS_ASSERTION(this == nsView::GetViewFor(aWidget), "wrong view for widget?");
1068
0
1069
0
  RefPtr<nsViewManager> vm = mViewManager;
1070
0
  bool result = vm->PaintWindow(aWidget, aRegion);
1071
0
  return result;
1072
0
}
1073
1074
void
1075
nsView::DidPaintWindow()
1076
0
{
1077
0
  RefPtr<nsViewManager> vm = mViewManager;
1078
0
  vm->DidPaintWindow();
1079
0
}
1080
1081
void
1082
nsView::DidCompositeWindow(mozilla::layers::TransactionId aTransactionId,
1083
                           const TimeStamp& aCompositeStart,
1084
                           const TimeStamp& aCompositeEnd)
1085
0
{
1086
0
  nsIPresShell* presShell = mViewManager->GetPresShell();
1087
0
  if (presShell) {
1088
0
    nsAutoScriptBlocker scriptBlocker;
1089
0
1090
0
    nsPresContext* context = presShell->GetPresContext();
1091
0
    nsRootPresContext* rootContext = context->GetRootPresContext();
1092
0
    if (rootContext) {
1093
0
      rootContext->NotifyDidPaintForSubtree(aTransactionId, aCompositeEnd);
1094
0
    }
1095
0
1096
0
    // If the two timestamps are identical, this was likely a fake composite
1097
0
    // event which wouldn't be terribly useful to display.
1098
0
    if (aCompositeStart == aCompositeEnd) {
1099
0
      return;
1100
0
    }
1101
0
1102
0
    nsIDocShell* docShell = context->GetDocShell();
1103
0
    RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1104
0
1105
0
    if (timelines && timelines->HasConsumer(docShell)) {
1106
0
      timelines->AddMarkerForDocShell(docShell,
1107
0
        MakeUnique<CompositeTimelineMarker>(aCompositeStart, MarkerTracingType::START));
1108
0
      timelines->AddMarkerForDocShell(docShell,
1109
0
        MakeUnique<CompositeTimelineMarker>(aCompositeEnd, MarkerTracingType::END));
1110
0
    }
1111
0
  }
1112
0
}
1113
1114
void
1115
nsView::RequestRepaint()
1116
0
{
1117
0
  nsIPresShell* presShell = mViewManager->GetPresShell();
1118
0
  if (presShell) {
1119
0
    presShell->ScheduleViewManagerFlush();
1120
0
  }
1121
0
}
1122
1123
nsEventStatus
1124
nsView::HandleEvent(WidgetGUIEvent* aEvent,
1125
                    bool aUseAttachedEvents)
1126
0
{
1127
0
  MOZ_ASSERT(nullptr != aEvent->mWidget, "null widget ptr");
1128
0
1129
0
  nsEventStatus result = nsEventStatus_eIgnore;
1130
0
  nsView* view;
1131
0
  if (aUseAttachedEvents) {
1132
0
    nsIWidgetListener* listener = aEvent->mWidget->GetAttachedWidgetListener();
1133
0
    view = listener ? listener->GetView() : nullptr;
1134
0
  }
1135
0
  else {
1136
0
    view = GetViewFor(aEvent->mWidget);
1137
0
  }
1138
0
1139
0
  if (view) {
1140
0
    RefPtr<nsViewManager> vm = view->GetViewManager();
1141
0
    vm->DispatchEvent(aEvent, view, &result);
1142
0
  }
1143
0
1144
0
  return result;
1145
0
}
1146
1147
bool
1148
nsView::IsPrimaryFramePaintSuppressed()
1149
0
{
1150
0
  return sShowPreviousPage && mFrame && mFrame->PresShell()->IsPaintingSuppressed();
1151
0
}