view/src/nsViewManager.cpp

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

mercurial