Bug 1482193 [wpt PR 12382] - [PE] Allow blending for svg root etc., a=testonly
[gecko.git] / view / nsViewManager.cpp
blob2975ca10a20209d41cc8062ad683010f854e0672
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 "nsAutoPtr.h"
7 #include "nsViewManager.h"
8 #include "nsGfxCIID.h"
9 #include "nsView.h"
10 #include "nsCOMPtr.h"
11 #include "mozilla/MouseEvents.h"
12 #include "nsRegion.h"
13 #include "nsCOMArray.h"
14 #include "nsIPluginWidget.h"
15 #include "nsXULPopupManager.h"
16 #include "nsIPresShell.h"
17 #include "nsIPresShellInlines.h"
18 #include "nsPresContext.h"
19 #include "mozilla/StartupTimeline.h"
20 #include "GeckoProfiler.h"
21 #include "nsRefreshDriver.h"
22 #include "mozilla/Preferences.h"
23 #include "nsContentUtils.h" // for nsAutoScriptBlocker
24 #include "nsLayoutUtils.h"
25 #include "Layers.h"
26 #include "gfxPlatform.h"
27 #include "gfxPrefs.h"
28 #include "nsIDocument.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. Even though
43 we ask for a specific z-order, we don't assume that widget 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 nsTArray<nsViewManager*>* nsViewManager::gViewManagers = nullptr;
55 uint32_t nsViewManager::gLastUserEventTime = 0;
57 nsViewManager::nsViewManager()
58 : mPresShell(nullptr)
59 , mDelayedResize(NSCOORD_NONE, NSCOORD_NONE)
60 , mRootView(nullptr)
61 , mRootViewManager(this)
62 , mRefreshDisableCount(0)
63 , mPainting(false)
64 , mRecursiveRefreshPending(false)
65 , mHasPendingWidgetGeometryChanges(false)
67 if (gViewManagers == nullptr) {
68 // Create an array to hold a list of view managers
69 gViewManagers = new nsTArray<nsViewManager*>;
72 gViewManagers->AppendElement(this);
75 nsViewManager::~nsViewManager()
77 if (mRootView) {
78 // Destroy any remaining views
79 mRootView->Destroy();
80 mRootView = nullptr;
83 if (!IsRootVM()) {
84 // We have a strong ref to mRootViewManager
85 NS_RELEASE(mRootViewManager);
88 NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
90 #ifdef DEBUG
91 bool removed =
92 #endif
93 gViewManagers->RemoveElement(this);
94 NS_ASSERTION(removed, "Viewmanager instance was not in the global list of viewmanagers");
96 if (gViewManagers->IsEmpty()) {
97 // There aren't any more view managers so
98 // release the global array of view managers
99 delete gViewManagers;
100 gViewManagers = nullptr;
103 MOZ_RELEASE_ASSERT(!mPresShell, "Releasing nsViewManager without having called Destroy on the PresShell!");
106 // We don't hold a reference to the presentation context because it
107 // holds a reference to us.
108 nsresult
109 nsViewManager::Init(nsDeviceContext* aContext)
111 MOZ_ASSERT(nullptr != aContext, "null ptr");
113 if (nullptr == aContext) {
114 return NS_ERROR_NULL_POINTER;
116 if (nullptr != mContext) {
117 return NS_ERROR_ALREADY_INITIALIZED;
119 mContext = aContext;
121 return NS_OK;
124 nsView*
125 nsViewManager::CreateView(const nsRect& aBounds,
126 nsView* aParent,
127 nsViewVisibility aVisibilityFlag)
129 auto *v = new nsView(this, aVisibilityFlag);
130 v->SetParent(aParent);
131 v->SetPosition(aBounds.X(), aBounds.Y());
132 nsRect dim(0, 0, aBounds.Width(), aBounds.Height());
133 v->SetDimensions(dim, false);
134 return v;
137 void
138 nsViewManager::SetRootView(nsView *aView)
140 MOZ_ASSERT(!aView || aView->GetViewManager() == this,
141 "Unexpected viewmanager on root view");
143 // Do NOT destroy the current root view. It's the caller's responsibility
144 // to destroy it
145 mRootView = aView;
147 if (mRootView) {
148 nsView* parent = mRootView->GetParent();
149 if (parent) {
150 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
151 // no need to set mRootViewManager ourselves here.
152 parent->InsertChild(mRootView, nullptr);
153 } else {
154 InvalidateHierarchy();
157 mRootView->SetZIndex(false, 0);
159 // Else don't touch mRootViewManager
162 void
163 nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
165 if (nullptr != mRootView) {
166 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
167 nsRect dim = mRootView->GetDimensions();
168 *aWidth = dim.Width();
169 *aHeight = dim.Height();
170 } else {
171 *aWidth = mDelayedResize.width;
172 *aHeight = mDelayedResize.height;
175 else
177 *aWidth = 0;
178 *aHeight = 0;
182 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
184 nsRect oldDim = mRootView->GetDimensions();
185 nsRect newDim(0, 0, aWidth, aHeight);
186 // We care about resizes even when one dimension is already zero.
187 if (!oldDim.IsEqualEdges(newDim)) {
188 // Don't resize the widget. It is already being set elsewhere.
189 mRootView->SetDimensions(newDim, true, false);
190 if (mPresShell)
191 mPresShell->ResizeReflow(aWidth, aHeight, oldDim.Width(), oldDim.Height());
195 bool
196 nsViewManager::ShouldDelayResize() const
198 MOZ_ASSERT(mRootView);
199 if (!mRootView->IsEffectivelyVisible() ||
200 !mPresShell || !mPresShell->IsVisible()) {
201 return true;
203 if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
204 if (rd->IsResizeSuppressed()) {
205 return true;
208 return false;
211 void
212 nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight, bool aDelayResize)
214 if (mRootView) {
215 if (!ShouldDelayResize() && !aDelayResize) {
216 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
217 mDelayedResize != nsSize(aWidth, aHeight)) {
218 // We have a delayed resize; that now obsolete size may already have
219 // been flushed to the PresContext so we need to update the PresContext
220 // with the new size because if the new size is exactly the same as the
221 // root view's current size then DoSetWindowDimensions will not
222 // request a resize reflow (which would correct it). See bug 617076.
223 mDelayedResize = nsSize(aWidth, aHeight);
224 FlushDelayedResize(false);
226 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
227 DoSetWindowDimensions(aWidth, aHeight);
228 } else {
229 mDelayedResize.SizeTo(aWidth, aHeight);
230 if (mPresShell) {
231 mPresShell->SetNeedStyleFlush();
232 mPresShell->SetNeedLayoutFlush();
238 void
239 nsViewManager::FlushDelayedResize(bool aDoReflow)
241 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
242 if (aDoReflow) {
243 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height);
244 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
245 } else if (mPresShell && !mPresShell->GetIsViewportOverridden()) {
246 nsPresContext* presContext = mPresShell->GetPresContext();
247 if (presContext) {
248 presContext->SetVisibleArea(nsRect(nsPoint(0, 0), mDelayedResize));
254 // Convert aIn from being relative to and in appunits of aFromView, to being
255 // relative to and in appunits of aToView.
256 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
257 nsView* aFromView,
258 nsView* aToView)
260 nsRegion out = aIn;
261 out.MoveBy(aFromView->GetOffsetTo(aToView));
262 out = out.ScaleToOtherAppUnitsRoundOut(
263 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
264 aToView->GetViewManager()->AppUnitsPerDevPixel());
265 return out;
268 nsView* nsViewManager::GetDisplayRootFor(nsView* aView)
270 nsView *displayRoot = aView;
271 for (;;) {
272 nsView *displayParent = displayRoot->GetParent();
273 if (!displayParent)
274 return displayRoot;
276 if (displayRoot->GetFloating() && !displayParent->GetFloating())
277 return displayRoot;
279 // If we have a combobox dropdown popup within a panel popup, both the view
280 // for the dropdown popup and its parent will be floating, so we need to
281 // distinguish this situation. We do this by looking for a widget. Any view
282 // with a widget is a display root, except for plugins.
283 nsIWidget* widget = displayRoot->GetWidget();
284 if (widget && widget->WindowType() == eWindowType_popup) {
285 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
286 "this should only happen with floating views that have floating parents");
287 return displayRoot;
290 displayRoot = displayParent;
295 aRegion is given in device coordinates!!
296 aContext may be null, in which case layers should be used for
297 rendering.
299 void nsViewManager::Refresh(nsView* aView, const LayoutDeviceIntRegion& aRegion)
301 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
303 if (mPresShell && mPresShell->IsNeverPainting()) {
304 return;
307 // damageRegion is the damaged area, in twips, relative to the view origin
308 nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
310 // move region from widget coordinates into view coordinates
311 damageRegion.MoveBy(-aView->ViewToWidgetOffset());
313 if (damageRegion.IsEmpty()) {
314 #ifdef DEBUG_roc
315 nsRect viewRect = aView->GetDimensions();
316 nsRect damageRect = damageRegion.GetBounds();
317 printf_stderr("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
318 damageRect.x, damageRect.y, damageRect.width, damageRect.height,
319 viewRect.x, viewRect.y, viewRect.width, viewRect.height);
320 #endif
321 return;
324 nsIWidget *widget = aView->GetWidget();
325 if (!widget) {
326 return;
329 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
330 if (IsPainting()) {
331 RootViewManager()->mRecursiveRefreshPending = true;
332 return;
336 nsAutoScriptBlocker scriptBlocker;
337 SetPainting(true);
339 NS_ASSERTION(GetDisplayRootFor(aView) == aView,
340 "Widgets that we paint must all be display roots");
342 if (mPresShell) {
343 #ifdef MOZ_DUMP_PAINTING
344 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
345 printf_stderr("--COMPOSITE-- %p\n", mPresShell);
347 #endif
348 uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE;
349 LayerManager *manager = widget->GetLayerManager();
350 if (!manager->NeedsWidgetInvalidation()) {
351 manager->FlushRendering();
352 } else {
353 mPresShell->Paint(aView, damageRegion,
354 paintFlags);
356 #ifdef MOZ_DUMP_PAINTING
357 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
358 printf_stderr("--ENDCOMPOSITE--\n");
360 #endif
361 mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
364 SetPainting(false);
367 if (RootViewManager()->mRecursiveRefreshPending) {
368 RootViewManager()->mRecursiveRefreshPending = false;
369 InvalidateAllViews();
373 void
374 nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
375 bool aFlushDirtyRegion)
377 NS_ASSERTION(IsRootVM(), "Updates will be missed");
378 if (!aView) {
379 return;
382 nsCOMPtr<nsIPresShell> rootShell(mPresShell);
383 AutoTArray<nsCOMPtr<nsIWidget>, 1> widgets;
384 aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
385 for (uint32_t i = 0; i < widgets.Length(); ++i) {
386 nsView* view = nsView::GetViewFor(widgets[i]);
387 if (view) {
388 if (view->mNeedsWindowPropertiesSync) {
389 view->mNeedsWindowPropertiesSync = false;
390 if (nsViewManager* vm = view->GetViewManager()) {
391 if (nsIPresShell* ps = vm->GetPresShell()) {
392 ps->SyncWindowProperties(view);
397 view = nsView::GetViewFor(widgets[i]);
398 if (view) {
399 view->ResetWidgetBounds(false, true);
402 if (rootShell->GetViewManager() != this) {
403 return; // presentation might have been torn down
405 if (aFlushDirtyRegion) {
406 nsAutoScriptBlocker scriptBlocker;
407 SetPainting(true);
408 for (uint32_t i = 0; i < widgets.Length(); ++i) {
409 nsIWidget* widget = widgets[i];
410 nsView* view = nsView::GetViewFor(widget);
411 if (view) {
412 view->GetViewManager()->ProcessPendingUpdatesPaint(widget);
415 SetPainting(false);
419 void
420 nsViewManager::ProcessPendingUpdatesRecurse(nsView* aView,
421 AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets)
423 if (mPresShell && mPresShell->IsNeverPainting()) {
424 return;
427 for (nsView* childView = aView->GetFirstChild(); childView;
428 childView = childView->GetNextSibling()) {
429 childView->GetViewManager()->
430 ProcessPendingUpdatesRecurse(childView, aWidgets);
433 nsIWidget* widget = aView->GetWidget();
434 if (widget) {
435 aWidgets.AppendElement(widget);
436 } else {
437 FlushDirtyRegionToWidget(aView);
441 void
442 nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget)
444 if (aWidget->NeedsPaint()) {
445 // If an ancestor widget was hidden and then shown, we could
446 // have a delayed resize to handle.
447 for (RefPtr<nsViewManager> vm = this; vm;
448 vm = vm->mRootView->GetParent()
449 ? vm->mRootView->GetParent()->GetViewManager()
450 : nullptr) {
451 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
452 vm->mRootView->IsEffectivelyVisible() &&
453 vm->mPresShell && vm->mPresShell->IsVisible()) {
454 vm->FlushDelayedResize(true);
457 nsView* view = nsView::GetViewFor(aWidget);
459 if (!view) {
460 NS_ERROR("FlushDelayedResize destroyed the nsView?");
461 return;
464 nsIWidgetListener* previousListener = aWidget->GetPreviouslyAttachedWidgetListener();
466 if (previousListener &&
467 previousListener != view &&
468 view->IsPrimaryFramePaintSuppressed()) {
469 return;
472 if (mPresShell) {
473 #ifdef MOZ_DUMP_PAINTING
474 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
475 printf_stderr("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
476 mPresShell, view, aWidget);
478 #endif
480 mPresShell->Paint(view, nsRegion(), nsIPresShell::PAINT_LAYERS);
481 view->SetForcedRepaint(false);
483 #ifdef MOZ_DUMP_PAINTING
484 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
485 printf_stderr("---- PAINT END ----\n");
487 #endif
490 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
493 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
495 NS_ASSERTION(aView->GetViewManager() == this,
496 "FlushDirtyRegionToWidget called on view we don't own");
498 if (!aView->HasNonEmptyDirtyRegion())
499 return;
501 nsRegion* dirtyRegion = aView->GetDirtyRegion();
502 nsView* nearestViewWithWidget = aView;
503 while (!nearestViewWithWidget->HasWidget() &&
504 nearestViewWithWidget->GetParent()) {
505 nearestViewWithWidget = nearestViewWithWidget->GetParent();
507 nsRegion r =
508 ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
510 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
511 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
512 dirtyRegion->SetEmpty();
515 void
516 nsViewManager::InvalidateView(nsView *aView)
518 // Mark the entire view as damaged
519 InvalidateView(aView, aView->GetDimensions());
522 static void
523 AddDirtyRegion(nsView *aView, const nsRegion &aDamagedRegion)
525 nsRegion* dirtyRegion = aView->GetDirtyRegion();
526 if (!dirtyRegion)
527 return;
529 dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
530 dirtyRegion->SimplifyOutward(8);
533 void
534 nsViewManager::PostPendingUpdate()
536 nsViewManager* rootVM = RootViewManager();
537 rootVM->mHasPendingWidgetGeometryChanges = true;
538 if (rootVM->mPresShell) {
539 rootVM->mPresShell->ScheduleViewManagerFlush();
544 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
545 * every widget child of aWidgetView, plus aWidgetView's own widget
547 void
548 nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
549 const nsRegion &aDamagedRegion)
551 NS_ASSERTION(aWidgetView->GetViewManager() == this,
552 "InvalidateWidgetArea called on view we don't own");
553 nsIWidget* widget = aWidgetView->GetWidget();
555 #if 0
556 nsRect dbgBounds = aDamagedRegion.GetBounds();
557 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
558 aWidgetView, aWidgetView->IsAttachedToTopLevel(),
559 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
560 #endif
562 // If the widget is hidden, it don't cover nothing
563 if (widget && !widget->IsVisible()) {
564 return;
567 if (!widget) {
568 // The root view or a scrolling view might not have a widget
569 // (for example, during printing). We get here when we scroll
570 // during printing to show selected options in a listbox, for example.
571 return;
574 // Update all child widgets with the damage. In the process,
575 // accumulate the union of all the child widget areas, or at least
576 // some subset of that.
577 nsRegion children;
578 if (widget->GetTransparencyMode() != eTransparencyTransparent) {
579 for (nsIWidget* childWidget = widget->GetFirstChild();
580 childWidget;
581 childWidget = childWidget->GetNextSibling()) {
582 nsView* view = nsView::GetViewFor(childWidget);
583 NS_ASSERTION(view != aWidgetView, "will recur infinitely");
584 nsWindowType type = childWidget->WindowType();
585 if (view && childWidget->IsVisible() && type != eWindowType_popup) {
586 NS_ASSERTION(childWidget->IsPlugin(),
587 "Only plugin or popup widgets can be children!");
589 // We do not need to invalidate in plugin widgets, but we should
590 // exclude them from the invalidation region IF we're not on
591 // Mac. On Mac we need to draw under plugin widgets, because
592 // plugin widgets are basically invisible
593 #ifndef XP_MACOSX
594 // GetBounds should compensate for chrome on a toplevel widget
595 LayoutDeviceIntRect bounds = childWidget->GetBounds();
597 nsTArray<LayoutDeviceIntRect> clipRects;
598 childWidget->GetWindowClipRegion(&clipRects);
599 for (uint32_t i = 0; i < clipRects.Length(); ++i) {
600 nsRect rr = LayoutDeviceIntRect::ToAppUnits(
601 clipRects[i] + bounds.TopLeft(), AppUnitsPerDevPixel());
602 children.Or(children, rr - aWidgetView->ViewToWidgetOffset());
603 children.SimplifyInward(20);
605 #endif
610 nsRegion leftOver;
611 leftOver.Sub(aDamagedRegion, children);
613 if (!leftOver.IsEmpty()) {
614 for (auto iter = leftOver.RectIter(); !iter.Done(); iter.Next()) {
615 LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get());
616 widget->Invalidate(bounds);
621 static bool
622 ShouldIgnoreInvalidation(nsViewManager* aVM)
624 while (aVM) {
625 nsIPresShell* shell = aVM->GetPresShell();
626 if (!shell || shell->ShouldIgnoreInvalidation()) {
627 return true;
629 nsView* view = aVM->GetRootView()->GetParent();
630 aVM = view ? view->GetViewManager() : nullptr;
632 return false;
635 void
636 nsViewManager::InvalidateView(nsView *aView, const nsRect &aRect)
638 // If painting is suppressed in the presshell or an ancestor drop all
639 // invalidates, it will invalidate everything when it unsuppresses.
640 if (ShouldIgnoreInvalidation(this)) {
641 return;
644 InvalidateViewNoSuppression(aView, aRect);
647 void
648 nsViewManager::InvalidateViewNoSuppression(nsView *aView,
649 const nsRect &aRect)
651 MOZ_ASSERT(nullptr != aView, "null view");
653 NS_ASSERTION(aView->GetViewManager() == this,
654 "InvalidateViewNoSuppression called on view we don't own");
656 nsRect damagedRect(aRect);
657 if (damagedRect.IsEmpty()) {
658 return;
661 nsView* displayRoot = GetDisplayRootFor(aView);
662 nsViewManager* displayRootVM = displayRoot->GetViewManager();
663 // Propagate the update to the displayRoot, since iframes, for example,
664 // can overlap each other and be translucent. So we have to possibly
665 // invalidate our rect in each of the widgets we have lying about.
666 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
667 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
668 int32_t APD = AppUnitsPerDevPixel();
669 damagedRect = damagedRect.ScaleToOtherAppUnitsRoundOut(APD, rootAPD);
671 // accumulate this rectangle in the view's dirty region, so we can
672 // process it later.
673 AddDirtyRegion(displayRoot, nsRegion(damagedRect));
676 void
677 nsViewManager::InvalidateAllViews()
679 if (RootViewManager() != this) {
680 return RootViewManager()->InvalidateAllViews();
683 InvalidateViews(mRootView);
686 void nsViewManager::InvalidateViews(nsView *aView)
688 // Invalidate this view.
689 InvalidateView(aView);
691 // Invalidate all children as well.
692 nsView* childView = aView->GetFirstChild();
693 while (nullptr != childView) {
694 childView->GetViewManager()->InvalidateViews(childView);
695 childView = childView->GetNextSibling();
699 void nsViewManager::WillPaintWindow(nsIWidget* aWidget)
701 RefPtr<nsIWidget> widget(aWidget);
702 if (widget) {
703 nsView* view = nsView::GetViewFor(widget);
704 LayerManager* manager = widget->GetLayerManager();
705 if (view &&
706 (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) {
707 ProcessPendingUpdates();
708 // Re-get the view pointer here since the ProcessPendingUpdates might have
709 // destroyed it during CallWillPaintOnObservers.
710 view = nsView::GetViewFor(widget);
711 if (view) {
712 view->SetForcedRepaint(false);
717 nsCOMPtr<nsIPresShell> shell = mPresShell;
718 if (shell) {
719 shell->WillPaintWindow();
723 bool nsViewManager::PaintWindow(nsIWidget* aWidget,
724 const LayoutDeviceIntRegion& aRegion)
726 if (!aWidget || !mContext)
727 return false;
729 NS_ASSERTION(IsPaintingAllowed(),
730 "shouldn't be receiving paint events while painting is disallowed!");
732 // Get the view pointer here since NS_WILL_PAINT might have
733 // destroyed it during CallWillPaintOnObservers (bug 378273).
734 nsView* view = nsView::GetViewFor(aWidget);
735 if (view && !aRegion.IsEmpty()) {
736 Refresh(view, aRegion);
739 return true;
742 void nsViewManager::DidPaintWindow()
744 nsCOMPtr<nsIPresShell> shell = mPresShell;
745 if (shell) {
746 shell->DidPaintWindow();
750 void
751 nsViewManager::DispatchEvent(WidgetGUIEvent *aEvent,
752 nsView* aView,
753 nsEventStatus* aStatus)
755 AUTO_PROFILER_LABEL("nsViewManager::DispatchEvent", OTHER);
757 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
758 if ((mouseEvent &&
759 // Ignore mouse events that we synthesize.
760 mouseEvent->mReason == WidgetMouseEvent::eReal &&
761 // Ignore mouse exit and enter (we'll get moves if the user
762 // is really moving the mouse) since we get them when we
763 // create and destroy widgets.
764 mouseEvent->mMessage != eMouseExitFromWidget &&
765 mouseEvent->mMessage != eMouseEnterIntoWidget) ||
766 aEvent->HasKeyEventMessage() ||
767 aEvent->HasIMEEventMessage() ||
768 aEvent->mMessage == ePluginInputEvent) {
769 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
772 // Find the view whose coordinates system we're in.
773 nsView* view = aView;
774 bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
775 if (dispatchUsingCoordinates) {
776 // Will dispatch using coordinates. Pretty bogus but it's consistent
777 // with what presshell does.
778 view = GetDisplayRootFor(view);
781 // If the view has no frame, look for a view that does.
782 nsIFrame* frame = view->GetFrame();
783 if (!frame &&
784 (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
785 aEvent->IsIMERelatedEvent() ||
786 aEvent->IsNonRetargetedNativeEventDelivererForPlugin() ||
787 aEvent->HasPluginActivationEventMessage())) {
788 while (view && !view->GetFrame()) {
789 view = view->GetParent();
792 if (view) {
793 frame = view->GetFrame();
797 if (nullptr != frame) {
798 // Hold a refcount to the presshell. The continued existence of the
799 // presshell will delay deletion of this view hierarchy should the event
800 // want to cause its destruction in, say, some JavaScript event handler.
801 nsCOMPtr<nsIPresShell> shell = view->GetViewManager()->GetPresShell();
802 if (shell) {
803 if (aEvent->mMessage == eMouseDown ||
804 aEvent->mMessage == eMouseUp) {
805 AutoWeakFrame weakFrame(frame);
806 shell->FlushPendingNotifications(FlushType::Layout);
807 if (!weakFrame.IsAlive()) {
808 *aStatus = nsEventStatus_eIgnore;
809 return;
812 shell->HandleEvent(frame, aEvent, false, aStatus);
813 return;
817 *aStatus = nsEventStatus_eIgnore;
820 // Recursively reparent widgets if necessary
822 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget *aNewWidget)
824 MOZ_ASSERT(aNewWidget, "null widget");
826 if (aView->HasWidget()) {
827 // Check to see if the parent widget is the
828 // same as the new parent. If not then reparent
829 // the widget, otherwise there is nothing more
830 // to do for the view and its descendants
831 nsIWidget* widget = aView->GetWidget();
832 nsIWidget* parentWidget = widget->GetParent();
833 if (parentWidget) {
834 // Child widget
835 if (parentWidget != aNewWidget) {
836 widget->SetParent(aNewWidget);
838 } else {
839 // Toplevel widget (popup, dialog, etc)
840 widget->ReparentNativeWidget(aNewWidget);
842 return;
845 // Need to check each of the views children to see
846 // if they have a widget and reparent it.
848 for (nsView *kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
849 ReparentChildWidgets(kid, aNewWidget);
853 // Reparent a view and its descendant views widgets if necessary
855 void nsViewManager::ReparentWidgets(nsView* aView, nsView *aParent)
857 MOZ_ASSERT(aParent, "Must have a parent");
858 MOZ_ASSERT(aView, "Must have a view");
860 // Quickly determine whether the view has pre-existing children or a
861 // widget. In most cases the view will not have any pre-existing
862 // children when this is called. Only in the case
863 // where a view has been reparented by removing it from
864 // a reinserting it into a new location in the view hierarchy do we
865 // have to consider reparenting the existing widgets for the view and
866 // it's descendants.
867 if (aView->HasWidget() || aView->GetFirstChild()) {
868 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
869 if (parentWidget) {
870 ReparentChildWidgets(aView, parentWidget);
871 return;
873 NS_WARNING("Can not find a widget for the parent view");
877 void
878 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, nsView *aSibling,
879 bool aAfter)
881 MOZ_ASSERT(nullptr != aParent, "null ptr");
882 MOZ_ASSERT(nullptr != aChild, "null ptr");
883 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
884 "tried to insert view with invalid sibling");
885 NS_ASSERTION(!IsViewInserted(aChild), "tried to insert an already-inserted view");
887 if ((nullptr != aParent) && (nullptr != aChild))
889 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
890 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
892 if (nullptr == aSibling) {
893 if (aAfter) {
894 // insert at end of document order, i.e., before first view
895 // this is the common case, by far
896 aParent->InsertChild(aChild, nullptr);
897 ReparentWidgets(aChild, aParent);
898 } else {
899 // insert at beginning of document order, i.e., after last view
900 nsView *kid = aParent->GetFirstChild();
901 nsView *prev = nullptr;
902 while (kid) {
903 prev = kid;
904 kid = kid->GetNextSibling();
906 // prev is last view or null if there are no children
907 aParent->InsertChild(aChild, prev);
908 ReparentWidgets(aChild, aParent);
910 } else {
911 nsView *kid = aParent->GetFirstChild();
912 nsView *prev = nullptr;
913 while (kid && aSibling != kid) {
914 //get the next sibling view
915 prev = kid;
916 kid = kid->GetNextSibling();
918 NS_ASSERTION(kid != nullptr,
919 "couldn't find sibling in child list");
920 if (aAfter) {
921 // insert after 'kid' in document order, i.e. before in view order
922 aParent->InsertChild(aChild, prev);
923 ReparentWidgets(aChild, aParent);
924 } else {
925 // insert before 'kid' in document order, i.e. after in view order
926 aParent->InsertChild(aChild, kid);
927 ReparentWidgets(aChild, aParent);
931 // if the parent view is marked as "floating", make the newly added view float as well.
932 if (aParent->GetFloating())
933 aChild->SetFloating(true);
937 void
938 nsViewManager::RemoveChild(nsView *aChild)
940 NS_ASSERTION(aChild, "aChild must not be null");
942 nsView* parent = aChild->GetParent();
944 if (nullptr != parent) {
945 NS_ASSERTION(aChild->GetViewManager() == this ||
946 parent->GetViewManager() == this, "wrong view manager");
947 parent->RemoveChild(aChild);
951 void
952 nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY)
954 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
955 aView->SetPosition(aX, aY);
958 void
959 nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly)
961 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
963 nsRect oldDimensions = aView->GetDimensions();
964 if (!oldDimensions.IsEqualEdges(aRect)) {
965 aView->SetDimensions(aRect, true);
968 // Note that if layout resizes the view and the view has a custom clip
969 // region set, then we expect layout to update the clip region too. Thus
970 // in the case where mClipRect has been optimized away to just be a null
971 // pointer, and this resize is implicitly changing the clip rect, it's OK
972 // because layout will change it back again if necessary.
975 void
976 nsViewManager::SetViewFloating(nsView *aView, bool aFloating)
978 NS_ASSERTION(!(nullptr == aView), "no view");
980 aView->SetFloating(aFloating);
983 void
984 nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible)
986 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
988 if (aVisible != aView->GetVisibility()) {
989 aView->SetVisibility(aVisible);
993 bool nsViewManager::IsViewInserted(nsView *aView)
995 if (mRootView == aView) {
996 return true;
998 if (aView->GetParent() == nullptr) {
999 return false;
1001 nsView* view = aView->GetParent()->GetFirstChild();
1002 while (view != nullptr) {
1003 if (view == aView) {
1004 return true;
1006 view = view->GetNextSibling();
1008 return false;
1011 void
1012 nsViewManager::SetViewZIndex(nsView *aView, bool aAutoZIndex, int32_t aZIndex)
1014 NS_ASSERTION((aView != nullptr), "no view");
1016 // don't allow the root view's z-index to be changed. It should always be zero.
1017 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1018 if (aView == mRootView) {
1019 return;
1022 if (aAutoZIndex) {
1023 aZIndex = 0;
1026 aView->SetZIndex(aAutoZIndex, aZIndex);
1029 nsViewManager*
1030 nsViewManager::IncrementDisableRefreshCount()
1032 if (!IsRootVM()) {
1033 return RootViewManager()->IncrementDisableRefreshCount();
1036 ++mRefreshDisableCount;
1038 return this;
1041 void
1042 nsViewManager::DecrementDisableRefreshCount()
1044 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1045 --mRefreshDisableCount;
1046 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
1049 void
1050 nsViewManager::GetRootWidget(nsIWidget **aWidget)
1052 if (!mRootView) {
1053 *aWidget = nullptr;
1054 return;
1056 if (mRootView->HasWidget()) {
1057 *aWidget = mRootView->GetWidget();
1058 NS_ADDREF(*aWidget);
1059 return;
1061 if (mRootView->GetParent()) {
1062 mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
1063 return;
1065 *aWidget = nullptr;
1068 LayoutDeviceIntRect
1069 nsViewManager::ViewToWidget(nsView* aView, const nsRect& aRect) const
1071 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
1073 // account for the view's origin not lining up with the widget's
1074 nsRect rect = aRect + aView->ViewToWidgetOffset();
1076 // finally, convert to device coordinates.
1077 return LayoutDeviceIntRect::FromUnknownRect(
1078 rect.ToOutsidePixels(AppUnitsPerDevPixel()));
1081 void
1082 nsViewManager::IsPainting(bool& aIsPainting)
1084 aIsPainting = IsPainting();
1087 void
1088 nsViewManager::ProcessPendingUpdates()
1090 if (!IsRootVM()) {
1091 RootViewManager()->ProcessPendingUpdates();
1092 return;
1095 // Flush things like reflows by calling WillPaint on observer presShells.
1096 if (mPresShell) {
1097 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
1099 RefPtr<nsViewManager> strongThis(this);
1100 CallWillPaintOnObservers();
1102 ProcessPendingUpdatesForView(mRootView, true);
1106 void
1107 nsViewManager::UpdateWidgetGeometry()
1109 if (!IsRootVM()) {
1110 RootViewManager()->UpdateWidgetGeometry();
1111 return;
1114 if (mHasPendingWidgetGeometryChanges) {
1115 mHasPendingWidgetGeometryChanges = false;
1116 RefPtr<nsViewManager> strongThis(this);
1117 ProcessPendingUpdatesForView(mRootView, false);
1121 void
1122 nsViewManager::CallWillPaintOnObservers()
1124 MOZ_ASSERT(IsRootVM(), "Must be root VM for this to be called!");
1126 if (NS_WARN_IF(!gViewManagers)) {
1127 return;
1130 uint32_t index;
1131 for (index = 0; index < gViewManagers->Length(); index++) {
1132 nsViewManager* vm = gViewManagers->ElementAt(index);
1133 if (vm->RootViewManager() == this) {
1134 // One of our kids.
1135 if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
1136 nsCOMPtr<nsIPresShell> shell = vm->GetPresShell();
1137 if (shell) {
1138 shell->WillPaint();
1145 void
1146 nsViewManager::GetLastUserEventTime(uint32_t& aTime)
1148 aTime = gLastUserEventTime;
1151 void
1152 nsViewManager::InvalidateHierarchy()
1154 if (mRootView) {
1155 if (!IsRootVM()) {
1156 NS_RELEASE(mRootViewManager);
1158 nsView *parent = mRootView->GetParent();
1159 if (parent) {
1160 mRootViewManager = parent->GetViewManager()->RootViewManager();
1161 NS_ADDREF(mRootViewManager);
1162 NS_ASSERTION(mRootViewManager != this,
1163 "Root view had a parent, but it has the same view manager");
1164 } else {
1165 mRootViewManager = this;