Backed out changeset 9157ea42ff41 (bug 914925) for Android/B2G test bustage.
[gecko.git] / view / src / nsViewManager.cpp
blob807be26fa5d07420143e52f6d751cbe7dc130dce
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 "nsGUIEvent.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 "nsEventStateManager.h"
22 #include "mozilla/StartupTimeline.h"
23 #include "GeckoProfiler.h"
24 #include "nsRefreshDriver.h"
25 #include "mozilla/Preferences.h"
26 #include "nsContentUtils.h" // for nsAutoScriptBlocker
27 #include "nsLayoutUtils.h"
28 #include "Layers.h"
29 #include "gfxPlatform.h"
31 /**
32 XXX TODO XXX
34 DeCOMify newly private methods
35 Optimize view storage
38 /**
39 A note about platform assumptions:
41 We assume that a widget is z-ordered on top of its parent.
43 We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
44 we ask for a specific z-order, we don't assume that widget z-ordering actually works.
47 using namespace mozilla::layers;
49 #define NSCOORD_NONE INT32_MIN
51 #undef DEBUG_MOUSE_LOCATION
53 int32_t nsViewManager::mVMCount = 0;
55 // Weakly held references to all of the view managers
56 nsVoidArray* 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 NS_ASSERTION(mVMCount == 0, "View Manager count is incorrect");
65 // Create an array to hold a list of view managers
66 gViewManagers = new nsVoidArray;
69 gViewManagers->AppendElement(this);
71 ++mVMCount;
73 // NOTE: we use a zeroing operator new, so all data members are
74 // assumed to be cleared here.
75 mHasPendingWidgetGeometryChanges = false;
76 mRecursiveRefreshPending = false;
79 nsViewManager::~nsViewManager()
81 if (mRootView) {
82 // Destroy any remaining views
83 mRootView->Destroy();
84 mRootView = nullptr;
87 if (!IsRootVM()) {
88 // We have a strong ref to mRootViewManager
89 NS_RELEASE(mRootViewManager);
92 NS_ASSERTION((mVMCount > 0), "underflow of viewmanagers");
93 --mVMCount;
95 #ifdef DEBUG
96 bool removed =
97 #endif
98 gViewManagers->RemoveElement(this);
99 NS_ASSERTION(removed, "Viewmanager instance not was not in the global list of viewmanagers");
101 if (0 == mVMCount) {
102 // There aren't any more view managers so
103 // release the global array of view managers
105 NS_ASSERTION(gViewManagers != nullptr, "About to delete null gViewManagers");
106 delete gViewManagers;
107 gViewManagers = nullptr;
110 mPresShell = nullptr;
113 // We don't hold a reference to the presentation context because it
114 // holds a reference to us.
115 nsresult
116 nsViewManager::Init(nsDeviceContext* aContext)
118 NS_PRECONDITION(nullptr != aContext, "null ptr");
120 if (nullptr == aContext) {
121 return NS_ERROR_NULL_POINTER;
123 if (nullptr != mContext) {
124 return NS_ERROR_ALREADY_INITIALIZED;
126 mContext = aContext;
128 return NS_OK;
131 nsView*
132 nsViewManager::CreateView(const nsRect& aBounds,
133 nsView* aParent,
134 nsViewVisibility aVisibilityFlag)
136 nsView *v = new nsView(this, aVisibilityFlag);
137 v->SetParent(aParent);
138 v->SetPosition(aBounds.x, aBounds.y);
139 nsRect dim(0, 0, aBounds.width, aBounds.height);
140 v->SetDimensions(dim, false);
141 return v;
144 void
145 nsViewManager::SetRootView(nsView *aView)
147 NS_PRECONDITION(!aView || aView->GetViewManager() == this,
148 "Unexpected viewmanager on root view");
150 // Do NOT destroy the current root view. It's the caller's responsibility
151 // to destroy it
152 mRootView = aView;
154 if (mRootView) {
155 nsView* parent = mRootView->GetParent();
156 if (parent) {
157 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
158 // no need to set mRootViewManager ourselves here.
159 parent->InsertChild(mRootView, nullptr);
160 } else {
161 InvalidateHierarchy();
164 mRootView->SetZIndex(false, 0);
166 // Else don't touch mRootViewManager
169 void
170 nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
172 if (nullptr != mRootView) {
173 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
174 nsRect dim = mRootView->GetDimensions();
175 *aWidth = dim.width;
176 *aHeight = dim.height;
177 } else {
178 *aWidth = mDelayedResize.width;
179 *aHeight = mDelayedResize.height;
182 else
184 *aWidth = 0;
185 *aHeight = 0;
189 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
191 nsRect oldDim = mRootView->GetDimensions();
192 nsRect newDim(0, 0, aWidth, aHeight);
193 // We care about resizes even when one dimension is already zero.
194 if (!oldDim.IsEqualEdges(newDim)) {
195 // Don't resize the widget. It is already being set elsewhere.
196 mRootView->SetDimensions(newDim, true, false);
197 if (mPresShell)
198 mPresShell->ResizeReflow(aWidth, aHeight);
202 void
203 nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
205 if (mRootView) {
206 if (mRootView->IsEffectivelyVisible() && mPresShell && mPresShell->IsVisible()) {
207 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
208 mDelayedResize != nsSize(aWidth, aHeight)) {
209 // We have a delayed resize; that now obsolete size may already have
210 // been flushed to the PresContext so we need to update the PresContext
211 // with the new size because if the new size is exactly the same as the
212 // root view's current size then DoSetWindowDimensions will not
213 // request a resize reflow (which would correct it). See bug 617076.
214 mDelayedResize = nsSize(aWidth, aHeight);
215 FlushDelayedResize(false);
217 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
218 DoSetWindowDimensions(aWidth, aHeight);
219 } else {
220 mDelayedResize.SizeTo(aWidth, aHeight);
221 if (mPresShell && mPresShell->GetDocument()) {
222 mPresShell->GetDocument()->SetNeedStyleFlush();
228 void
229 nsViewManager::FlushDelayedResize(bool aDoReflow)
231 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
232 if (aDoReflow) {
233 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height);
234 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
235 } else if (mPresShell) {
236 nsPresContext* presContext = mPresShell->GetPresContext();
237 if (presContext) {
238 presContext->SetVisibleArea(nsRect(nsPoint(0, 0), mDelayedResize));
244 // Convert aIn from being relative to and in appunits of aFromView, to being
245 // relative to and in appunits of aToView.
246 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
247 nsView* aFromView,
248 nsView* aToView)
250 nsRegion out = aIn;
251 out.MoveBy(aFromView->GetOffsetTo(aToView));
252 out = out.ConvertAppUnitsRoundOut(
253 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
254 aToView->GetViewManager()->AppUnitsPerDevPixel());
255 return out;
258 nsView* nsViewManager::GetDisplayRootFor(nsView* aView)
260 nsView *displayRoot = aView;
261 for (;;) {
262 nsView *displayParent = displayRoot->GetParent();
263 if (!displayParent)
264 return displayRoot;
266 if (displayRoot->GetFloating() && !displayParent->GetFloating())
267 return displayRoot;
269 // If we have a combobox dropdown popup within a panel popup, both the view
270 // for the dropdown popup and its parent will be floating, so we need to
271 // distinguish this situation. We do this by looking for a widget. Any view
272 // with a widget is a display root, except for plugins.
273 nsIWidget* widget = displayRoot->GetWidget();
274 if (widget) {
275 nsWindowType type;
276 widget->GetWindowType(type);
277 if (type == eWindowType_popup) {
278 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
279 "this should only happen with floating views that have floating parents");
280 return displayRoot;
284 displayRoot = displayParent;
289 aRegion is given in device coordinates!!
290 aContext may be null, in which case layers should be used for
291 rendering.
293 void nsViewManager::Refresh(nsView *aView, const nsIntRegion& aRegion)
295 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
297 // damageRegion is the damaged area, in twips, relative to the view origin
298 nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
299 // move region from widget coordinates into view coordinates
300 damageRegion.MoveBy(-aView->ViewToWidgetOffset());
302 if (damageRegion.IsEmpty()) {
303 #ifdef DEBUG_roc
304 nsRect viewRect = aView->GetDimensions();
305 nsRect damageRect = damageRegion.GetBounds();
306 printf("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
307 damageRect.x, damageRect.y, damageRect.width, damageRect.height,
308 viewRect.x, viewRect.y, viewRect.width, viewRect.height);
309 #endif
310 return;
313 nsIWidget *widget = aView->GetWidget();
314 if (!widget) {
315 return;
318 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
319 if (IsPainting()) {
320 RootViewManager()->mRecursiveRefreshPending = true;
321 return;
325 nsAutoScriptBlocker scriptBlocker;
326 SetPainting(true);
328 NS_ASSERTION(GetDisplayRootFor(aView) == aView,
329 "Widgets that we paint must all be display roots");
331 if (mPresShell) {
332 #ifdef MOZ_DUMP_PAINTING
333 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
334 printf("--COMPOSITE-- %p\n", mPresShell);
336 #endif
337 uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE;
338 LayerManager *manager = widget->GetLayerManager();
339 if (!manager->NeedsWidgetInvalidation()) {
340 manager->FlushRendering();
341 } else {
342 mPresShell->Paint(aView, damageRegion,
343 paintFlags);
345 #ifdef MOZ_DUMP_PAINTING
346 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
347 printf("--ENDCOMPOSITE--\n");
349 #endif
350 mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
353 SetPainting(false);
356 if (RootViewManager()->mRecursiveRefreshPending) {
357 RootViewManager()->mRecursiveRefreshPending = false;
358 InvalidateAllViews();
362 void nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
363 bool aFlushDirtyRegion)
365 NS_ASSERTION(IsRootVM(), "Updates will be missed");
367 // Protect against a null-view.
368 if (!aView) {
369 return;
372 if (aView->HasWidget()) {
373 aView->ResetWidgetBounds(false, true);
376 // process pending updates in child view.
377 for (nsView* childView = aView->GetFirstChild(); childView;
378 childView = childView->GetNextSibling()) {
379 ProcessPendingUpdatesForView(childView, aFlushDirtyRegion);
382 // Push out updates after we've processed the children; ensures that
383 // damage is applied based on the final widget geometry
384 if (aFlushDirtyRegion) {
385 nsIWidget *widget = aView->GetWidget();
386 if (widget && widget->NeedsPaint()) {
387 // If an ancestor widget was hidden and then shown, we could
388 // have a delayed resize to handle.
389 for (nsViewManager *vm = this; vm;
390 vm = vm->mRootView->GetParent()
391 ? vm->mRootView->GetParent()->GetViewManager()
392 : nullptr) {
393 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
394 vm->mRootView->IsEffectivelyVisible() &&
395 mPresShell && mPresShell->IsVisible()) {
396 vm->FlushDelayedResize(true);
400 NS_ASSERTION(aView->HasWidget(), "Must have a widget!");
402 #ifdef MOZ_DUMP_PAINTING
403 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
404 printf("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget);
406 #endif
407 nsAutoScriptBlocker scriptBlocker;
408 NS_ASSERTION(aView->HasWidget(), "Must have a widget!");
409 SetPainting(true);
410 mPresShell->Paint(aView, nsRegion(),
411 nsIPresShell::PAINT_LAYERS);
412 #ifdef MOZ_DUMP_PAINTING
413 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
414 printf("---- PAINT END ----\n");
416 #endif
417 aView->SetForcedRepaint(false);
418 SetPainting(false);
419 FlushDirtyRegionToWidget(aView);
420 } else {
421 FlushDirtyRegionToWidget(aView);
426 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
428 if (!aView->HasNonEmptyDirtyRegion())
429 return;
431 nsRegion* dirtyRegion = aView->GetDirtyRegion();
432 nsView* nearestViewWithWidget = aView;
433 while (!nearestViewWithWidget->HasWidget() &&
434 nearestViewWithWidget->GetParent()) {
435 nearestViewWithWidget = nearestViewWithWidget->GetParent();
437 nsRegion r =
438 ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
440 // If we draw the frame counter we need to make sure we invalidate the area
441 // for it to make it on screen
442 if (gfxPlatform::DrawFrameCounter()) {
443 nsRect counterBounds = gfxPlatform::FrameCounterBounds().ToAppUnits(AppUnitsPerDevPixel());
444 r = r.Or(r, counterBounds);
447 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
448 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
449 dirtyRegion->SetEmpty();
452 void
453 nsViewManager::InvalidateView(nsView *aView)
455 // Mark the entire view as damaged
456 InvalidateView(aView, aView->GetDimensions());
459 static void
460 AddDirtyRegion(nsView *aView, const nsRegion &aDamagedRegion)
462 nsRegion* dirtyRegion = aView->GetDirtyRegion();
463 if (!dirtyRegion)
464 return;
466 dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
467 dirtyRegion->SimplifyOutward(8);
470 void
471 nsViewManager::PostPendingUpdate()
473 nsViewManager* rootVM = RootViewManager();
474 rootVM->mHasPendingWidgetGeometryChanges = true;
475 if (rootVM->mPresShell) {
476 rootVM->mPresShell->ScheduleViewManagerFlush();
481 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
482 * every widget child of aWidgetView, plus aWidgetView's own widget
484 void
485 nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
486 const nsRegion &aDamagedRegion)
488 NS_ASSERTION(aWidgetView->GetViewManager() == this,
489 "InvalidateWidgetArea called on view we don't own");
490 nsIWidget* widget = aWidgetView->GetWidget();
492 #if 0
493 nsRect dbgBounds = aDamagedRegion.GetBounds();
494 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
495 aWidgetView, aWidgetView->IsAttachedToTopLevel(),
496 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
497 #endif
499 // If the widget is hidden, it don't cover nothing
500 if (widget && !widget->IsVisible()) {
501 return;
504 if (!widget) {
505 // The root view or a scrolling view might not have a widget
506 // (for example, during printing). We get here when we scroll
507 // during printing to show selected options in a listbox, for example.
508 return;
511 // Update all child widgets with the damage. In the process,
512 // accumulate the union of all the child widget areas, or at least
513 // some subset of that.
514 nsRegion children;
515 if (widget->GetTransparencyMode() != eTransparencyTransparent) {
516 for (nsIWidget* childWidget = widget->GetFirstChild();
517 childWidget;
518 childWidget = childWidget->GetNextSibling()) {
519 nsView* view = nsView::GetViewFor(childWidget);
520 NS_ASSERTION(view != aWidgetView, "will recur infinitely");
521 nsWindowType type;
522 childWidget->GetWindowType(type);
523 if (view && childWidget->IsVisible() && type != eWindowType_popup) {
524 NS_ASSERTION(type == eWindowType_plugin,
525 "Only plugin or popup widgets can be children!");
527 // We do not need to invalidate in plugin widgets, but we should
528 // exclude them from the invalidation region IF we're not on
529 // Mac. On Mac we need to draw under plugin widgets, because
530 // plugin widgets are basically invisible
531 #ifndef XP_MACOSX
532 // GetBounds should compensate for chrome on a toplevel widget
533 nsIntRect bounds;
534 childWidget->GetBounds(bounds);
536 nsTArray<nsIntRect> clipRects;
537 childWidget->GetWindowClipRegion(&clipRects);
538 for (uint32_t i = 0; i < clipRects.Length(); ++i) {
539 nsRect rr = (clipRects[i] + bounds.TopLeft()).
540 ToAppUnits(AppUnitsPerDevPixel());
541 children.Or(children, rr - aWidgetView->ViewToWidgetOffset());
542 children.SimplifyInward(20);
544 #endif
549 nsRegion leftOver;
550 leftOver.Sub(aDamagedRegion, children);
552 if (!leftOver.IsEmpty()) {
553 const nsRect* r;
554 for (nsRegionRectIterator iter(leftOver); (r = iter.Next());) {
555 nsIntRect bounds = ViewToWidget(aWidgetView, *r);
556 widget->Invalidate(bounds);
561 static bool
562 ShouldIgnoreInvalidation(nsViewManager* aVM)
564 while (aVM) {
565 nsIPresShell* shell = aVM->GetPresShell();
566 if (!shell || shell->ShouldIgnoreInvalidation()) {
567 return true;
569 nsView* view = aVM->GetRootView()->GetParent();
570 aVM = view ? view->GetViewManager() : nullptr;
572 return false;
575 void
576 nsViewManager::InvalidateView(nsView *aView, const nsRect &aRect)
578 // If painting is suppressed in the presshell or an ancestor drop all
579 // invalidates, it will invalidate everything when it unsuppresses.
580 if (ShouldIgnoreInvalidation(this)) {
581 return;
584 InvalidateViewNoSuppression(aView, aRect);
587 void
588 nsViewManager::InvalidateViewNoSuppression(nsView *aView,
589 const nsRect &aRect)
591 NS_PRECONDITION(nullptr != aView, "null view");
593 NS_ASSERTION(aView->GetViewManager() == this,
594 "InvalidateViewNoSuppression called on view we don't own");
596 nsRect damagedRect(aRect);
597 if (damagedRect.IsEmpty()) {
598 return;
601 nsView* displayRoot = GetDisplayRootFor(aView);
602 nsViewManager* displayRootVM = displayRoot->GetViewManager();
603 // Propagate the update to the displayRoot, since iframes, for example,
604 // can overlap each other and be translucent. So we have to possibly
605 // invalidate our rect in each of the widgets we have lying about.
606 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
607 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
608 int32_t APD = AppUnitsPerDevPixel();
609 damagedRect = damagedRect.ConvertAppUnitsRoundOut(APD, rootAPD);
611 // accumulate this rectangle in the view's dirty region, so we can
612 // process it later.
613 AddDirtyRegion(displayRoot, nsRegion(damagedRect));
616 void
617 nsViewManager::InvalidateAllViews()
619 if (RootViewManager() != this) {
620 return RootViewManager()->InvalidateAllViews();
623 InvalidateViews(mRootView);
626 void nsViewManager::InvalidateViews(nsView *aView)
628 // Invalidate this view.
629 InvalidateView(aView);
631 // Invalidate all children as well.
632 nsView* childView = aView->GetFirstChild();
633 while (nullptr != childView) {
634 childView->GetViewManager()->InvalidateViews(childView);
635 childView = childView->GetNextSibling();
639 void nsViewManager::WillPaintWindow(nsIWidget* aWidget)
641 if (aWidget) {
642 nsView* view = nsView::GetViewFor(aWidget);
643 LayerManager *manager = aWidget->GetLayerManager();
644 if (view &&
645 (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) {
646 ProcessPendingUpdates();
647 // Re-get the view pointer here since the ProcessPendingUpdates might have
648 // destroyed it during CallWillPaintOnObservers.
649 view = nsView::GetViewFor(aWidget);
650 if (view) {
651 view->SetForcedRepaint(false);
656 nsCOMPtr<nsIPresShell> shell = mPresShell;
657 if (shell) {
658 shell->WillPaintWindow();
662 bool nsViewManager::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion)
664 if (!aWidget || !mContext)
665 return false;
667 NS_ASSERTION(IsPaintingAllowed(),
668 "shouldn't be receiving paint events while painting is disallowed!");
670 // Get the view pointer here since NS_WILL_PAINT might have
671 // destroyed it during CallWillPaintOnObservers (bug 378273).
672 nsView* view = nsView::GetViewFor(aWidget);
673 if (view && !aRegion.IsEmpty()) {
674 Refresh(view, aRegion);
677 return true;
680 void nsViewManager::DidPaintWindow()
682 nsCOMPtr<nsIPresShell> shell = mPresShell;
683 if (shell) {
684 shell->DidPaintWindow();
688 void
689 nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsView* aView, nsEventStatus* aStatus)
691 PROFILER_LABEL("event", "nsViewManager::DispatchEvent");
693 if ((NS_IS_MOUSE_EVENT(aEvent) &&
694 // Ignore mouse events that we synthesize.
695 static_cast<nsMouseEvent*>(aEvent)->reason == nsMouseEvent::eReal &&
696 // Ignore mouse exit and enter (we'll get moves if the user
697 // is really moving the mouse) since we get them when we
698 // create and destroy widgets.
699 aEvent->message != NS_MOUSE_EXIT &&
700 aEvent->message != NS_MOUSE_ENTER) ||
701 NS_IS_KEY_EVENT(aEvent) ||
702 NS_IS_IME_EVENT(aEvent) ||
703 aEvent->message == NS_PLUGIN_INPUT_EVENT) {
704 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
707 // Find the view whose coordinates system we're in.
708 nsView* view = aView;
709 bool dispatchUsingCoordinates = NS_IsEventUsingCoordinates(aEvent);
710 if (dispatchUsingCoordinates) {
711 // Will dispatch using coordinates. Pretty bogus but it's consistent
712 // with what presshell does.
713 view = GetDisplayRootFor(view);
716 // If the view has no frame, look for a view that does.
717 nsIFrame* frame = view->GetFrame();
718 if (!frame &&
719 (dispatchUsingCoordinates || NS_IS_KEY_EVENT(aEvent) ||
720 NS_IS_IME_RELATED_EVENT(aEvent) ||
721 NS_IS_NON_RETARGETED_PLUGIN_EVENT(aEvent) ||
722 aEvent->message == NS_PLUGIN_ACTIVATE ||
723 aEvent->message == NS_PLUGIN_FOCUS ||
724 aEvent->message == NS_PLUGIN_RESOLUTION_CHANGED)) {
725 while (view && !view->GetFrame()) {
726 view = view->GetParent();
729 if (view) {
730 frame = view->GetFrame();
734 if (nullptr != frame) {
735 // Hold a refcount to the presshell. The continued existence of the
736 // presshell will delay deletion of this view hierarchy should the event
737 // want to cause its destruction in, say, some JavaScript event handler.
738 nsCOMPtr<nsIPresShell> shell = view->GetViewManager()->GetPresShell();
739 if (shell) {
740 shell->HandleEvent(frame, aEvent, false, aStatus);
741 return;
745 *aStatus = nsEventStatus_eIgnore;
748 // Recursively reparent widgets if necessary
750 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget *aNewWidget)
752 NS_PRECONDITION(aNewWidget, "");
754 if (aView->HasWidget()) {
755 // Check to see if the parent widget is the
756 // same as the new parent. If not then reparent
757 // the widget, otherwise there is nothing more
758 // to do for the view and its descendants
759 nsIWidget* widget = aView->GetWidget();
760 nsIWidget* parentWidget = widget->GetParent();
761 if (parentWidget) {
762 // Child widget
763 if (parentWidget != aNewWidget) {
764 #ifdef DEBUG
765 nsresult rv =
766 #endif
767 widget->SetParent(aNewWidget);
768 NS_ASSERTION(NS_SUCCEEDED(rv), "SetParent failed!");
770 } else {
771 // Toplevel widget (popup, dialog, etc)
772 widget->ReparentNativeWidget(aNewWidget);
774 return;
777 // Need to check each of the views children to see
778 // if they have a widget and reparent it.
780 for (nsView *kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
781 ReparentChildWidgets(kid, aNewWidget);
785 // Reparent a view and its descendant views widgets if necessary
787 void nsViewManager::ReparentWidgets(nsView* aView, nsView *aParent)
789 NS_PRECONDITION(aParent, "Must have a parent");
790 NS_PRECONDITION(aView, "Must have a view");
792 // Quickly determine whether the view has pre-existing children or a
793 // widget. In most cases the view will not have any pre-existing
794 // children when this is called. Only in the case
795 // where a view has been reparented by removing it from
796 // a reinserting it into a new location in the view hierarchy do we
797 // have to consider reparenting the existing widgets for the view and
798 // it's descendants.
799 if (aView->HasWidget() || aView->GetFirstChild()) {
800 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
801 if (parentWidget) {
802 ReparentChildWidgets(aView, parentWidget);
803 return;
805 NS_WARNING("Can not find a widget for the parent view");
809 void
810 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, nsView *aSibling,
811 bool aAfter)
813 NS_PRECONDITION(nullptr != aParent, "null ptr");
814 NS_PRECONDITION(nullptr != aChild, "null ptr");
815 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
816 "tried to insert view with invalid sibling");
817 NS_ASSERTION(!IsViewInserted(aChild), "tried to insert an already-inserted view");
819 if ((nullptr != aParent) && (nullptr != aChild))
821 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
822 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
824 if (nullptr == aSibling) {
825 if (aAfter) {
826 // insert at end of document order, i.e., before first view
827 // this is the common case, by far
828 aParent->InsertChild(aChild, nullptr);
829 ReparentWidgets(aChild, aParent);
830 } else {
831 // insert at beginning of document order, i.e., after last view
832 nsView *kid = aParent->GetFirstChild();
833 nsView *prev = nullptr;
834 while (kid) {
835 prev = kid;
836 kid = kid->GetNextSibling();
838 // prev is last view or null if there are no children
839 aParent->InsertChild(aChild, prev);
840 ReparentWidgets(aChild, aParent);
842 } else {
843 nsView *kid = aParent->GetFirstChild();
844 nsView *prev = nullptr;
845 while (kid && aSibling != kid) {
846 //get the next sibling view
847 prev = kid;
848 kid = kid->GetNextSibling();
850 NS_ASSERTION(kid != nullptr,
851 "couldn't find sibling in child list");
852 if (aAfter) {
853 // insert after 'kid' in document order, i.e. before in view order
854 aParent->InsertChild(aChild, prev);
855 ReparentWidgets(aChild, aParent);
856 } else {
857 // insert before 'kid' in document order, i.e. after in view order
858 aParent->InsertChild(aChild, kid);
859 ReparentWidgets(aChild, aParent);
863 // if the parent view is marked as "floating", make the newly added view float as well.
864 if (aParent->GetFloating())
865 aChild->SetFloating(true);
869 void
870 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, int32_t aZIndex)
872 // no-one really calls this with anything other than aZIndex == 0 on a fresh view
873 // XXX this method should simply be eliminated and its callers redirected to the real method
874 SetViewZIndex(aChild, false, aZIndex);
875 InsertChild(aParent, aChild, nullptr, true);
878 void
879 nsViewManager::RemoveChild(nsView *aChild)
881 NS_ASSERTION(aChild, "aChild must not be null");
883 nsView* parent = aChild->GetParent();
885 if (nullptr != parent) {
886 NS_ASSERTION(aChild->GetViewManager() == this ||
887 parent->GetViewManager() == this, "wrong view manager");
888 parent->RemoveChild(aChild);
892 void
893 nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY)
895 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
896 aView->SetPosition(aX, aY);
899 void
900 nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly)
902 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
904 nsRect oldDimensions = aView->GetDimensions();
905 if (!oldDimensions.IsEqualEdges(aRect)) {
906 aView->SetDimensions(aRect, true);
909 // Note that if layout resizes the view and the view has a custom clip
910 // region set, then we expect layout to update the clip region too. Thus
911 // in the case where mClipRect has been optimized away to just be a null
912 // pointer, and this resize is implicitly changing the clip rect, it's OK
913 // because layout will change it back again if necessary.
916 void
917 nsViewManager::SetViewFloating(nsView *aView, bool aFloating)
919 NS_ASSERTION(!(nullptr == aView), "no view");
921 aView->SetFloating(aFloating);
924 void
925 nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible)
927 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
929 if (aVisible != aView->GetVisibility()) {
930 aView->SetVisibility(aVisible);
934 bool nsViewManager::IsViewInserted(nsView *aView)
936 if (mRootView == aView) {
937 return true;
938 } else if (aView->GetParent() == nullptr) {
939 return false;
940 } else {
941 nsView* view = aView->GetParent()->GetFirstChild();
942 while (view != nullptr) {
943 if (view == aView) {
944 return true;
946 view = view->GetNextSibling();
948 return false;
952 void
953 nsViewManager::SetViewZIndex(nsView *aView, bool aAutoZIndex, int32_t aZIndex)
955 NS_ASSERTION((aView != nullptr), "no view");
957 // don't allow the root view's z-index to be changed. It should always be zero.
958 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
959 if (aView == mRootView) {
960 return;
963 if (aAutoZIndex) {
964 aZIndex = 0;
967 aView->SetZIndex(aAutoZIndex, aZIndex);
970 nsViewManager*
971 nsViewManager::IncrementDisableRefreshCount()
973 if (!IsRootVM()) {
974 return RootViewManager()->IncrementDisableRefreshCount();
977 ++mRefreshDisableCount;
979 return this;
982 void
983 nsViewManager::DecrementDisableRefreshCount()
985 NS_ASSERTION(IsRootVM(), "Should only be called on root");
986 --mRefreshDisableCount;
987 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
990 void
991 nsViewManager::GetRootWidget(nsIWidget **aWidget)
993 if (!mRootView) {
994 *aWidget = nullptr;
995 return;
997 if (mRootView->HasWidget()) {
998 *aWidget = mRootView->GetWidget();
999 NS_ADDREF(*aWidget);
1000 return;
1002 if (mRootView->GetParent()) {
1003 mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
1004 return;
1006 *aWidget = nullptr;
1009 nsIntRect nsViewManager::ViewToWidget(nsView *aView, const nsRect &aRect) const
1011 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
1013 // account for the view's origin not lining up with the widget's
1014 nsRect rect = aRect + aView->ViewToWidgetOffset();
1016 // finally, convert to device coordinates.
1017 return rect.ToOutsidePixels(AppUnitsPerDevPixel());
1020 void
1021 nsViewManager::IsPainting(bool& aIsPainting)
1023 aIsPainting = IsPainting();
1026 void
1027 nsViewManager::ProcessPendingUpdates()
1029 if (!IsRootVM()) {
1030 RootViewManager()->ProcessPendingUpdates();
1031 return;
1034 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
1036 // Flush things like reflows by calling WillPaint on observer presShells.
1037 if (mPresShell) {
1038 CallWillPaintOnObservers();
1040 ProcessPendingUpdatesForView(mRootView, true);
1043 void
1044 nsViewManager::UpdateWidgetGeometry()
1046 if (!IsRootVM()) {
1047 RootViewManager()->UpdateWidgetGeometry();
1048 return;
1051 if (mHasPendingWidgetGeometryChanges) {
1052 mHasPendingWidgetGeometryChanges = false;
1053 ProcessPendingUpdatesForView(mRootView, false);
1057 void
1058 nsViewManager::CallWillPaintOnObservers()
1060 NS_PRECONDITION(IsRootVM(), "Must be root VM for this to be called!");
1062 int32_t index;
1063 for (index = 0; index < mVMCount; index++) {
1064 nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index);
1065 if (vm->RootViewManager() == this) {
1066 // One of our kids.
1067 if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
1068 nsCOMPtr<nsIPresShell> shell = vm->GetPresShell();
1069 if (shell) {
1070 shell->WillPaint();
1077 void
1078 nsViewManager::GetLastUserEventTime(uint32_t& aTime)
1080 aTime = gLastUserEventTime;
1083 void
1084 nsViewManager::InvalidateHierarchy()
1086 if (mRootView) {
1087 if (!IsRootVM()) {
1088 NS_RELEASE(mRootViewManager);
1090 nsView *parent = mRootView->GetParent();
1091 if (parent) {
1092 mRootViewManager = parent->GetViewManager()->RootViewManager();
1093 NS_ADDREF(mRootViewManager);
1094 NS_ASSERTION(mRootViewManager != this,
1095 "Root view had a parent, but it has the same view manager");
1096 } else {
1097 mRootViewManager = this;