Bug 1578745 - Introduce a preference to enable fission frame inspection in DevTools...
[gecko.git] / view / nsViewManager.cpp
blobaa110a031e0b0f76f57e3e51764c190d395c2028
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"
16 #include "nsView.h"
17 #include "nsCOMPtr.h"
18 #include "nsRegion.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"
27 #include "Layers.h"
28 #include "gfxPlatform.h"
30 /**
31 XXX TODO XXX
33 DeCOMify newly private methods
34 Optimize view storage
37 /**
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),
61 mRootView(nullptr),
62 mRootViewManager(this),
63 mRefreshDisableCount(0),
64 mPainting(false),
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() {
76 if (mRootView) {
77 // Destroy any remaining views
78 mRootView->Destroy();
79 mRootView = nullptr;
82 if (!IsRootVM()) {
83 // We have a strong ref to mRootViewManager
84 NS_RELEASE(mRootViewManager);
87 NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
89 #ifdef DEBUG
90 bool removed =
91 #endif
92 gViewManagers->RemoveElement(this);
93 NS_ASSERTION(
94 removed,
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 "
106 "the PresShell!");
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;
120 mContext = aContext;
122 return NS_OK;
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);
132 return v;
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
140 // to destroy it
141 mRootView = aView;
143 if (mRootView) {
144 nsView* parent = mRootView->GetParent();
145 if (parent) {
146 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
147 // no need to set mRootViewManager ourselves here.
148 parent->InsertChild(mRootView, nullptr);
149 } else {
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();
164 } else {
165 *aWidth = mDelayedResize.width;
166 *aHeight = mDelayedResize.height;
168 } else {
169 *aWidth = 0;
170 *aHeight = 0;
174 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight,
175 bool aDoReflow) {
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)) {
180 return;
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;
186 if (!aDoReflow) {
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()) {
197 return true;
199 if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
200 if (rd->IsResizeSuppressed()) {
201 return true;
204 return false;
207 void nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight,
208 bool aDelayResize) {
209 if (mRootView) {
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);
223 } else {
224 mDelayedResize.SizeTo(aWidth, aHeight);
225 if (mPresShell) {
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,
236 aDoReflow);
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) {
245 nsRegion out = aIn;
246 out.MoveBy(aFromView->GetOffsetTo(aToView));
247 out = out.ScaleToOtherAppUnitsRoundOut(
248 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
249 aToView->GetViewManager()->AppUnitsPerDevPixel());
250 return out;
253 nsView* nsViewManager::GetDisplayRootFor(nsView* aView) {
254 nsView* displayRoot = aView;
255 for (;;) {
256 nsView* displayParent = displayRoot->GetParent();
257 if (!displayParent) return displayRoot;
259 if (displayRoot->GetFloating() && !displayParent->GetFloating())
260 return displayRoot;
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 "
270 "floating parents");
271 return displayRoot;
274 displayRoot = displayParent;
279 aRegion is given in device coordinates!!
280 aContext may be null, in which case layers should be used for
281 rendering.
283 void nsViewManager::Refresh(nsView* aView,
284 const LayoutDeviceIntRegion& aRegion) {
285 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
287 if (mPresShell && mPresShell->IsNeverPainting()) {
288 return;
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()) {
298 #ifdef DEBUG_roc
299 nsRect viewRect = aView->GetDimensions();
300 nsRect damageRect = damageRegion.GetBounds();
301 printf_stderr(
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);
306 #endif
307 return;
310 nsIWidget* widget = aView->GetWidget();
311 if (!widget) {
312 return;
315 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
316 if (IsPainting()) {
317 RootViewManager()->mRecursiveRefreshPending = true;
318 return;
322 nsAutoScriptBlocker scriptBlocker;
323 SetPainting(true);
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());
333 #endif
334 LayerManager* manager = widget->GetLayerManager();
335 if (!manager->NeedsWidgetInvalidation()) {
336 manager->FlushRendering();
337 } else {
338 presShell->Paint(aView, damageRegion, PaintFlags::PaintComposite);
340 #ifdef MOZ_DUMP_PAINTING
341 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
342 printf_stderr("--ENDCOMPOSITE--\n");
344 #endif
345 mozilla::StartupTimeline::RecordOnce(
346 mozilla::StartupTimeline::FIRST_PAINT);
349 SetPainting(false);
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");
361 if (!aView) {
362 return;
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]);
370 if (view) {
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]);
381 if (view) {
382 view->ResetWidgetBounds(false, true);
385 if (rootPresShell->GetViewManager() != this) {
386 return; // presentation might have been torn down
388 if (aFlushDirtyRegion) {
389 nsAutoScriptBlocker scriptBlocker;
390 SetPainting(true);
391 for (uint32_t i = 0; i < widgets.Length(); ++i) {
392 nsIWidget* widget = widgets[i];
393 nsView* view = nsView::GetViewFor(widget);
394 if (view) {
395 RefPtr<nsViewManager> viewManager = view->GetViewManager();
396 viewManager->ProcessPendingUpdatesPaint(MOZ_KnownLive(widget));
399 SetPainting(false);
403 void nsViewManager::ProcessPendingUpdatesRecurse(
404 nsView* aView, AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets) {
405 if (mPresShell && mPresShell->IsNeverPainting()) {
406 return;
409 for (nsView* childView = aView->GetFirstChild(); childView;
410 childView = childView->GetNextSibling()) {
411 childView->GetViewManager()->ProcessPendingUpdatesRecurse(childView,
412 aWidgets);
415 nsIWidget* widget = aView->GetWidget();
416 if (widget) {
417 aWidgets.AppendElement(widget);
418 } else {
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()
430 : nullptr) {
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);
439 if (!view) {
440 NS_ERROR("FlushDelayedResize destroyed the nsView?");
441 return;
444 nsIWidgetListener* previousListener =
445 aWidget->GetPreviouslyAttachedWidgetListener();
447 if (previousListener && previousListener != view &&
448 view->IsPrimaryFramePaintSuppressed()) {
449 return;
452 if (RefPtr<PresShell> presShell = mPresShell) {
453 #ifdef MOZ_DUMP_PAINTING
454 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
455 printf_stderr(
456 "---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
457 presShell.get(), view, aWidget);
459 #endif
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");
468 #endif
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();
486 nsRegion r =
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();
525 #if 0
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);
530 #endif
532 // If the widget is hidden, it don't cover nothing
533 if (widget && !widget->IsVisible()) {
534 return;
537 if (!widget) {
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.
541 return;
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.
547 nsRegion children;
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
562 #ifndef XP_MACOSX
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);
574 #endif
579 nsRegion leftOver;
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) {
591 while (aVM) {
592 PresShell* presShell = aVM->GetPresShell();
593 if (!presShell || presShell->ShouldIgnoreInvalidation()) {
594 return true;
596 nsView* view = aVM->GetRootView()->GetParent();
597 aVM = view ? view->GetViewManager() : nullptr;
599 return false;
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)) {
606 return;
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()) {
621 return;
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
635 // process it later.
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) {
660 if (aWidget) {
661 nsView* view = nsView::GetViewFor(aWidget);
662 LayerManager* manager = aWidget->GetLayerManager();
663 if (view &&
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);
669 if (view) {
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;
684 NS_ASSERTION(
685 IsPaintingAllowed(),
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);
695 return true;
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();
709 if ((mouseEvent &&
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();
741 if (view) {
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);
752 return;
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();
771 if (parentWidget) {
772 // Child widget
773 if (parentWidget != aNewWidget) {
774 widget->SetParent(aNewWidget);
776 } else {
777 // Toplevel widget (popup, dialog, etc)
778 widget->ReparentNativeWidget(aNewWidget);
780 return;
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
803 // it's descendants.
804 if (aView->HasWidget() || aView->GetFirstChild()) {
805 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
806 if (parentWidget) {
807 ReparentChildWidgets(aView, parentWidget);
808 return;
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
826 // order).
828 if (nullptr == aSibling) {
829 if (aAfter) {
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);
834 } else {
835 // insert at beginning of document order, i.e., after last view
836 nsView* kid = aParent->GetFirstChild();
837 nsView* prev = nullptr;
838 while (kid) {
839 prev = kid;
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);
846 } else {
847 nsView* kid = aParent->GetFirstChild();
848 nsView* prev = nullptr;
849 while (kid && aSibling != kid) {
850 // get the next sibling view
851 prev = kid;
852 kid = kid->GetNextSibling();
854 NS_ASSERTION(kid != nullptr, "couldn't find sibling in child list");
855 if (aAfter) {
856 // insert after 'kid' in document order, i.e. before in view order
857 aParent->InsertChild(aChild, prev);
858 ReparentWidgets(aChild, aParent);
859 } else {
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
867 // float as well.
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) {
878 NS_ASSERTION(
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) {
923 return true;
925 if (aView->GetParent() == nullptr) {
926 return false;
928 nsView* view = aView->GetParent()->GetFirstChild();
929 while (view != nullptr) {
930 if (view == aView) {
931 return true;
933 view = view->GetNextSibling();
935 return false;
938 void nsViewManager::SetViewZIndex(nsView* aView, bool aAutoZIndex,
939 int32_t aZIndex) {
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) {
946 return;
949 if (aAutoZIndex) {
950 aZIndex = 0;
953 aView->SetZIndex(aAutoZIndex, aZIndex);
956 nsViewManager* nsViewManager::IncrementDisableRefreshCount() {
957 if (!IsRootVM()) {
958 return RootViewManager()->IncrementDisableRefreshCount();
961 ++mRefreshDisableCount;
963 return this;
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) {
973 if (!mRootView) {
974 *aWidget = nullptr;
975 return;
977 if (mRootView->HasWidget()) {
978 *aWidget = mRootView->GetWidget();
979 NS_ADDREF(*aWidget);
980 return;
982 if (mRootView->GetParent()) {
983 mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
984 return;
986 *aWidget = nullptr;
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() {
1006 if (!IsRootVM()) {
1007 RefPtr<nsViewManager> rootViewManager = RootViewManager();
1008 rootViewManager->ProcessPendingUpdates();
1009 return;
1012 // Flush things like reflows by calling WillPaint on observer presShells.
1013 if (mPresShell) {
1014 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
1016 RefPtr<nsViewManager> strongThis(this);
1017 CallWillPaintOnObservers();
1019 ProcessPendingUpdatesForView(mRootView, true);
1023 void nsViewManager::UpdateWidgetGeometry() {
1024 if (!IsRootVM()) {
1025 RefPtr<nsViewManager> rootViewManager = RootViewManager();
1026 rootViewManager->UpdateWidgetGeometry();
1027 return;
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)) {
1040 return;
1043 uint32_t index;
1044 for (index = 0; index < gViewManagers->Length(); index++) {
1045 nsViewManager* vm = gViewManagers->ElementAt(index);
1046 if (vm->RootViewManager() == this) {
1047 // One of our kids.
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() {
1062 if (mRootView) {
1063 if (!IsRootVM()) {
1064 NS_RELEASE(mRootViewManager);
1066 nsView* parent = mRootView->GetParent();
1067 if (parent) {
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");
1072 } else {
1073 mRootViewManager = this;