Bug 1752445 [wpt PR 32578] - App history: implement transition.finished promise,...
[gecko.git] / view / nsViewManager.cpp
blob6e039e7e9494bc0124dc04431a024cb1a5e0b80a
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 "Layers.h"
26 #include "gfxPlatform.h"
27 #include "WindowRenderer.h"
29 /**
30 XXX TODO XXX
32 DeCOMify newly private methods
33 Optimize view storage
36 /**
37 A note about platform assumptions:
39 We assume that a widget is z-ordered on top of its parent.
41 We do NOT assume anything about the relative z-ordering of sibling widgets.
42 Even though we ask for a specific z-order, we don't assume that widget
43 z-ordering actually works.
46 using namespace mozilla;
47 using namespace mozilla::layers;
49 #define NSCOORD_NONE INT32_MIN
51 #undef DEBUG_MOUSE_LOCATION
53 // Weakly held references to all of the view managers
54 StaticAutoPtr<nsTArray<nsViewManager*>> nsViewManager::gViewManagers;
55 uint32_t nsViewManager::gLastUserEventTime = 0;
57 nsViewManager::nsViewManager()
58 : mPresShell(nullptr),
59 mDelayedResize(NSCOORD_NONE, NSCOORD_NONE),
60 mRootView(nullptr),
61 mRefreshDisableCount(0),
62 mPainting(false),
63 mRecursiveRefreshPending(false),
64 mHasPendingWidgetGeometryChanges(false) {
65 if (gViewManagers == nullptr) {
66 // Create an array to hold a list of view managers
67 gViewManagers = new nsTArray<nsViewManager*>;
70 gViewManagers->AppendElement(this);
73 nsViewManager::~nsViewManager() {
74 if (mRootView) {
75 // Destroy any remaining views
76 mRootView->Destroy();
77 mRootView = nullptr;
80 mRootViewManager = nullptr;
82 NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
84 #ifdef DEBUG
85 bool removed =
86 #endif
87 gViewManagers->RemoveElement(this);
88 NS_ASSERTION(
89 removed,
90 "Viewmanager instance was not in the global list of viewmanagers");
92 if (gViewManagers->IsEmpty()) {
93 // There aren't any more view managers so
94 // release the global array of view managers
95 gViewManagers = nullptr;
98 MOZ_RELEASE_ASSERT(!mPresShell,
99 "Releasing nsViewManager without having called Destroy on "
100 "the PresShell!");
103 // We don't hold a reference to the presentation context because it
104 // holds a reference to us.
105 nsresult nsViewManager::Init(nsDeviceContext* aContext) {
106 MOZ_ASSERT(nullptr != aContext, "null ptr");
108 if (nullptr == aContext) {
109 return NS_ERROR_NULL_POINTER;
111 if (nullptr != mContext) {
112 return NS_ERROR_ALREADY_INITIALIZED;
114 mContext = aContext;
116 return NS_OK;
119 nsView* nsViewManager::CreateView(const nsRect& aBounds, nsView* aParent,
120 nsViewVisibility aVisibilityFlag) {
121 auto* v = new nsView(this, aVisibilityFlag);
122 v->SetParent(aParent);
123 v->SetPosition(aBounds.X(), aBounds.Y());
124 nsRect dim(0, 0, aBounds.Width(), aBounds.Height());
125 v->SetDimensions(dim, false);
126 return v;
129 void nsViewManager::SetRootView(nsView* aView) {
130 MOZ_ASSERT(!aView || aView->GetViewManager() == this,
131 "Unexpected viewmanager on root view");
133 // Do NOT destroy the current root view. It's the caller's responsibility
134 // to destroy it
135 mRootView = aView;
137 if (mRootView) {
138 nsView* parent = mRootView->GetParent();
139 if (parent) {
140 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
141 // no need to set mRootViewManager ourselves here.
142 parent->InsertChild(mRootView, nullptr);
143 } else {
144 InvalidateHierarchy();
147 mRootView->SetZIndex(false, 0);
149 // Else don't touch mRootViewManager
152 void nsViewManager::GetWindowDimensions(nscoord* aWidth, nscoord* aHeight) {
153 if (nullptr != mRootView) {
154 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
155 nsRect dim = mRootView->GetDimensions();
156 *aWidth = dim.Width();
157 *aHeight = dim.Height();
158 } else {
159 *aWidth = mDelayedResize.width;
160 *aHeight = mDelayedResize.height;
162 } else {
163 *aWidth = 0;
164 *aHeight = 0;
168 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight,
169 bool aDoReflow) {
170 nsRect oldDim = mRootView->GetDimensions();
171 nsRect newDim(0, 0, aWidth, aHeight);
172 // We care about resizes even when one dimension is already zero.
173 if (oldDim.IsEqualEdges(newDim)) {
174 return;
176 // Don't resize the widget. It is already being set elsewhere.
177 mRootView->SetDimensions(newDim, true, false);
178 if (RefPtr<PresShell> presShell = mPresShell) {
179 auto options = ResizeReflowOptions::NoOption;
180 if (!aDoReflow) {
181 options |= ResizeReflowOptions::SuppressReflow;
183 presShell->ResizeReflow(aWidth, aHeight, options);
187 bool nsViewManager::ShouldDelayResize() const {
188 MOZ_ASSERT(mRootView);
189 if (!mRootView->IsEffectivelyVisible() || !mPresShell ||
190 !mPresShell->IsVisible()) {
191 return true;
193 if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
194 if (rd->IsResizeSuppressed()) {
195 return true;
198 return false;
201 void nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight,
202 bool aDelayResize) {
203 if (mRootView) {
204 if (!ShouldDelayResize() && !aDelayResize) {
205 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
206 mDelayedResize != nsSize(aWidth, aHeight)) {
207 // We have a delayed resize; that now obsolete size may already have
208 // been flushed to the PresContext so we need to update the PresContext
209 // with the new size because if the new size is exactly the same as the
210 // root view's current size then DoSetWindowDimensions will not
211 // request a resize reflow (which would correct it). See bug 617076.
212 mDelayedResize = nsSize(aWidth, aHeight);
213 FlushDelayedResize(false);
215 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
216 DoSetWindowDimensions(aWidth, aHeight, /* aDoReflow = */ true);
217 } else {
218 mDelayedResize.SizeTo(aWidth, aHeight);
219 if (mPresShell) {
220 mPresShell->SetNeedStyleFlush();
221 mPresShell->SetNeedLayoutFlush();
227 void nsViewManager::FlushDelayedResize(bool aDoReflow) {
228 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
229 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height,
230 aDoReflow);
231 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
235 // Convert aIn from being relative to and in appunits of aFromView, to being
236 // relative to and in appunits of aToView.
237 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
238 nsView* aFromView, nsView* aToView) {
239 nsRegion out = aIn;
240 out.MoveBy(aFromView->GetOffsetTo(aToView));
241 out = out.ScaleToOtherAppUnitsRoundOut(
242 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
243 aToView->GetViewManager()->AppUnitsPerDevPixel());
244 return out;
247 nsView* nsViewManager::GetDisplayRootFor(nsView* aView) {
248 nsView* displayRoot = aView;
249 for (;;) {
250 nsView* displayParent = displayRoot->GetParent();
251 if (!displayParent) return displayRoot;
253 if (displayRoot->GetFloating() && !displayParent->GetFloating())
254 return displayRoot;
256 // If we have a combobox dropdown popup within a panel popup, both the view
257 // for the dropdown popup and its parent will be floating, so we need to
258 // distinguish this situation. We do this by looking for a widget. Any view
259 // with a widget is a display root.
260 nsIWidget* widget = displayRoot->GetWidget();
261 if (widget && widget->WindowType() == eWindowType_popup) {
262 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
263 "this should only happen with floating views that have "
264 "floating parents");
265 return displayRoot;
268 displayRoot = displayParent;
273 aRegion is given in device coordinates!!
274 aContext may be null, in which case layers should be used for
275 rendering.
277 void nsViewManager::Refresh(nsView* aView,
278 const LayoutDeviceIntRegion& aRegion) {
279 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
281 if (mPresShell && mPresShell->IsNeverPainting()) {
282 return;
285 if (aRegion.IsEmpty()) {
286 return;
289 nsIWidget* widget = aView->GetWidget();
290 if (!widget) {
291 return;
294 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
295 if (IsPainting()) {
296 RootViewManager()->mRecursiveRefreshPending = true;
297 return;
301 nsAutoScriptBlocker scriptBlocker;
302 SetPainting(true);
304 NS_ASSERTION(GetDisplayRootFor(aView) == aView,
305 "Widgets that we paint must all be display roots");
307 if (RefPtr<PresShell> presShell = mPresShell) {
308 #ifdef MOZ_DUMP_PAINTING
309 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
310 printf_stderr("--COMPOSITE-- %p\n", presShell.get());
312 #endif
313 WindowRenderer* renderer = widget->GetWindowRenderer();
314 if (!renderer->NeedsWidgetInvalidation()) {
315 renderer->FlushRendering(wr::RenderReasons::WIDGET);
316 } else {
317 presShell->SyncPaintFallback(aView);
319 #ifdef MOZ_DUMP_PAINTING
320 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
321 printf_stderr("--ENDCOMPOSITE--\n");
323 #endif
324 mozilla::StartupTimeline::RecordOnce(
325 mozilla::StartupTimeline::FIRST_PAINT);
328 SetPainting(false);
331 if (RootViewManager()->mRecursiveRefreshPending) {
332 RootViewManager()->mRecursiveRefreshPending = false;
333 InvalidateAllViews();
337 void nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
338 bool aFlushDirtyRegion) {
339 NS_ASSERTION(IsRootVM(), "Updates will be missed");
340 if (!aView) {
341 return;
344 RefPtr<PresShell> rootPresShell = mPresShell;
345 AutoTArray<nsCOMPtr<nsIWidget>, 1> widgets;
346 aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
347 for (uint32_t i = 0; i < widgets.Length(); ++i) {
348 nsView* view = nsView::GetViewFor(widgets[i]);
349 if (view) {
350 if (view->mNeedsWindowPropertiesSync) {
351 view->mNeedsWindowPropertiesSync = false;
352 if (nsViewManager* vm = view->GetViewManager()) {
353 if (PresShell* presShell = vm->GetPresShell()) {
354 presShell->SyncWindowProperties(view);
359 view = nsView::GetViewFor(widgets[i]);
360 if (view) {
361 view->ResetWidgetBounds(false, true);
364 if (rootPresShell->GetViewManager() != this) {
365 return; // presentation might have been torn down
367 if (aFlushDirtyRegion) {
368 nsAutoScriptBlocker scriptBlocker;
369 SetPainting(true);
370 for (uint32_t i = 0; i < widgets.Length(); ++i) {
371 nsIWidget* widget = widgets[i];
372 nsView* view = nsView::GetViewFor(widget);
373 if (view) {
374 RefPtr<nsViewManager> viewManager = view->GetViewManager();
375 viewManager->ProcessPendingUpdatesPaint(MOZ_KnownLive(widget));
378 SetPainting(false);
382 void nsViewManager::ProcessPendingUpdatesRecurse(
383 nsView* aView, AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets) {
384 if (mPresShell && mPresShell->IsNeverPainting()) {
385 return;
388 for (nsView* childView = aView->GetFirstChild(); childView;
389 childView = childView->GetNextSibling()) {
390 childView->GetViewManager()->ProcessPendingUpdatesRecurse(childView,
391 aWidgets);
394 nsIWidget* widget = aView->GetWidget();
395 if (widget) {
396 aWidgets.AppendElement(widget);
397 } else {
398 FlushDirtyRegionToWidget(aView);
402 void nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget) {
403 if (aWidget->NeedsPaint()) {
404 // If an ancestor widget was hidden and then shown, we could
405 // have a delayed resize to handle.
406 for (RefPtr<nsViewManager> vm = this; vm;
407 vm = vm->mRootView->GetParent()
408 ? vm->mRootView->GetParent()->GetViewManager()
409 : nullptr) {
410 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
411 vm->mRootView->IsEffectivelyVisible() && vm->mPresShell &&
412 vm->mPresShell->IsVisible()) {
413 vm->FlushDelayedResize(true);
416 nsView* view = nsView::GetViewFor(aWidget);
418 if (!view) {
419 NS_ERROR("FlushDelayedResize destroyed the nsView?");
420 return;
423 nsIWidgetListener* previousListener =
424 aWidget->GetPreviouslyAttachedWidgetListener();
426 if (previousListener && previousListener != view &&
427 view->IsPrimaryFramePaintSuppressed()) {
428 return;
431 if (RefPtr<PresShell> presShell = mPresShell) {
432 #ifdef MOZ_DUMP_PAINTING
433 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
434 printf_stderr(
435 "---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
436 presShell.get(), view, aWidget);
438 #endif
440 presShell->PaintAndRequestComposite(view, PaintFlags::None);
441 view->SetForcedRepaint(false);
443 #ifdef MOZ_DUMP_PAINTING
444 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
445 printf_stderr("---- PAINT END ----\n");
447 #endif
450 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
453 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView) {
454 NS_ASSERTION(aView->GetViewManager() == this,
455 "FlushDirtyRegionToWidget called on view we don't own");
457 if (!aView->HasNonEmptyDirtyRegion()) {
458 return;
461 nsRegion& dirtyRegion = aView->GetDirtyRegion();
462 nsView* nearestViewWithWidget = aView;
463 while (!nearestViewWithWidget->HasWidget() &&
464 nearestViewWithWidget->GetParent()) {
465 nearestViewWithWidget = nearestViewWithWidget->GetParent();
467 nsRegion r =
468 ConvertRegionBetweenViews(dirtyRegion, aView, nearestViewWithWidget);
470 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
471 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
472 dirtyRegion.SetEmpty();
475 void nsViewManager::InvalidateView(nsView* aView) {
476 // Mark the entire view as damaged
477 InvalidateView(aView, aView->GetDimensions());
480 static void AddDirtyRegion(nsView* aView, const nsRegion& aDamagedRegion) {
481 nsRegion& dirtyRegion = aView->GetDirtyRegion();
482 dirtyRegion.Or(dirtyRegion, aDamagedRegion);
483 dirtyRegion.SimplifyOutward(8);
486 void nsViewManager::PostPendingUpdate() {
487 nsViewManager* rootVM = RootViewManager();
488 rootVM->mHasPendingWidgetGeometryChanges = true;
489 if (rootVM->mPresShell) {
490 rootVM->mPresShell->ScheduleViewManagerFlush();
495 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
496 * every widget child of aWidgetView, plus aWidgetView's own widget
498 void nsViewManager::InvalidateWidgetArea(nsView* aWidgetView,
499 const nsRegion& aDamagedRegion) {
500 NS_ASSERTION(aWidgetView->GetViewManager() == this,
501 "InvalidateWidgetArea called on view we don't own");
502 nsIWidget* widget = aWidgetView->GetWidget();
504 #if 0
505 nsRect dbgBounds = aDamagedRegion.GetBounds();
506 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
507 aWidgetView, aWidgetView->IsAttachedToTopLevel(),
508 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
509 #endif
511 // If the widget is hidden, it don't cover nothing
512 if (widget && !widget->IsVisible()) {
513 return;
516 if (!widget) {
517 // The root view or a scrolling view might not have a widget
518 // (for example, during printing). We get here when we scroll
519 // during printing to show selected options in a listbox, for example.
520 return;
523 if (!aDamagedRegion.IsEmpty()) {
524 for (auto iter = aDamagedRegion.RectIter(); !iter.Done(); iter.Next()) {
525 LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get());
526 widget->Invalidate(bounds);
531 static bool ShouldIgnoreInvalidation(nsViewManager* aVM) {
532 while (aVM) {
533 PresShell* presShell = aVM->GetPresShell();
534 if (!presShell || presShell->ShouldIgnoreInvalidation()) {
535 return true;
537 nsView* view = aVM->GetRootView()->GetParent();
538 aVM = view ? view->GetViewManager() : nullptr;
540 return false;
543 void nsViewManager::InvalidateView(nsView* aView, const nsRect& aRect) {
544 // If painting is suppressed in the presshell or an ancestor drop all
545 // invalidates, it will invalidate everything when it unsuppresses.
546 if (ShouldIgnoreInvalidation(this)) {
547 return;
550 InvalidateViewNoSuppression(aView, aRect);
553 void nsViewManager::InvalidateViewNoSuppression(nsView* aView,
554 const nsRect& aRect) {
555 MOZ_ASSERT(nullptr != aView, "null view");
557 NS_ASSERTION(aView->GetViewManager() == this,
558 "InvalidateViewNoSuppression called on view we don't own");
560 nsRect damagedRect(aRect);
561 if (damagedRect.IsEmpty()) {
562 return;
565 nsView* displayRoot = GetDisplayRootFor(aView);
566 nsViewManager* displayRootVM = displayRoot->GetViewManager();
567 // Propagate the update to the displayRoot, since iframes, for example,
568 // can overlap each other and be translucent. So we have to possibly
569 // invalidate our rect in each of the widgets we have lying about.
570 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
571 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
572 int32_t APD = AppUnitsPerDevPixel();
573 damagedRect = damagedRect.ScaleToOtherAppUnitsRoundOut(APD, rootAPD);
575 // accumulate this rectangle in the view's dirty region, so we can
576 // process it later.
577 AddDirtyRegion(displayRoot, nsRegion(damagedRect));
580 void nsViewManager::InvalidateAllViews() {
581 if (RootViewManager() != this) {
582 return RootViewManager()->InvalidateAllViews();
585 InvalidateViews(mRootView);
588 void nsViewManager::InvalidateViews(nsView* aView) {
589 // Invalidate this view.
590 InvalidateView(aView);
592 // Invalidate all children as well.
593 nsView* childView = aView->GetFirstChild();
594 while (nullptr != childView) {
595 childView->GetViewManager()->InvalidateViews(childView);
596 childView = childView->GetNextSibling();
600 void nsViewManager::WillPaintWindow(nsIWidget* aWidget) {
601 if (aWidget) {
602 nsView* view = nsView::GetViewFor(aWidget);
603 WindowRenderer* renderer = aWidget->GetWindowRenderer();
604 if (view &&
605 (view->ForcedRepaint() || !renderer->NeedsWidgetInvalidation())) {
606 ProcessPendingUpdates();
607 // Re-get the view pointer here since the ProcessPendingUpdates might have
608 // destroyed it during CallWillPaintOnObservers.
609 view = nsView::GetViewFor(aWidget);
610 if (view) {
611 view->SetForcedRepaint(false);
617 bool nsViewManager::PaintWindow(nsIWidget* aWidget,
618 const LayoutDeviceIntRegion& aRegion) {
619 if (!aWidget || !mContext) return false;
621 NS_ASSERTION(
622 IsPaintingAllowed(),
623 "shouldn't be receiving paint events while painting is disallowed!");
625 // Get the view pointer here since NS_WILL_PAINT might have
626 // destroyed it during CallWillPaintOnObservers (bug 378273).
627 nsView* view = nsView::GetViewFor(aWidget);
628 if (view && !aRegion.IsEmpty()) {
629 Refresh(view, aRegion);
632 return true;
635 void nsViewManager::DidPaintWindow() {
636 if (RefPtr<PresShell> presShell = mPresShell) {
637 presShell->DidPaintWindow();
641 void nsViewManager::DispatchEvent(WidgetGUIEvent* aEvent, nsView* aView,
642 nsEventStatus* aStatus) {
643 AUTO_PROFILER_LABEL("nsViewManager::DispatchEvent", OTHER);
645 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
646 if ((mouseEvent &&
647 // Ignore mouse events that we synthesize.
648 mouseEvent->mReason == WidgetMouseEvent::eReal &&
649 // Ignore mouse exit and enter (we'll get moves if the user
650 // is really moving the mouse) since we get them when we
651 // create and destroy widgets.
652 mouseEvent->mMessage != eMouseExitFromWidget &&
653 mouseEvent->mMessage != eMouseEnterIntoWidget) ||
654 aEvent->HasKeyEventMessage() || aEvent->HasIMEEventMessage()) {
655 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
658 // Find the view whose coordinates system we're in.
659 nsView* view = aView;
660 bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
661 if (dispatchUsingCoordinates) {
662 // Will dispatch using coordinates. Pretty bogus but it's consistent
663 // with what presshell does.
664 view = GetDisplayRootFor(view);
667 // If the view has no frame, look for a view that does.
668 nsIFrame* frame = view->GetFrame();
669 if (!frame && (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
670 aEvent->IsIMERelatedEvent())) {
671 while (view && !view->GetFrame()) {
672 view = view->GetParent();
675 if (view) {
676 frame = view->GetFrame();
680 if (nullptr != frame) {
681 // Hold a refcount to the presshell. The continued existence of the
682 // presshell will delay deletion of this view hierarchy should the event
683 // want to cause its destruction in, say, some JavaScript event handler.
684 if (RefPtr<PresShell> presShell = view->GetViewManager()->GetPresShell()) {
685 presShell->HandleEvent(frame, aEvent, false, aStatus);
686 return;
690 *aStatus = nsEventStatus_eIgnore;
693 // Recursively reparent widgets if necessary
695 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget* aNewWidget) {
696 MOZ_ASSERT(aNewWidget, "null widget");
698 if (aView->HasWidget()) {
699 // Check to see if the parent widget is the
700 // same as the new parent. If not then reparent
701 // the widget, otherwise there is nothing more
702 // to do for the view and its descendants
703 nsIWidget* widget = aView->GetWidget();
704 nsIWidget* parentWidget = widget->GetParent();
705 if (parentWidget) {
706 // Child widget
707 if (parentWidget != aNewWidget) {
708 widget->SetParent(aNewWidget);
710 } else {
711 // Toplevel widget (popup, dialog, etc)
712 widget->ReparentNativeWidget(aNewWidget);
714 return;
717 // Need to check each of the views children to see
718 // if they have a widget and reparent it.
720 for (nsView* kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
721 ReparentChildWidgets(kid, aNewWidget);
725 // Reparent a view and its descendant views widgets if necessary
727 void nsViewManager::ReparentWidgets(nsView* aView, nsView* aParent) {
728 MOZ_ASSERT(aParent, "Must have a parent");
729 MOZ_ASSERT(aView, "Must have a view");
731 // Quickly determine whether the view has pre-existing children or a
732 // widget. In most cases the view will not have any pre-existing
733 // children when this is called. Only in the case
734 // where a view has been reparented by removing it from
735 // a reinserting it into a new location in the view hierarchy do we
736 // have to consider reparenting the existing widgets for the view and
737 // it's descendants.
738 if (aView->HasWidget() || aView->GetFirstChild()) {
739 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
740 if (parentWidget) {
741 ReparentChildWidgets(aView, parentWidget);
742 return;
744 NS_WARNING("Can not find a widget for the parent view");
748 void nsViewManager::InsertChild(nsView* aParent, nsView* aChild,
749 nsView* aSibling, bool aAfter) {
750 MOZ_ASSERT(nullptr != aParent, "null ptr");
751 MOZ_ASSERT(nullptr != aChild, "null ptr");
752 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
753 "tried to insert view with invalid sibling");
754 NS_ASSERTION(!IsViewInserted(aChild),
755 "tried to insert an already-inserted view");
757 if ((nullptr != aParent) && (nullptr != aChild)) {
758 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid'
759 // in document order, otherwise after 'kid' (i.e. before 'kid' in document
760 // order).
762 if (nullptr == aSibling) {
763 if (aAfter) {
764 // insert at end of document order, i.e., before first view
765 // this is the common case, by far
766 aParent->InsertChild(aChild, nullptr);
767 ReparentWidgets(aChild, aParent);
768 } else {
769 // insert at beginning of document order, i.e., after last view
770 nsView* kid = aParent->GetFirstChild();
771 nsView* prev = nullptr;
772 while (kid) {
773 prev = kid;
774 kid = kid->GetNextSibling();
776 // prev is last view or null if there are no children
777 aParent->InsertChild(aChild, prev);
778 ReparentWidgets(aChild, aParent);
780 } else {
781 nsView* kid = aParent->GetFirstChild();
782 nsView* prev = nullptr;
783 while (kid && aSibling != kid) {
784 // get the next sibling view
785 prev = kid;
786 kid = kid->GetNextSibling();
788 NS_ASSERTION(kid != nullptr, "couldn't find sibling in child list");
789 if (aAfter) {
790 // insert after 'kid' in document order, i.e. before in view order
791 aParent->InsertChild(aChild, prev);
792 ReparentWidgets(aChild, aParent);
793 } else {
794 // insert before 'kid' in document order, i.e. after in view order
795 aParent->InsertChild(aChild, kid);
796 ReparentWidgets(aChild, aParent);
800 // if the parent view is marked as "floating", make the newly added view
801 // float as well.
802 if (aParent->GetFloating()) aChild->SetFloating(true);
806 void nsViewManager::RemoveChild(nsView* aChild) {
807 NS_ASSERTION(aChild, "aChild must not be null");
809 nsView* parent = aChild->GetParent();
811 if (nullptr != parent) {
812 NS_ASSERTION(
813 aChild->GetViewManager() == this || parent->GetViewManager() == this,
814 "wrong view manager");
815 parent->RemoveChild(aChild);
819 void nsViewManager::MoveViewTo(nsView* aView, nscoord aX, nscoord aY) {
820 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
821 aView->SetPosition(aX, aY);
824 void nsViewManager::ResizeView(nsView* aView, const nsRect& aRect,
825 bool aRepaintExposedAreaOnly) {
826 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
828 nsRect oldDimensions = aView->GetDimensions();
829 if (!oldDimensions.IsEqualEdges(aRect)) {
830 aView->SetDimensions(aRect, true);
833 // Note that if layout resizes the view and the view has a custom clip
834 // region set, then we expect layout to update the clip region too. Thus
835 // in the case where mClipRect has been optimized away to just be a null
836 // pointer, and this resize is implicitly changing the clip rect, it's OK
837 // because layout will change it back again if necessary.
840 void nsViewManager::SetViewFloating(nsView* aView, bool aFloating) {
841 NS_ASSERTION(!(nullptr == aView), "no view");
843 aView->SetFloating(aFloating);
846 void nsViewManager::SetViewVisibility(nsView* aView,
847 nsViewVisibility aVisible) {
848 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
850 if (aVisible != aView->GetVisibility()) {
851 aView->SetVisibility(aVisible);
855 bool nsViewManager::IsViewInserted(nsView* aView) {
856 if (mRootView == aView) {
857 return true;
859 if (aView->GetParent() == nullptr) {
860 return false;
862 nsView* view = aView->GetParent()->GetFirstChild();
863 while (view != nullptr) {
864 if (view == aView) {
865 return true;
867 view = view->GetNextSibling();
869 return false;
872 void nsViewManager::SetViewZIndex(nsView* aView, bool aAutoZIndex,
873 int32_t aZIndex) {
874 NS_ASSERTION((aView != nullptr), "no view");
876 // don't allow the root view's z-index to be changed. It should always be
877 // zero. This could be removed and replaced with a style rule, or just removed
878 // altogether, with interesting consequences
879 if (aView == mRootView) {
880 return;
883 if (aAutoZIndex) {
884 aZIndex = 0;
887 aView->SetZIndex(aAutoZIndex, aZIndex);
890 nsViewManager* nsViewManager::IncrementDisableRefreshCount() {
891 if (!IsRootVM()) {
892 return RootViewManager()->IncrementDisableRefreshCount();
895 ++mRefreshDisableCount;
897 return this;
900 void nsViewManager::DecrementDisableRefreshCount() {
901 NS_ASSERTION(IsRootVM(), "Should only be called on root");
902 --mRefreshDisableCount;
903 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
906 already_AddRefed<nsIWidget> nsViewManager::GetRootWidget() {
907 nsCOMPtr<nsIWidget> rootWidget;
908 if (mRootView) {
909 if (mRootView->HasWidget()) {
910 rootWidget = mRootView->GetWidget();
911 } else if (mRootView->GetParent()) {
912 rootWidget = mRootView->GetParent()->GetViewManager()->GetRootWidget();
915 return rootWidget.forget();
918 LayoutDeviceIntRect nsViewManager::ViewToWidget(nsView* aView,
919 const nsRect& aRect) const {
920 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
922 // account for the view's origin not lining up with the widget's
923 nsRect rect = aRect + aView->ViewToWidgetOffset();
925 // finally, convert to device coordinates.
926 return LayoutDeviceIntRect::FromUnknownRect(
927 rect.ToOutsidePixels(AppUnitsPerDevPixel()));
930 void nsViewManager::IsPainting(bool& aIsPainting) {
931 aIsPainting = IsPainting();
934 void nsViewManager::ProcessPendingUpdates() {
935 if (!IsRootVM()) {
936 RefPtr<nsViewManager> rootViewManager = RootViewManager();
937 rootViewManager->ProcessPendingUpdates();
938 return;
941 // Flush things like reflows by calling WillPaint on observer presShells.
942 if (mPresShell) {
943 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
945 RefPtr<nsViewManager> strongThis(this);
946 CallWillPaintOnObservers();
948 ProcessPendingUpdatesForView(mRootView, true);
949 if (mPresShell) {
950 if (nsPresContext* pc = mPresShell->GetPresContext()) {
951 pc->RefreshDriver()->ClearHasScheduleFlush();
957 void nsViewManager::UpdateWidgetGeometry() {
958 if (!IsRootVM()) {
959 RefPtr<nsViewManager> rootViewManager = RootViewManager();
960 rootViewManager->UpdateWidgetGeometry();
961 return;
964 if (mHasPendingWidgetGeometryChanges) {
965 mHasPendingWidgetGeometryChanges = false;
966 ProcessPendingUpdatesForView(mRootView, false);
970 void nsViewManager::CallWillPaintOnObservers() {
971 MOZ_ASSERT(IsRootVM(), "Must be root VM for this to be called!");
973 if (NS_WARN_IF(!gViewManagers)) {
974 return;
977 uint32_t index;
978 for (index = 0; index < gViewManagers->Length(); index++) {
979 nsViewManager* vm = gViewManagers->ElementAt(index);
980 if (vm->RootViewManager() == this) {
981 // One of our kids.
982 if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
983 if (RefPtr<PresShell> presShell = vm->GetPresShell()) {
984 presShell->WillPaint();
991 void nsViewManager::GetLastUserEventTime(uint32_t& aTime) {
992 aTime = gLastUserEventTime;
995 void nsViewManager::InvalidateHierarchy() {
996 if (mRootView) {
997 mRootViewManager = nullptr;
998 nsView* parent = mRootView->GetParent();
999 if (parent) {
1000 mRootViewManager = parent->GetViewManager()->RootViewManager();
1001 NS_ASSERTION(mRootViewManager != this,
1002 "Root view had a parent, but it has the same view manager");
1004 // else, we are implicitly our own root view manager.