Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/view/nsViewManager.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 "nsAutoPtr.h"
7
#include "nsViewManager.h"
8
#include "nsGfxCIID.h"
9
#include "nsView.h"
10
#include "nsCOMPtr.h"
11
#include "mozilla/MouseEvents.h"
12
#include "nsRegion.h"
13
#include "nsCOMArray.h"
14
#include "nsIPluginWidget.h"
15
#include "nsXULPopupManager.h"
16
#include "nsIPresShell.h"
17
#include "nsIPresShellInlines.h"
18
#include "nsPresContext.h"
19
#include "mozilla/StartupTimeline.h"
20
#include "GeckoProfiler.h"
21
#include "nsRefreshDriver.h"
22
#include "mozilla/Preferences.h"
23
#include "nsContentUtils.h" // for nsAutoScriptBlocker
24
#include "nsLayoutUtils.h"
25
#include "Layers.h"
26
#include "gfxPlatform.h"
27
#include "gfxPrefs.h"
28
#include "nsIDocument.h"
29
30
/**
31
   XXX TODO XXX
32
33
   DeCOMify newly private methods
34
   Optimize view storage
35
*/
36
37
/**
38
   A note about platform assumptions:
39
40
   We assume that a widget is z-ordered on top of its parent.
41
42
   We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
43
   we ask for a specific z-order, we don't assume that widget z-ordering actually works.
44
*/
45
46
using namespace mozilla;
47
using namespace mozilla::layers;
48
49
0
#define NSCOORD_NONE      INT32_MIN
50
51
#undef DEBUG_MOUSE_LOCATION
52
53
// Weakly held references to all of the view managers
54
nsTArray<nsViewManager*>* nsViewManager::gViewManagers = nullptr;
55
uint32_t nsViewManager::gLastUserEventTime = 0;
56
57
nsViewManager::nsViewManager()
58
  : mPresShell(nullptr)
59
  , mDelayedResize(NSCOORD_NONE, NSCOORD_NONE)
60
  , mRootView(nullptr)
61
  , mRootViewManager(this)
62
  , mRefreshDisableCount(0)
63
  , mPainting(false)
64
  , mRecursiveRefreshPending(false)
65
  , mHasPendingWidgetGeometryChanges(false)
66
0
{
67
0
  if (gViewManagers == nullptr) {
68
0
    // Create an array to hold a list of view managers
69
0
    gViewManagers = new nsTArray<nsViewManager*>;
70
0
  }
71
0
72
0
  gViewManagers->AppendElement(this);
73
0
}
74
75
nsViewManager::~nsViewManager()
76
0
{
77
0
  if (mRootView) {
78
0
    // Destroy any remaining views
79
0
    mRootView->Destroy();
80
0
    mRootView = nullptr;
81
0
  }
82
0
83
0
  if (!IsRootVM()) {
84
0
    // We have a strong ref to mRootViewManager
85
0
    NS_RELEASE(mRootViewManager);
86
0
  }
87
0
88
0
  NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
89
0
90
#ifdef DEBUG
91
  bool removed =
92
#endif
93
    gViewManagers->RemoveElement(this);
94
0
  NS_ASSERTION(removed, "Viewmanager instance was not in the global list of viewmanagers");
95
0
96
0
  if (gViewManagers->IsEmpty()) {
97
0
    // There aren't any more view managers so
98
0
    // release the global array of view managers
99
0
    delete gViewManagers;
100
0
    gViewManagers = nullptr;
101
0
  }
102
0
103
0
  MOZ_RELEASE_ASSERT(!mPresShell, "Releasing nsViewManager without having called Destroy on the PresShell!");
104
0
}
105
106
// We don't hold a reference to the presentation context because it
107
// holds a reference to us.
108
nsresult
109
nsViewManager::Init(nsDeviceContext* aContext)
110
0
{
111
0
  MOZ_ASSERT(nullptr != aContext, "null ptr");
112
0
113
0
  if (nullptr == aContext) {
114
0
    return NS_ERROR_NULL_POINTER;
115
0
  }
116
0
  if (nullptr != mContext) {
117
0
    return NS_ERROR_ALREADY_INITIALIZED;
118
0
  }
119
0
  mContext = aContext;
120
0
121
0
  return NS_OK;
122
0
}
123
124
nsView*
125
nsViewManager::CreateView(const nsRect& aBounds,
126
                          nsView* aParent,
127
                          nsViewVisibility aVisibilityFlag)
128
0
{
129
0
  auto *v = new nsView(this, aVisibilityFlag);
130
0
  v->SetParent(aParent);
131
0
  v->SetPosition(aBounds.X(), aBounds.Y());
132
0
  nsRect dim(0, 0, aBounds.Width(), aBounds.Height());
133
0
  v->SetDimensions(dim, false);
134
0
  return v;
135
0
}
136
137
void
138
nsViewManager::SetRootView(nsView *aView)
139
0
{
140
0
  MOZ_ASSERT(!aView || aView->GetViewManager() == this,
141
0
             "Unexpected viewmanager on root view");
142
0
143
0
  // Do NOT destroy the current root view. It's the caller's responsibility
144
0
  // to destroy it
145
0
  mRootView = aView;
146
0
147
0
  if (mRootView) {
148
0
    nsView* parent = mRootView->GetParent();
149
0
    if (parent) {
150
0
      // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
151
0
      // no need to set mRootViewManager ourselves here.
152
0
      parent->InsertChild(mRootView, nullptr);
153
0
    } else {
154
0
      InvalidateHierarchy();
155
0
    }
156
0
157
0
    mRootView->SetZIndex(false, 0);
158
0
  }
159
0
  // Else don't touch mRootViewManager
160
0
}
161
162
void
163
nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
164
0
{
165
0
  if (nullptr != mRootView) {
166
0
    if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
167
0
      nsRect dim = mRootView->GetDimensions();
168
0
      *aWidth = dim.Width();
169
0
      *aHeight = dim.Height();
170
0
    } else {
171
0
      *aWidth = mDelayedResize.width;
172
0
      *aHeight = mDelayedResize.height;
173
0
    }
174
0
  }
175
0
  else
176
0
    {
177
0
      *aWidth = 0;
178
0
      *aHeight = 0;
179
0
    }
180
0
}
181
182
void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
183
0
{
184
0
  nsRect oldDim = mRootView->GetDimensions();
185
0
  nsRect newDim(0, 0, aWidth, aHeight);
186
0
  // We care about resizes even when one dimension is already zero.
187
0
  if (!oldDim.IsEqualEdges(newDim)) {
188
0
    // Don't resize the widget. It is already being set elsewhere.
189
0
    mRootView->SetDimensions(newDim, true, false);
190
0
    if (mPresShell)
191
0
      mPresShell->ResizeReflow(aWidth, aHeight, oldDim.Width(), oldDim.Height());
192
0
  }
193
0
}
194
195
bool
196
nsViewManager::ShouldDelayResize() const
197
0
{
198
0
  MOZ_ASSERT(mRootView);
199
0
  if (!mRootView->IsEffectivelyVisible() ||
200
0
      !mPresShell || !mPresShell->IsVisible()) {
201
0
    return true;
202
0
  }
203
0
  if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
204
0
    if (rd->IsResizeSuppressed()) {
205
0
      return true;
206
0
    }
207
0
  }
208
0
  return false;
209
0
}
210
211
void
212
nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight, bool aDelayResize)
213
0
{
214
0
  if (mRootView) {
215
0
    if (!ShouldDelayResize() && !aDelayResize) {
216
0
      if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
217
0
          mDelayedResize != nsSize(aWidth, aHeight)) {
218
0
        // We have a delayed resize; that now obsolete size may already have
219
0
        // been flushed to the PresContext so we need to update the PresContext
220
0
        // with the new size because if the new size is exactly the same as the
221
0
        // root view's current size then DoSetWindowDimensions will not
222
0
        // request a resize reflow (which would correct it). See bug 617076.
223
0
        mDelayedResize = nsSize(aWidth, aHeight);
224
0
        FlushDelayedResize(false);
225
0
      }
226
0
      mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
227
0
      DoSetWindowDimensions(aWidth, aHeight);
228
0
    } else {
229
0
      mDelayedResize.SizeTo(aWidth, aHeight);
230
0
      if (mPresShell) {
231
0
        mPresShell->SetNeedStyleFlush();
232
0
        mPresShell->SetNeedLayoutFlush();
233
0
      }
234
0
    }
235
0
  }
236
0
}
237
238
void
239
nsViewManager::FlushDelayedResize(bool aDoReflow)
240
0
{
241
0
  if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
242
0
    if (aDoReflow) {
243
0
      DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height);
244
0
      mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
245
0
    } else if (mPresShell && !mPresShell->GetIsViewportOverridden()) {
246
0
      nsPresContext* presContext = mPresShell->GetPresContext();
247
0
      if (presContext) {
248
0
        presContext->SetVisibleArea(nsRect(nsPoint(0, 0), mDelayedResize));
249
0
      }
250
0
    }
251
0
  }
252
0
}
253
254
// Convert aIn from being relative to and in appunits of aFromView, to being
255
// relative to and in appunits of aToView.
256
static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
257
                                          nsView* aFromView,
258
                                          nsView* aToView)
259
0
{
260
0
  nsRegion out = aIn;
261
0
  out.MoveBy(aFromView->GetOffsetTo(aToView));
262
0
  out = out.ScaleToOtherAppUnitsRoundOut(
263
0
    aFromView->GetViewManager()->AppUnitsPerDevPixel(),
264
0
    aToView->GetViewManager()->AppUnitsPerDevPixel());
265
0
  return out;
266
0
}
267
268
nsView* nsViewManager::GetDisplayRootFor(nsView* aView)
269
0
{
270
0
  nsView *displayRoot = aView;
271
0
  for (;;) {
272
0
    nsView *displayParent = displayRoot->GetParent();
273
0
    if (!displayParent)
274
0
      return displayRoot;
275
0
276
0
    if (displayRoot->GetFloating() && !displayParent->GetFloating())
277
0
      return displayRoot;
278
0
279
0
    // If we have a combobox dropdown popup within a panel popup, both the view
280
0
    // for the dropdown popup and its parent will be floating, so we need to
281
0
    // distinguish this situation. We do this by looking for a widget. Any view
282
0
    // with a widget is a display root, except for plugins.
283
0
    nsIWidget* widget = displayRoot->GetWidget();
284
0
    if (widget && widget->WindowType() == eWindowType_popup) {
285
0
      NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
286
0
        "this should only happen with floating views that have floating parents");
287
0
      return displayRoot;
288
0
    }
289
0
290
0
    displayRoot = displayParent;
291
0
  }
292
0
}
293
294
/**
295
   aRegion is given in device coordinates!!
296
   aContext may be null, in which case layers should be used for
297
   rendering.
298
*/
299
void nsViewManager::Refresh(nsView* aView, const LayoutDeviceIntRegion& aRegion)
300
0
{
301
0
  NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
302
0
303
0
  if (mPresShell && mPresShell->IsNeverPainting()) {
304
0
    return;
305
0
  }
306
0
307
0
  // damageRegion is the damaged area, in twips, relative to the view origin
308
0
  nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
309
0
310
0
  // move region from widget coordinates into view coordinates
311
0
  damageRegion.MoveBy(-aView->ViewToWidgetOffset());
312
0
313
0
  if (damageRegion.IsEmpty()) {
314
#ifdef DEBUG_roc
315
    nsRect viewRect = aView->GetDimensions();
316
    nsRect damageRect = damageRegion.GetBounds();
317
    printf_stderr("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
318
           damageRect.x, damageRect.y, damageRect.width, damageRect.height,
319
           viewRect.x, viewRect.y, viewRect.width, viewRect.height);
320
#endif
321
    return;
322
0
  }
323
0
324
0
  nsIWidget *widget = aView->GetWidget();
325
0
  if (!widget) {
326
0
    return;
327
0
  }
328
0
329
0
  NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
330
0
  if (IsPainting()) {
331
0
    RootViewManager()->mRecursiveRefreshPending = true;
332
0
    return;
333
0
  }
334
0
335
0
  {
336
0
    nsAutoScriptBlocker scriptBlocker;
337
0
    SetPainting(true);
338
0
339
0
    NS_ASSERTION(GetDisplayRootFor(aView) == aView,
340
0
                 "Widgets that we paint must all be display roots");
341
0
342
0
    if (mPresShell) {
343
#ifdef MOZ_DUMP_PAINTING
344
      if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
345
        printf_stderr("--COMPOSITE-- %p\n", mPresShell);
346
      }
347
#endif
348
      uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE;
349
0
      LayerManager *manager = widget->GetLayerManager();
350
0
      if (!manager->NeedsWidgetInvalidation()) {
351
0
        manager->FlushRendering();
352
0
      } else {
353
0
        mPresShell->Paint(aView, damageRegion,
354
0
                          paintFlags);
355
0
      }
356
#ifdef MOZ_DUMP_PAINTING
357
      if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
358
        printf_stderr("--ENDCOMPOSITE--\n");
359
      }
360
#endif
361
      mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
362
0
    }
363
0
364
0
    SetPainting(false);
365
0
  }
366
0
367
0
  if (RootViewManager()->mRecursiveRefreshPending) {
368
0
    RootViewManager()->mRecursiveRefreshPending = false;
369
0
    InvalidateAllViews();
370
0
  }
371
0
}
372
373
void
374
nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
375
                                            bool aFlushDirtyRegion)
376
0
{
377
0
  NS_ASSERTION(IsRootVM(), "Updates will be missed");
378
0
  if (!aView) {
379
0
    return;
380
0
  }
381
0
382
0
  nsCOMPtr<nsIPresShell> rootShell(mPresShell);
383
0
  AutoTArray<nsCOMPtr<nsIWidget>, 1> widgets;
384
0
  aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
385
0
  for (uint32_t i = 0; i < widgets.Length(); ++i) {
386
0
    nsView* view = nsView::GetViewFor(widgets[i]);
387
0
    if (view) {
388
0
      if (view->mNeedsWindowPropertiesSync) {
389
0
        view->mNeedsWindowPropertiesSync = false;
390
0
        if (nsViewManager* vm = view->GetViewManager()) {
391
0
          if (nsIPresShell* ps = vm->GetPresShell()) {
392
0
            ps->SyncWindowProperties(view);
393
0
          }
394
0
        }
395
0
      }
396
0
    }
397
0
    view = nsView::GetViewFor(widgets[i]);
398
0
    if (view) {
399
0
      view->ResetWidgetBounds(false, true);
400
0
    }
401
0
  }
402
0
  if (rootShell->GetViewManager() != this) {
403
0
    return; // presentation might have been torn down
404
0
  }
405
0
  if (aFlushDirtyRegion) {
406
0
    nsAutoScriptBlocker scriptBlocker;
407
0
    SetPainting(true);
408
0
    for (uint32_t i = 0; i < widgets.Length(); ++i) {
409
0
      nsIWidget* widget = widgets[i];
410
0
      nsView* view = nsView::GetViewFor(widget);
411
0
      if (view) {
412
0
        view->GetViewManager()->ProcessPendingUpdatesPaint(widget);
413
0
      }
414
0
    }
415
0
    SetPainting(false);
416
0
  }
417
0
}
418
419
void
420
nsViewManager::ProcessPendingUpdatesRecurse(nsView* aView,
421
                                            AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets)
422
0
{
423
0
  if (mPresShell && mPresShell->IsNeverPainting()) {
424
0
    return;
425
0
  }
426
0
427
0
  for (nsView* childView = aView->GetFirstChild(); childView;
428
0
       childView = childView->GetNextSibling()) {
429
0
    childView->GetViewManager()->
430
0
      ProcessPendingUpdatesRecurse(childView, aWidgets);
431
0
  }
432
0
433
0
  nsIWidget* widget = aView->GetWidget();
434
0
  if (widget) {
435
0
    aWidgets.AppendElement(widget);
436
0
  } else {
437
0
    FlushDirtyRegionToWidget(aView);
438
0
  }
439
0
}
440
441
void
442
nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget)
443
0
{
444
0
  if (aWidget->NeedsPaint()) {
445
0
    // If an ancestor widget was hidden and then shown, we could
446
0
    // have a delayed resize to handle.
447
0
    for (RefPtr<nsViewManager> vm = this; vm;
448
0
         vm = vm->mRootView->GetParent()
449
0
           ? vm->mRootView->GetParent()->GetViewManager()
450
0
           : nullptr) {
451
0
      if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
452
0
          vm->mRootView->IsEffectivelyVisible() &&
453
0
          vm->mPresShell && vm->mPresShell->IsVisible()) {
454
0
        vm->FlushDelayedResize(true);
455
0
      }
456
0
    }
457
0
    nsView* view = nsView::GetViewFor(aWidget);
458
0
459
0
    if (!view) {
460
0
      NS_ERROR("FlushDelayedResize destroyed the nsView?");
461
0
      return;
462
0
    }
463
0
464
0
    nsIWidgetListener* previousListener = aWidget->GetPreviouslyAttachedWidgetListener();
465
0
466
0
    if (previousListener &&
467
0
        previousListener != view &&
468
0
        view->IsPrimaryFramePaintSuppressed()) {
469
0
      return;
470
0
    }
471
0
472
0
    if (mPresShell) {
473
#ifdef MOZ_DUMP_PAINTING
474
      if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
475
        printf_stderr("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
476
                      mPresShell, view, aWidget);
477
      }
478
#endif
479
480
0
      mPresShell->Paint(view, nsRegion(), nsIPresShell::PAINT_LAYERS);
481
0
      view->SetForcedRepaint(false);
482
0
483
#ifdef MOZ_DUMP_PAINTING
484
      if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
485
        printf_stderr("---- PAINT END ----\n");
486
      }
487
#endif
488
    }
489
0
  }
490
0
  FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
491
0
}
492
493
void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
494
0
{
495
0
  NS_ASSERTION(aView->GetViewManager() == this,
496
0
               "FlushDirtyRegionToWidget called on view we don't own");
497
0
498
0
  if (!aView->HasNonEmptyDirtyRegion())
499
0
    return;
500
0
501
0
  nsRegion* dirtyRegion = aView->GetDirtyRegion();
502
0
  nsView* nearestViewWithWidget = aView;
503
0
  while (!nearestViewWithWidget->HasWidget() &&
504
0
         nearestViewWithWidget->GetParent()) {
505
0
    nearestViewWithWidget = nearestViewWithWidget->GetParent();
506
0
  }
507
0
  nsRegion r =
508
0
    ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
509
0
510
0
  nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
511
0
  widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
512
0
  dirtyRegion->SetEmpty();
513
0
}
514
515
void
516
nsViewManager::InvalidateView(nsView *aView)
517
0
{
518
0
  // Mark the entire view as damaged
519
0
  InvalidateView(aView, aView->GetDimensions());
520
0
}
521
522
static void
523
AddDirtyRegion(nsView *aView, const nsRegion &aDamagedRegion)
524
0
{
525
0
  nsRegion* dirtyRegion = aView->GetDirtyRegion();
526
0
  if (!dirtyRegion)
527
0
    return;
528
0
529
0
  dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
530
0
  dirtyRegion->SimplifyOutward(8);
531
0
}
532
533
void
534
nsViewManager::PostPendingUpdate()
535
0
{
536
0
  nsViewManager* rootVM = RootViewManager();
537
0
  rootVM->mHasPendingWidgetGeometryChanges = true;
538
0
  if (rootVM->mPresShell) {
539
0
    rootVM->mPresShell->ScheduleViewManagerFlush();
540
0
  }
541
0
}
542
543
/**
544
 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
545
 * every widget child of aWidgetView, plus aWidgetView's own widget
546
 */
547
void
548
nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
549
                                    const nsRegion &aDamagedRegion)
550
0
{
551
0
  NS_ASSERTION(aWidgetView->GetViewManager() == this,
552
0
               "InvalidateWidgetArea called on view we don't own");
553
0
  nsIWidget* widget = aWidgetView->GetWidget();
554
0
555
#if 0
556
  nsRect dbgBounds = aDamagedRegion.GetBounds();
557
  printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
558
    aWidgetView, aWidgetView->IsAttachedToTopLevel(),
559
    widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
560
#endif
561
562
0
  // If the widget is hidden, it don't cover nothing
563
0
  if (widget && !widget->IsVisible()) {
564
0
    return;
565
0
  }
566
0
567
0
  if (!widget) {
568
0
    // The root view or a scrolling view might not have a widget
569
0
    // (for example, during printing). We get here when we scroll
570
0
    // during printing to show selected options in a listbox, for example.
571
0
    return;
572
0
  }
573
0
574
0
  // Update all child widgets with the damage. In the process,
575
0
  // accumulate the union of all the child widget areas, or at least
576
0
  // some subset of that.
577
0
  nsRegion children;
578
0
  if (widget->GetTransparencyMode() != eTransparencyTransparent) {
579
0
    for (nsIWidget* childWidget = widget->GetFirstChild();
580
0
         childWidget;
581
0
         childWidget = childWidget->GetNextSibling()) {
582
0
      nsView* view = nsView::GetViewFor(childWidget);
583
0
      NS_ASSERTION(view != aWidgetView, "will recur infinitely");
584
0
      nsWindowType type = childWidget->WindowType();
585
0
      if (view && childWidget->IsVisible() && type != eWindowType_popup) {
586
0
        NS_ASSERTION(childWidget->IsPlugin(),
587
0
                     "Only plugin or popup widgets can be children!");
588
0
589
0
        // We do not need to invalidate in plugin widgets, but we should
590
0
        // exclude them from the invalidation region IF we're not on
591
0
        // Mac. On Mac we need to draw under plugin widgets, because
592
0
        // plugin widgets are basically invisible
593
0
#ifndef XP_MACOSX
594
0
        // GetBounds should compensate for chrome on a toplevel widget
595
0
        LayoutDeviceIntRect bounds = childWidget->GetBounds();
596
0
597
0
        nsTArray<LayoutDeviceIntRect> clipRects;
598
0
        childWidget->GetWindowClipRegion(&clipRects);
599
0
        for (uint32_t i = 0; i < clipRects.Length(); ++i) {
600
0
          nsRect rr = LayoutDeviceIntRect::ToAppUnits(
601
0
            clipRects[i] + bounds.TopLeft(), AppUnitsPerDevPixel());
602
0
          children.Or(children, rr - aWidgetView->ViewToWidgetOffset());
603
0
          children.SimplifyInward(20);
604
0
        }
605
0
#endif
606
0
      }
607
0
    }
608
0
  }
609
0
610
0
  nsRegion leftOver;
611
0
  leftOver.Sub(aDamagedRegion, children);
612
0
613
0
  if (!leftOver.IsEmpty()) {
614
0
    for (auto iter = leftOver.RectIter(); !iter.Done(); iter.Next()) {
615
0
      LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get());
616
0
      widget->Invalidate(bounds);
617
0
    }
618
0
  }
619
0
}
620
621
static bool
622
ShouldIgnoreInvalidation(nsViewManager* aVM)
623
0
{
624
0
  while (aVM) {
625
0
    nsIPresShell* shell = aVM->GetPresShell();
626
0
    if (!shell || shell->ShouldIgnoreInvalidation()) {
627
0
      return true;
628
0
    }
629
0
    nsView* view = aVM->GetRootView()->GetParent();
630
0
    aVM = view ? view->GetViewManager() : nullptr;
631
0
  }
632
0
  return false;
633
0
}
634
635
void
636
nsViewManager::InvalidateView(nsView *aView, const nsRect &aRect)
637
0
{
638
0
  // If painting is suppressed in the presshell or an ancestor drop all
639
0
  // invalidates, it will invalidate everything when it unsuppresses.
640
0
  if (ShouldIgnoreInvalidation(this)) {
641
0
    return;
642
0
  }
643
0
644
0
  InvalidateViewNoSuppression(aView, aRect);
645
0
}
646
647
void
648
nsViewManager::InvalidateViewNoSuppression(nsView *aView,
649
                                           const nsRect &aRect)
650
0
{
651
0
  MOZ_ASSERT(nullptr != aView, "null view");
652
0
653
0
  NS_ASSERTION(aView->GetViewManager() == this,
654
0
               "InvalidateViewNoSuppression called on view we don't own");
655
0
656
0
  nsRect damagedRect(aRect);
657
0
  if (damagedRect.IsEmpty()) {
658
0
    return;
659
0
  }
660
0
661
0
  nsView* displayRoot = GetDisplayRootFor(aView);
662
0
  nsViewManager* displayRootVM = displayRoot->GetViewManager();
663
0
  // Propagate the update to the displayRoot, since iframes, for example,
664
0
  // can overlap each other and be translucent.  So we have to possibly
665
0
  // invalidate our rect in each of the widgets we have lying about.
666
0
  damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
667
0
  int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
668
0
  int32_t APD = AppUnitsPerDevPixel();
669
0
  damagedRect = damagedRect.ScaleToOtherAppUnitsRoundOut(APD, rootAPD);
670
0
671
0
  // accumulate this rectangle in the view's dirty region, so we can
672
0
  // process it later.
673
0
  AddDirtyRegion(displayRoot, nsRegion(damagedRect));
674
0
}
675
676
void
677
nsViewManager::InvalidateAllViews()
678
0
{
679
0
  if (RootViewManager() != this) {
680
0
    return RootViewManager()->InvalidateAllViews();
681
0
  }
682
0
683
0
  InvalidateViews(mRootView);
684
0
}
685
686
void nsViewManager::InvalidateViews(nsView *aView)
687
0
{
688
0
  // Invalidate this view.
689
0
  InvalidateView(aView);
690
0
691
0
  // Invalidate all children as well.
692
0
  nsView* childView = aView->GetFirstChild();
693
0
  while (nullptr != childView)  {
694
0
    childView->GetViewManager()->InvalidateViews(childView);
695
0
    childView = childView->GetNextSibling();
696
0
  }
697
0
}
698
699
void nsViewManager::WillPaintWindow(nsIWidget* aWidget)
700
0
{
701
0
  RefPtr<nsIWidget> widget(aWidget);
702
0
  if (widget) {
703
0
    nsView* view = nsView::GetViewFor(widget);
704
0
    LayerManager* manager = widget->GetLayerManager();
705
0
    if (view &&
706
0
        (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) {
707
0
      ProcessPendingUpdates();
708
0
      // Re-get the view pointer here since the ProcessPendingUpdates might have
709
0
      // destroyed it during CallWillPaintOnObservers.
710
0
      view = nsView::GetViewFor(widget);
711
0
      if (view) {
712
0
        view->SetForcedRepaint(false);
713
0
      }
714
0
    }
715
0
  }
716
0
717
0
  nsCOMPtr<nsIPresShell> shell = mPresShell;
718
0
  if (shell) {
719
0
    shell->WillPaintWindow();
720
0
  }
721
0
}
722
723
bool nsViewManager::PaintWindow(nsIWidget* aWidget,
724
                                const LayoutDeviceIntRegion& aRegion)
725
0
{
726
0
  if (!aWidget || !mContext)
727
0
    return false;
728
0
729
0
  NS_ASSERTION(IsPaintingAllowed(),
730
0
               "shouldn't be receiving paint events while painting is disallowed!");
731
0
732
0
  // Get the view pointer here since NS_WILL_PAINT might have
733
0
  // destroyed it during CallWillPaintOnObservers (bug 378273).
734
0
  nsView* view = nsView::GetViewFor(aWidget);
735
0
  if (view && !aRegion.IsEmpty()) {
736
0
    Refresh(view, aRegion);
737
0
  }
738
0
739
0
  return true;
740
0
}
741
742
void nsViewManager::DidPaintWindow()
743
0
{
744
0
  nsCOMPtr<nsIPresShell> shell = mPresShell;
745
0
  if (shell) {
746
0
    shell->DidPaintWindow();
747
0
  }
748
0
}
749
750
void
751
nsViewManager::DispatchEvent(WidgetGUIEvent *aEvent,
752
                             nsView* aView,
753
                             nsEventStatus* aStatus)
754
0
{
755
0
  AUTO_PROFILER_LABEL("nsViewManager::DispatchEvent", OTHER);
756
0
757
0
  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
758
0
  if ((mouseEvent &&
759
0
       // Ignore mouse events that we synthesize.
760
0
       mouseEvent->mReason == WidgetMouseEvent::eReal &&
761
0
       // Ignore mouse exit and enter (we'll get moves if the user
762
0
       // is really moving the mouse) since we get them when we
763
0
       // create and destroy widgets.
764
0
       mouseEvent->mMessage != eMouseExitFromWidget &&
765
0
       mouseEvent->mMessage != eMouseEnterIntoWidget) ||
766
0
      aEvent->HasKeyEventMessage() ||
767
0
      aEvent->HasIMEEventMessage() ||
768
0
      aEvent->mMessage == ePluginInputEvent) {
769
0
    gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
770
0
  }
771
0
772
0
  // Find the view whose coordinates system we're in.
773
0
  nsView* view = aView;
774
0
  bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
775
0
  if (dispatchUsingCoordinates) {
776
0
    // Will dispatch using coordinates. Pretty bogus but it's consistent
777
0
    // with what presshell does.
778
0
    view = GetDisplayRootFor(view);
779
0
  }
780
0
781
0
  // If the view has no frame, look for a view that does.
782
0
  nsIFrame* frame = view->GetFrame();
783
0
  if (!frame &&
784
0
      (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
785
0
       aEvent->IsIMERelatedEvent() ||
786
0
       aEvent->IsNonRetargetedNativeEventDelivererForPlugin() ||
787
0
       aEvent->HasPluginActivationEventMessage())) {
788
0
    while (view && !view->GetFrame()) {
789
0
      view = view->GetParent();
790
0
    }
791
0
792
0
    if (view) {
793
0
      frame = view->GetFrame();
794
0
    }
795
0
  }
796
0
797
0
  if (nullptr != frame) {
798
0
    // Hold a refcount to the presshell. The continued existence of the
799
0
    // presshell will delay deletion of this view hierarchy should the event
800
0
    // want to cause its destruction in, say, some JavaScript event handler.
801
0
    nsCOMPtr<nsIPresShell> shell = view->GetViewManager()->GetPresShell();
802
0
    if (shell) {
803
0
      if (aEvent->mMessage == eMouseDown ||
804
0
          aEvent->mMessage == eMouseUp) {
805
0
        AutoWeakFrame weakFrame(frame);
806
0
        shell->FlushPendingNotifications(FlushType::Layout);
807
0
        if (!weakFrame.IsAlive()) {
808
0
          *aStatus = nsEventStatus_eIgnore;
809
0
          return;
810
0
        }
811
0
      }
812
0
      shell->HandleEvent(frame, aEvent, false, aStatus);
813
0
    return;
814
0
    }
815
0
  }
816
0
817
0
  *aStatus = nsEventStatus_eIgnore;
818
0
}
819
820
// Recursively reparent widgets if necessary
821
822
void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget *aNewWidget)
823
0
{
824
0
  MOZ_ASSERT(aNewWidget, "null widget");
825
0
826
0
  if (aView->HasWidget()) {
827
0
    // Check to see if the parent widget is the
828
0
    // same as the new parent. If not then reparent
829
0
    // the widget, otherwise there is nothing more
830
0
    // to do for the view and its descendants
831
0
    nsIWidget* widget = aView->GetWidget();
832
0
    nsIWidget* parentWidget = widget->GetParent();
833
0
    if (parentWidget) {
834
0
      // Child widget
835
0
      if (parentWidget != aNewWidget) {
836
0
        widget->SetParent(aNewWidget);
837
0
      }
838
0
    } else {
839
0
      // Toplevel widget (popup, dialog, etc)
840
0
      widget->ReparentNativeWidget(aNewWidget);
841
0
    }
842
0
    return;
843
0
  }
844
0
845
0
  // Need to check each of the views children to see
846
0
  // if they have a widget and reparent it.
847
0
848
0
  for (nsView *kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
849
0
    ReparentChildWidgets(kid, aNewWidget);
850
0
  }
851
0
}
852
853
// Reparent a view and its descendant views widgets if necessary
854
855
void nsViewManager::ReparentWidgets(nsView* aView, nsView *aParent)
856
0
{
857
0
  MOZ_ASSERT(aParent, "Must have a parent");
858
0
  MOZ_ASSERT(aView, "Must have a view");
859
0
860
0
  // Quickly determine whether the view has pre-existing children or a
861
0
  // widget. In most cases the view will not have any pre-existing
862
0
  // children when this is called.  Only in the case
863
0
  // where a view has been reparented by removing it from
864
0
  // a reinserting it into a new location in the view hierarchy do we
865
0
  // have to consider reparenting the existing widgets for the view and
866
0
  // it's descendants.
867
0
  if (aView->HasWidget() || aView->GetFirstChild()) {
868
0
    nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
869
0
    if (parentWidget) {
870
0
      ReparentChildWidgets(aView, parentWidget);
871
0
      return;
872
0
    }
873
0
    NS_WARNING("Can not find a widget for the parent view");
874
0
  }
875
0
}
876
877
void
878
nsViewManager::InsertChild(nsView *aParent, nsView *aChild, nsView *aSibling,
879
                           bool aAfter)
880
0
{
881
0
  MOZ_ASSERT(nullptr != aParent, "null ptr");
882
0
  MOZ_ASSERT(nullptr != aChild, "null ptr");
883
0
  NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
884
0
               "tried to insert view with invalid sibling");
885
0
  NS_ASSERTION(!IsViewInserted(aChild), "tried to insert an already-inserted view");
886
0
887
0
  if ((nullptr != aParent) && (nullptr != aChild))
888
0
    {
889
0
      // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
890
0
      // order, otherwise after 'kid' (i.e. before 'kid' in document order).
891
0
892
0
      if (nullptr == aSibling) {
893
0
        if (aAfter) {
894
0
          // insert at end of document order, i.e., before first view
895
0
          // this is the common case, by far
896
0
          aParent->InsertChild(aChild, nullptr);
897
0
          ReparentWidgets(aChild, aParent);
898
0
        } else {
899
0
          // insert at beginning of document order, i.e., after last view
900
0
          nsView *kid = aParent->GetFirstChild();
901
0
          nsView *prev = nullptr;
902
0
          while (kid) {
903
0
            prev = kid;
904
0
            kid = kid->GetNextSibling();
905
0
          }
906
0
          // prev is last view or null if there are no children
907
0
          aParent->InsertChild(aChild, prev);
908
0
          ReparentWidgets(aChild, aParent);
909
0
        }
910
0
      } else {
911
0
        nsView *kid = aParent->GetFirstChild();
912
0
        nsView *prev = nullptr;
913
0
        while (kid && aSibling != kid) {
914
0
          //get the next sibling view
915
0
          prev = kid;
916
0
          kid = kid->GetNextSibling();
917
0
        }
918
0
        NS_ASSERTION(kid != nullptr,
919
0
                     "couldn't find sibling in child list");
920
0
        if (aAfter) {
921
0
          // insert after 'kid' in document order, i.e. before in view order
922
0
          aParent->InsertChild(aChild, prev);
923
0
          ReparentWidgets(aChild, aParent);
924
0
        } else {
925
0
          // insert before 'kid' in document order, i.e. after in view order
926
0
          aParent->InsertChild(aChild, kid);
927
0
          ReparentWidgets(aChild, aParent);
928
0
        }
929
0
      }
930
0
931
0
      // if the parent view is marked as "floating", make the newly added view float as well.
932
0
      if (aParent->GetFloating())
933
0
        aChild->SetFloating(true);
934
0
    }
935
0
}
936
937
void
938
nsViewManager::RemoveChild(nsView *aChild)
939
0
{
940
0
  NS_ASSERTION(aChild, "aChild must not be null");
941
0
942
0
  nsView* parent = aChild->GetParent();
943
0
944
0
  if (nullptr != parent) {
945
0
    NS_ASSERTION(aChild->GetViewManager() == this ||
946
0
                 parent->GetViewManager() == this, "wrong view manager");
947
0
    parent->RemoveChild(aChild);
948
0
  }
949
0
}
950
951
void
952
nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY)
953
0
{
954
0
  NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
955
0
  aView->SetPosition(aX, aY);
956
0
}
957
958
void
959
nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly)
960
0
{
961
0
  NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
962
0
963
0
  nsRect oldDimensions = aView->GetDimensions();
964
0
  if (!oldDimensions.IsEqualEdges(aRect)) {
965
0
    aView->SetDimensions(aRect, true);
966
0
  }
967
0
968
0
  // Note that if layout resizes the view and the view has a custom clip
969
0
  // region set, then we expect layout to update the clip region too. Thus
970
0
  // in the case where mClipRect has been optimized away to just be a null
971
0
  // pointer, and this resize is implicitly changing the clip rect, it's OK
972
0
  // because layout will change it back again if necessary.
973
0
}
974
975
void
976
nsViewManager::SetViewFloating(nsView *aView, bool aFloating)
977
0
{
978
0
  NS_ASSERTION(!(nullptr == aView), "no view");
979
0
980
0
  aView->SetFloating(aFloating);
981
0
}
982
983
void
984
nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible)
985
0
{
986
0
  NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
987
0
988
0
  if (aVisible != aView->GetVisibility()) {
989
0
    aView->SetVisibility(aVisible);
990
0
  }
991
0
}
992
993
bool nsViewManager::IsViewInserted(nsView *aView)
994
0
{
995
0
  if (mRootView == aView) {
996
0
    return true;
997
0
  }
998
0
  if (aView->GetParent() == nullptr) {
999
0
    return false;
1000
0
  }
1001
0
  nsView* view = aView->GetParent()->GetFirstChild();
1002
0
  while (view != nullptr) {
1003
0
    if (view == aView) {
1004
0
      return true;
1005
0
    }
1006
0
    view = view->GetNextSibling();
1007
0
  }
1008
0
  return false;
1009
0
}
1010
1011
void
1012
nsViewManager::SetViewZIndex(nsView *aView, bool aAutoZIndex, int32_t aZIndex)
1013
0
{
1014
0
  NS_ASSERTION((aView != nullptr), "no view");
1015
0
1016
0
  // don't allow the root view's z-index to be changed. It should always be zero.
1017
0
  // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1018
0
  if (aView == mRootView) {
1019
0
    return;
1020
0
  }
1021
0
1022
0
  if (aAutoZIndex) {
1023
0
    aZIndex = 0;
1024
0
  }
1025
0
1026
0
  aView->SetZIndex(aAutoZIndex, aZIndex);
1027
0
}
1028
1029
nsViewManager*
1030
nsViewManager::IncrementDisableRefreshCount()
1031
0
{
1032
0
  if (!IsRootVM()) {
1033
0
    return RootViewManager()->IncrementDisableRefreshCount();
1034
0
  }
1035
0
1036
0
  ++mRefreshDisableCount;
1037
0
1038
0
  return this;
1039
0
}
1040
1041
void
1042
nsViewManager::DecrementDisableRefreshCount()
1043
0
{
1044
0
  NS_ASSERTION(IsRootVM(), "Should only be called on root");
1045
0
  --mRefreshDisableCount;
1046
0
  NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
1047
0
}
1048
1049
void
1050
nsViewManager::GetRootWidget(nsIWidget **aWidget)
1051
0
{
1052
0
  if (!mRootView) {
1053
0
    *aWidget = nullptr;
1054
0
    return;
1055
0
  }
1056
0
  if (mRootView->HasWidget()) {
1057
0
    *aWidget = mRootView->GetWidget();
1058
0
    NS_ADDREF(*aWidget);
1059
0
    return;
1060
0
  }
1061
0
  if (mRootView->GetParent()) {
1062
0
    mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
1063
0
    return;
1064
0
  }
1065
0
  *aWidget = nullptr;
1066
0
}
1067
1068
LayoutDeviceIntRect
1069
nsViewManager::ViewToWidget(nsView* aView, const nsRect& aRect) const
1070
0
{
1071
0
  NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
1072
0
1073
0
  // account for the view's origin not lining up with the widget's
1074
0
  nsRect rect = aRect + aView->ViewToWidgetOffset();
1075
0
1076
0
  // finally, convert to device coordinates.
1077
0
  return LayoutDeviceIntRect::FromUnknownRect(
1078
0
    rect.ToOutsidePixels(AppUnitsPerDevPixel()));
1079
0
}
1080
1081
void
1082
nsViewManager::IsPainting(bool& aIsPainting)
1083
0
{
1084
0
  aIsPainting = IsPainting();
1085
0
}
1086
1087
void
1088
nsViewManager::ProcessPendingUpdates()
1089
0
{
1090
0
  if (!IsRootVM()) {
1091
0
    RootViewManager()->ProcessPendingUpdates();
1092
0
    return;
1093
0
  }
1094
0
1095
0
  // Flush things like reflows by calling WillPaint on observer presShells.
1096
0
  if (mPresShell) {
1097
0
    mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
1098
0
1099
0
    RefPtr<nsViewManager> strongThis(this);
1100
0
    CallWillPaintOnObservers();
1101
0
1102
0
    ProcessPendingUpdatesForView(mRootView, true);
1103
0
  }
1104
0
}
1105
1106
void
1107
nsViewManager::UpdateWidgetGeometry()
1108
0
{
1109
0
  if (!IsRootVM()) {
1110
0
    RootViewManager()->UpdateWidgetGeometry();
1111
0
    return;
1112
0
  }
1113
0
1114
0
  if (mHasPendingWidgetGeometryChanges) {
1115
0
    mHasPendingWidgetGeometryChanges = false;
1116
0
    RefPtr<nsViewManager> strongThis(this);
1117
0
    ProcessPendingUpdatesForView(mRootView, false);
1118
0
  }
1119
0
}
1120
1121
void
1122
nsViewManager::CallWillPaintOnObservers()
1123
0
{
1124
0
  MOZ_ASSERT(IsRootVM(), "Must be root VM for this to be called!");
1125
0
1126
0
  if (NS_WARN_IF(!gViewManagers)) {
1127
0
    return;
1128
0
  }
1129
0
1130
0
  uint32_t index;
1131
0
  for (index = 0; index < gViewManagers->Length(); index++) {
1132
0
    nsViewManager* vm = gViewManagers->ElementAt(index);
1133
0
    if (vm->RootViewManager() == this) {
1134
0
      // One of our kids.
1135
0
      if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
1136
0
        nsCOMPtr<nsIPresShell> shell = vm->GetPresShell();
1137
0
        if (shell) {
1138
0
          shell->WillPaint();
1139
0
        }
1140
0
      }
1141
0
    }
1142
0
  }
1143
0
}
1144
1145
void
1146
nsViewManager::GetLastUserEventTime(uint32_t& aTime)
1147
0
{
1148
0
  aTime = gLastUserEventTime;
1149
0
}
1150
1151
void
1152
nsViewManager::InvalidateHierarchy()
1153
0
{
1154
0
  if (mRootView) {
1155
0
    if (!IsRootVM()) {
1156
0
      NS_RELEASE(mRootViewManager);
1157
0
    }
1158
0
    nsView *parent = mRootView->GetParent();
1159
0
    if (parent) {
1160
0
      mRootViewManager = parent->GetViewManager()->RootViewManager();
1161
0
      NS_ADDREF(mRootViewManager);
1162
0
      NS_ASSERTION(mRootViewManager != this,
1163
0
                   "Root view had a parent, but it has the same view manager");
1164
0
    } else {
1165
0
      mRootViewManager = this;
1166
0
    }
1167
0
  }
1168
0
}