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"
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"
31 DeCOMify newly private methods
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
),
58 mRefreshDisableCount(0),
60 mRecursiveRefreshPending(false),
61 mHasPendingWidgetGeometryChanges(false) {}
63 nsViewManager::~nsViewManager() {
65 // Destroy any remaining views
70 mRootViewManager
= nullptr;
72 MOZ_RELEASE_ASSERT(!mPresShell
,
73 "Releasing nsViewManager without having called Destroy on "
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
;
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);
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
112 nsView
* parent
= mRootView
->GetParent();
114 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
115 // no need to set mRootViewManager ourselves here.
116 parent
->InsertChild(mRootView
, nullptr);
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();
133 *aWidth
= mDelayedResize
.width
;
134 *aHeight
= mDelayedResize
.height
;
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
)) {
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()) {
162 if (nsRefreshDriver
* rd
= mPresShell
->GetRefreshDriver()) {
163 if (rd
->IsResizeSuppressed()) {
170 void nsViewManager::SetWindowDimensions(nscoord aWidth
, nscoord aHeight
,
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
);
187 mDelayedResize
.SizeTo(aWidth
, aHeight
);
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
) {
208 out
.MoveBy(aFromView
->GetOffsetTo(aToView
));
209 out
= out
.ScaleToOtherAppUnitsRoundOut(
210 aFromView
->GetViewManager()->AppUnitsPerDevPixel(),
211 aToView
->GetViewManager()->AppUnitsPerDevPixel());
215 nsView
* nsViewManager::GetDisplayRootFor(nsView
* aView
) {
216 nsView
* displayRoot
= aView
;
218 nsView
* displayParent
= displayRoot
->GetParent();
219 if (!displayParent
) return displayRoot
;
221 if (displayRoot
->GetFloating() && !displayParent
->GetFloating())
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 "
236 displayRoot
= displayParent
;
241 aRegion is given in device coordinates!!
242 aContext may be null, in which case layers should be used for
245 void nsViewManager::Refresh(nsView
* aView
,
246 const LayoutDeviceIntRegion
& aRegion
) {
247 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
249 if (mPresShell
&& mPresShell
->IsNeverPainting()) {
253 if (aRegion
.IsEmpty()) {
257 nsIWidget
* widget
= aView
->GetWidget();
262 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
264 RootViewManager()->mRecursiveRefreshPending
= true;
269 nsAutoScriptBlocker scriptBlocker
;
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());
281 WindowRenderer
* renderer
= widget
->GetWindowRenderer();
282 if (!renderer
->NeedsWidgetInvalidation()) {
283 renderer
->FlushRendering(wr::RenderReasons::WIDGET
);
285 presShell
->SyncPaintFallback(aView
);
287 #ifdef MOZ_DUMP_PAINTING
288 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
289 printf_stderr("--ENDCOMPOSITE--\n");
292 mozilla::StartupTimeline::RecordOnce(
293 mozilla::StartupTimeline::FIRST_PAINT
);
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");
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
]);
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
]);
329 view
->ResetWidgetBounds(false, true);
332 if (rootPresShell
->GetViewManager() != this) {
333 return; // presentation might have been torn down
335 if (aFlushDirtyRegion
) {
336 nsAutoScriptBlocker scriptBlocker
;
338 for (uint32_t i
= 0; i
< widgets
.Length(); ++i
) {
339 nsIWidget
* widget
= widgets
[i
];
340 nsView
* view
= nsView::GetViewFor(widget
);
342 RefPtr
<nsViewManager
> viewManager
= view
->GetViewManager();
343 viewManager
->ProcessPendingUpdatesPaint(MOZ_KnownLive(widget
));
350 void nsViewManager::ProcessPendingUpdatesRecurse(
351 nsView
* aView
, AutoTArray
<nsCOMPtr
<nsIWidget
>, 1>& aWidgets
) {
352 if (mPresShell
&& mPresShell
->IsNeverPainting()) {
356 for (nsView
* childView
= aView
->GetFirstChild(); childView
;
357 childView
= childView
->GetNextSibling()) {
358 childView
->GetViewManager()->ProcessPendingUpdatesRecurse(childView
,
362 nsIWidget
* widget
= aView
->GetWidget();
364 aWidgets
.AppendElement(widget
);
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()
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
);
387 NS_ERROR("FlushDelayedResize destroyed the nsView?");
391 nsIWidgetListener
* previousListener
=
392 aWidget
->GetPreviouslyAttachedWidgetListener();
394 if (previousListener
&& previousListener
!= view
&&
395 view
->IsPrimaryFramePaintSuppressed()) {
399 if (RefPtr
<PresShell
> presShell
= mPresShell
) {
400 #ifdef MOZ_DUMP_PAINTING
401 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
403 "---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
404 presShell
.get(), view
, aWidget
);
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");
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()) {
429 nsRegion
& dirtyRegion
= aView
->GetDirtyRegion();
430 nsView
* nearestViewWithWidget
= aView
;
431 while (!nearestViewWithWidget
->HasWidget() &&
432 nearestViewWithWidget
->GetParent()) {
433 nearestViewWithWidget
= nearestViewWithWidget
->GetParent();
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();
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
);
479 // If the widget is hidden, it don't cover nothing
480 if (widget
&& !widget
->IsVisible()) {
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.
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
) {
501 PresShell
* presShell
= aVM
->GetPresShell();
502 if (!presShell
|| presShell
->ShouldIgnoreInvalidation()) {
505 nsView
* view
= aVM
->GetRootView()->GetParent();
506 aVM
= view
? view
->GetViewManager() : nullptr;
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)) {
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()) {
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
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
) {
570 nsView
* view
= nsView::GetViewFor(aWidget
);
571 WindowRenderer
* renderer
= aWidget
->GetWindowRenderer();
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
);
579 view
->SetForcedRepaint(false);
585 bool nsViewManager::PaintWindow(nsIWidget
* aWidget
,
586 const LayoutDeviceIntRegion
& aRegion
) {
587 if (!aWidget
|| !mContext
) return false;
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
);
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();
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();
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
);
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();
675 if (parentWidget
!= aNewWidget
) {
676 widget
->SetParent(aNewWidget
);
679 // Toplevel widget (popup, dialog, etc)
680 widget
->ReparentNativeWidget(aNewWidget
);
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
706 if (aView
->HasWidget() || aView
->GetFirstChild()) {
707 nsIWidget
* parentWidget
= aParent
->GetNearestWidget(nullptr);
709 ReparentChildWidgets(aView
, parentWidget
);
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
730 if (nullptr == aSibling
) {
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
);
737 // insert at beginning of document order, i.e., after last view
738 nsView
* kid
= aParent
->GetFirstChild();
739 nsView
* prev
= nullptr;
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
);
749 nsView
* kid
= aParent
->GetFirstChild();
750 nsView
* prev
= nullptr;
751 while (kid
&& aSibling
!= kid
) {
752 // get the next sibling view
754 kid
= kid
->GetNextSibling();
756 NS_ASSERTION(kid
!= nullptr, "couldn't find sibling in child list");
758 // insert after 'kid' in document order, i.e. before in view order
759 aParent
->InsertChild(aChild
, prev
);
760 ReparentWidgets(aChild
, aParent
);
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
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
) {
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
) {
826 if (aView
->GetParent() == nullptr) {
829 nsView
* view
= aView
->GetParent()->GetFirstChild();
830 while (view
!= nullptr) {
834 view
= view
->GetNextSibling();
839 void nsViewManager::SetViewZIndex(nsView
* aView
, bool aAutoZIndex
,
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
) {
854 aView
->SetZIndex(aAutoZIndex
, aZIndex
);
857 nsViewManager
* nsViewManager::IncrementDisableRefreshCount() {
859 return RootViewManager()->IncrementDisableRefreshCount();
862 ++mRefreshDisableCount
;
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 {
877 if (mRootView
->HasWidget()) {
878 return mRootView
->GetWidget();
880 if (mRootView
->GetParent()) {
881 return mRootView
->GetParent()->GetViewManager()->GetRootWidget();
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() {
904 RefPtr
<nsViewManager
> rootViewManager
= RootViewManager();
905 rootViewManager
->ProcessPendingUpdates();
909 // Flush things like reflows by calling WillPaint on observer presShells.
911 mPresShell
->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
913 RefPtr
<nsViewManager
> strongThis(this);
914 CallWillPaintOnObservers();
916 ProcessPendingUpdatesForView(mRootView
, true);
918 if (nsPresContext
* pc
= mPresShell
->GetPresContext()) {
919 pc
->RefreshDriver()->ClearHasScheduleFlush();
925 void nsViewManager::UpdateWidgetGeometry() {
927 RefPtr
<nsViewManager
> rootViewManager
= RootViewManager();
928 rootViewManager
->UpdateWidgetGeometry();
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!");
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() {
976 mRootViewManager
= nullptr;
977 nsView
* parent
= mRootView
->GetParent();
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.