Bumping manifests a=b2g-bump
[gecko.git] / view / nsViewManager.cpp
blob3b5e8c58d89ed7eadc785968d0b6fc10888b4b6c
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/. */
6 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
7 #include "plarena.h"
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"
32 /**
33 XXX TODO XXX
35 DeCOMify newly private methods
36 Optimize view storage
39 /**
40 A note about platform assumptions:
42 We assume that a widget is z-ordered on top of its parent.
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.
48 using namespace mozilla;
49 using namespace mozilla::layers;
51 #define NSCOORD_NONE INT32_MIN
53 #undef DEBUG_MOUSE_LOCATION
55 // Weakly held references to all of the view managers
56 nsTArray<nsViewManager*>* nsViewManager::gViewManagers = nullptr;
57 uint32_t nsViewManager::gLastUserEventTime = 0;
59 nsViewManager::nsViewManager()
60 : mDelayedResize(NSCOORD_NONE, NSCOORD_NONE)
62 mRootViewManager = this;
63 if (gViewManagers == nullptr) {
64 // Create an array to hold a list of view managers
65 gViewManagers = new nsTArray<nsViewManager*>;
68 gViewManagers->AppendElement(this);
70 // NOTE: we use a zeroing operator new, so all data members are
71 // assumed to be cleared here.
72 mHasPendingWidgetGeometryChanges = false;
73 mRecursiveRefreshPending = false;
76 nsViewManager::~nsViewManager()
78 if (mRootView) {
79 // Destroy any remaining views
80 mRootView->Destroy();
81 mRootView = nullptr;
84 if (!IsRootVM()) {
85 // We have a strong ref to mRootViewManager
86 NS_RELEASE(mRootViewManager);
89 NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
91 #ifdef DEBUG
92 bool removed =
93 #endif
94 gViewManagers->RemoveElement(this);
95 NS_ASSERTION(removed, "Viewmanager instance was not in the global list of viewmanagers");
97 if (gViewManagers->IsEmpty()) {
98 // There aren't any more view managers so
99 // release the global array of view managers
100 delete gViewManagers;
101 gViewManagers = nullptr;
104 mPresShell = nullptr;
107 // We don't hold a reference to the presentation context because it
108 // holds a reference to us.
109 nsresult
110 nsViewManager::Init(nsDeviceContext* aContext)
112 NS_PRECONDITION(nullptr != aContext, "null ptr");
114 if (nullptr == aContext) {
115 return NS_ERROR_NULL_POINTER;
117 if (nullptr != mContext) {
118 return NS_ERROR_ALREADY_INITIALIZED;
120 mContext = aContext;
122 return NS_OK;
125 nsView*
126 nsViewManager::CreateView(const nsRect& aBounds,
127 nsView* aParent,
128 nsViewVisibility aVisibilityFlag)
130 nsView *v = new nsView(this, aVisibilityFlag);
131 v->SetParent(aParent);
132 v->SetPosition(aBounds.x, aBounds.y);
133 nsRect dim(0, 0, aBounds.width, aBounds.height);
134 v->SetDimensions(dim, false);
135 return v;
138 void
139 nsViewManager::SetRootView(nsView *aView)
141 NS_PRECONDITION(!aView || aView->GetViewManager() == this,
142 "Unexpected viewmanager on root view");
144 // Do NOT destroy the current root view. It's the caller's responsibility
145 // to destroy it
146 mRootView = aView;
148 if (mRootView) {
149 nsView* parent = mRootView->GetParent();
150 if (parent) {
151 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
152 // no need to set mRootViewManager ourselves here.
153 parent->InsertChild(mRootView, nullptr);
154 } else {
155 InvalidateHierarchy();
158 mRootView->SetZIndex(false, 0);
160 // Else don't touch mRootViewManager
163 void
164 nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
166 if (nullptr != mRootView) {
167 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
168 nsRect dim = mRootView->GetDimensions();
169 *aWidth = dim.width;
170 *aHeight = dim.height;
171 } else {
172 *aWidth = mDelayedResize.width;
173 *aHeight = mDelayedResize.height;
176 else
178 *aWidth = 0;
179 *aHeight = 0;
183 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
185 nsRect oldDim = mRootView->GetDimensions();
186 nsRect newDim(0, 0, aWidth, aHeight);
187 // We care about resizes even when one dimension is already zero.
188 if (!oldDim.IsEqualEdges(newDim)) {
189 // Don't resize the widget. It is already being set elsewhere.
190 mRootView->SetDimensions(newDim, true, false);
191 if (mPresShell)
192 mPresShell->ResizeReflow(aWidth, aHeight);
196 void
197 nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
199 if (mRootView) {
200 if (mRootView->IsEffectivelyVisible() && mPresShell && mPresShell->IsVisible()) {
201 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
202 mDelayedResize != nsSize(aWidth, aHeight)) {
203 // We have a delayed resize; that now obsolete size may already have
204 // been flushed to the PresContext so we need to update the PresContext
205 // with the new size because if the new size is exactly the same as the
206 // root view's current size then DoSetWindowDimensions will not
207 // request a resize reflow (which would correct it). See bug 617076.
208 mDelayedResize = nsSize(aWidth, aHeight);
209 FlushDelayedResize(false);
211 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
212 DoSetWindowDimensions(aWidth, aHeight);
213 } else {
214 mDelayedResize.SizeTo(aWidth, aHeight);
215 if (mPresShell && mPresShell->GetDocument()) {
216 mPresShell->GetDocument()->SetNeedStyleFlush();
222 void
223 nsViewManager::FlushDelayedResize(bool aDoReflow)
225 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
226 if (aDoReflow) {
227 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height);
228 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
229 } else if (mPresShell) {
230 nsPresContext* presContext = mPresShell->GetPresContext();
231 if (presContext) {
232 presContext->SetVisibleArea(nsRect(nsPoint(0, 0), mDelayedResize));
238 // Convert aIn from being relative to and in appunits of aFromView, to being
239 // relative to and in appunits of aToView.
240 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
241 nsView* aFromView,
242 nsView* aToView)
244 nsRegion out = aIn;
245 out.MoveBy(aFromView->GetOffsetTo(aToView));
246 out = out.ConvertAppUnitsRoundOut(
247 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
248 aToView->GetViewManager()->AppUnitsPerDevPixel());
249 return out;
252 nsView* nsViewManager::GetDisplayRootFor(nsView* aView)
254 nsView *displayRoot = aView;
255 for (;;) {
256 nsView *displayParent = displayRoot->GetParent();
257 if (!displayParent)
258 return displayRoot;
260 if (displayRoot->GetFloating() && !displayParent->GetFloating())
261 return displayRoot;
263 // If we have a combobox dropdown popup within a panel popup, both the view
264 // for the dropdown popup and its parent will be floating, so we need to
265 // distinguish this situation. We do this by looking for a widget. Any view
266 // with a widget is a display root, except for plugins.
267 nsIWidget* widget = displayRoot->GetWidget();
268 if (widget && widget->WindowType() == eWindowType_popup) {
269 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
270 "this should only happen with floating views that have floating parents");
271 return displayRoot;
274 displayRoot = displayParent;
279 aRegion is given in device coordinates!!
280 aContext may be null, in which case layers should be used for
281 rendering.
283 void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion)
285 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
287 if (mPresShell && mPresShell->IsNeverPainting()) {
288 return;
291 // damageRegion is the damaged area, in twips, relative to the view origin
292 nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
293 // move region from widget coordinates into view coordinates
294 damageRegion.MoveBy(-aView->ViewToWidgetOffset());
296 if (damageRegion.IsEmpty()) {
297 #ifdef DEBUG_roc
298 nsRect viewRect = aView->GetDimensions();
299 nsRect damageRect = damageRegion.GetBounds();
300 printf_stderr("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
301 damageRect.x, damageRect.y, damageRect.width, damageRect.height,
302 viewRect.x, viewRect.y, viewRect.width, viewRect.height);
303 #endif
304 return;
307 nsIWidget *widget = aView->GetWidget();
308 if (!widget) {
309 return;
312 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
313 if (IsPainting()) {
314 RootViewManager()->mRecursiveRefreshPending = true;
315 return;
319 nsAutoScriptBlocker scriptBlocker;
320 SetPainting(true);
322 NS_ASSERTION(GetDisplayRootFor(aView) == aView,
323 "Widgets that we paint must all be display roots");
325 if (mPresShell) {
326 #ifdef MOZ_DUMP_PAINTING
327 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
328 printf_stderr("--COMPOSITE-- %p\n", mPresShell);
330 #endif
331 uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE;
332 LayerManager *manager = widget->GetLayerManager();
333 if (!manager->NeedsWidgetInvalidation()) {
334 manager->FlushRendering();
335 } else {
336 mPresShell->Paint(aView, damageRegion,
337 paintFlags);
339 #ifdef MOZ_DUMP_PAINTING
340 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
341 printf_stderr("--ENDCOMPOSITE--\n");
343 #endif
344 mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
347 SetPainting(false);
350 if (RootViewManager()->mRecursiveRefreshPending) {
351 RootViewManager()->mRecursiveRefreshPending = false;
352 InvalidateAllViews();
356 void
357 nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
358 bool aFlushDirtyRegion)
360 NS_ASSERTION(IsRootVM(), "Updates will be missed");
361 if (!aView) {
362 return;
365 nsCOMPtr<nsIPresShell> rootShell(mPresShell);
366 nsTArray<nsCOMPtr<nsIWidget> > widgets;
367 aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
368 for (uint32_t i = 0; i < widgets.Length(); ++i) {
369 nsView* view = nsView::GetViewFor(widgets[i]);
370 if (view) {
371 view->ResetWidgetBounds(false, true);
374 if (rootShell->GetViewManager() != this) {
375 return; // 'this' might have been destroyed
377 if (aFlushDirtyRegion) {
378 nsAutoScriptBlocker scriptBlocker;
379 SetPainting(true);
380 for (uint32_t i = 0; i < widgets.Length(); ++i) {
381 nsIWidget* widget = widgets[i];
382 nsView* view = nsView::GetViewFor(widget);
383 if (view) {
384 view->GetViewManager()->ProcessPendingUpdatesPaint(widget);
387 SetPainting(false);
391 void
392 nsViewManager::ProcessPendingUpdatesRecurse(nsView* aView,
393 nsTArray<nsCOMPtr<nsIWidget> >& aWidgets)
395 if (mPresShell && mPresShell->IsNeverPainting()) {
396 return;
399 for (nsView* childView = aView->GetFirstChild(); childView;
400 childView = childView->GetNextSibling()) {
401 childView->GetViewManager()->
402 ProcessPendingUpdatesRecurse(childView, aWidgets);
405 nsIWidget* widget = aView->GetWidget();
406 if (widget) {
407 aWidgets.AppendElement(widget);
408 } else {
409 FlushDirtyRegionToWidget(aView);
413 void
414 nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget)
416 if (aWidget->NeedsPaint()) {
417 // If an ancestor widget was hidden and then shown, we could
418 // have a delayed resize to handle.
419 for (nsViewManager *vm = this; vm;
420 vm = vm->mRootView->GetParent()
421 ? vm->mRootView->GetParent()->GetViewManager()
422 : nullptr) {
423 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
424 vm->mRootView->IsEffectivelyVisible() &&
425 vm->mPresShell && vm->mPresShell->IsVisible()) {
426 vm->FlushDelayedResize(true);
429 nsView* view = nsView::GetViewFor(aWidget);
430 if (!view) {
431 NS_ERROR("FlushDelayedResize destroyed the nsView?");
432 return;
435 if (mPresShell) {
436 #ifdef MOZ_DUMP_PAINTING
437 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
438 printf_stderr("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
439 mPresShell, view, aWidget);
441 #endif
443 mPresShell->Paint(view, nsRegion(), nsIPresShell::PAINT_LAYERS);
444 view->SetForcedRepaint(false);
446 #ifdef MOZ_DUMP_PAINTING
447 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
448 printf_stderr("---- PAINT END ----\n");
450 #endif
453 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
456 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
458 NS_ASSERTION(aView->GetViewManager() == this,
459 "FlushDirtyRegionToWidget called on view we don't own");
461 if (!aView->HasNonEmptyDirtyRegion())
462 return;
464 nsRegion* dirtyRegion = aView->GetDirtyRegion();
465 nsView* nearestViewWithWidget = aView;
466 while (!nearestViewWithWidget->HasWidget() &&
467 nearestViewWithWidget->GetParent()) {
468 nearestViewWithWidget = nearestViewWithWidget->GetParent();
470 nsRegion r =
471 ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
473 // If we draw the frame counter we need to make sure we invalidate the area
474 // for it to make it on screen
475 if (gfxPrefs::DrawFrameCounter()) {
476 nsRect counterBounds = gfxPlatform::FrameCounterBounds().ToAppUnits(AppUnitsPerDevPixel());
477 r = r.Or(r, counterBounds);
480 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
481 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
482 dirtyRegion->SetEmpty();
485 void
486 nsViewManager::InvalidateView(nsView *aView)
488 // Mark the entire view as damaged
489 InvalidateView(aView, aView->GetDimensions());
492 static void
493 AddDirtyRegion(nsView *aView, const nsRegion &aDamagedRegion)
495 nsRegion* dirtyRegion = aView->GetDirtyRegion();
496 if (!dirtyRegion)
497 return;
499 dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
500 dirtyRegion->SimplifyOutward(8);
503 void
504 nsViewManager::PostPendingUpdate()
506 nsViewManager* rootVM = RootViewManager();
507 rootVM->mHasPendingWidgetGeometryChanges = true;
508 if (rootVM->mPresShell) {
509 rootVM->mPresShell->ScheduleViewManagerFlush();
514 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
515 * every widget child of aWidgetView, plus aWidgetView's own widget
517 void
518 nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
519 const nsRegion &aDamagedRegion)
521 NS_ASSERTION(aWidgetView->GetViewManager() == this,
522 "InvalidateWidgetArea called on view we don't own");
523 nsIWidget* widget = aWidgetView->GetWidget();
525 #if 0
526 nsRect dbgBounds = aDamagedRegion.GetBounds();
527 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
528 aWidgetView, aWidgetView->IsAttachedToTopLevel(),
529 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
530 #endif
532 // If the widget is hidden, it don't cover nothing
533 if (widget && !widget->IsVisible()) {
534 return;
537 if (!widget) {
538 // The root view or a scrolling view might not have a widget
539 // (for example, during printing). We get here when we scroll
540 // during printing to show selected options in a listbox, for example.
541 return;
544 // Update all child widgets with the damage. In the process,
545 // accumulate the union of all the child widget areas, or at least
546 // some subset of that.
547 nsRegion children;
548 if (widget->GetTransparencyMode() != eTransparencyTransparent) {
549 for (nsIWidget* childWidget = widget->GetFirstChild();
550 childWidget;
551 childWidget = childWidget->GetNextSibling()) {
552 nsView* view = nsView::GetViewFor(childWidget);
553 NS_ASSERTION(view != aWidgetView, "will recur infinitely");
554 nsWindowType type = childWidget->WindowType();
555 if (view && childWidget->IsVisible() && type != eWindowType_popup) {
556 NS_ASSERTION(type == eWindowType_plugin,
557 "Only plugin or popup widgets can be children!");
559 // We do not need to invalidate in plugin widgets, but we should
560 // exclude them from the invalidation region IF we're not on
561 // Mac. On Mac we need to draw under plugin widgets, because
562 // plugin widgets are basically invisible
563 #ifndef XP_MACOSX
564 // GetBounds should compensate for chrome on a toplevel widget
565 nsIntRect bounds;
566 childWidget->GetBounds(bounds);
568 nsTArray<nsIntRect> clipRects;
569 childWidget->GetWindowClipRegion(&clipRects);
570 for (uint32_t i = 0; i < clipRects.Length(); ++i) {
571 nsRect rr = (clipRects[i] + bounds.TopLeft()).
572 ToAppUnits(AppUnitsPerDevPixel());
573 children.Or(children, rr - aWidgetView->ViewToWidgetOffset());
574 children.SimplifyInward(20);
576 #endif
581 nsRegion leftOver;
582 leftOver.Sub(aDamagedRegion, children);
584 if (!leftOver.IsEmpty()) {
585 const nsRect* r;
586 for (nsRegionRectIterator iter(leftOver); (r = iter.Next());) {
587 nsIntRect bounds = ViewToWidget(aWidgetView, *r);
588 widget->Invalidate(bounds);
593 static bool
594 ShouldIgnoreInvalidation(nsViewManager* aVM)
596 while (aVM) {
597 nsIPresShell* shell = aVM->GetPresShell();
598 if (!shell || shell->ShouldIgnoreInvalidation()) {
599 return true;
601 nsView* view = aVM->GetRootView()->GetParent();
602 aVM = view ? view->GetViewManager() : nullptr;
604 return false;
607 void
608 nsViewManager::InvalidateView(nsView *aView, const nsRect &aRect)
610 // If painting is suppressed in the presshell or an ancestor drop all
611 // invalidates, it will invalidate everything when it unsuppresses.
612 if (ShouldIgnoreInvalidation(this)) {
613 return;
616 InvalidateViewNoSuppression(aView, aRect);
619 void
620 nsViewManager::InvalidateViewNoSuppression(nsView *aView,
621 const nsRect &aRect)
623 NS_PRECONDITION(nullptr != aView, "null view");
625 NS_ASSERTION(aView->GetViewManager() == this,
626 "InvalidateViewNoSuppression called on view we don't own");
628 nsRect damagedRect(aRect);
629 if (damagedRect.IsEmpty()) {
630 return;
633 nsView* displayRoot = GetDisplayRootFor(aView);
634 nsViewManager* displayRootVM = displayRoot->GetViewManager();
635 // Propagate the update to the displayRoot, since iframes, for example,
636 // can overlap each other and be translucent. So we have to possibly
637 // invalidate our rect in each of the widgets we have lying about.
638 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
639 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
640 int32_t APD = AppUnitsPerDevPixel();
641 damagedRect = damagedRect.ConvertAppUnitsRoundOut(APD, rootAPD);
643 // accumulate this rectangle in the view's dirty region, so we can
644 // process it later.
645 AddDirtyRegion(displayRoot, nsRegion(damagedRect));
648 void
649 nsViewManager::InvalidateAllViews()
651 if (RootViewManager() != this) {
652 return RootViewManager()->InvalidateAllViews();
655 InvalidateViews(mRootView);
658 void nsViewManager::InvalidateViews(nsView *aView)
660 // Invalidate this view.
661 InvalidateView(aView);
663 // Invalidate all children as well.
664 nsView* childView = aView->GetFirstChild();
665 while (nullptr != childView) {
666 childView->GetViewManager()->InvalidateViews(childView);
667 childView = childView->GetNextSibling();
671 void nsViewManager::WillPaintWindow(nsIWidget* aWidget)
673 if (aWidget) {
674 nsView* view = nsView::GetViewFor(aWidget);
675 LayerManager *manager = aWidget->GetLayerManager();
676 if (view &&
677 (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) {
678 ProcessPendingUpdates();
679 // Re-get the view pointer here since the ProcessPendingUpdates might have
680 // destroyed it during CallWillPaintOnObservers.
681 view = nsView::GetViewFor(aWidget);
682 if (view) {
683 view->SetForcedRepaint(false);
688 nsCOMPtr<nsIPresShell> shell = mPresShell;
689 if (shell) {
690 shell->WillPaintWindow();
694 bool nsViewManager::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion)
696 if (!aWidget || !mContext)
697 return false;
699 NS_ASSERTION(IsPaintingAllowed(),
700 "shouldn't be receiving paint events while painting is disallowed!");
702 // Get the view pointer here since NS_WILL_PAINT might have
703 // destroyed it during CallWillPaintOnObservers (bug 378273).
704 nsView* view = nsView::GetViewFor(aWidget);
705 if (view && !aRegion.IsEmpty()) {
706 Refresh(view, aRegion);
709 return true;
712 void nsViewManager::DidPaintWindow()
714 nsCOMPtr<nsIPresShell> shell = mPresShell;
715 if (shell) {
716 shell->DidPaintWindow();
720 void
721 nsViewManager::DispatchEvent(WidgetGUIEvent *aEvent,
722 nsView* aView,
723 nsEventStatus* aStatus)
725 PROFILER_LABEL("nsViewManager", "DispatchEvent",
726 js::ProfileEntry::Category::EVENTS);
728 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
729 if ((mouseEvent &&
730 // Ignore mouse events that we synthesize.
731 mouseEvent->reason == WidgetMouseEvent::eReal &&
732 // Ignore mouse exit and enter (we'll get moves if the user
733 // is really moving the mouse) since we get them when we
734 // create and destroy widgets.
735 mouseEvent->message != NS_MOUSE_EXIT &&
736 mouseEvent->message != NS_MOUSE_ENTER) ||
737 aEvent->HasKeyEventMessage() ||
738 aEvent->HasIMEEventMessage() ||
739 aEvent->message == NS_PLUGIN_INPUT_EVENT) {
740 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
743 // Find the view whose coordinates system we're in.
744 nsView* view = aView;
745 bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
746 if (dispatchUsingCoordinates) {
747 // Will dispatch using coordinates. Pretty bogus but it's consistent
748 // with what presshell does.
749 view = GetDisplayRootFor(view);
752 // If the view has no frame, look for a view that does.
753 nsIFrame* frame = view->GetFrame();
754 if (!frame &&
755 (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
756 aEvent->IsIMERelatedEvent() ||
757 aEvent->IsNonRetargetedNativeEventDelivererForPlugin() ||
758 aEvent->HasPluginActivationEventMessage() ||
759 aEvent->message == NS_PLUGIN_RESOLUTION_CHANGED)) {
760 while (view && !view->GetFrame()) {
761 view = view->GetParent();
764 if (view) {
765 frame = view->GetFrame();
769 if (nullptr != frame) {
770 // Hold a refcount to the presshell. The continued existence of the
771 // presshell will delay deletion of this view hierarchy should the event
772 // want to cause its destruction in, say, some JavaScript event handler.
773 nsCOMPtr<nsIPresShell> shell = view->GetViewManager()->GetPresShell();
774 if (shell) {
775 shell->HandleEvent(frame, aEvent, false, aStatus);
776 return;
780 *aStatus = nsEventStatus_eIgnore;
783 // Recursively reparent widgets if necessary
785 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget *aNewWidget)
787 NS_PRECONDITION(aNewWidget, "");
789 if (aView->HasWidget()) {
790 // Check to see if the parent widget is the
791 // same as the new parent. If not then reparent
792 // the widget, otherwise there is nothing more
793 // to do for the view and its descendants
794 nsIWidget* widget = aView->GetWidget();
795 nsIWidget* parentWidget = widget->GetParent();
796 if (parentWidget) {
797 // Child widget
798 if (parentWidget != aNewWidget) {
799 #ifdef DEBUG
800 nsresult rv =
801 #endif
802 widget->SetParent(aNewWidget);
803 NS_ASSERTION(NS_SUCCEEDED(rv), "SetParent failed!");
805 } else {
806 // Toplevel widget (popup, dialog, etc)
807 widget->ReparentNativeWidget(aNewWidget);
809 return;
812 // Need to check each of the views children to see
813 // if they have a widget and reparent it.
815 for (nsView *kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
816 ReparentChildWidgets(kid, aNewWidget);
820 // Reparent a view and its descendant views widgets if necessary
822 void nsViewManager::ReparentWidgets(nsView* aView, nsView *aParent)
824 NS_PRECONDITION(aParent, "Must have a parent");
825 NS_PRECONDITION(aView, "Must have a view");
827 // Quickly determine whether the view has pre-existing children or a
828 // widget. In most cases the view will not have any pre-existing
829 // children when this is called. Only in the case
830 // where a view has been reparented by removing it from
831 // a reinserting it into a new location in the view hierarchy do we
832 // have to consider reparenting the existing widgets for the view and
833 // it's descendants.
834 if (aView->HasWidget() || aView->GetFirstChild()) {
835 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
836 if (parentWidget) {
837 ReparentChildWidgets(aView, parentWidget);
838 return;
840 NS_WARNING("Can not find a widget for the parent view");
844 void
845 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, nsView *aSibling,
846 bool aAfter)
848 NS_PRECONDITION(nullptr != aParent, "null ptr");
849 NS_PRECONDITION(nullptr != aChild, "null ptr");
850 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
851 "tried to insert view with invalid sibling");
852 NS_ASSERTION(!IsViewInserted(aChild), "tried to insert an already-inserted view");
854 if ((nullptr != aParent) && (nullptr != aChild))
856 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
857 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
859 if (nullptr == aSibling) {
860 if (aAfter) {
861 // insert at end of document order, i.e., before first view
862 // this is the common case, by far
863 aParent->InsertChild(aChild, nullptr);
864 ReparentWidgets(aChild, aParent);
865 } else {
866 // insert at beginning of document order, i.e., after last view
867 nsView *kid = aParent->GetFirstChild();
868 nsView *prev = nullptr;
869 while (kid) {
870 prev = kid;
871 kid = kid->GetNextSibling();
873 // prev is last view or null if there are no children
874 aParent->InsertChild(aChild, prev);
875 ReparentWidgets(aChild, aParent);
877 } else {
878 nsView *kid = aParent->GetFirstChild();
879 nsView *prev = nullptr;
880 while (kid && aSibling != kid) {
881 //get the next sibling view
882 prev = kid;
883 kid = kid->GetNextSibling();
885 NS_ASSERTION(kid != nullptr,
886 "couldn't find sibling in child list");
887 if (aAfter) {
888 // insert after 'kid' in document order, i.e. before in view order
889 aParent->InsertChild(aChild, prev);
890 ReparentWidgets(aChild, aParent);
891 } else {
892 // insert before 'kid' in document order, i.e. after in view order
893 aParent->InsertChild(aChild, kid);
894 ReparentWidgets(aChild, aParent);
898 // if the parent view is marked as "floating", make the newly added view float as well.
899 if (aParent->GetFloating())
900 aChild->SetFloating(true);
904 void
905 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, int32_t aZIndex)
907 // no-one really calls this with anything other than aZIndex == 0 on a fresh view
908 // XXX this method should simply be eliminated and its callers redirected to the real method
909 SetViewZIndex(aChild, false, aZIndex);
910 InsertChild(aParent, aChild, nullptr, true);
913 void
914 nsViewManager::RemoveChild(nsView *aChild)
916 NS_ASSERTION(aChild, "aChild must not be null");
918 nsView* parent = aChild->GetParent();
920 if (nullptr != parent) {
921 NS_ASSERTION(aChild->GetViewManager() == this ||
922 parent->GetViewManager() == this, "wrong view manager");
923 parent->RemoveChild(aChild);
927 void
928 nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY)
930 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
931 aView->SetPosition(aX, aY);
934 void
935 nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly)
937 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
939 nsRect oldDimensions = aView->GetDimensions();
940 if (!oldDimensions.IsEqualEdges(aRect)) {
941 aView->SetDimensions(aRect, true);
944 // Note that if layout resizes the view and the view has a custom clip
945 // region set, then we expect layout to update the clip region too. Thus
946 // in the case where mClipRect has been optimized away to just be a null
947 // pointer, and this resize is implicitly changing the clip rect, it's OK
948 // because layout will change it back again if necessary.
951 void
952 nsViewManager::SetViewFloating(nsView *aView, bool aFloating)
954 NS_ASSERTION(!(nullptr == aView), "no view");
956 aView->SetFloating(aFloating);
959 void
960 nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible)
962 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
964 if (aVisible != aView->GetVisibility()) {
965 aView->SetVisibility(aVisible);
969 bool nsViewManager::IsViewInserted(nsView *aView)
971 if (mRootView == aView) {
972 return true;
973 } else if (aView->GetParent() == nullptr) {
974 return false;
975 } else {
976 nsView* view = aView->GetParent()->GetFirstChild();
977 while (view != nullptr) {
978 if (view == aView) {
979 return true;
981 view = view->GetNextSibling();
983 return false;
987 void
988 nsViewManager::SetViewZIndex(nsView *aView, bool aAutoZIndex, int32_t aZIndex)
990 NS_ASSERTION((aView != nullptr), "no view");
992 // don't allow the root view's z-index to be changed. It should always be zero.
993 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
994 if (aView == mRootView) {
995 return;
998 if (aAutoZIndex) {
999 aZIndex = 0;
1002 aView->SetZIndex(aAutoZIndex, aZIndex);
1005 nsViewManager*
1006 nsViewManager::IncrementDisableRefreshCount()
1008 if (!IsRootVM()) {
1009 return RootViewManager()->IncrementDisableRefreshCount();
1012 ++mRefreshDisableCount;
1014 return this;
1017 void
1018 nsViewManager::DecrementDisableRefreshCount()
1020 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1021 --mRefreshDisableCount;
1022 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
1025 void
1026 nsViewManager::GetRootWidget(nsIWidget **aWidget)
1028 if (!mRootView) {
1029 *aWidget = nullptr;
1030 return;
1032 if (mRootView->HasWidget()) {
1033 *aWidget = mRootView->GetWidget();
1034 NS_ADDREF(*aWidget);
1035 return;
1037 if (mRootView->GetParent()) {
1038 mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
1039 return;
1041 *aWidget = nullptr;
1044 nsIntRect nsViewManager::ViewToWidget(nsView *aView, const nsRect &aRect) const
1046 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
1048 // account for the view's origin not lining up with the widget's
1049 nsRect rect = aRect + aView->ViewToWidgetOffset();
1051 // finally, convert to device coordinates.
1052 return rect.ToOutsidePixels(AppUnitsPerDevPixel());
1055 void
1056 nsViewManager::IsPainting(bool& aIsPainting)
1058 aIsPainting = IsPainting();
1061 void
1062 nsViewManager::ProcessPendingUpdates()
1064 if (!IsRootVM()) {
1065 RootViewManager()->ProcessPendingUpdates();
1066 return;
1069 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
1071 // Flush things like reflows by calling WillPaint on observer presShells.
1072 if (mPresShell) {
1073 CallWillPaintOnObservers();
1075 ProcessPendingUpdatesForView(mRootView, true);
1078 void
1079 nsViewManager::UpdateWidgetGeometry()
1081 if (!IsRootVM()) {
1082 RootViewManager()->UpdateWidgetGeometry();
1083 return;
1086 if (mHasPendingWidgetGeometryChanges) {
1087 mHasPendingWidgetGeometryChanges = false;
1088 ProcessPendingUpdatesForView(mRootView, false);
1092 void
1093 nsViewManager::CallWillPaintOnObservers()
1095 NS_PRECONDITION(IsRootVM(), "Must be root VM for this to be called!");
1097 if (NS_WARN_IF(!gViewManagers)) {
1098 return;
1101 uint32_t index;
1102 for (index = 0; index < gViewManagers->Length(); index++) {
1103 nsViewManager* vm = gViewManagers->ElementAt(index);
1104 if (vm->RootViewManager() == this) {
1105 // One of our kids.
1106 if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
1107 nsCOMPtr<nsIPresShell> shell = vm->GetPresShell();
1108 if (shell) {
1109 shell->WillPaint();
1116 void
1117 nsViewManager::GetLastUserEventTime(uint32_t& aTime)
1119 aTime = gLastUserEventTime;
1122 void
1123 nsViewManager::InvalidateHierarchy()
1125 if (mRootView) {
1126 if (!IsRootVM()) {
1127 NS_RELEASE(mRootViewManager);
1129 nsView *parent = mRootView->GetParent();
1130 if (parent) {
1131 mRootViewManager = parent->GetViewManager()->RootViewManager();
1132 NS_ADDREF(mRootViewManager);
1133 NS_ASSERTION(mRootViewManager != this,
1134 "Root view had a parent, but it has the same view manager");
1135 } else {
1136 mRootViewManager = this;