Bug 1833753 [wpt PR 40065] - Allow newly-added test to also pass when mutation events...
[gecko.git] / view / nsViewManager.cpp
blob842237f4db198415a59752195a66aa0cdc77d367
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 // Weakly held references to all of the view managers
53 StaticAutoPtr<nsTArray<nsViewManager*>> nsViewManager::gViewManagers;
54 uint32_t nsViewManager::gLastUserEventTime = 0;
56 nsViewManager::nsViewManager()
57 : mPresShell(nullptr),
58 mDelayedResize(NSCOORD_NONE, NSCOORD_NONE),
59 mRootView(nullptr),
60 mRefreshDisableCount(0),
61 mPainting(false),
62 mRecursiveRefreshPending(false),
63 mHasPendingWidgetGeometryChanges(false) {
64 if (gViewManagers == nullptr) {
65 // Create an array to hold a list of view managers
66 gViewManagers = new nsTArray<nsViewManager*>;
69 gViewManagers->AppendElement(this);
72 nsViewManager::~nsViewManager() {
73 if (mRootView) {
74 // Destroy any remaining views
75 mRootView->Destroy();
76 mRootView = nullptr;
79 mRootViewManager = nullptr;
81 NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
83 #ifdef DEBUG
84 bool removed =
85 #endif
86 gViewManagers->RemoveElement(this);
87 NS_ASSERTION(
88 removed,
89 "Viewmanager instance was not in the global list of viewmanagers");
91 if (gViewManagers->IsEmpty()) {
92 // There aren't any more view managers so
93 // release the global array of view managers
94 gViewManagers = nullptr;
97 MOZ_RELEASE_ASSERT(!mPresShell,
98 "Releasing nsViewManager without having called Destroy on "
99 "the PresShell!");
102 // We don't hold a reference to the presentation context because it
103 // holds a reference to us.
104 nsresult nsViewManager::Init(nsDeviceContext* aContext) {
105 MOZ_ASSERT(nullptr != aContext, "null ptr");
107 if (nullptr == aContext) {
108 return NS_ERROR_NULL_POINTER;
110 if (nullptr != mContext) {
111 return NS_ERROR_ALREADY_INITIALIZED;
113 mContext = aContext;
115 return NS_OK;
118 nsView* nsViewManager::CreateView(const nsRect& aBounds, nsView* aParent,
119 ViewVisibility aVisibilityFlag) {
120 auto* v = new nsView(this, aVisibilityFlag);
121 v->SetParent(aParent);
122 v->SetPosition(aBounds.X(), aBounds.Y());
123 nsRect dim(0, 0, aBounds.Width(), aBounds.Height());
124 v->SetDimensions(dim, false);
125 return v;
128 void nsViewManager::SetRootView(nsView* aView) {
129 MOZ_ASSERT(!aView || aView->GetViewManager() == this,
130 "Unexpected viewmanager on root view");
132 // Do NOT destroy the current root view. It's the caller's responsibility
133 // to destroy it
134 mRootView = aView;
136 if (mRootView) {
137 nsView* parent = mRootView->GetParent();
138 if (parent) {
139 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
140 // no need to set mRootViewManager ourselves here.
141 parent->InsertChild(mRootView, nullptr);
142 } else {
143 InvalidateHierarchy();
146 mRootView->SetZIndex(false, 0);
148 // Else don't touch mRootViewManager
151 void nsViewManager::GetWindowDimensions(nscoord* aWidth, nscoord* aHeight) {
152 if (nullptr != mRootView) {
153 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
154 nsRect dim = mRootView->GetDimensions();
155 *aWidth = dim.Width();
156 *aHeight = dim.Height();
157 } else {
158 *aWidth = mDelayedResize.width;
159 *aHeight = mDelayedResize.height;
161 } else {
162 *aWidth = 0;
163 *aHeight = 0;
167 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight) {
168 nsRect oldDim = mRootView->GetDimensions();
169 nsRect newDim(0, 0, aWidth, aHeight);
170 // We care about resizes even when one dimension is already zero.
171 if (oldDim.IsEqualEdges(newDim)) {
172 return;
174 // Don't resize the widget. It is already being set elsewhere.
175 mRootView->SetDimensions(newDim, true, false);
176 if (RefPtr<PresShell> presShell = mPresShell) {
177 presShell->ResizeReflow(aWidth, aHeight);
181 bool nsViewManager::ShouldDelayResize() const {
182 MOZ_ASSERT(mRootView);
183 if (!mRootView->IsEffectivelyVisible() || !mPresShell ||
184 !mPresShell->IsVisible()) {
185 return true;
187 if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
188 if (rd->IsResizeSuppressed()) {
189 return true;
192 return false;
195 void nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight,
196 bool aDelayResize) {
197 if (mRootView) {
198 if (!ShouldDelayResize() && !aDelayResize) {
199 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
200 mDelayedResize != nsSize(aWidth, aHeight)) {
201 // We have a delayed resize; that now obsolete size may already have
202 // been flushed to the PresContext so we need to update the PresContext
203 // with the new size because if the new size is exactly the same as the
204 // root view's current size then DoSetWindowDimensions will not
205 // request a resize reflow (which would correct it). See bug 617076.
206 mDelayedResize = nsSize(aWidth, aHeight);
207 FlushDelayedResize();
209 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
210 DoSetWindowDimensions(aWidth, aHeight);
211 } else {
212 mDelayedResize.SizeTo(aWidth, aHeight);
213 if (mPresShell) {
214 mPresShell->SetNeedStyleFlush();
215 mPresShell->SetNeedLayoutFlush();
221 void nsViewManager::FlushDelayedResize() {
222 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
223 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height);
224 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
228 // Convert aIn from being relative to and in appunits of aFromView, to being
229 // relative to and in appunits of aToView.
230 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
231 nsView* aFromView, nsView* aToView) {
232 nsRegion out = aIn;
233 out.MoveBy(aFromView->GetOffsetTo(aToView));
234 out = out.ScaleToOtherAppUnitsRoundOut(
235 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
236 aToView->GetViewManager()->AppUnitsPerDevPixel());
237 return out;
240 nsView* nsViewManager::GetDisplayRootFor(nsView* aView) {
241 nsView* displayRoot = aView;
242 for (;;) {
243 nsView* displayParent = displayRoot->GetParent();
244 if (!displayParent) return displayRoot;
246 if (displayRoot->GetFloating() && !displayParent->GetFloating())
247 return displayRoot;
249 // If we have a combobox dropdown popup within a panel popup, both the view
250 // for the dropdown popup and its parent will be floating, so we need to
251 // distinguish this situation. We do this by looking for a widget. Any view
252 // with a widget is a display root.
253 nsIWidget* widget = displayRoot->GetWidget();
254 if (widget && widget->GetWindowType() == widget::WindowType::Popup) {
255 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
256 "this should only happen with floating views that have "
257 "floating parents");
258 return displayRoot;
261 displayRoot = displayParent;
266 aRegion is given in device coordinates!!
267 aContext may be null, in which case layers should be used for
268 rendering.
270 void nsViewManager::Refresh(nsView* aView,
271 const LayoutDeviceIntRegion& aRegion) {
272 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
274 if (mPresShell && mPresShell->IsNeverPainting()) {
275 return;
278 if (aRegion.IsEmpty()) {
279 return;
282 nsIWidget* widget = aView->GetWidget();
283 if (!widget) {
284 return;
287 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
288 if (IsPainting()) {
289 RootViewManager()->mRecursiveRefreshPending = true;
290 return;
294 nsAutoScriptBlocker scriptBlocker;
295 SetPainting(true);
297 NS_ASSERTION(GetDisplayRootFor(aView) == aView,
298 "Widgets that we paint must all be display roots");
300 if (RefPtr<PresShell> presShell = mPresShell) {
301 #ifdef MOZ_DUMP_PAINTING
302 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
303 printf_stderr("--COMPOSITE-- %p\n", presShell.get());
305 #endif
306 WindowRenderer* renderer = widget->GetWindowRenderer();
307 if (!renderer->NeedsWidgetInvalidation()) {
308 renderer->FlushRendering(wr::RenderReasons::WIDGET);
309 } else {
310 presShell->SyncPaintFallback(aView);
312 #ifdef MOZ_DUMP_PAINTING
313 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
314 printf_stderr("--ENDCOMPOSITE--\n");
316 #endif
317 mozilla::StartupTimeline::RecordOnce(
318 mozilla::StartupTimeline::FIRST_PAINT);
321 SetPainting(false);
324 if (RootViewManager()->mRecursiveRefreshPending) {
325 RootViewManager()->mRecursiveRefreshPending = false;
326 InvalidateAllViews();
330 void nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
331 bool aFlushDirtyRegion) {
332 NS_ASSERTION(IsRootVM(), "Updates will be missed");
333 if (!aView) {
334 return;
337 RefPtr<PresShell> rootPresShell = mPresShell;
338 AutoTArray<nsCOMPtr<nsIWidget>, 1> widgets;
339 aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
340 for (uint32_t i = 0; i < widgets.Length(); ++i) {
341 nsView* view = nsView::GetViewFor(widgets[i]);
342 if (view) {
343 if (view->mNeedsWindowPropertiesSync) {
344 view->mNeedsWindowPropertiesSync = false;
345 if (nsViewManager* vm = view->GetViewManager()) {
346 if (PresShell* presShell = vm->GetPresShell()) {
347 presShell->SyncWindowProperties(/* aSync */ true);
352 view = nsView::GetViewFor(widgets[i]);
353 if (view) {
354 view->ResetWidgetBounds(false, true);
357 if (rootPresShell->GetViewManager() != this) {
358 return; // presentation might have been torn down
360 if (aFlushDirtyRegion) {
361 nsAutoScriptBlocker scriptBlocker;
362 SetPainting(true);
363 for (uint32_t i = 0; i < widgets.Length(); ++i) {
364 nsIWidget* widget = widgets[i];
365 nsView* view = nsView::GetViewFor(widget);
366 if (view) {
367 RefPtr<nsViewManager> viewManager = view->GetViewManager();
368 viewManager->ProcessPendingUpdatesPaint(MOZ_KnownLive(widget));
371 SetPainting(false);
375 void nsViewManager::ProcessPendingUpdatesRecurse(
376 nsView* aView, AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets) {
377 if (mPresShell && mPresShell->IsNeverPainting()) {
378 return;
381 for (nsView* childView = aView->GetFirstChild(); childView;
382 childView = childView->GetNextSibling()) {
383 childView->GetViewManager()->ProcessPendingUpdatesRecurse(childView,
384 aWidgets);
387 nsIWidget* widget = aView->GetWidget();
388 if (widget) {
389 aWidgets.AppendElement(widget);
390 } else {
391 FlushDirtyRegionToWidget(aView);
395 void nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget) {
396 if (aWidget->NeedsPaint()) {
397 // If an ancestor widget was hidden and then shown, we could
398 // have a delayed resize to handle.
399 for (RefPtr<nsViewManager> vm = this; vm;
400 vm = vm->mRootView->GetParent()
401 ? vm->mRootView->GetParent()->GetViewManager()
402 : nullptr) {
403 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
404 vm->mRootView->IsEffectivelyVisible() && vm->mPresShell &&
405 vm->mPresShell->IsVisible()) {
406 vm->FlushDelayedResize();
409 nsView* view = nsView::GetViewFor(aWidget);
411 if (!view) {
412 NS_ERROR("FlushDelayedResize destroyed the nsView?");
413 return;
416 nsIWidgetListener* previousListener =
417 aWidget->GetPreviouslyAttachedWidgetListener();
419 if (previousListener && previousListener != view &&
420 view->IsPrimaryFramePaintSuppressed()) {
421 return;
424 if (RefPtr<PresShell> presShell = mPresShell) {
425 #ifdef MOZ_DUMP_PAINTING
426 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
427 printf_stderr(
428 "---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
429 presShell.get(), view, aWidget);
431 #endif
433 presShell->PaintAndRequestComposite(view, PaintFlags::None);
434 view->SetForcedRepaint(false);
436 #ifdef MOZ_DUMP_PAINTING
437 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
438 printf_stderr("---- PAINT END ----\n");
440 #endif
443 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
446 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView) {
447 NS_ASSERTION(aView->GetViewManager() == this,
448 "FlushDirtyRegionToWidget called on view we don't own");
450 if (!aView->HasNonEmptyDirtyRegion()) {
451 return;
454 nsRegion& dirtyRegion = aView->GetDirtyRegion();
455 nsView* nearestViewWithWidget = aView;
456 while (!nearestViewWithWidget->HasWidget() &&
457 nearestViewWithWidget->GetParent()) {
458 nearestViewWithWidget = nearestViewWithWidget->GetParent();
460 nsRegion r =
461 ConvertRegionBetweenViews(dirtyRegion, aView, nearestViewWithWidget);
463 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
464 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
465 dirtyRegion.SetEmpty();
468 void nsViewManager::InvalidateView(nsView* aView) {
469 // Mark the entire view as damaged
470 InvalidateView(aView, aView->GetDimensions());
473 static void AddDirtyRegion(nsView* aView, const nsRegion& aDamagedRegion) {
474 nsRegion& dirtyRegion = aView->GetDirtyRegion();
475 dirtyRegion.Or(dirtyRegion, aDamagedRegion);
476 dirtyRegion.SimplifyOutward(8);
479 void nsViewManager::PostPendingUpdate() {
480 nsViewManager* rootVM = RootViewManager();
481 rootVM->mHasPendingWidgetGeometryChanges = true;
482 if (rootVM->mPresShell) {
483 rootVM->mPresShell->ScheduleViewManagerFlush();
488 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
489 * every widget child of aWidgetView, plus aWidgetView's own widget
491 void nsViewManager::InvalidateWidgetArea(nsView* aWidgetView,
492 const nsRegion& aDamagedRegion) {
493 NS_ASSERTION(aWidgetView->GetViewManager() == this,
494 "InvalidateWidgetArea called on view we don't own");
495 nsIWidget* widget = aWidgetView->GetWidget();
497 #if 0
498 nsRect dbgBounds = aDamagedRegion.GetBounds();
499 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
500 aWidgetView, aWidgetView->IsAttachedToTopLevel(),
501 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
502 #endif
504 // If the widget is hidden, it don't cover nothing
505 if (widget && !widget->IsVisible()) {
506 return;
509 if (!widget) {
510 // The root view or a scrolling view might not have a widget
511 // (for example, during printing). We get here when we scroll
512 // during printing to show selected options in a listbox, for example.
513 return;
516 if (!aDamagedRegion.IsEmpty()) {
517 for (auto iter = aDamagedRegion.RectIter(); !iter.Done(); iter.Next()) {
518 LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get());
519 widget->Invalidate(bounds);
524 static bool ShouldIgnoreInvalidation(nsViewManager* aVM) {
525 while (aVM) {
526 PresShell* presShell = aVM->GetPresShell();
527 if (!presShell || presShell->ShouldIgnoreInvalidation()) {
528 return true;
530 nsView* view = aVM->GetRootView()->GetParent();
531 aVM = view ? view->GetViewManager() : nullptr;
533 return false;
536 void nsViewManager::InvalidateView(nsView* aView, const nsRect& aRect) {
537 // If painting is suppressed in the presshell or an ancestor drop all
538 // invalidates, it will invalidate everything when it unsuppresses.
539 if (ShouldIgnoreInvalidation(this)) {
540 return;
543 InvalidateViewNoSuppression(aView, aRect);
546 void nsViewManager::InvalidateViewNoSuppression(nsView* aView,
547 const nsRect& aRect) {
548 MOZ_ASSERT(nullptr != aView, "null view");
550 NS_ASSERTION(aView->GetViewManager() == this,
551 "InvalidateViewNoSuppression called on view we don't own");
553 nsRect damagedRect(aRect);
554 if (damagedRect.IsEmpty()) {
555 return;
558 nsView* displayRoot = GetDisplayRootFor(aView);
559 nsViewManager* displayRootVM = displayRoot->GetViewManager();
560 // Propagate the update to the displayRoot, since iframes, for example,
561 // can overlap each other and be translucent. So we have to possibly
562 // invalidate our rect in each of the widgets we have lying about.
563 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
564 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
565 int32_t APD = AppUnitsPerDevPixel();
566 damagedRect = damagedRect.ScaleToOtherAppUnitsRoundOut(APD, rootAPD);
568 // accumulate this rectangle in the view's dirty region, so we can
569 // process it later.
570 AddDirtyRegion(displayRoot, nsRegion(damagedRect));
573 void nsViewManager::InvalidateAllViews() {
574 if (RootViewManager() != this) {
575 return RootViewManager()->InvalidateAllViews();
578 InvalidateViews(mRootView);
581 void nsViewManager::InvalidateViews(nsView* aView) {
582 // Invalidate this view.
583 InvalidateView(aView);
585 // Invalidate all children as well.
586 nsView* childView = aView->GetFirstChild();
587 while (nullptr != childView) {
588 childView->GetViewManager()->InvalidateViews(childView);
589 childView = childView->GetNextSibling();
593 void nsViewManager::WillPaintWindow(nsIWidget* aWidget) {
594 if (aWidget) {
595 nsView* view = nsView::GetViewFor(aWidget);
596 WindowRenderer* renderer = aWidget->GetWindowRenderer();
597 if (view &&
598 (view->ForcedRepaint() || !renderer->NeedsWidgetInvalidation())) {
599 ProcessPendingUpdates();
600 // Re-get the view pointer here since the ProcessPendingUpdates might have
601 // destroyed it during CallWillPaintOnObservers.
602 view = nsView::GetViewFor(aWidget);
603 if (view) {
604 view->SetForcedRepaint(false);
610 bool nsViewManager::PaintWindow(nsIWidget* aWidget,
611 const LayoutDeviceIntRegion& aRegion) {
612 if (!aWidget || !mContext) return false;
614 NS_ASSERTION(
615 IsPaintingAllowed(),
616 "shouldn't be receiving paint events while painting is disallowed!");
618 // Get the view pointer here since NS_WILL_PAINT might have
619 // destroyed it during CallWillPaintOnObservers (bug 378273).
620 nsView* view = nsView::GetViewFor(aWidget);
621 if (view && !aRegion.IsEmpty()) {
622 Refresh(view, aRegion);
625 return true;
628 void nsViewManager::DidPaintWindow() {
629 if (RefPtr<PresShell> presShell = mPresShell) {
630 presShell->DidPaintWindow();
634 void nsViewManager::DispatchEvent(WidgetGUIEvent* aEvent, nsView* aView,
635 nsEventStatus* aStatus) {
636 AUTO_PROFILER_LABEL("nsViewManager::DispatchEvent", OTHER);
638 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
639 if ((mouseEvent &&
640 // Ignore mouse events that we synthesize.
641 mouseEvent->mReason == WidgetMouseEvent::eReal &&
642 // Ignore mouse exit and enter (we'll get moves if the user
643 // is really moving the mouse) since we get them when we
644 // create and destroy widgets.
645 mouseEvent->mMessage != eMouseExitFromWidget &&
646 mouseEvent->mMessage != eMouseEnterIntoWidget) ||
647 aEvent->HasKeyEventMessage() || aEvent->HasIMEEventMessage()) {
648 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
651 // Find the view whose coordinates system we're in.
652 nsView* view = aView;
653 bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
654 if (dispatchUsingCoordinates) {
655 // Will dispatch using coordinates. Pretty bogus but it's consistent
656 // with what presshell does.
657 view = GetDisplayRootFor(view);
660 // If the view has no frame, look for a view that does.
661 nsIFrame* frame = view->GetFrame();
662 if (!frame && (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
663 aEvent->IsIMERelatedEvent())) {
664 while (view && !view->GetFrame()) {
665 view = view->GetParent();
668 if (view) {
669 frame = view->GetFrame();
673 if (nullptr != frame) {
674 // Hold a refcount to the presshell. The continued existence of the
675 // presshell will delay deletion of this view hierarchy should the event
676 // want to cause its destruction in, say, some JavaScript event handler.
677 if (RefPtr<PresShell> presShell = view->GetViewManager()->GetPresShell()) {
678 presShell->HandleEvent(frame, aEvent, false, aStatus);
679 return;
683 *aStatus = nsEventStatus_eIgnore;
686 // Recursively reparent widgets if necessary
688 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget* aNewWidget) {
689 MOZ_ASSERT(aNewWidget, "null widget");
691 if (aView->HasWidget()) {
692 // Check to see if the parent widget is the
693 // same as the new parent. If not then reparent
694 // the widget, otherwise there is nothing more
695 // to do for the view and its descendants
696 nsIWidget* widget = aView->GetWidget();
697 nsIWidget* parentWidget = widget->GetParent();
698 if (parentWidget) {
699 // Child widget
700 if (parentWidget != aNewWidget) {
701 widget->SetParent(aNewWidget);
703 } else {
704 // Toplevel widget (popup, dialog, etc)
705 widget->ReparentNativeWidget(aNewWidget);
707 return;
710 // Need to check each of the views children to see
711 // if they have a widget and reparent it.
713 for (nsView* kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
714 ReparentChildWidgets(kid, aNewWidget);
718 // Reparent a view and its descendant views widgets if necessary
720 void nsViewManager::ReparentWidgets(nsView* aView, nsView* aParent) {
721 MOZ_ASSERT(aParent, "Must have a parent");
722 MOZ_ASSERT(aView, "Must have a view");
724 // Quickly determine whether the view has pre-existing children or a
725 // widget. In most cases the view will not have any pre-existing
726 // children when this is called. Only in the case
727 // where a view has been reparented by removing it from
728 // a reinserting it into a new location in the view hierarchy do we
729 // have to consider reparenting the existing widgets for the view and
730 // it's descendants.
731 if (aView->HasWidget() || aView->GetFirstChild()) {
732 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
733 if (parentWidget) {
734 ReparentChildWidgets(aView, parentWidget);
735 return;
737 NS_WARNING("Can not find a widget for the parent view");
741 void nsViewManager::InsertChild(nsView* aParent, nsView* aChild,
742 nsView* aSibling, bool aAfter) {
743 MOZ_ASSERT(nullptr != aParent, "null ptr");
744 MOZ_ASSERT(nullptr != aChild, "null ptr");
745 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
746 "tried to insert view with invalid sibling");
747 NS_ASSERTION(!IsViewInserted(aChild),
748 "tried to insert an already-inserted view");
750 if ((nullptr != aParent) && (nullptr != aChild)) {
751 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid'
752 // in document order, otherwise after 'kid' (i.e. before 'kid' in document
753 // order).
755 if (nullptr == aSibling) {
756 if (aAfter) {
757 // insert at end of document order, i.e., before first view
758 // this is the common case, by far
759 aParent->InsertChild(aChild, nullptr);
760 ReparentWidgets(aChild, aParent);
761 } else {
762 // insert at beginning of document order, i.e., after last view
763 nsView* kid = aParent->GetFirstChild();
764 nsView* prev = nullptr;
765 while (kid) {
766 prev = kid;
767 kid = kid->GetNextSibling();
769 // prev is last view or null if there are no children
770 aParent->InsertChild(aChild, prev);
771 ReparentWidgets(aChild, aParent);
773 } else {
774 nsView* kid = aParent->GetFirstChild();
775 nsView* prev = nullptr;
776 while (kid && aSibling != kid) {
777 // get the next sibling view
778 prev = kid;
779 kid = kid->GetNextSibling();
781 NS_ASSERTION(kid != nullptr, "couldn't find sibling in child list");
782 if (aAfter) {
783 // insert after 'kid' in document order, i.e. before in view order
784 aParent->InsertChild(aChild, prev);
785 ReparentWidgets(aChild, aParent);
786 } else {
787 // insert before 'kid' in document order, i.e. after in view order
788 aParent->InsertChild(aChild, kid);
789 ReparentWidgets(aChild, aParent);
793 // if the parent view is marked as "floating", make the newly added view
794 // float as well.
795 if (aParent->GetFloating()) aChild->SetFloating(true);
799 void nsViewManager::RemoveChild(nsView* aChild) {
800 NS_ASSERTION(aChild, "aChild must not be null");
802 nsView* parent = aChild->GetParent();
804 if (nullptr != parent) {
805 NS_ASSERTION(
806 aChild->GetViewManager() == this || parent->GetViewManager() == this,
807 "wrong view manager");
808 parent->RemoveChild(aChild);
812 void nsViewManager::MoveViewTo(nsView* aView, nscoord aX, nscoord aY) {
813 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
814 aView->SetPosition(aX, aY);
817 void nsViewManager::ResizeView(nsView* aView, const nsRect& aRect,
818 bool aRepaintExposedAreaOnly) {
819 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
821 nsRect oldDimensions = aView->GetDimensions();
822 if (!oldDimensions.IsEqualEdges(aRect)) {
823 aView->SetDimensions(aRect, true);
826 // Note that if layout resizes the view and the view has a custom clip
827 // region set, then we expect layout to update the clip region too. Thus
828 // in the case where mClipRect has been optimized away to just be a null
829 // pointer, and this resize is implicitly changing the clip rect, it's OK
830 // because layout will change it back again if necessary.
833 void nsViewManager::SetViewFloating(nsView* aView, bool aFloating) {
834 NS_ASSERTION(!(nullptr == aView), "no view");
836 aView->SetFloating(aFloating);
839 void nsViewManager::SetViewVisibility(nsView* aView, ViewVisibility aVisible) {
840 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
842 if (aVisible != aView->GetVisibility()) {
843 aView->SetVisibility(aVisible);
847 bool nsViewManager::IsViewInserted(nsView* aView) {
848 if (mRootView == aView) {
849 return true;
851 if (aView->GetParent() == nullptr) {
852 return false;
854 nsView* view = aView->GetParent()->GetFirstChild();
855 while (view != nullptr) {
856 if (view == aView) {
857 return true;
859 view = view->GetNextSibling();
861 return false;
864 void nsViewManager::SetViewZIndex(nsView* aView, bool aAutoZIndex,
865 int32_t aZIndex) {
866 NS_ASSERTION((aView != nullptr), "no view");
868 // don't allow the root view's z-index to be changed. It should always be
869 // zero. This could be removed and replaced with a style rule, or just removed
870 // altogether, with interesting consequences
871 if (aView == mRootView) {
872 return;
875 if (aAutoZIndex) {
876 aZIndex = 0;
879 aView->SetZIndex(aAutoZIndex, aZIndex);
882 nsViewManager* nsViewManager::IncrementDisableRefreshCount() {
883 if (!IsRootVM()) {
884 return RootViewManager()->IncrementDisableRefreshCount();
887 ++mRefreshDisableCount;
889 return this;
892 void nsViewManager::DecrementDisableRefreshCount() {
893 NS_ASSERTION(IsRootVM(), "Should only be called on root");
894 --mRefreshDisableCount;
895 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
898 nsIWidget* nsViewManager::GetRootWidget() const {
899 if (!mRootView) {
900 return nullptr;
902 if (mRootView->HasWidget()) {
903 return mRootView->GetWidget();
905 if (mRootView->GetParent()) {
906 return mRootView->GetParent()->GetViewManager()->GetRootWidget();
908 return nullptr;
911 LayoutDeviceIntRect nsViewManager::ViewToWidget(nsView* aView,
912 const nsRect& aRect) const {
913 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
915 // account for the view's origin not lining up with the widget's
916 nsRect rect = aRect + aView->ViewToWidgetOffset();
918 // finally, convert to device coordinates.
919 return LayoutDeviceIntRect::FromUnknownRect(
920 rect.ToOutsidePixels(AppUnitsPerDevPixel()));
923 void nsViewManager::IsPainting(bool& aIsPainting) {
924 aIsPainting = IsPainting();
927 void nsViewManager::ProcessPendingUpdates() {
928 if (!IsRootVM()) {
929 RefPtr<nsViewManager> rootViewManager = RootViewManager();
930 rootViewManager->ProcessPendingUpdates();
931 return;
934 // Flush things like reflows by calling WillPaint on observer presShells.
935 if (mPresShell) {
936 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
938 RefPtr<nsViewManager> strongThis(this);
939 CallWillPaintOnObservers();
941 ProcessPendingUpdatesForView(mRootView, true);
942 if (mPresShell) {
943 if (nsPresContext* pc = mPresShell->GetPresContext()) {
944 pc->RefreshDriver()->ClearHasScheduleFlush();
950 void nsViewManager::UpdateWidgetGeometry() {
951 if (!IsRootVM()) {
952 RefPtr<nsViewManager> rootViewManager = RootViewManager();
953 rootViewManager->UpdateWidgetGeometry();
954 return;
957 if (mHasPendingWidgetGeometryChanges) {
958 mHasPendingWidgetGeometryChanges = false;
959 ProcessPendingUpdatesForView(mRootView, false);
963 void nsViewManager::CallWillPaintOnObservers() {
964 MOZ_ASSERT(IsRootVM(), "Must be root VM for this to be called!");
966 if (NS_WARN_IF(!gViewManagers)) {
967 return;
970 uint32_t index;
971 for (index = 0; index < gViewManagers->Length(); index++) {
972 nsViewManager* vm = gViewManagers->ElementAt(index);
973 if (vm->RootViewManager() == this) {
974 // One of our kids.
975 if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
976 if (RefPtr<PresShell> presShell = vm->GetPresShell()) {
977 presShell->WillPaint();
984 void nsViewManager::GetLastUserEventTime(uint32_t& aTime) {
985 aTime = gLastUserEventTime;
988 void nsViewManager::InvalidateHierarchy() {
989 if (mRootView) {
990 mRootViewManager = nullptr;
991 nsView* parent = mRootView->GetParent();
992 if (parent) {
993 mRootViewManager = parent->GetViewManager()->RootViewManager();
994 NS_ASSERTION(mRootViewManager != this,
995 "Root view had a parent, but it has the same view manager");
997 // else, we are implicitly our own root view manager.