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/StartupTimeline.h"
13 #include "mozilla/dom/Document.h"
14 #include "nsAutoPtr.h"
15 #include "nsGfxCIID.h"
19 #include "nsCOMArray.h"
20 #include "nsIPluginWidget.h"
21 #include "nsXULPopupManager.h"
22 #include "nsPresContext.h"
23 #include "GeckoProfiler.h"
24 #include "nsRefreshDriver.h"
25 #include "nsContentUtils.h" // for nsAutoScriptBlocker
26 #include "nsLayoutUtils.h"
28 #include "gfxPlatform.h"
33 DeCOMify newly private methods
38 A note about platform assumptions:
40 We assume that a widget is z-ordered on top of its parent.
42 We do NOT assume anything about the relative z-ordering of sibling widgets.
43 Even though we ask for a specific z-order, we don't assume that widget
44 z-ordering actually works.
47 using namespace mozilla
;
48 using namespace mozilla::layers
;
50 #define NSCOORD_NONE INT32_MIN
52 #undef DEBUG_MOUSE_LOCATION
54 // Weakly held references to all of the view managers
55 nsTArray
<nsViewManager
*>* nsViewManager::gViewManagers
= nullptr;
56 uint32_t nsViewManager::gLastUserEventTime
= 0;
58 nsViewManager::nsViewManager()
59 : mPresShell(nullptr),
60 mDelayedResize(NSCOORD_NONE
, NSCOORD_NONE
),
62 mRootViewManager(this),
63 mRefreshDisableCount(0),
65 mRecursiveRefreshPending(false),
66 mHasPendingWidgetGeometryChanges(false) {
67 if (gViewManagers
== nullptr) {
68 // Create an array to hold a list of view managers
69 gViewManagers
= new nsTArray
<nsViewManager
*>;
72 gViewManagers
->AppendElement(this);
75 nsViewManager::~nsViewManager() {
77 // Destroy any remaining views
83 // We have a strong ref to mRootViewManager
84 NS_RELEASE(mRootViewManager
);
87 NS_ASSERTION(gViewManagers
!= nullptr, "About to use null gViewManagers");
92 gViewManagers
->RemoveElement(this);
95 "Viewmanager instance was not in the global list of viewmanagers");
97 if (gViewManagers
->IsEmpty()) {
98 // There aren't any more view managers so
99 // release the global array of view managers
100 delete gViewManagers
;
101 gViewManagers
= nullptr;
104 MOZ_RELEASE_ASSERT(!mPresShell
,
105 "Releasing nsViewManager without having called Destroy on "
109 // We don't hold a reference to the presentation context because it
110 // holds a reference to us.
111 nsresult
nsViewManager::Init(nsDeviceContext
* aContext
) {
112 MOZ_ASSERT(nullptr != aContext
, "null ptr");
114 if (nullptr == aContext
) {
115 return NS_ERROR_NULL_POINTER
;
117 if (nullptr != mContext
) {
118 return NS_ERROR_ALREADY_INITIALIZED
;
125 nsView
* nsViewManager::CreateView(const nsRect
& aBounds
, nsView
* aParent
,
126 nsViewVisibility aVisibilityFlag
) {
127 auto* v
= new nsView(this, aVisibilityFlag
);
128 v
->SetParent(aParent
);
129 v
->SetPosition(aBounds
.X(), aBounds
.Y());
130 nsRect
dim(0, 0, aBounds
.Width(), aBounds
.Height());
131 v
->SetDimensions(dim
, false);
135 void nsViewManager::SetRootView(nsView
* aView
) {
136 MOZ_ASSERT(!aView
|| aView
->GetViewManager() == this,
137 "Unexpected viewmanager on root view");
139 // Do NOT destroy the current root view. It's the caller's responsibility
144 nsView
* parent
= mRootView
->GetParent();
146 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
147 // no need to set mRootViewManager ourselves here.
148 parent
->InsertChild(mRootView
, nullptr);
150 InvalidateHierarchy();
153 mRootView
->SetZIndex(false, 0);
155 // Else don't touch mRootViewManager
158 void nsViewManager::GetWindowDimensions(nscoord
* aWidth
, nscoord
* aHeight
) {
159 if (nullptr != mRootView
) {
160 if (mDelayedResize
== nsSize(NSCOORD_NONE
, NSCOORD_NONE
)) {
161 nsRect dim
= mRootView
->GetDimensions();
162 *aWidth
= dim
.Width();
163 *aHeight
= dim
.Height();
165 *aWidth
= mDelayedResize
.width
;
166 *aHeight
= mDelayedResize
.height
;
174 void nsViewManager::DoSetWindowDimensions(nscoord aWidth
, nscoord aHeight
,
176 nsRect oldDim
= mRootView
->GetDimensions();
177 nsRect
newDim(0, 0, aWidth
, aHeight
);
178 // We care about resizes even when one dimension is already zero.
179 if (oldDim
.IsEqualEdges(newDim
)) {
182 // Don't resize the widget. It is already being set elsewhere.
183 mRootView
->SetDimensions(newDim
, true, false);
184 if (RefPtr
<PresShell
> presShell
= mPresShell
) {
185 auto options
= ResizeReflowOptions::NoOption
;
187 options
|= ResizeReflowOptions::SuppressReflow
;
189 presShell
->ResizeReflow(aWidth
, aHeight
, options
);
193 bool nsViewManager::ShouldDelayResize() const {
194 MOZ_ASSERT(mRootView
);
195 if (!mRootView
->IsEffectivelyVisible() || !mPresShell
||
196 !mPresShell
->IsVisible()) {
199 if (nsRefreshDriver
* rd
= mPresShell
->GetRefreshDriver()) {
200 if (rd
->IsResizeSuppressed()) {
207 void nsViewManager::SetWindowDimensions(nscoord aWidth
, nscoord aHeight
,
210 if (!ShouldDelayResize() && !aDelayResize
) {
211 if (mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
) &&
212 mDelayedResize
!= nsSize(aWidth
, aHeight
)) {
213 // We have a delayed resize; that now obsolete size may already have
214 // been flushed to the PresContext so we need to update the PresContext
215 // with the new size because if the new size is exactly the same as the
216 // root view's current size then DoSetWindowDimensions will not
217 // request a resize reflow (which would correct it). See bug 617076.
218 mDelayedResize
= nsSize(aWidth
, aHeight
);
219 FlushDelayedResize(false);
221 mDelayedResize
.SizeTo(NSCOORD_NONE
, NSCOORD_NONE
);
222 DoSetWindowDimensions(aWidth
, aHeight
, /* aDoReflow = */ true);
224 mDelayedResize
.SizeTo(aWidth
, aHeight
);
226 mPresShell
->SetNeedStyleFlush();
227 mPresShell
->SetNeedLayoutFlush();
233 void nsViewManager::FlushDelayedResize(bool aDoReflow
) {
234 if (mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
)) {
235 DoSetWindowDimensions(mDelayedResize
.width
, mDelayedResize
.height
,
237 mDelayedResize
.SizeTo(NSCOORD_NONE
, NSCOORD_NONE
);
241 // Convert aIn from being relative to and in appunits of aFromView, to being
242 // relative to and in appunits of aToView.
243 static nsRegion
ConvertRegionBetweenViews(const nsRegion
& aIn
,
244 nsView
* aFromView
, nsView
* aToView
) {
246 out
.MoveBy(aFromView
->GetOffsetTo(aToView
));
247 out
= out
.ScaleToOtherAppUnitsRoundOut(
248 aFromView
->GetViewManager()->AppUnitsPerDevPixel(),
249 aToView
->GetViewManager()->AppUnitsPerDevPixel());
253 nsView
* nsViewManager::GetDisplayRootFor(nsView
* aView
) {
254 nsView
* displayRoot
= aView
;
256 nsView
* displayParent
= displayRoot
->GetParent();
257 if (!displayParent
) return displayRoot
;
259 if (displayRoot
->GetFloating() && !displayParent
->GetFloating())
262 // If we have a combobox dropdown popup within a panel popup, both the view
263 // for the dropdown popup and its parent will be floating, so we need to
264 // distinguish this situation. We do this by looking for a widget. Any view
265 // with a widget is a display root, except for plugins.
266 nsIWidget
* widget
= displayRoot
->GetWidget();
267 if (widget
&& widget
->WindowType() == eWindowType_popup
) {
268 NS_ASSERTION(displayRoot
->GetFloating() && displayParent
->GetFloating(),
269 "this should only happen with floating views that have "
274 displayRoot
= displayParent
;
279 aRegion is given in device coordinates!!
280 aContext may be null, in which case layers should be used for
283 void nsViewManager::Refresh(nsView
* aView
,
284 const LayoutDeviceIntRegion
& aRegion
) {
285 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
287 if (mPresShell
&& mPresShell
->IsNeverPainting()) {
291 // damageRegion is the damaged area, in twips, relative to the view origin
292 nsRegion damageRegion
= aRegion
.ToAppUnits(AppUnitsPerDevPixel());
294 // move region from widget coordinates into view coordinates
295 damageRegion
.MoveBy(-aView
->ViewToWidgetOffset());
297 if (damageRegion
.IsEmpty()) {
299 nsRect viewRect
= aView
->GetDimensions();
300 nsRect damageRect
= damageRegion
.GetBounds();
302 "XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's "
303 "view (%d,%d,%d,%d)!\n",
304 damageRect
.x
, damageRect
.y
, damageRect
.width
, damageRect
.height
,
305 viewRect
.x
, viewRect
.y
, viewRect
.width
, viewRect
.height
);
310 nsIWidget
* widget
= aView
->GetWidget();
315 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
317 RootViewManager()->mRecursiveRefreshPending
= true;
322 nsAutoScriptBlocker scriptBlocker
;
325 NS_ASSERTION(GetDisplayRootFor(aView
) == aView
,
326 "Widgets that we paint must all be display roots");
328 if (RefPtr
<PresShell
> presShell
= mPresShell
) {
329 #ifdef MOZ_DUMP_PAINTING
330 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
331 printf_stderr("--COMPOSITE-- %p\n", presShell
.get());
334 LayerManager
* manager
= widget
->GetLayerManager();
335 if (!manager
->NeedsWidgetInvalidation()) {
336 manager
->FlushRendering();
338 presShell
->Paint(aView
, damageRegion
, PaintFlags::PaintComposite
);
340 #ifdef MOZ_DUMP_PAINTING
341 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
342 printf_stderr("--ENDCOMPOSITE--\n");
345 mozilla::StartupTimeline::RecordOnce(
346 mozilla::StartupTimeline::FIRST_PAINT
);
352 if (RootViewManager()->mRecursiveRefreshPending
) {
353 RootViewManager()->mRecursiveRefreshPending
= false;
354 InvalidateAllViews();
358 void nsViewManager::ProcessPendingUpdatesForView(nsView
* aView
,
359 bool aFlushDirtyRegion
) {
360 NS_ASSERTION(IsRootVM(), "Updates will be missed");
365 RefPtr
<PresShell
> rootPresShell
= mPresShell
;
366 AutoTArray
<nsCOMPtr
<nsIWidget
>, 1> widgets
;
367 aView
->GetViewManager()->ProcessPendingUpdatesRecurse(aView
, widgets
);
368 for (uint32_t i
= 0; i
< widgets
.Length(); ++i
) {
369 nsView
* view
= nsView::GetViewFor(widgets
[i
]);
371 if (view
->mNeedsWindowPropertiesSync
) {
372 view
->mNeedsWindowPropertiesSync
= false;
373 if (nsViewManager
* vm
= view
->GetViewManager()) {
374 if (PresShell
* presShell
= vm
->GetPresShell()) {
375 presShell
->SyncWindowProperties(view
);
380 view
= nsView::GetViewFor(widgets
[i
]);
382 view
->ResetWidgetBounds(false, true);
385 if (rootPresShell
->GetViewManager() != this) {
386 return; // presentation might have been torn down
388 if (aFlushDirtyRegion
) {
389 nsAutoScriptBlocker scriptBlocker
;
391 for (uint32_t i
= 0; i
< widgets
.Length(); ++i
) {
392 nsIWidget
* widget
= widgets
[i
];
393 nsView
* view
= nsView::GetViewFor(widget
);
395 RefPtr
<nsViewManager
> viewManager
= view
->GetViewManager();
396 viewManager
->ProcessPendingUpdatesPaint(MOZ_KnownLive(widget
));
403 void nsViewManager::ProcessPendingUpdatesRecurse(
404 nsView
* aView
, AutoTArray
<nsCOMPtr
<nsIWidget
>, 1>& aWidgets
) {
405 if (mPresShell
&& mPresShell
->IsNeverPainting()) {
409 for (nsView
* childView
= aView
->GetFirstChild(); childView
;
410 childView
= childView
->GetNextSibling()) {
411 childView
->GetViewManager()->ProcessPendingUpdatesRecurse(childView
,
415 nsIWidget
* widget
= aView
->GetWidget();
417 aWidgets
.AppendElement(widget
);
419 FlushDirtyRegionToWidget(aView
);
423 void nsViewManager::ProcessPendingUpdatesPaint(nsIWidget
* aWidget
) {
424 if (aWidget
->NeedsPaint()) {
425 // If an ancestor widget was hidden and then shown, we could
426 // have a delayed resize to handle.
427 for (RefPtr
<nsViewManager
> vm
= this; vm
;
428 vm
= vm
->mRootView
->GetParent()
429 ? vm
->mRootView
->GetParent()->GetViewManager()
431 if (vm
->mDelayedResize
!= nsSize(NSCOORD_NONE
, NSCOORD_NONE
) &&
432 vm
->mRootView
->IsEffectivelyVisible() && vm
->mPresShell
&&
433 vm
->mPresShell
->IsVisible()) {
434 vm
->FlushDelayedResize(true);
437 nsView
* view
= nsView::GetViewFor(aWidget
);
440 NS_ERROR("FlushDelayedResize destroyed the nsView?");
444 nsIWidgetListener
* previousListener
=
445 aWidget
->GetPreviouslyAttachedWidgetListener();
447 if (previousListener
&& previousListener
!= view
&&
448 view
->IsPrimaryFramePaintSuppressed()) {
452 if (RefPtr
<PresShell
> presShell
= mPresShell
) {
453 #ifdef MOZ_DUMP_PAINTING
454 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
456 "---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
457 presShell
.get(), view
, aWidget
);
461 presShell
->Paint(view
, nsRegion(), PaintFlags::PaintLayers
);
462 view
->SetForcedRepaint(false);
464 #ifdef MOZ_DUMP_PAINTING
465 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
466 printf_stderr("---- PAINT END ----\n");
471 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget
));
474 void nsViewManager::FlushDirtyRegionToWidget(nsView
* aView
) {
475 NS_ASSERTION(aView
->GetViewManager() == this,
476 "FlushDirtyRegionToWidget called on view we don't own");
478 if (!aView
->HasNonEmptyDirtyRegion()) return;
480 nsRegion
* dirtyRegion
= aView
->GetDirtyRegion();
481 nsView
* nearestViewWithWidget
= aView
;
482 while (!nearestViewWithWidget
->HasWidget() &&
483 nearestViewWithWidget
->GetParent()) {
484 nearestViewWithWidget
= nearestViewWithWidget
->GetParent();
487 ConvertRegionBetweenViews(*dirtyRegion
, aView
, nearestViewWithWidget
);
489 nsViewManager
* widgetVM
= nearestViewWithWidget
->GetViewManager();
490 widgetVM
->InvalidateWidgetArea(nearestViewWithWidget
, r
);
491 dirtyRegion
->SetEmpty();
494 void nsViewManager::InvalidateView(nsView
* aView
) {
495 // Mark the entire view as damaged
496 InvalidateView(aView
, aView
->GetDimensions());
499 static void AddDirtyRegion(nsView
* aView
, const nsRegion
& aDamagedRegion
) {
500 nsRegion
* dirtyRegion
= aView
->GetDirtyRegion();
501 if (!dirtyRegion
) return;
503 dirtyRegion
->Or(*dirtyRegion
, aDamagedRegion
);
504 dirtyRegion
->SimplifyOutward(8);
507 void nsViewManager::PostPendingUpdate() {
508 nsViewManager
* rootVM
= RootViewManager();
509 rootVM
->mHasPendingWidgetGeometryChanges
= true;
510 if (rootVM
->mPresShell
) {
511 rootVM
->mPresShell
->ScheduleViewManagerFlush();
516 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
517 * every widget child of aWidgetView, plus aWidgetView's own widget
519 void nsViewManager::InvalidateWidgetArea(nsView
* aWidgetView
,
520 const nsRegion
& aDamagedRegion
) {
521 NS_ASSERTION(aWidgetView
->GetViewManager() == this,
522 "InvalidateWidgetArea called on view we don't own");
523 nsIWidget
* widget
= aWidgetView
->GetWidget();
526 nsRect dbgBounds
= aDamagedRegion
.GetBounds();
527 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
528 aWidgetView
, aWidgetView
->IsAttachedToTopLevel(),
529 widget
, dbgBounds
.x
, dbgBounds
.y
, dbgBounds
.width
, dbgBounds
.height
);
532 // If the widget is hidden, it don't cover nothing
533 if (widget
&& !widget
->IsVisible()) {
538 // The root view or a scrolling view might not have a widget
539 // (for example, during printing). We get here when we scroll
540 // during printing to show selected options in a listbox, for example.
544 // Update all child widgets with the damage. In the process,
545 // accumulate the union of all the child widget areas, or at least
546 // some subset of that.
548 if (widget
->GetTransparencyMode() != eTransparencyTransparent
) {
549 for (nsIWidget
* childWidget
= widget
->GetFirstChild(); childWidget
;
550 childWidget
= childWidget
->GetNextSibling()) {
551 nsView
* view
= nsView::GetViewFor(childWidget
);
552 NS_ASSERTION(view
!= aWidgetView
, "will recur infinitely");
553 nsWindowType type
= childWidget
->WindowType();
554 if (view
&& childWidget
->IsVisible() && type
!= eWindowType_popup
) {
555 NS_ASSERTION(childWidget
->IsPlugin(),
556 "Only plugin or popup widgets can be children!");
558 // We do not need to invalidate in plugin widgets, but we should
559 // exclude them from the invalidation region IF we're not on
560 // Mac. On Mac we need to draw under plugin widgets, because
561 // plugin widgets are basically invisible
563 // GetBounds should compensate for chrome on a toplevel widget
564 LayoutDeviceIntRect bounds
= childWidget
->GetBounds();
566 nsTArray
<LayoutDeviceIntRect
> clipRects
;
567 childWidget
->GetWindowClipRegion(&clipRects
);
568 for (uint32_t i
= 0; i
< clipRects
.Length(); ++i
) {
569 nsRect rr
= LayoutDeviceIntRect::ToAppUnits(
570 clipRects
[i
] + bounds
.TopLeft(), AppUnitsPerDevPixel());
571 children
.Or(children
, rr
- aWidgetView
->ViewToWidgetOffset());
572 children
.SimplifyInward(20);
580 leftOver
.Sub(aDamagedRegion
, children
);
582 if (!leftOver
.IsEmpty()) {
583 for (auto iter
= leftOver
.RectIter(); !iter
.Done(); iter
.Next()) {
584 LayoutDeviceIntRect bounds
= ViewToWidget(aWidgetView
, iter
.Get());
585 widget
->Invalidate(bounds
);
590 static bool ShouldIgnoreInvalidation(nsViewManager
* aVM
) {
592 PresShell
* presShell
= aVM
->GetPresShell();
593 if (!presShell
|| presShell
->ShouldIgnoreInvalidation()) {
596 nsView
* view
= aVM
->GetRootView()->GetParent();
597 aVM
= view
? view
->GetViewManager() : nullptr;
602 void nsViewManager::InvalidateView(nsView
* aView
, const nsRect
& aRect
) {
603 // If painting is suppressed in the presshell or an ancestor drop all
604 // invalidates, it will invalidate everything when it unsuppresses.
605 if (ShouldIgnoreInvalidation(this)) {
609 InvalidateViewNoSuppression(aView
, aRect
);
612 void nsViewManager::InvalidateViewNoSuppression(nsView
* aView
,
613 const nsRect
& aRect
) {
614 MOZ_ASSERT(nullptr != aView
, "null view");
616 NS_ASSERTION(aView
->GetViewManager() == this,
617 "InvalidateViewNoSuppression called on view we don't own");
619 nsRect
damagedRect(aRect
);
620 if (damagedRect
.IsEmpty()) {
624 nsView
* displayRoot
= GetDisplayRootFor(aView
);
625 nsViewManager
* displayRootVM
= displayRoot
->GetViewManager();
626 // Propagate the update to the displayRoot, since iframes, for example,
627 // can overlap each other and be translucent. So we have to possibly
628 // invalidate our rect in each of the widgets we have lying about.
629 damagedRect
.MoveBy(aView
->GetOffsetTo(displayRoot
));
630 int32_t rootAPD
= displayRootVM
->AppUnitsPerDevPixel();
631 int32_t APD
= AppUnitsPerDevPixel();
632 damagedRect
= damagedRect
.ScaleToOtherAppUnitsRoundOut(APD
, rootAPD
);
634 // accumulate this rectangle in the view's dirty region, so we can
636 AddDirtyRegion(displayRoot
, nsRegion(damagedRect
));
639 void nsViewManager::InvalidateAllViews() {
640 if (RootViewManager() != this) {
641 return RootViewManager()->InvalidateAllViews();
644 InvalidateViews(mRootView
);
647 void nsViewManager::InvalidateViews(nsView
* aView
) {
648 // Invalidate this view.
649 InvalidateView(aView
);
651 // Invalidate all children as well.
652 nsView
* childView
= aView
->GetFirstChild();
653 while (nullptr != childView
) {
654 childView
->GetViewManager()->InvalidateViews(childView
);
655 childView
= childView
->GetNextSibling();
659 void nsViewManager::WillPaintWindow(nsIWidget
* aWidget
) {
661 nsView
* view
= nsView::GetViewFor(aWidget
);
662 LayerManager
* manager
= aWidget
->GetLayerManager();
664 (view
->ForcedRepaint() || !manager
->NeedsWidgetInvalidation())) {
665 ProcessPendingUpdates();
666 // Re-get the view pointer here since the ProcessPendingUpdates might have
667 // destroyed it during CallWillPaintOnObservers.
668 view
= nsView::GetViewFor(aWidget
);
670 view
->SetForcedRepaint(false);
675 if (RefPtr
<PresShell
> presShell
= mPresShell
) {
676 presShell
->WillPaintWindow();
680 bool nsViewManager::PaintWindow(nsIWidget
* aWidget
,
681 const LayoutDeviceIntRegion
& aRegion
) {
682 if (!aWidget
|| !mContext
) return false;
686 "shouldn't be receiving paint events while painting is disallowed!");
688 // Get the view pointer here since NS_WILL_PAINT might have
689 // destroyed it during CallWillPaintOnObservers (bug 378273).
690 nsView
* view
= nsView::GetViewFor(aWidget
);
691 if (view
&& !aRegion
.IsEmpty()) {
692 Refresh(view
, aRegion
);
698 void nsViewManager::DidPaintWindow() {
699 if (RefPtr
<PresShell
> presShell
= mPresShell
) {
700 presShell
->DidPaintWindow();
704 void nsViewManager::DispatchEvent(WidgetGUIEvent
* aEvent
, nsView
* aView
,
705 nsEventStatus
* aStatus
) {
706 AUTO_PROFILER_LABEL("nsViewManager::DispatchEvent", OTHER
);
708 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
710 // Ignore mouse events that we synthesize.
711 mouseEvent
->mReason
== WidgetMouseEvent::eReal
&&
712 // Ignore mouse exit and enter (we'll get moves if the user
713 // is really moving the mouse) since we get them when we
714 // create and destroy widgets.
715 mouseEvent
->mMessage
!= eMouseExitFromWidget
&&
716 mouseEvent
->mMessage
!= eMouseEnterIntoWidget
) ||
717 aEvent
->HasKeyEventMessage() || aEvent
->HasIMEEventMessage() ||
718 aEvent
->mMessage
== ePluginInputEvent
) {
719 gLastUserEventTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
722 // Find the view whose coordinates system we're in.
723 nsView
* view
= aView
;
724 bool dispatchUsingCoordinates
= aEvent
->IsUsingCoordinates();
725 if (dispatchUsingCoordinates
) {
726 // Will dispatch using coordinates. Pretty bogus but it's consistent
727 // with what presshell does.
728 view
= GetDisplayRootFor(view
);
731 // If the view has no frame, look for a view that does.
732 nsIFrame
* frame
= view
->GetFrame();
733 if (!frame
&& (dispatchUsingCoordinates
|| aEvent
->HasKeyEventMessage() ||
734 aEvent
->IsIMERelatedEvent() ||
735 aEvent
->IsNonRetargetedNativeEventDelivererForPlugin() ||
736 aEvent
->HasPluginActivationEventMessage())) {
737 while (view
&& !view
->GetFrame()) {
738 view
= view
->GetParent();
742 frame
= view
->GetFrame();
746 if (nullptr != frame
) {
747 // Hold a refcount to the presshell. The continued existence of the
748 // presshell will delay deletion of this view hierarchy should the event
749 // want to cause its destruction in, say, some JavaScript event handler.
750 if (RefPtr
<PresShell
> presShell
= view
->GetViewManager()->GetPresShell()) {
751 presShell
->HandleEvent(frame
, aEvent
, false, aStatus
);
756 *aStatus
= nsEventStatus_eIgnore
;
759 // Recursively reparent widgets if necessary
761 void nsViewManager::ReparentChildWidgets(nsView
* aView
, nsIWidget
* aNewWidget
) {
762 MOZ_ASSERT(aNewWidget
, "null widget");
764 if (aView
->HasWidget()) {
765 // Check to see if the parent widget is the
766 // same as the new parent. If not then reparent
767 // the widget, otherwise there is nothing more
768 // to do for the view and its descendants
769 nsIWidget
* widget
= aView
->GetWidget();
770 nsIWidget
* parentWidget
= widget
->GetParent();
773 if (parentWidget
!= aNewWidget
) {
774 widget
->SetParent(aNewWidget
);
777 // Toplevel widget (popup, dialog, etc)
778 widget
->ReparentNativeWidget(aNewWidget
);
783 // Need to check each of the views children to see
784 // if they have a widget and reparent it.
786 for (nsView
* kid
= aView
->GetFirstChild(); kid
; kid
= kid
->GetNextSibling()) {
787 ReparentChildWidgets(kid
, aNewWidget
);
791 // Reparent a view and its descendant views widgets if necessary
793 void nsViewManager::ReparentWidgets(nsView
* aView
, nsView
* aParent
) {
794 MOZ_ASSERT(aParent
, "Must have a parent");
795 MOZ_ASSERT(aView
, "Must have a view");
797 // Quickly determine whether the view has pre-existing children or a
798 // widget. In most cases the view will not have any pre-existing
799 // children when this is called. Only in the case
800 // where a view has been reparented by removing it from
801 // a reinserting it into a new location in the view hierarchy do we
802 // have to consider reparenting the existing widgets for the view and
804 if (aView
->HasWidget() || aView
->GetFirstChild()) {
805 nsIWidget
* parentWidget
= aParent
->GetNearestWidget(nullptr);
807 ReparentChildWidgets(aView
, parentWidget
);
810 NS_WARNING("Can not find a widget for the parent view");
814 void nsViewManager::InsertChild(nsView
* aParent
, nsView
* aChild
,
815 nsView
* aSibling
, bool aAfter
) {
816 MOZ_ASSERT(nullptr != aParent
, "null ptr");
817 MOZ_ASSERT(nullptr != aChild
, "null ptr");
818 NS_ASSERTION(aSibling
== nullptr || aSibling
->GetParent() == aParent
,
819 "tried to insert view with invalid sibling");
820 NS_ASSERTION(!IsViewInserted(aChild
),
821 "tried to insert an already-inserted view");
823 if ((nullptr != aParent
) && (nullptr != aChild
)) {
824 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid'
825 // in document order, otherwise after 'kid' (i.e. before 'kid' in document
828 if (nullptr == aSibling
) {
830 // insert at end of document order, i.e., before first view
831 // this is the common case, by far
832 aParent
->InsertChild(aChild
, nullptr);
833 ReparentWidgets(aChild
, aParent
);
835 // insert at beginning of document order, i.e., after last view
836 nsView
* kid
= aParent
->GetFirstChild();
837 nsView
* prev
= nullptr;
840 kid
= kid
->GetNextSibling();
842 // prev is last view or null if there are no children
843 aParent
->InsertChild(aChild
, prev
);
844 ReparentWidgets(aChild
, aParent
);
847 nsView
* kid
= aParent
->GetFirstChild();
848 nsView
* prev
= nullptr;
849 while (kid
&& aSibling
!= kid
) {
850 // get the next sibling view
852 kid
= kid
->GetNextSibling();
854 NS_ASSERTION(kid
!= nullptr, "couldn't find sibling in child list");
856 // insert after 'kid' in document order, i.e. before in view order
857 aParent
->InsertChild(aChild
, prev
);
858 ReparentWidgets(aChild
, aParent
);
860 // insert before 'kid' in document order, i.e. after in view order
861 aParent
->InsertChild(aChild
, kid
);
862 ReparentWidgets(aChild
, aParent
);
866 // if the parent view is marked as "floating", make the newly added view
868 if (aParent
->GetFloating()) aChild
->SetFloating(true);
872 void nsViewManager::RemoveChild(nsView
* aChild
) {
873 NS_ASSERTION(aChild
, "aChild must not be null");
875 nsView
* parent
= aChild
->GetParent();
877 if (nullptr != parent
) {
879 aChild
->GetViewManager() == this || parent
->GetViewManager() == this,
880 "wrong view manager");
881 parent
->RemoveChild(aChild
);
885 void nsViewManager::MoveViewTo(nsView
* aView
, nscoord aX
, nscoord aY
) {
886 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
887 aView
->SetPosition(aX
, aY
);
890 void nsViewManager::ResizeView(nsView
* aView
, const nsRect
& aRect
,
891 bool aRepaintExposedAreaOnly
) {
892 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
894 nsRect oldDimensions
= aView
->GetDimensions();
895 if (!oldDimensions
.IsEqualEdges(aRect
)) {
896 aView
->SetDimensions(aRect
, true);
899 // Note that if layout resizes the view and the view has a custom clip
900 // region set, then we expect layout to update the clip region too. Thus
901 // in the case where mClipRect has been optimized away to just be a null
902 // pointer, and this resize is implicitly changing the clip rect, it's OK
903 // because layout will change it back again if necessary.
906 void nsViewManager::SetViewFloating(nsView
* aView
, bool aFloating
) {
907 NS_ASSERTION(!(nullptr == aView
), "no view");
909 aView
->SetFloating(aFloating
);
912 void nsViewManager::SetViewVisibility(nsView
* aView
,
913 nsViewVisibility aVisible
) {
914 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
916 if (aVisible
!= aView
->GetVisibility()) {
917 aView
->SetVisibility(aVisible
);
921 bool nsViewManager::IsViewInserted(nsView
* aView
) {
922 if (mRootView
== aView
) {
925 if (aView
->GetParent() == nullptr) {
928 nsView
* view
= aView
->GetParent()->GetFirstChild();
929 while (view
!= nullptr) {
933 view
= view
->GetNextSibling();
938 void nsViewManager::SetViewZIndex(nsView
* aView
, bool aAutoZIndex
,
940 NS_ASSERTION((aView
!= nullptr), "no view");
942 // don't allow the root view's z-index to be changed. It should always be
943 // zero. This could be removed and replaced with a style rule, or just removed
944 // altogether, with interesting consequences
945 if (aView
== mRootView
) {
953 aView
->SetZIndex(aAutoZIndex
, aZIndex
);
956 nsViewManager
* nsViewManager::IncrementDisableRefreshCount() {
958 return RootViewManager()->IncrementDisableRefreshCount();
961 ++mRefreshDisableCount
;
966 void nsViewManager::DecrementDisableRefreshCount() {
967 NS_ASSERTION(IsRootVM(), "Should only be called on root");
968 --mRefreshDisableCount
;
969 NS_ASSERTION(mRefreshDisableCount
>= 0, "Invalid refresh disable count!");
972 void nsViewManager::GetRootWidget(nsIWidget
** aWidget
) {
977 if (mRootView
->HasWidget()) {
978 *aWidget
= mRootView
->GetWidget();
982 if (mRootView
->GetParent()) {
983 mRootView
->GetParent()->GetViewManager()->GetRootWidget(aWidget
);
989 LayoutDeviceIntRect
nsViewManager::ViewToWidget(nsView
* aView
,
990 const nsRect
& aRect
) const {
991 NS_ASSERTION(aView
->GetViewManager() == this, "wrong view manager");
993 // account for the view's origin not lining up with the widget's
994 nsRect rect
= aRect
+ aView
->ViewToWidgetOffset();
996 // finally, convert to device coordinates.
997 return LayoutDeviceIntRect::FromUnknownRect(
998 rect
.ToOutsidePixels(AppUnitsPerDevPixel()));
1001 void nsViewManager::IsPainting(bool& aIsPainting
) {
1002 aIsPainting
= IsPainting();
1005 void nsViewManager::ProcessPendingUpdates() {
1007 RefPtr
<nsViewManager
> rootViewManager
= RootViewManager();
1008 rootViewManager
->ProcessPendingUpdates();
1012 // Flush things like reflows by calling WillPaint on observer presShells.
1014 mPresShell
->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
1016 RefPtr
<nsViewManager
> strongThis(this);
1017 CallWillPaintOnObservers();
1019 ProcessPendingUpdatesForView(mRootView
, true);
1023 void nsViewManager::UpdateWidgetGeometry() {
1025 RefPtr
<nsViewManager
> rootViewManager
= RootViewManager();
1026 rootViewManager
->UpdateWidgetGeometry();
1030 if (mHasPendingWidgetGeometryChanges
) {
1031 mHasPendingWidgetGeometryChanges
= false;
1032 ProcessPendingUpdatesForView(mRootView
, false);
1036 void nsViewManager::CallWillPaintOnObservers() {
1037 MOZ_ASSERT(IsRootVM(), "Must be root VM for this to be called!");
1039 if (NS_WARN_IF(!gViewManagers
)) {
1044 for (index
= 0; index
< gViewManagers
->Length(); index
++) {
1045 nsViewManager
* vm
= gViewManagers
->ElementAt(index
);
1046 if (vm
->RootViewManager() == this) {
1048 if (vm
->mRootView
&& vm
->mRootView
->IsEffectivelyVisible()) {
1049 if (RefPtr
<PresShell
> presShell
= vm
->GetPresShell()) {
1050 presShell
->WillPaint();
1057 void nsViewManager::GetLastUserEventTime(uint32_t& aTime
) {
1058 aTime
= gLastUserEventTime
;
1061 void nsViewManager::InvalidateHierarchy() {
1064 NS_RELEASE(mRootViewManager
);
1066 nsView
* parent
= mRootView
->GetParent();
1068 mRootViewManager
= parent
->GetViewManager()->RootViewManager();
1069 NS_ADDREF(mRootViewManager
);
1070 NS_ASSERTION(mRootViewManager
!= this,
1071 "Root view had a parent, but it has the same view manager");
1073 mRootViewManager
= this;