Merge mozilla-central to autoland. a=merge CLOSED TREE
[gecko.git] / view / nsViewManager.cpp
blobedc05230dbf1ef7acbf1762097a534f92b0defed
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 #include "nsViewManager.h"
8 #include "mozilla/MouseEvents.h"
9 #include "mozilla/PresShell.h"
10 #include "mozilla/PresShellInlines.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/ProfilerLabels.h"
13 #include "mozilla/StartupTimeline.h"
14 #include "mozilla/dom/Document.h"
15 #include "nsGfxCIID.h"
16 #include "nsView.h"
17 #include "nsCOMPtr.h"
18 #include "nsRegion.h"
19 #include "nsCOMArray.h"
20 #include "nsXULPopupManager.h"
21 #include "nsPresContext.h"
22 #include "nsRefreshDriver.h"
23 #include "nsContentUtils.h" // for nsAutoScriptBlocker
24 #include "nsLayoutUtils.h"
25 #include "gfxPlatform.h"
26 #include "WindowRenderer.h"
28 /**
29 XXX TODO XXX
31 DeCOMify newly private methods
32 Optimize view storage
35 /**
36 A note about platform assumptions:
38 We assume that a widget is z-ordered on top of its parent.
40 We do NOT assume anything about the relative z-ordering of sibling widgets.
41 Even though we ask for a specific z-order, we don't assume that widget
42 z-ordering actually works.
45 using namespace mozilla;
46 using namespace mozilla::layers;
48 #define NSCOORD_NONE INT32_MIN
50 #undef DEBUG_MOUSE_LOCATION
52 uint32_t nsViewManager::gLastUserEventTime = 0;
54 nsViewManager::nsViewManager()
55 : mPresShell(nullptr),
56 mDelayedResize(NSCOORD_NONE, NSCOORD_NONE),
57 mRootView(nullptr),
58 mRefreshDisableCount(0),
59 mPainting(false),
60 mRecursiveRefreshPending(false),
61 mHasPendingWidgetGeometryChanges(false) {}
63 nsViewManager::~nsViewManager() {
64 if (mRootView) {
65 // Destroy any remaining views
66 mRootView->Destroy();
67 mRootView = nullptr;
70 mRootViewManager = nullptr;
72 MOZ_RELEASE_ASSERT(!mPresShell,
73 "Releasing nsViewManager without having called Destroy on "
74 "the PresShell!");
77 // We don't hold a reference to the presentation context because it
78 // holds a reference to us.
79 nsresult nsViewManager::Init(nsDeviceContext* aContext) {
80 MOZ_ASSERT(nullptr != aContext, "null ptr");
82 if (nullptr == aContext) {
83 return NS_ERROR_NULL_POINTER;
85 if (nullptr != mContext) {
86 return NS_ERROR_ALREADY_INITIALIZED;
88 mContext = aContext;
90 return NS_OK;
93 nsView* nsViewManager::CreateView(const nsRect& aBounds, nsView* aParent,
94 ViewVisibility aVisibilityFlag) {
95 auto* v = new nsView(this, aVisibilityFlag);
96 v->SetParent(aParent);
97 v->SetPosition(aBounds.X(), aBounds.Y());
98 nsRect dim(0, 0, aBounds.Width(), aBounds.Height());
99 v->SetDimensions(dim, false);
100 return v;
103 void nsViewManager::SetRootView(nsView* aView) {
104 MOZ_ASSERT(!aView || aView->GetViewManager() == this,
105 "Unexpected viewmanager on root view");
107 // Do NOT destroy the current root view. It's the caller's responsibility
108 // to destroy it
109 mRootView = aView;
111 if (mRootView) {
112 nsView* parent = mRootView->GetParent();
113 if (parent) {
114 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
115 // no need to set mRootViewManager ourselves here.
116 parent->InsertChild(mRootView, nullptr);
117 } else {
118 InvalidateHierarchy();
121 mRootView->SetZIndex(false, 0);
123 // Else don't touch mRootViewManager
126 void nsViewManager::GetWindowDimensions(nscoord* aWidth, nscoord* aHeight) {
127 if (nullptr != mRootView) {
128 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
129 nsRect dim = mRootView->GetDimensions();
130 *aWidth = dim.Width();
131 *aHeight = dim.Height();
132 } else {
133 *aWidth = mDelayedResize.width;
134 *aHeight = mDelayedResize.height;
136 } else {
137 *aWidth = 0;
138 *aHeight = 0;
142 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight) {
143 nsRect oldDim = mRootView->GetDimensions();
144 nsRect newDim(0, 0, aWidth, aHeight);
145 // We care about resizes even when one dimension is already zero.
146 if (oldDim.IsEqualEdges(newDim)) {
147 return;
149 // Don't resize the widget. It is already being set elsewhere.
150 mRootView->SetDimensions(newDim, true, false);
151 if (RefPtr<PresShell> presShell = mPresShell) {
152 presShell->ResizeReflow(aWidth, aHeight);
156 bool nsViewManager::ShouldDelayResize() const {
157 MOZ_ASSERT(mRootView);
158 if (!mRootView->IsEffectivelyVisible() || !mPresShell ||
159 !mPresShell->IsVisible()) {
160 return true;
162 if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
163 if (rd->IsResizeSuppressed()) {
164 return true;
167 return false;
170 void nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight,
171 bool aDelayResize) {
172 if (mRootView) {
173 if (!ShouldDelayResize() && !aDelayResize) {
174 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
175 mDelayedResize != nsSize(aWidth, aHeight)) {
176 // We have a delayed resize; that now obsolete size may already have
177 // been flushed to the PresContext so we need to update the PresContext
178 // with the new size because if the new size is exactly the same as the
179 // root view's current size then DoSetWindowDimensions will not
180 // request a resize reflow (which would correct it). See bug 617076.
181 mDelayedResize = nsSize(aWidth, aHeight);
182 FlushDelayedResize();
184 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
185 DoSetWindowDimensions(aWidth, aHeight);
186 } else {
187 mDelayedResize.SizeTo(aWidth, aHeight);
188 if (mPresShell) {
189 mPresShell->SetNeedStyleFlush();
190 mPresShell->SetNeedLayoutFlush();
196 void nsViewManager::FlushDelayedResize() {
197 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
198 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height);
199 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
203 // Convert aIn from being relative to and in appunits of aFromView, to being
204 // relative to and in appunits of aToView.
205 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
206 nsView* aFromView, nsView* aToView) {
207 nsRegion out = aIn;
208 out.MoveBy(aFromView->GetOffsetTo(aToView));
209 out = out.ScaleToOtherAppUnitsRoundOut(
210 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
211 aToView->GetViewManager()->AppUnitsPerDevPixel());
212 return out;
215 nsView* nsViewManager::GetDisplayRootFor(nsView* aView) {
216 nsView* displayRoot = aView;
217 for (;;) {
218 nsView* displayParent = displayRoot->GetParent();
219 if (!displayParent) return displayRoot;
221 if (displayRoot->GetFloating() && !displayParent->GetFloating())
222 return displayRoot;
224 // If we have a combobox dropdown popup within a panel popup, both the view
225 // for the dropdown popup and its parent will be floating, so we need to
226 // distinguish this situation. We do this by looking for a widget. Any view
227 // with a widget is a display root.
228 nsIWidget* widget = displayRoot->GetWidget();
229 if (widget && widget->GetWindowType() == widget::WindowType::Popup) {
230 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
231 "this should only happen with floating views that have "
232 "floating parents");
233 return displayRoot;
236 displayRoot = displayParent;
241 aRegion is given in device coordinates!!
242 aContext may be null, in which case layers should be used for
243 rendering.
245 void nsViewManager::Refresh(nsView* aView,
246 const LayoutDeviceIntRegion& aRegion) {
247 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
249 if (mPresShell && mPresShell->IsNeverPainting()) {
250 return;
253 if (aRegion.IsEmpty()) {
254 return;
257 nsIWidget* widget = aView->GetWidget();
258 if (!widget) {
259 return;
262 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
263 if (IsPainting()) {
264 RootViewManager()->mRecursiveRefreshPending = true;
265 return;
269 nsAutoScriptBlocker scriptBlocker;
270 SetPainting(true);
272 NS_ASSERTION(GetDisplayRootFor(aView) == aView,
273 "Widgets that we paint must all be display roots");
275 if (RefPtr<PresShell> presShell = mPresShell) {
276 #ifdef MOZ_DUMP_PAINTING
277 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
278 printf_stderr("--COMPOSITE-- %p\n", presShell.get());
280 #endif
281 WindowRenderer* renderer = widget->GetWindowRenderer();
282 if (!renderer->NeedsWidgetInvalidation()) {
283 renderer->FlushRendering(wr::RenderReasons::WIDGET);
284 } else {
285 presShell->SyncPaintFallback(aView);
287 #ifdef MOZ_DUMP_PAINTING
288 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
289 printf_stderr("--ENDCOMPOSITE--\n");
291 #endif
292 mozilla::StartupTimeline::RecordOnce(
293 mozilla::StartupTimeline::FIRST_PAINT);
296 SetPainting(false);
299 if (RootViewManager()->mRecursiveRefreshPending) {
300 RootViewManager()->mRecursiveRefreshPending = false;
301 InvalidateAllViews();
305 void nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
306 bool aFlushDirtyRegion) {
307 NS_ASSERTION(IsRootVM(), "Updates will be missed");
308 if (!aView) {
309 return;
312 RefPtr<PresShell> rootPresShell = mPresShell;
313 AutoTArray<nsCOMPtr<nsIWidget>, 1> widgets;
314 aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
315 for (uint32_t i = 0; i < widgets.Length(); ++i) {
316 nsView* view = nsView::GetViewFor(widgets[i]);
317 if (view) {
318 if (view->mNeedsWindowPropertiesSync) {
319 view->mNeedsWindowPropertiesSync = false;
320 if (nsViewManager* vm = view->GetViewManager()) {
321 if (PresShell* presShell = vm->GetPresShell()) {
322 presShell->SyncWindowProperties(/* aSync */ true);
327 view = nsView::GetViewFor(widgets[i]);
328 if (view) {
329 view->ResetWidgetBounds(false, true);
332 if (rootPresShell->GetViewManager() != this) {
333 return; // presentation might have been torn down
335 if (aFlushDirtyRegion) {
336 nsAutoScriptBlocker scriptBlocker;
337 SetPainting(true);
338 for (uint32_t i = 0; i < widgets.Length(); ++i) {
339 nsIWidget* widget = widgets[i];
340 nsView* view = nsView::GetViewFor(widget);
341 if (view) {
342 RefPtr<nsViewManager> viewManager = view->GetViewManager();
343 viewManager->ProcessPendingUpdatesPaint(MOZ_KnownLive(widget));
346 SetPainting(false);
350 void nsViewManager::ProcessPendingUpdatesRecurse(
351 nsView* aView, AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets) {
352 if (mPresShell && mPresShell->IsNeverPainting()) {
353 return;
356 for (nsView* childView = aView->GetFirstChild(); childView;
357 childView = childView->GetNextSibling()) {
358 childView->GetViewManager()->ProcessPendingUpdatesRecurse(childView,
359 aWidgets);
362 nsIWidget* widget = aView->GetWidget();
363 if (widget) {
364 aWidgets.AppendElement(widget);
365 } else {
366 FlushDirtyRegionToWidget(aView);
370 void nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget) {
371 if (aWidget->NeedsPaint()) {
372 // If an ancestor widget was hidden and then shown, we could
373 // have a delayed resize to handle.
374 for (RefPtr<nsViewManager> vm = this; vm;
375 vm = vm->mRootView->GetParent()
376 ? vm->mRootView->GetParent()->GetViewManager()
377 : nullptr) {
378 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
379 vm->mRootView->IsEffectivelyVisible() && vm->mPresShell &&
380 vm->mPresShell->IsVisible()) {
381 vm->FlushDelayedResize();
384 nsView* view = nsView::GetViewFor(aWidget);
386 if (!view) {
387 NS_ERROR("FlushDelayedResize destroyed the nsView?");
388 return;
391 nsIWidgetListener* previousListener =
392 aWidget->GetPreviouslyAttachedWidgetListener();
394 if (previousListener && previousListener != view &&
395 view->IsPrimaryFramePaintSuppressed()) {
396 return;
399 if (RefPtr<PresShell> presShell = mPresShell) {
400 #ifdef MOZ_DUMP_PAINTING
401 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
402 printf_stderr(
403 "---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
404 presShell.get(), view, aWidget);
406 #endif
408 presShell->PaintAndRequestComposite(view, PaintFlags::None);
409 view->SetForcedRepaint(false);
411 #ifdef MOZ_DUMP_PAINTING
412 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
413 printf_stderr("---- PAINT END ----\n");
415 #endif
418 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
421 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView) {
422 NS_ASSERTION(aView->GetViewManager() == this,
423 "FlushDirtyRegionToWidget called on view we don't own");
425 if (!aView->HasNonEmptyDirtyRegion()) {
426 return;
429 nsRegion& dirtyRegion = aView->GetDirtyRegion();
430 nsView* nearestViewWithWidget = aView;
431 while (!nearestViewWithWidget->HasWidget() &&
432 nearestViewWithWidget->GetParent()) {
433 nearestViewWithWidget = nearestViewWithWidget->GetParent();
435 nsRegion r =
436 ConvertRegionBetweenViews(dirtyRegion, aView, nearestViewWithWidget);
438 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
439 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
440 dirtyRegion.SetEmpty();
443 void nsViewManager::InvalidateView(nsView* aView) {
444 // Mark the entire view as damaged
445 InvalidateView(aView, aView->GetDimensions());
448 static void AddDirtyRegion(nsView* aView, const nsRegion& aDamagedRegion) {
449 nsRegion& dirtyRegion = aView->GetDirtyRegion();
450 dirtyRegion.Or(dirtyRegion, aDamagedRegion);
451 dirtyRegion.SimplifyOutward(8);
454 void nsViewManager::PostPendingUpdate() {
455 nsViewManager* rootVM = RootViewManager();
456 rootVM->mHasPendingWidgetGeometryChanges = true;
457 if (rootVM->mPresShell) {
458 rootVM->mPresShell->ScheduleViewManagerFlush();
463 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
464 * every widget child of aWidgetView, plus aWidgetView's own widget
466 void nsViewManager::InvalidateWidgetArea(nsView* aWidgetView,
467 const nsRegion& aDamagedRegion) {
468 NS_ASSERTION(aWidgetView->GetViewManager() == this,
469 "InvalidateWidgetArea called on view we don't own");
470 nsIWidget* widget = aWidgetView->GetWidget();
472 #if 0
473 nsRect dbgBounds = aDamagedRegion.GetBounds();
474 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
475 aWidgetView, aWidgetView->IsAttachedToTopLevel(),
476 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
477 #endif
479 // If the widget is hidden, it don't cover nothing
480 if (widget && !widget->IsVisible()) {
481 return;
484 if (!widget) {
485 // The root view or a scrolling view might not have a widget
486 // (for example, during printing). We get here when we scroll
487 // during printing to show selected options in a listbox, for example.
488 return;
491 if (!aDamagedRegion.IsEmpty()) {
492 for (auto iter = aDamagedRegion.RectIter(); !iter.Done(); iter.Next()) {
493 LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get());
494 widget->Invalidate(bounds);
499 static bool ShouldIgnoreInvalidation(nsViewManager* aVM) {
500 while (aVM) {
501 PresShell* presShell = aVM->GetPresShell();
502 if (!presShell || presShell->ShouldIgnoreInvalidation()) {
503 return true;
505 nsView* view = aVM->GetRootView()->GetParent();
506 aVM = view ? view->GetViewManager() : nullptr;
508 return false;
511 void nsViewManager::InvalidateView(nsView* aView, const nsRect& aRect) {
512 // If painting is suppressed in the presshell or an ancestor drop all
513 // invalidates, it will invalidate everything when it unsuppresses.
514 if (ShouldIgnoreInvalidation(this)) {
515 return;
518 InvalidateViewNoSuppression(aView, aRect);
521 void nsViewManager::InvalidateViewNoSuppression(nsView* aView,
522 const nsRect& aRect) {
523 MOZ_ASSERT(nullptr != aView, "null view");
525 NS_ASSERTION(aView->GetViewManager() == this,
526 "InvalidateViewNoSuppression called on view we don't own");
528 nsRect damagedRect(aRect);
529 if (damagedRect.IsEmpty()) {
530 return;
533 nsView* displayRoot = GetDisplayRootFor(aView);
534 nsViewManager* displayRootVM = displayRoot->GetViewManager();
535 // Propagate the update to the displayRoot, since iframes, for example,
536 // can overlap each other and be translucent. So we have to possibly
537 // invalidate our rect in each of the widgets we have lying about.
538 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
539 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
540 int32_t APD = AppUnitsPerDevPixel();
541 damagedRect = damagedRect.ScaleToOtherAppUnitsRoundOut(APD, rootAPD);
543 // accumulate this rectangle in the view's dirty region, so we can
544 // process it later.
545 AddDirtyRegion(displayRoot, nsRegion(damagedRect));
548 void nsViewManager::InvalidateAllViews() {
549 if (RootViewManager() != this) {
550 return RootViewManager()->InvalidateAllViews();
553 InvalidateViews(mRootView);
556 void nsViewManager::InvalidateViews(nsView* aView) {
557 // Invalidate this view.
558 InvalidateView(aView);
560 // Invalidate all children as well.
561 nsView* childView = aView->GetFirstChild();
562 while (nullptr != childView) {
563 childView->GetViewManager()->InvalidateViews(childView);
564 childView = childView->GetNextSibling();
568 void nsViewManager::WillPaintWindow(nsIWidget* aWidget) {
569 if (aWidget) {
570 nsView* view = nsView::GetViewFor(aWidget);
571 WindowRenderer* renderer = aWidget->GetWindowRenderer();
572 if (view &&
573 (view->ForcedRepaint() || !renderer->NeedsWidgetInvalidation())) {
574 ProcessPendingUpdates();
575 // Re-get the view pointer here since the ProcessPendingUpdates might have
576 // destroyed it during CallWillPaintOnObservers.
577 view = nsView::GetViewFor(aWidget);
578 if (view) {
579 view->SetForcedRepaint(false);
585 bool nsViewManager::PaintWindow(nsIWidget* aWidget,
586 const LayoutDeviceIntRegion& aRegion) {
587 if (!aWidget || !mContext) return false;
589 NS_ASSERTION(
590 IsPaintingAllowed(),
591 "shouldn't be receiving paint events while painting is disallowed!");
593 // Get the view pointer here since NS_WILL_PAINT might have
594 // destroyed it during CallWillPaintOnObservers (bug 378273).
595 nsView* view = nsView::GetViewFor(aWidget);
596 if (view && !aRegion.IsEmpty()) {
597 Refresh(view, aRegion);
600 return true;
603 void nsViewManager::DidPaintWindow() {
604 if (RefPtr<PresShell> presShell = mPresShell) {
605 presShell->DidPaintWindow();
609 void nsViewManager::DispatchEvent(WidgetGUIEvent* aEvent, nsView* aView,
610 nsEventStatus* aStatus) {
611 AUTO_PROFILER_LABEL("nsViewManager::DispatchEvent", OTHER);
613 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
614 if ((mouseEvent &&
615 // Ignore mouse events that we synthesize.
616 mouseEvent->mReason == WidgetMouseEvent::eReal &&
617 // Ignore mouse exit and enter (we'll get moves if the user
618 // is really moving the mouse) since we get them when we
619 // create and destroy widgets.
620 mouseEvent->mMessage != eMouseExitFromWidget &&
621 mouseEvent->mMessage != eMouseEnterIntoWidget) ||
622 aEvent->HasKeyEventMessage() || aEvent->HasIMEEventMessage()) {
623 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
626 // Find the view whose coordinates system we're in.
627 nsView* view = aView;
628 bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
629 if (dispatchUsingCoordinates) {
630 // Will dispatch using coordinates. Pretty bogus but it's consistent
631 // with what presshell does.
632 view = GetDisplayRootFor(view);
635 // If the view has no frame, look for a view that does.
636 nsIFrame* frame = view->GetFrame();
637 if (!frame && (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
638 aEvent->IsIMERelatedEvent())) {
639 while (view && !view->GetFrame()) {
640 view = view->GetParent();
643 if (view) {
644 frame = view->GetFrame();
648 if (nullptr != frame) {
649 // Hold a refcount to the presshell. The continued existence of the
650 // presshell will delay deletion of this view hierarchy should the event
651 // want to cause its destruction in, say, some JavaScript event handler.
652 if (RefPtr<PresShell> presShell = view->GetViewManager()->GetPresShell()) {
653 presShell->HandleEvent(frame, aEvent, false, aStatus);
654 return;
658 *aStatus = nsEventStatus_eIgnore;
661 // Recursively reparent widgets if necessary
663 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget* aNewWidget) {
664 MOZ_ASSERT(aNewWidget, "null widget");
666 if (aView->HasWidget()) {
667 // Check to see if the parent widget is the
668 // same as the new parent. If not then reparent
669 // the widget, otherwise there is nothing more
670 // to do for the view and its descendants
671 nsIWidget* widget = aView->GetWidget();
672 nsIWidget* parentWidget = widget->GetParent();
673 if (parentWidget) {
674 // Child widget
675 if (parentWidget != aNewWidget) {
676 widget->SetParent(aNewWidget);
678 } else {
679 // Toplevel widget (popup, dialog, etc)
680 widget->ReparentNativeWidget(aNewWidget);
682 return;
685 // Need to check each of the views children to see
686 // if they have a widget and reparent it.
688 for (nsView* kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
689 ReparentChildWidgets(kid, aNewWidget);
693 // Reparent a view and its descendant views widgets if necessary
695 void nsViewManager::ReparentWidgets(nsView* aView, nsView* aParent) {
696 MOZ_ASSERT(aParent, "Must have a parent");
697 MOZ_ASSERT(aView, "Must have a view");
699 // Quickly determine whether the view has pre-existing children or a
700 // widget. In most cases the view will not have any pre-existing
701 // children when this is called. Only in the case
702 // where a view has been reparented by removing it from
703 // a reinserting it into a new location in the view hierarchy do we
704 // have to consider reparenting the existing widgets for the view and
705 // it's descendants.
706 if (aView->HasWidget() || aView->GetFirstChild()) {
707 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
708 if (parentWidget) {
709 ReparentChildWidgets(aView, parentWidget);
710 return;
712 NS_WARNING("Can not find a widget for the parent view");
716 void nsViewManager::InsertChild(nsView* aParent, nsView* aChild,
717 nsView* aSibling, bool aAfter) {
718 MOZ_ASSERT(nullptr != aParent, "null ptr");
719 MOZ_ASSERT(nullptr != aChild, "null ptr");
720 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
721 "tried to insert view with invalid sibling");
722 NS_ASSERTION(!IsViewInserted(aChild),
723 "tried to insert an already-inserted view");
725 if ((nullptr != aParent) && (nullptr != aChild)) {
726 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid'
727 // in document order, otherwise after 'kid' (i.e. before 'kid' in document
728 // order).
730 if (nullptr == aSibling) {
731 if (aAfter) {
732 // insert at end of document order, i.e., before first view
733 // this is the common case, by far
734 aParent->InsertChild(aChild, nullptr);
735 ReparentWidgets(aChild, aParent);
736 } else {
737 // insert at beginning of document order, i.e., after last view
738 nsView* kid = aParent->GetFirstChild();
739 nsView* prev = nullptr;
740 while (kid) {
741 prev = kid;
742 kid = kid->GetNextSibling();
744 // prev is last view or null if there are no children
745 aParent->InsertChild(aChild, prev);
746 ReparentWidgets(aChild, aParent);
748 } else {
749 nsView* kid = aParent->GetFirstChild();
750 nsView* prev = nullptr;
751 while (kid && aSibling != kid) {
752 // get the next sibling view
753 prev = kid;
754 kid = kid->GetNextSibling();
756 NS_ASSERTION(kid != nullptr, "couldn't find sibling in child list");
757 if (aAfter) {
758 // insert after 'kid' in document order, i.e. before in view order
759 aParent->InsertChild(aChild, prev);
760 ReparentWidgets(aChild, aParent);
761 } else {
762 // insert before 'kid' in document order, i.e. after in view order
763 aParent->InsertChild(aChild, kid);
764 ReparentWidgets(aChild, aParent);
768 // if the parent view is marked as "floating", make the newly added view
769 // float as well.
770 if (aParent->GetFloating()) aChild->SetFloating(true);
774 void nsViewManager::RemoveChild(nsView* aChild) {
775 NS_ASSERTION(aChild, "aChild must not be null");
777 nsView* parent = aChild->GetParent();
779 if (nullptr != parent) {
780 NS_ASSERTION(
781 aChild->GetViewManager() == this || parent->GetViewManager() == this,
782 "wrong view manager");
783 parent->RemoveChild(aChild);
787 void nsViewManager::MoveViewTo(nsView* aView, nscoord aX, nscoord aY) {
788 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
789 aView->SetPosition(aX, aY);
792 void nsViewManager::ResizeView(nsView* aView, const nsRect& aRect,
793 bool aRepaintExposedAreaOnly) {
794 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
796 nsRect oldDimensions = aView->GetDimensions();
797 if (!oldDimensions.IsEqualEdges(aRect)) {
798 aView->SetDimensions(aRect, true);
801 // Note that if layout resizes the view and the view has a custom clip
802 // region set, then we expect layout to update the clip region too. Thus
803 // in the case where mClipRect has been optimized away to just be a null
804 // pointer, and this resize is implicitly changing the clip rect, it's OK
805 // because layout will change it back again if necessary.
808 void nsViewManager::SetViewFloating(nsView* aView, bool aFloating) {
809 NS_ASSERTION(!(nullptr == aView), "no view");
811 aView->SetFloating(aFloating);
814 void nsViewManager::SetViewVisibility(nsView* aView, ViewVisibility aVisible) {
815 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
817 if (aVisible != aView->GetVisibility()) {
818 aView->SetVisibility(aVisible);
822 bool nsViewManager::IsViewInserted(nsView* aView) {
823 if (mRootView == aView) {
824 return true;
826 if (aView->GetParent() == nullptr) {
827 return false;
829 nsView* view = aView->GetParent()->GetFirstChild();
830 while (view != nullptr) {
831 if (view == aView) {
832 return true;
834 view = view->GetNextSibling();
836 return false;
839 void nsViewManager::SetViewZIndex(nsView* aView, bool aAutoZIndex,
840 int32_t aZIndex) {
841 NS_ASSERTION((aView != nullptr), "no view");
843 // don't allow the root view's z-index to be changed. It should always be
844 // zero. This could be removed and replaced with a style rule, or just removed
845 // altogether, with interesting consequences
846 if (aView == mRootView) {
847 return;
850 if (aAutoZIndex) {
851 aZIndex = 0;
854 aView->SetZIndex(aAutoZIndex, aZIndex);
857 nsViewManager* nsViewManager::IncrementDisableRefreshCount() {
858 if (!IsRootVM()) {
859 return RootViewManager()->IncrementDisableRefreshCount();
862 ++mRefreshDisableCount;
864 return this;
867 void nsViewManager::DecrementDisableRefreshCount() {
868 NS_ASSERTION(IsRootVM(), "Should only be called on root");
869 --mRefreshDisableCount;
870 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
873 nsIWidget* nsViewManager::GetRootWidget() const {
874 if (!mRootView) {
875 return nullptr;
877 if (mRootView->HasWidget()) {
878 return mRootView->GetWidget();
880 if (mRootView->GetParent()) {
881 return mRootView->GetParent()->GetViewManager()->GetRootWidget();
883 return nullptr;
886 LayoutDeviceIntRect nsViewManager::ViewToWidget(nsView* aView,
887 const nsRect& aRect) const {
888 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
890 // account for the view's origin not lining up with the widget's
891 nsRect rect = aRect + aView->ViewToWidgetOffset();
893 // finally, convert to device coordinates.
894 return LayoutDeviceIntRect::FromUnknownRect(
895 rect.ToOutsidePixels(AppUnitsPerDevPixel()));
898 void nsViewManager::IsPainting(bool& aIsPainting) {
899 aIsPainting = IsPainting();
902 void nsViewManager::ProcessPendingUpdates() {
903 if (!IsRootVM()) {
904 RefPtr<nsViewManager> rootViewManager = RootViewManager();
905 rootViewManager->ProcessPendingUpdates();
906 return;
909 // Flush things like reflows by calling WillPaint on observer presShells.
910 if (mPresShell) {
911 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
913 RefPtr<nsViewManager> strongThis(this);
914 CallWillPaintOnObservers();
916 ProcessPendingUpdatesForView(mRootView, true);
917 if (mPresShell) {
918 if (nsPresContext* pc = mPresShell->GetPresContext()) {
919 pc->RefreshDriver()->ClearHasScheduleFlush();
925 void nsViewManager::UpdateWidgetGeometry() {
926 if (!IsRootVM()) {
927 RefPtr<nsViewManager> rootViewManager = RootViewManager();
928 rootViewManager->UpdateWidgetGeometry();
929 return;
932 if (mHasPendingWidgetGeometryChanges) {
933 mHasPendingWidgetGeometryChanges = false;
934 ProcessPendingUpdatesForView(mRootView, false);
938 /* static */ void nsViewManager::CollectVMsForWillPaint(
939 nsView* aView, nsViewManager* aParentVM,
940 nsTArray<RefPtr<nsViewManager>>& aVMs) {
941 nsViewManager* vm = aView->GetViewManager();
942 if (vm != aParentVM) {
943 aVMs.AppendElement(vm);
946 for (nsView* child = aView->GetFirstChild(); child;
947 child = child->GetNextSibling()) {
948 CollectVMsForWillPaint(child, vm, aVMs);
952 void nsViewManager::CallWillPaintOnObservers() {
953 MOZ_ASSERT(IsRootVM(), "Must be root VM for this to be called!");
955 if (!mRootView) {
956 return;
959 AutoTArray<RefPtr<nsViewManager>, 2> VMs;
960 CollectVMsForWillPaint(mRootView, nullptr, VMs);
961 for (const auto& vm : VMs) {
962 if (vm->GetRootView() && vm->GetRootView()->IsEffectivelyVisible()) {
963 if (RefPtr<PresShell> presShell = vm->GetPresShell()) {
964 presShell->WillPaint();
970 void nsViewManager::GetLastUserEventTime(uint32_t& aTime) {
971 aTime = gLastUserEventTime;
974 void nsViewManager::InvalidateHierarchy() {
975 if (mRootView) {
976 mRootViewManager = nullptr;
977 nsView* parent = mRootView->GetParent();
978 if (parent) {
979 mRootViewManager = parent->GetViewManager()->RootViewManager();
980 NS_ASSERTION(mRootViewManager != this,
981 "Root view had a parent, but it has the same view manager");
983 // else, we are implicitly our own root view manager.