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)
10 #include "nsViewManager.h"
11 #include "nsGfxCIID.h"
14 #include "mozilla/MouseEvents.h"
16 #include "nsCOMArray.h"
17 #include "nsIPluginWidget.h"
18 #include "nsXULPopupManager.h"
19 #include "nsIPresShell.h"
20 #include "nsIPresShellInlines.h"
21 #include "nsPresContext.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"
29 #include "gfxPlatform.h"
31 #include "nsIDocument.h"
36 DeCOMify newly private methods
41 A note about platform assumptions:
43 We assume that a widget is z-ordered on top of its parent.
45 We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
46 we ask for a specific z-order, we don't assume that widget z-ordering actually works.
49 using namespace mozilla
;
50 using namespace mozilla::layers
;
52 #define NSCOORD_NONE INT32_MIN
54 #undef DEBUG_MOUSE_LOCATION
56 // Weakly held references to all of the view managers
57 nsTArray
<nsViewManager
*>* nsViewManager::gViewManagers
= nullptr;
58 uint32_t nsViewManager::gLastUserEventTime
= 0;
60 nsViewManager::nsViewManager()
62 , mDelayedResize(NSCOORD_NONE
, NSCOORD_NONE
)
64 , mRootViewManager(this)
65 , mRefreshDisableCount(0)
67 , mRecursiveRefreshPending(false)
68 , mHasPendingWidgetGeometryChanges(false)
70 if (gViewManagers
== nullptr) {
71 // Create an array to hold a list of view managers
72 gViewManagers
= new nsTArray
<nsViewManager
*>;
75 gViewManagers
->AppendElement(this);
78 nsViewManager::~nsViewManager()
81 // Destroy any remaining views
87 // We have a strong ref to mRootViewManager
88 NS_RELEASE(mRootViewManager
);
91 NS_ASSERTION(gViewManagers
!= nullptr, "About to use null gViewManagers");
96 gViewManagers
->RemoveElement(this);
97 NS_ASSERTION(removed
, "Viewmanager instance was not in the global list of viewmanagers");
99 if (gViewManagers
->IsEmpty()) {
100 // There aren't any more view managers so
101 // release the global array of view managers
102 delete gViewManagers
;
103 gViewManagers
= nullptr;
106 mPresShell
= nullptr;
109 // We don't hold a reference to the presentation context because it
110 // holds a reference to us.
112 nsViewManager::Init(nsDeviceContext
* aContext
)
114 NS_PRECONDITION(nullptr != aContext
, "null ptr");
116 if (nullptr == aContext
) {
117 return NS_ERROR_NULL_POINTER
;
119 if (nullptr != mContext
) {
120 return NS_ERROR_ALREADY_INITIALIZED
;
128 nsViewManager::CreateView(const nsRect
& aBounds
,
130 nsViewVisibility aVisibilityFlag
)
132 auto *v
= new nsView(this, aVisibilityFlag
);
133 v
->SetParent(aParent
);
134 v
->SetPosition(aBounds
.x
, aBounds
.y
);
135 nsRect
dim(0, 0, aBounds
.width
, aBounds
.height
);
136 v
->SetDimensions(dim
, false);
141 nsViewManager::SetRootView(nsView
*aView
)
143 NS_PRECONDITION(!aView
|| aView
->GetViewManager() == this,
144 "Unexpected viewmanager on root view");
146 // Do NOT destroy the current root view. It's the caller's responsibility
151 nsView
* parent
= mRootView
->GetParent();
153 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
154 // no need to set mRootViewManager ourselves here.
155 parent
->InsertChild(mRootView
, nullptr);
157 InvalidateHierarchy();
160 mRootView
->SetZIndex(false, 0);
162 // Else don't touch mRootViewManager
166 nsViewManager::GetWindowDimensions(nscoord
*aWidth
, nscoord
*aHeight
)
168 if (nullptr != mRootView
) {
169 if (mDelayedResize
== nsSize(NSCOORD_NONE
, NSCOORD_NONE
)) {
170 nsRect dim
= mRootView
->GetDimensions();
172 *aHeight
= dim
.height
;
174 *aWidth
= mDelayedResize
.width
;
175 *aHeight
= mDelayedResize
.height
;
185 void nsViewManager::DoSetWindowDimensions(nscoord aWidth
, nscoord aHeight
)
187 nsRect oldDim
= mRootView
->GetDimensions();
188 nsRect
newDim(0, 0, aWidth
, aHeight
);
189 // We care about resizes even when one dimension is already zero.
190 if (!oldDim
.IsEqualEdges(newDim
)) {
191 // Don't resize the widget. It is already being set elsewhere.
192 mRootView
->SetDimensions(newDim
, true, false);
194 mPresShell
->ResizeReflow(aWidth
, aHeight
, oldDim
.width
, oldDim
.height
);
199 nsViewManager::ShouldDelayResize() const
201 MOZ_ASSERT(mRootView
);
202 if (!mRootView
->IsEffectivelyVisible() ||
203 !mPresShell
|| !mPresShell
->IsVisible()) {
206 if (nsRefreshDriver
* rd
= mPresShell
->GetRefreshDriver()) {
207 if (rd
->IsResizeSuppressed()) {
215 nsViewManager::SetWindowDimensions(nscoord aWidth
, nscoord aHeight
, bool aDelayResize
)
218 if (!ShouldDelayResize() && !aDelayResize
) {
219 if (mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
) &&
220 mDelayedResize
!= nsSize(aWidth
, aHeight
)) {
221 // We have a delayed resize; that now obsolete size may already have
222 // been flushed to the PresContext so we need to update the PresContext
223 // with the new size because if the new size is exactly the same as the
224 // root view's current size then DoSetWindowDimensions will not
225 // request a resize reflow (which would correct it). See bug 617076.
226 mDelayedResize
= nsSize(aWidth
, aHeight
);
227 FlushDelayedResize(false);
229 mDelayedResize
.SizeTo(NSCOORD_NONE
, NSCOORD_NONE
);
230 DoSetWindowDimensions(aWidth
, aHeight
);
232 mDelayedResize
.SizeTo(aWidth
, aHeight
);
234 mPresShell
->SetNeedStyleFlush();
235 mPresShell
->SetNeedLayoutFlush();
242 nsViewManager::FlushDelayedResize(bool aDoReflow
)
244 if (mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
)) {
246 DoSetWindowDimensions(mDelayedResize
.width
, mDelayedResize
.height
);
247 mDelayedResize
.SizeTo(NSCOORD_NONE
, NSCOORD_NONE
);
248 } else if (mPresShell
&& !mPresShell
->GetIsViewportOverridden()) {
249 nsPresContext
* presContext
= mPresShell
->GetPresContext();
251 presContext
->SetVisibleArea(nsRect(nsPoint(0, 0), mDelayedResize
));
257 // Convert aIn from being relative to and in appunits of aFromView, to being
258 // relative to and in appunits of aToView.
259 static nsRegion
ConvertRegionBetweenViews(const nsRegion
& aIn
,
264 out
.MoveBy(aFromView
->GetOffsetTo(aToView
));
265 out
= out
.ScaleToOtherAppUnitsRoundOut(
266 aFromView
->GetViewManager()->AppUnitsPerDevPixel(),
267 aToView
->GetViewManager()->AppUnitsPerDevPixel());
271 nsView
* nsViewManager::GetDisplayRootFor(nsView
* aView
)
273 nsView
*displayRoot
= aView
;
275 nsView
*displayParent
= displayRoot
->GetParent();
279 if (displayRoot
->GetFloating() && !displayParent
->GetFloating())
282 // If we have a combobox dropdown popup within a panel popup, both the view
283 // for the dropdown popup and its parent will be floating, so we need to
284 // distinguish this situation. We do this by looking for a widget. Any view
285 // with a widget is a display root, except for plugins.
286 nsIWidget
* widget
= displayRoot
->GetWidget();
287 if (widget
&& widget
->WindowType() == eWindowType_popup
) {
288 NS_ASSERTION(displayRoot
->GetFloating() && displayParent
->GetFloating(),
289 "this should only happen with floating views that have floating parents");
293 displayRoot
= displayParent
;
298 aRegion is given in device coordinates!!
299 aContext may be null, in which case layers should be used for
302 void nsViewManager::Refresh(nsView
* aView
, const LayoutDeviceIntRegion
& aRegion
)
304 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
306 if (mPresShell
&& mPresShell
->IsNeverPainting()) {
310 // damageRegion is the damaged area, in twips, relative to the view origin
311 nsRegion damageRegion
= aRegion
.ToAppUnits(AppUnitsPerDevPixel());
313 // move region from widget coordinates into view coordinates
314 damageRegion
.MoveBy(-aView
->ViewToWidgetOffset());
316 if (damageRegion
.IsEmpty()) {
318 nsRect viewRect
= aView
->GetDimensions();
319 nsRect damageRect
= damageRegion
.GetBounds();
320 printf_stderr("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
321 damageRect
.x
, damageRect
.y
, damageRect
.width
, damageRect
.height
,
322 viewRect
.x
, viewRect
.y
, viewRect
.width
, viewRect
.height
);
327 nsIWidget
*widget
= aView
->GetWidget();
332 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
334 RootViewManager()->mRecursiveRefreshPending
= true;
339 nsAutoScriptBlocker scriptBlocker
;
342 NS_ASSERTION(GetDisplayRootFor(aView
) == aView
,
343 "Widgets that we paint must all be display roots");
346 #ifdef MOZ_DUMP_PAINTING
347 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
348 printf_stderr("--COMPOSITE-- %p\n", mPresShell
);
351 uint32_t paintFlags
= nsIPresShell::PAINT_COMPOSITE
;
352 LayerManager
*manager
= widget
->GetLayerManager();
353 if (!manager
->NeedsWidgetInvalidation()) {
354 manager
->FlushRendering();
356 mPresShell
->Paint(aView
, damageRegion
,
359 #ifdef MOZ_DUMP_PAINTING
360 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
361 printf_stderr("--ENDCOMPOSITE--\n");
364 mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT
);
370 if (RootViewManager()->mRecursiveRefreshPending
) {
371 RootViewManager()->mRecursiveRefreshPending
= false;
372 InvalidateAllViews();
377 nsViewManager::ProcessPendingUpdatesForView(nsView
* aView
,
378 bool aFlushDirtyRegion
)
380 NS_ASSERTION(IsRootVM(), "Updates will be missed");
385 nsCOMPtr
<nsIPresShell
> rootShell(mPresShell
);
386 AutoTArray
<nsCOMPtr
<nsIWidget
>, 1> widgets
;
387 aView
->GetViewManager()->ProcessPendingUpdatesRecurse(aView
, widgets
);
388 for (uint32_t i
= 0; i
< widgets
.Length(); ++i
) {
389 nsView
* view
= nsView::GetViewFor(widgets
[i
]);
391 if (view
->mNeedsWindowPropertiesSync
) {
392 view
->mNeedsWindowPropertiesSync
= false;
393 if (nsViewManager
* vm
= view
->GetViewManager()) {
394 if (nsIPresShell
* ps
= vm
->GetPresShell()) {
395 ps
->SyncWindowProperties(view
);
400 view
= nsView::GetViewFor(widgets
[i
]);
402 view
->ResetWidgetBounds(false, true);
405 if (rootShell
->GetViewManager() != this) {
406 return; // presentation might have been torn down
408 if (aFlushDirtyRegion
) {
409 nsAutoScriptBlocker scriptBlocker
;
411 for (uint32_t i
= 0; i
< widgets
.Length(); ++i
) {
412 nsIWidget
* widget
= widgets
[i
];
413 nsView
* view
= nsView::GetViewFor(widget
);
415 view
->GetViewManager()->ProcessPendingUpdatesPaint(widget
);
423 nsViewManager::ProcessPendingUpdatesRecurse(nsView
* aView
,
424 AutoTArray
<nsCOMPtr
<nsIWidget
>, 1>& aWidgets
)
426 if (mPresShell
&& mPresShell
->IsNeverPainting()) {
430 for (nsView
* childView
= aView
->GetFirstChild(); childView
;
431 childView
= childView
->GetNextSibling()) {
432 childView
->GetViewManager()->
433 ProcessPendingUpdatesRecurse(childView
, aWidgets
);
436 nsIWidget
* widget
= aView
->GetWidget();
438 aWidgets
.AppendElement(widget
);
440 FlushDirtyRegionToWidget(aView
);
445 nsViewManager::ProcessPendingUpdatesPaint(nsIWidget
* aWidget
)
447 if (aWidget
->NeedsPaint()) {
448 // If an ancestor widget was hidden and then shown, we could
449 // have a delayed resize to handle.
450 for (RefPtr
<nsViewManager
> vm
= this; vm
;
451 vm
= vm
->mRootView
->GetParent()
452 ? vm
->mRootView
->GetParent()->GetViewManager()
454 if (vm
->mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
) &&
455 vm
->mRootView
->IsEffectivelyVisible() &&
456 vm
->mPresShell
&& vm
->mPresShell
->IsVisible()) {
457 vm
->FlushDelayedResize(true);
460 nsView
* view
= nsView::GetViewFor(aWidget
);
463 NS_ERROR("FlushDelayedResize destroyed the nsView?");
467 nsIWidgetListener
* previousListener
= aWidget
->GetPreviouslyAttachedWidgetListener();
469 if (previousListener
&&
470 previousListener
!= view
&&
471 view
->IsPrimaryFramePaintSuppressed()) {
476 #ifdef MOZ_DUMP_PAINTING
477 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
478 printf_stderr("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
479 mPresShell
, view
, aWidget
);
483 mPresShell
->Paint(view
, nsRegion(), nsIPresShell::PAINT_LAYERS
);
484 view
->SetForcedRepaint(false);
486 #ifdef MOZ_DUMP_PAINTING
487 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
488 printf_stderr("---- PAINT END ----\n");
493 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget
));
496 void nsViewManager::FlushDirtyRegionToWidget(nsView
* aView
)
498 NS_ASSERTION(aView
->GetViewManager() == this,
499 "FlushDirtyRegionToWidget called on view we don't own");
501 if (!aView
->HasNonEmptyDirtyRegion())
504 nsRegion
* dirtyRegion
= aView
->GetDirtyRegion();
505 nsView
* nearestViewWithWidget
= aView
;
506 while (!nearestViewWithWidget
->HasWidget() &&
507 nearestViewWithWidget
->GetParent()) {
508 nearestViewWithWidget
= nearestViewWithWidget
->GetParent();
511 ConvertRegionBetweenViews(*dirtyRegion
, aView
, nearestViewWithWidget
);
513 // If we draw the frame counter we need to make sure we invalidate the area
514 // for it to make it on screen
515 if (gfxPrefs::DrawFrameCounter()) {
516 nsRect counterBounds
= ToAppUnits(gfxPlatform::FrameCounterBounds(), AppUnitsPerDevPixel());
517 r
.OrWith(counterBounds
);
520 nsViewManager
* widgetVM
= nearestViewWithWidget
->GetViewManager();
521 widgetVM
->InvalidateWidgetArea(nearestViewWithWidget
, r
);
522 dirtyRegion
->SetEmpty();
526 nsViewManager::InvalidateView(nsView
*aView
)
528 // Mark the entire view as damaged
529 InvalidateView(aView
, aView
->GetDimensions());
533 AddDirtyRegion(nsView
*aView
, const nsRegion
&aDamagedRegion
)
535 nsRegion
* dirtyRegion
= aView
->GetDirtyRegion();
539 dirtyRegion
->Or(*dirtyRegion
, aDamagedRegion
);
540 dirtyRegion
->SimplifyOutward(8);
544 nsViewManager::PostPendingUpdate()
546 nsViewManager
* rootVM
= RootViewManager();
547 rootVM
->mHasPendingWidgetGeometryChanges
= true;
548 if (rootVM
->mPresShell
) {
549 rootVM
->mPresShell
->ScheduleViewManagerFlush();
554 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
555 * every widget child of aWidgetView, plus aWidgetView's own widget
558 nsViewManager::InvalidateWidgetArea(nsView
*aWidgetView
,
559 const nsRegion
&aDamagedRegion
)
561 NS_ASSERTION(aWidgetView
->GetViewManager() == this,
562 "InvalidateWidgetArea called on view we don't own");
563 nsIWidget
* widget
= aWidgetView
->GetWidget();
566 nsRect dbgBounds
= aDamagedRegion
.GetBounds();
567 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
568 aWidgetView
, aWidgetView
->IsAttachedToTopLevel(),
569 widget
, dbgBounds
.x
, dbgBounds
.y
, dbgBounds
.width
, dbgBounds
.height
);
572 // If the widget is hidden, it don't cover nothing
573 if (widget
&& !widget
->IsVisible()) {
578 // The root view or a scrolling view might not have a widget
579 // (for example, during printing). We get here when we scroll
580 // during printing to show selected options in a listbox, for example.
584 // Update all child widgets with the damage. In the process,
585 // accumulate the union of all the child widget areas, or at least
586 // some subset of that.
588 if (widget
->GetTransparencyMode() != eTransparencyTransparent
) {
589 for (nsIWidget
* childWidget
= widget
->GetFirstChild();
591 childWidget
= childWidget
->GetNextSibling()) {
592 nsView
* view
= nsView::GetViewFor(childWidget
);
593 NS_ASSERTION(view
!= aWidgetView
, "will recur infinitely");
594 nsWindowType type
= childWidget
->WindowType();
595 if (view
&& childWidget
->IsVisible() && type
!= eWindowType_popup
) {
596 NS_ASSERTION(childWidget
->IsPlugin(),
597 "Only plugin or popup widgets can be children!");
599 // We do not need to invalidate in plugin widgets, but we should
600 // exclude them from the invalidation region IF we're not on
601 // Mac. On Mac we need to draw under plugin widgets, because
602 // plugin widgets are basically invisible
604 // GetBounds should compensate for chrome on a toplevel widget
605 LayoutDeviceIntRect bounds
= childWidget
->GetBounds();
607 nsTArray
<LayoutDeviceIntRect
> clipRects
;
608 childWidget
->GetWindowClipRegion(&clipRects
);
609 for (uint32_t i
= 0; i
< clipRects
.Length(); ++i
) {
610 nsRect rr
= LayoutDeviceIntRect::ToAppUnits(
611 clipRects
[i
] + bounds
.TopLeft(), AppUnitsPerDevPixel());
612 children
.Or(children
, rr
- aWidgetView
->ViewToWidgetOffset());
613 children
.SimplifyInward(20);
621 leftOver
.Sub(aDamagedRegion
, children
);
623 if (!leftOver
.IsEmpty()) {
624 for (auto iter
= leftOver
.RectIter(); !iter
.Done(); iter
.Next()) {
625 LayoutDeviceIntRect bounds
= ViewToWidget(aWidgetView
, iter
.Get());
626 widget
->Invalidate(bounds
);
632 ShouldIgnoreInvalidation(nsViewManager
* aVM
)
635 nsIPresShell
* shell
= aVM
->GetPresShell();
636 if (!shell
|| shell
->ShouldIgnoreInvalidation()) {
639 nsView
* view
= aVM
->GetRootView()->GetParent();
640 aVM
= view
? view
->GetViewManager() : nullptr;
646 nsViewManager::InvalidateView(nsView
*aView
, const nsRect
&aRect
)
648 // If painting is suppressed in the presshell or an ancestor drop all
649 // invalidates, it will invalidate everything when it unsuppresses.
650 if (ShouldIgnoreInvalidation(this)) {
654 InvalidateViewNoSuppression(aView
, aRect
);
658 nsViewManager::InvalidateViewNoSuppression(nsView
*aView
,
661 NS_PRECONDITION(nullptr != aView
, "null view");
663 NS_ASSERTION(aView
->GetViewManager() == this,
664 "InvalidateViewNoSuppression called on view we don't own");
666 nsRect
damagedRect(aRect
);
667 if (damagedRect
.IsEmpty()) {
671 nsView
* displayRoot
= GetDisplayRootFor(aView
);
672 nsViewManager
* displayRootVM
= displayRoot
->GetViewManager();
673 // Propagate the update to the displayRoot, since iframes, for example,
674 // can overlap each other and be translucent. So we have to possibly
675 // invalidate our rect in each of the widgets we have lying about.
676 damagedRect
.MoveBy(aView
->GetOffsetTo(displayRoot
));
677 int32_t rootAPD
= displayRootVM
->AppUnitsPerDevPixel();
678 int32_t APD
= AppUnitsPerDevPixel();
679 damagedRect
= damagedRect
.ScaleToOtherAppUnitsRoundOut(APD
, rootAPD
);
681 // accumulate this rectangle in the view's dirty region, so we can
683 AddDirtyRegion(displayRoot
, nsRegion(damagedRect
));
687 nsViewManager::InvalidateAllViews()
689 if (RootViewManager() != this) {
690 return RootViewManager()->InvalidateAllViews();
693 InvalidateViews(mRootView
);
696 void nsViewManager::InvalidateViews(nsView
*aView
)
698 // Invalidate this view.
699 InvalidateView(aView
);
701 // Invalidate all children as well.
702 nsView
* childView
= aView
->GetFirstChild();
703 while (nullptr != childView
) {
704 childView
->GetViewManager()->InvalidateViews(childView
);
705 childView
= childView
->GetNextSibling();
709 void nsViewManager::WillPaintWindow(nsIWidget
* aWidget
)
711 RefPtr
<nsIWidget
> widget(aWidget
);
713 nsView
* view
= nsView::GetViewFor(widget
);
714 LayerManager
* manager
= widget
->GetLayerManager();
716 (view
->ForcedRepaint() || !manager
->NeedsWidgetInvalidation())) {
717 ProcessPendingUpdates();
718 // Re-get the view pointer here since the ProcessPendingUpdates might have
719 // destroyed it during CallWillPaintOnObservers.
720 view
= nsView::GetViewFor(widget
);
722 view
->SetForcedRepaint(false);
727 nsCOMPtr
<nsIPresShell
> shell
= mPresShell
;
729 shell
->WillPaintWindow();
733 bool nsViewManager::PaintWindow(nsIWidget
* aWidget
,
734 const LayoutDeviceIntRegion
& aRegion
)
736 if (!aWidget
|| !mContext
)
739 NS_ASSERTION(IsPaintingAllowed(),
740 "shouldn't be receiving paint events while painting is disallowed!");
742 // Get the view pointer here since NS_WILL_PAINT might have
743 // destroyed it during CallWillPaintOnObservers (bug 378273).
744 nsView
* view
= nsView::GetViewFor(aWidget
);
745 if (view
&& !aRegion
.IsEmpty()) {
746 Refresh(view
, aRegion
);
752 void nsViewManager::DidPaintWindow()
754 nsCOMPtr
<nsIPresShell
> shell
= mPresShell
;
756 shell
->DidPaintWindow();
761 nsViewManager::DispatchEvent(WidgetGUIEvent
*aEvent
,
763 nsEventStatus
* aStatus
)
765 PROFILER_LABEL("nsViewManager", "DispatchEvent",
766 js::ProfileEntry::Category::EVENTS
);
768 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
770 // Ignore mouse events that we synthesize.
771 mouseEvent
->mReason
== WidgetMouseEvent::eReal
&&
772 // Ignore mouse exit and enter (we'll get moves if the user
773 // is really moving the mouse) since we get them when we
774 // create and destroy widgets.
775 mouseEvent
->mMessage
!= eMouseExitFromWidget
&&
776 mouseEvent
->mMessage
!= eMouseEnterIntoWidget
) ||
777 aEvent
->HasKeyEventMessage() ||
778 aEvent
->HasIMEEventMessage() ||
779 aEvent
->mMessage
== ePluginInputEvent
) {
780 gLastUserEventTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
783 // Find the view whose coordinates system we're in.
784 nsView
* view
= aView
;
785 bool dispatchUsingCoordinates
= aEvent
->IsUsingCoordinates();
786 if (dispatchUsingCoordinates
) {
787 // Will dispatch using coordinates. Pretty bogus but it's consistent
788 // with what presshell does.
789 view
= GetDisplayRootFor(view
);
792 // If the view has no frame, look for a view that does.
793 nsIFrame
* frame
= view
->GetFrame();
795 (dispatchUsingCoordinates
|| aEvent
->HasKeyEventMessage() ||
796 aEvent
->IsIMERelatedEvent() ||
797 aEvent
->IsNonRetargetedNativeEventDelivererForPlugin() ||
798 aEvent
->HasPluginActivationEventMessage())) {
799 while (view
&& !view
->GetFrame()) {
800 view
= view
->GetParent();
804 frame
= view
->GetFrame();
808 if (nullptr != frame
) {
809 // Hold a refcount to the presshell. The continued existence of the
810 // presshell will delay deletion of this view hierarchy should the event
811 // want to cause its destruction in, say, some JavaScript event handler.
812 nsCOMPtr
<nsIPresShell
> shell
= view
->GetViewManager()->GetPresShell();
814 shell
->HandleEvent(frame
, aEvent
, false, aStatus
);
819 *aStatus
= nsEventStatus_eIgnore
;
822 // Recursively reparent widgets if necessary
824 void nsViewManager::ReparentChildWidgets(nsView
* aView
, nsIWidget
*aNewWidget
)
826 NS_PRECONDITION(aNewWidget
, "");
828 if (aView
->HasWidget()) {
829 // Check to see if the parent widget is the
830 // same as the new parent. If not then reparent
831 // the widget, otherwise there is nothing more
832 // to do for the view and its descendants
833 nsIWidget
* widget
= aView
->GetWidget();
834 nsIWidget
* parentWidget
= widget
->GetParent();
837 if (parentWidget
!= aNewWidget
) {
838 widget
->SetParent(aNewWidget
);
841 // Toplevel widget (popup, dialog, etc)
842 widget
->ReparentNativeWidget(aNewWidget
);
847 // Need to check each of the views children to see
848 // if they have a widget and reparent it.
850 for (nsView
*kid
= aView
->GetFirstChild(); kid
; kid
= kid
->GetNextSibling()) {
851 ReparentChildWidgets(kid
, aNewWidget
);
855 // Reparent a view and its descendant views widgets if necessary
857 void nsViewManager::ReparentWidgets(nsView
* aView
, nsView
*aParent
)
859 NS_PRECONDITION(aParent
, "Must have a parent");
860 NS_PRECONDITION(aView
, "Must have a view");
862 // Quickly determine whether the view has pre-existing children or a
863 // widget. In most cases the view will not have any pre-existing
864 // children when this is called. Only in the case
865 // where a view has been reparented by removing it from
866 // a reinserting it into a new location in the view hierarchy do we
867 // have to consider reparenting the existing widgets for the view and
869 if (aView
->HasWidget() || aView
->GetFirstChild()) {
870 nsIWidget
* parentWidget
= aParent
->GetNearestWidget(nullptr);
872 ReparentChildWidgets(aView
, parentWidget
);
875 NS_WARNING("Can not find a widget for the parent view");
880 nsViewManager::InsertChild(nsView
*aParent
, nsView
*aChild
, nsView
*aSibling
,
883 NS_PRECONDITION(nullptr != aParent
, "null ptr");
884 NS_PRECONDITION(nullptr != aChild
, "null ptr");
885 NS_ASSERTION(aSibling
== nullptr || aSibling
->GetParent() == aParent
,
886 "tried to insert view with invalid sibling");
887 NS_ASSERTION(!IsViewInserted(aChild
), "tried to insert an already-inserted view");
889 if ((nullptr != aParent
) && (nullptr != aChild
))
891 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
892 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
894 if (nullptr == aSibling
) {
896 // insert at end of document order, i.e., before first view
897 // this is the common case, by far
898 aParent
->InsertChild(aChild
, nullptr);
899 ReparentWidgets(aChild
, aParent
);
901 // insert at beginning of document order, i.e., after last view
902 nsView
*kid
= aParent
->GetFirstChild();
903 nsView
*prev
= nullptr;
906 kid
= kid
->GetNextSibling();
908 // prev is last view or null if there are no children
909 aParent
->InsertChild(aChild
, prev
);
910 ReparentWidgets(aChild
, aParent
);
913 nsView
*kid
= aParent
->GetFirstChild();
914 nsView
*prev
= nullptr;
915 while (kid
&& aSibling
!= kid
) {
916 //get the next sibling view
918 kid
= kid
->GetNextSibling();
920 NS_ASSERTION(kid
!= nullptr,
921 "couldn't find sibling in child list");
923 // insert after 'kid' in document order, i.e. before in view order
924 aParent
->InsertChild(aChild
, prev
);
925 ReparentWidgets(aChild
, aParent
);
927 // insert before 'kid' in document order, i.e. after in view order
928 aParent
->InsertChild(aChild
, kid
);
929 ReparentWidgets(aChild
, aParent
);
933 // if the parent view is marked as "floating", make the newly added view float as well.
934 if (aParent
->GetFloating())
935 aChild
->SetFloating(true);
940 nsViewManager::RemoveChild(nsView
*aChild
)
942 NS_ASSERTION(aChild
, "aChild must not be null");
944 nsView
* parent
= aChild
->GetParent();
946 if (nullptr != parent
) {
947 NS_ASSERTION(aChild
->GetViewManager() == this ||
948 parent
->GetViewManager() == this, "wrong view manager");
949 parent
->RemoveChild(aChild
);
954 nsViewManager::MoveViewTo(nsView
*aView
, nscoord aX
, nscoord aY
)
956 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
957 aView
->SetPosition(aX
, aY
);
961 nsViewManager::ResizeView(nsView
*aView
, const nsRect
&aRect
, bool aRepaintExposedAreaOnly
)
963 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
965 nsRect oldDimensions
= aView
->GetDimensions();
966 if (!oldDimensions
.IsEqualEdges(aRect
)) {
967 aView
->SetDimensions(aRect
, true);
970 // Note that if layout resizes the view and the view has a custom clip
971 // region set, then we expect layout to update the clip region too. Thus
972 // in the case where mClipRect has been optimized away to just be a null
973 // pointer, and this resize is implicitly changing the clip rect, it's OK
974 // because layout will change it back again if necessary.
978 nsViewManager::SetViewFloating(nsView
*aView
, bool aFloating
)
980 NS_ASSERTION(!(nullptr == aView
), "no view");
982 aView
->SetFloating(aFloating
);
986 nsViewManager::SetViewVisibility(nsView
*aView
, nsViewVisibility aVisible
)
988 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
990 if (aVisible
!= aView
->GetVisibility()) {
991 aView
->SetVisibility(aVisible
);
995 bool nsViewManager::IsViewInserted(nsView
*aView
)
997 if (mRootView
== aView
) {
1000 if (aView
->GetParent() == nullptr) {
1003 nsView
* view
= aView
->GetParent()->GetFirstChild();
1004 while (view
!= nullptr) {
1005 if (view
== aView
) {
1008 view
= view
->GetNextSibling();
1014 nsViewManager::SetViewZIndex(nsView
*aView
, bool aAutoZIndex
, int32_t aZIndex
)
1016 NS_ASSERTION((aView
!= nullptr), "no view");
1018 // don't allow the root view's z-index to be changed. It should always be zero.
1019 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1020 if (aView
== mRootView
) {
1028 aView
->SetZIndex(aAutoZIndex
, aZIndex
);
1032 nsViewManager::IncrementDisableRefreshCount()
1035 return RootViewManager()->IncrementDisableRefreshCount();
1038 ++mRefreshDisableCount
;
1044 nsViewManager::DecrementDisableRefreshCount()
1046 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1047 --mRefreshDisableCount
;
1048 NS_ASSERTION(mRefreshDisableCount
>= 0, "Invalid refresh disable count!");
1052 nsViewManager::GetRootWidget(nsIWidget
**aWidget
)
1058 if (mRootView
->HasWidget()) {
1059 *aWidget
= mRootView
->GetWidget();
1060 NS_ADDREF(*aWidget
);
1063 if (mRootView
->GetParent()) {
1064 mRootView
->GetParent()->GetViewManager()->GetRootWidget(aWidget
);
1071 nsViewManager::ViewToWidget(nsView
* aView
, const nsRect
& aRect
) const
1073 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
1075 // account for the view's origin not lining up with the widget's
1076 nsRect rect
= aRect
+ aView
->ViewToWidgetOffset();
1078 // finally, convert to device coordinates.
1079 return LayoutDeviceIntRect::FromUnknownRect(
1080 rect
.ToOutsidePixels(AppUnitsPerDevPixel()));
1084 nsViewManager::IsPainting(bool& aIsPainting
)
1086 aIsPainting
= IsPainting();
1090 nsViewManager::ProcessPendingUpdates()
1093 RootViewManager()->ProcessPendingUpdates();
1097 // Flush things like reflows by calling WillPaint on observer presShells.
1099 mPresShell
->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
1101 RefPtr
<nsViewManager
> strongThis(this);
1102 CallWillPaintOnObservers();
1104 ProcessPendingUpdatesForView(mRootView
, true);
1109 nsViewManager::UpdateWidgetGeometry()
1112 RootViewManager()->UpdateWidgetGeometry();
1116 if (mHasPendingWidgetGeometryChanges
) {
1117 mHasPendingWidgetGeometryChanges
= false;
1118 RefPtr
<nsViewManager
> strongThis(this);
1119 ProcessPendingUpdatesForView(mRootView
, false);
1124 nsViewManager::CallWillPaintOnObservers()
1126 NS_PRECONDITION(IsRootVM(), "Must be root VM for this to be called!");
1128 if (NS_WARN_IF(!gViewManagers
)) {
1133 for (index
= 0; index
< gViewManagers
->Length(); index
++) {
1134 nsViewManager
* vm
= gViewManagers
->ElementAt(index
);
1135 if (vm
->RootViewManager() == this) {
1137 if (vm
->mRootView
&& vm
->mRootView
->IsEffectivelyVisible()) {
1138 nsCOMPtr
<nsIPresShell
> shell
= vm
->GetPresShell();
1148 nsViewManager::GetLastUserEventTime(uint32_t& aTime
)
1150 aTime
= gLastUserEventTime
;
1154 nsViewManager::InvalidateHierarchy()
1158 NS_RELEASE(mRootViewManager
);
1160 nsView
*parent
= mRootView
->GetParent();
1162 mRootViewManager
= parent
->GetViewManager()->RootViewManager();
1163 NS_ADDREF(mRootViewManager
);
1164 NS_ASSERTION(mRootViewManager
!= this,
1165 "Root view had a parent, but it has the same view manager");
1167 mRootViewManager
= this;