Bug 1339559 - Identify script that resulted in non-structured-clonable data r=kmag
[gecko.git] / view / nsViewManager.cpp
blob7fffd12fc631827e7cf341dbd4d21584e32c83c4
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 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
7 #include "plarena.h"
9 #include "nsAutoPtr.h"
10 #include "nsViewManager.h"
11 #include "nsGfxCIID.h"
12 #include "nsView.h"
13 #include "nsCOMPtr.h"
14 #include "mozilla/MouseEvents.h"
15 #include "nsRegion.h"
16 #include "nsCOMArray.h"
17 #include "nsIPluginWidget.h"
18 #include "nsXULPopupManager.h"
19 #include "nsIPresShell.h"
20 #include "nsIPresShellInlines.h"
21 #include "nsPresContext.h"
22 #include "mozilla/StartupTimeline.h"
23 #include "GeckoProfiler.h"
24 #include "nsRefreshDriver.h"
25 #include "mozilla/Preferences.h"
26 #include "nsContentUtils.h" // for nsAutoScriptBlocker
27 #include "nsLayoutUtils.h"
28 #include "Layers.h"
29 #include "gfxPlatform.h"
30 #include "gfxPrefs.h"
31 #include "nsIDocument.h"
33 /**
34 XXX TODO XXX
36 DeCOMify newly private methods
37 Optimize view storage
40 /**
41 A note about platform assumptions:
43 We assume that a widget is z-ordered on top of its parent.
45 We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
46 we ask for a specific z-order, we don't assume that widget z-ordering actually works.
49 using namespace mozilla;
50 using namespace mozilla::layers;
52 #define NSCOORD_NONE INT32_MIN
54 #undef DEBUG_MOUSE_LOCATION
56 // Weakly held references to all of the view managers
57 nsTArray<nsViewManager*>* nsViewManager::gViewManagers = nullptr;
58 uint32_t nsViewManager::gLastUserEventTime = 0;
60 nsViewManager::nsViewManager()
61 : mPresShell(nullptr)
62 , mDelayedResize(NSCOORD_NONE, NSCOORD_NONE)
63 , mRootView(nullptr)
64 , mRootViewManager(this)
65 , mRefreshDisableCount(0)
66 , mPainting(false)
67 , mRecursiveRefreshPending(false)
68 , mHasPendingWidgetGeometryChanges(false)
70 if (gViewManagers == nullptr) {
71 // Create an array to hold a list of view managers
72 gViewManagers = new nsTArray<nsViewManager*>;
75 gViewManagers->AppendElement(this);
78 nsViewManager::~nsViewManager()
80 if (mRootView) {
81 // Destroy any remaining views
82 mRootView->Destroy();
83 mRootView = nullptr;
86 if (!IsRootVM()) {
87 // We have a strong ref to mRootViewManager
88 NS_RELEASE(mRootViewManager);
91 NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
93 #ifdef DEBUG
94 bool removed =
95 #endif
96 gViewManagers->RemoveElement(this);
97 NS_ASSERTION(removed, "Viewmanager instance was not in the global list of viewmanagers");
99 if (gViewManagers->IsEmpty()) {
100 // There aren't any more view managers so
101 // release the global array of view managers
102 delete gViewManagers;
103 gViewManagers = nullptr;
106 mPresShell = nullptr;
109 // We don't hold a reference to the presentation context because it
110 // holds a reference to us.
111 nsresult
112 nsViewManager::Init(nsDeviceContext* aContext)
114 NS_PRECONDITION(nullptr != aContext, "null ptr");
116 if (nullptr == aContext) {
117 return NS_ERROR_NULL_POINTER;
119 if (nullptr != mContext) {
120 return NS_ERROR_ALREADY_INITIALIZED;
122 mContext = aContext;
124 return NS_OK;
127 nsView*
128 nsViewManager::CreateView(const nsRect& aBounds,
129 nsView* aParent,
130 nsViewVisibility aVisibilityFlag)
132 auto *v = new nsView(this, aVisibilityFlag);
133 v->SetParent(aParent);
134 v->SetPosition(aBounds.x, aBounds.y);
135 nsRect dim(0, 0, aBounds.width, aBounds.height);
136 v->SetDimensions(dim, false);
137 return v;
140 void
141 nsViewManager::SetRootView(nsView *aView)
143 NS_PRECONDITION(!aView || aView->GetViewManager() == this,
144 "Unexpected viewmanager on root view");
146 // Do NOT destroy the current root view. It's the caller's responsibility
147 // to destroy it
148 mRootView = aView;
150 if (mRootView) {
151 nsView* parent = mRootView->GetParent();
152 if (parent) {
153 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
154 // no need to set mRootViewManager ourselves here.
155 parent->InsertChild(mRootView, nullptr);
156 } else {
157 InvalidateHierarchy();
160 mRootView->SetZIndex(false, 0);
162 // Else don't touch mRootViewManager
165 void
166 nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
168 if (nullptr != mRootView) {
169 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
170 nsRect dim = mRootView->GetDimensions();
171 *aWidth = dim.width;
172 *aHeight = dim.height;
173 } else {
174 *aWidth = mDelayedResize.width;
175 *aHeight = mDelayedResize.height;
178 else
180 *aWidth = 0;
181 *aHeight = 0;
185 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
187 nsRect oldDim = mRootView->GetDimensions();
188 nsRect newDim(0, 0, aWidth, aHeight);
189 // We care about resizes even when one dimension is already zero.
190 if (!oldDim.IsEqualEdges(newDim)) {
191 // Don't resize the widget. It is already being set elsewhere.
192 mRootView->SetDimensions(newDim, true, false);
193 if (mPresShell)
194 mPresShell->ResizeReflow(aWidth, aHeight, oldDim.width, oldDim.height);
198 bool
199 nsViewManager::ShouldDelayResize() const
201 MOZ_ASSERT(mRootView);
202 if (!mRootView->IsEffectivelyVisible() ||
203 !mPresShell || !mPresShell->IsVisible()) {
204 return true;
206 if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
207 if (rd->IsResizeSuppressed()) {
208 return true;
211 return false;
214 void
215 nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight, bool aDelayResize)
217 if (mRootView) {
218 if (!ShouldDelayResize() && !aDelayResize) {
219 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
220 mDelayedResize != nsSize(aWidth, aHeight)) {
221 // We have a delayed resize; that now obsolete size may already have
222 // been flushed to the PresContext so we need to update the PresContext
223 // with the new size because if the new size is exactly the same as the
224 // root view's current size then DoSetWindowDimensions will not
225 // request a resize reflow (which would correct it). See bug 617076.
226 mDelayedResize = nsSize(aWidth, aHeight);
227 FlushDelayedResize(false);
229 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
230 DoSetWindowDimensions(aWidth, aHeight);
231 } else {
232 mDelayedResize.SizeTo(aWidth, aHeight);
233 if (mPresShell) {
234 mPresShell->SetNeedStyleFlush();
235 mPresShell->SetNeedLayoutFlush();
241 void
242 nsViewManager::FlushDelayedResize(bool aDoReflow)
244 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
245 if (aDoReflow) {
246 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height);
247 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
248 } else if (mPresShell && !mPresShell->GetIsViewportOverridden()) {
249 nsPresContext* presContext = mPresShell->GetPresContext();
250 if (presContext) {
251 presContext->SetVisibleArea(nsRect(nsPoint(0, 0), mDelayedResize));
257 // Convert aIn from being relative to and in appunits of aFromView, to being
258 // relative to and in appunits of aToView.
259 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
260 nsView* aFromView,
261 nsView* aToView)
263 nsRegion out = aIn;
264 out.MoveBy(aFromView->GetOffsetTo(aToView));
265 out = out.ScaleToOtherAppUnitsRoundOut(
266 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
267 aToView->GetViewManager()->AppUnitsPerDevPixel());
268 return out;
271 nsView* nsViewManager::GetDisplayRootFor(nsView* aView)
273 nsView *displayRoot = aView;
274 for (;;) {
275 nsView *displayParent = displayRoot->GetParent();
276 if (!displayParent)
277 return displayRoot;
279 if (displayRoot->GetFloating() && !displayParent->GetFloating())
280 return displayRoot;
282 // If we have a combobox dropdown popup within a panel popup, both the view
283 // for the dropdown popup and its parent will be floating, so we need to
284 // distinguish this situation. We do this by looking for a widget. Any view
285 // with a widget is a display root, except for plugins.
286 nsIWidget* widget = displayRoot->GetWidget();
287 if (widget && widget->WindowType() == eWindowType_popup) {
288 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
289 "this should only happen with floating views that have floating parents");
290 return displayRoot;
293 displayRoot = displayParent;
298 aRegion is given in device coordinates!!
299 aContext may be null, in which case layers should be used for
300 rendering.
302 void nsViewManager::Refresh(nsView* aView, const LayoutDeviceIntRegion& aRegion)
304 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
306 if (mPresShell && mPresShell->IsNeverPainting()) {
307 return;
310 // damageRegion is the damaged area, in twips, relative to the view origin
311 nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
313 // move region from widget coordinates into view coordinates
314 damageRegion.MoveBy(-aView->ViewToWidgetOffset());
316 if (damageRegion.IsEmpty()) {
317 #ifdef DEBUG_roc
318 nsRect viewRect = aView->GetDimensions();
319 nsRect damageRect = damageRegion.GetBounds();
320 printf_stderr("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
321 damageRect.x, damageRect.y, damageRect.width, damageRect.height,
322 viewRect.x, viewRect.y, viewRect.width, viewRect.height);
323 #endif
324 return;
327 nsIWidget *widget = aView->GetWidget();
328 if (!widget) {
329 return;
332 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
333 if (IsPainting()) {
334 RootViewManager()->mRecursiveRefreshPending = true;
335 return;
339 nsAutoScriptBlocker scriptBlocker;
340 SetPainting(true);
342 NS_ASSERTION(GetDisplayRootFor(aView) == aView,
343 "Widgets that we paint must all be display roots");
345 if (mPresShell) {
346 #ifdef MOZ_DUMP_PAINTING
347 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
348 printf_stderr("--COMPOSITE-- %p\n", mPresShell);
350 #endif
351 uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE;
352 LayerManager *manager = widget->GetLayerManager();
353 if (!manager->NeedsWidgetInvalidation()) {
354 manager->FlushRendering();
355 } else {
356 mPresShell->Paint(aView, damageRegion,
357 paintFlags);
359 #ifdef MOZ_DUMP_PAINTING
360 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
361 printf_stderr("--ENDCOMPOSITE--\n");
363 #endif
364 mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
367 SetPainting(false);
370 if (RootViewManager()->mRecursiveRefreshPending) {
371 RootViewManager()->mRecursiveRefreshPending = false;
372 InvalidateAllViews();
376 void
377 nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
378 bool aFlushDirtyRegion)
380 NS_ASSERTION(IsRootVM(), "Updates will be missed");
381 if (!aView) {
382 return;
385 nsCOMPtr<nsIPresShell> rootShell(mPresShell);
386 AutoTArray<nsCOMPtr<nsIWidget>, 1> widgets;
387 aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
388 for (uint32_t i = 0; i < widgets.Length(); ++i) {
389 nsView* view = nsView::GetViewFor(widgets[i]);
390 if (view) {
391 if (view->mNeedsWindowPropertiesSync) {
392 view->mNeedsWindowPropertiesSync = false;
393 if (nsViewManager* vm = view->GetViewManager()) {
394 if (nsIPresShell* ps = vm->GetPresShell()) {
395 ps->SyncWindowProperties(view);
400 view = nsView::GetViewFor(widgets[i]);
401 if (view) {
402 view->ResetWidgetBounds(false, true);
405 if (rootShell->GetViewManager() != this) {
406 return; // presentation might have been torn down
408 if (aFlushDirtyRegion) {
409 nsAutoScriptBlocker scriptBlocker;
410 SetPainting(true);
411 for (uint32_t i = 0; i < widgets.Length(); ++i) {
412 nsIWidget* widget = widgets[i];
413 nsView* view = nsView::GetViewFor(widget);
414 if (view) {
415 view->GetViewManager()->ProcessPendingUpdatesPaint(widget);
418 SetPainting(false);
422 void
423 nsViewManager::ProcessPendingUpdatesRecurse(nsView* aView,
424 AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets)
426 if (mPresShell && mPresShell->IsNeverPainting()) {
427 return;
430 for (nsView* childView = aView->GetFirstChild(); childView;
431 childView = childView->GetNextSibling()) {
432 childView->GetViewManager()->
433 ProcessPendingUpdatesRecurse(childView, aWidgets);
436 nsIWidget* widget = aView->GetWidget();
437 if (widget) {
438 aWidgets.AppendElement(widget);
439 } else {
440 FlushDirtyRegionToWidget(aView);
444 void
445 nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget)
447 if (aWidget->NeedsPaint()) {
448 // If an ancestor widget was hidden and then shown, we could
449 // have a delayed resize to handle.
450 for (RefPtr<nsViewManager> vm = this; vm;
451 vm = vm->mRootView->GetParent()
452 ? vm->mRootView->GetParent()->GetViewManager()
453 : nullptr) {
454 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
455 vm->mRootView->IsEffectivelyVisible() &&
456 vm->mPresShell && vm->mPresShell->IsVisible()) {
457 vm->FlushDelayedResize(true);
460 nsView* view = nsView::GetViewFor(aWidget);
462 if (!view) {
463 NS_ERROR("FlushDelayedResize destroyed the nsView?");
464 return;
467 nsIWidgetListener* previousListener = aWidget->GetPreviouslyAttachedWidgetListener();
469 if (previousListener &&
470 previousListener != view &&
471 view->IsPrimaryFramePaintSuppressed()) {
472 return;
475 if (mPresShell) {
476 #ifdef MOZ_DUMP_PAINTING
477 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
478 printf_stderr("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
479 mPresShell, view, aWidget);
481 #endif
483 mPresShell->Paint(view, nsRegion(), nsIPresShell::PAINT_LAYERS);
484 view->SetForcedRepaint(false);
486 #ifdef MOZ_DUMP_PAINTING
487 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
488 printf_stderr("---- PAINT END ----\n");
490 #endif
493 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
496 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
498 NS_ASSERTION(aView->GetViewManager() == this,
499 "FlushDirtyRegionToWidget called on view we don't own");
501 if (!aView->HasNonEmptyDirtyRegion())
502 return;
504 nsRegion* dirtyRegion = aView->GetDirtyRegion();
505 nsView* nearestViewWithWidget = aView;
506 while (!nearestViewWithWidget->HasWidget() &&
507 nearestViewWithWidget->GetParent()) {
508 nearestViewWithWidget = nearestViewWithWidget->GetParent();
510 nsRegion r =
511 ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
513 // If we draw the frame counter we need to make sure we invalidate the area
514 // for it to make it on screen
515 if (gfxPrefs::DrawFrameCounter()) {
516 nsRect counterBounds = ToAppUnits(gfxPlatform::FrameCounterBounds(), AppUnitsPerDevPixel());
517 r.OrWith(counterBounds);
520 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
521 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
522 dirtyRegion->SetEmpty();
525 void
526 nsViewManager::InvalidateView(nsView *aView)
528 // Mark the entire view as damaged
529 InvalidateView(aView, aView->GetDimensions());
532 static void
533 AddDirtyRegion(nsView *aView, const nsRegion &aDamagedRegion)
535 nsRegion* dirtyRegion = aView->GetDirtyRegion();
536 if (!dirtyRegion)
537 return;
539 dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
540 dirtyRegion->SimplifyOutward(8);
543 void
544 nsViewManager::PostPendingUpdate()
546 nsViewManager* rootVM = RootViewManager();
547 rootVM->mHasPendingWidgetGeometryChanges = true;
548 if (rootVM->mPresShell) {
549 rootVM->mPresShell->ScheduleViewManagerFlush();
554 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
555 * every widget child of aWidgetView, plus aWidgetView's own widget
557 void
558 nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
559 const nsRegion &aDamagedRegion)
561 NS_ASSERTION(aWidgetView->GetViewManager() == this,
562 "InvalidateWidgetArea called on view we don't own");
563 nsIWidget* widget = aWidgetView->GetWidget();
565 #if 0
566 nsRect dbgBounds = aDamagedRegion.GetBounds();
567 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
568 aWidgetView, aWidgetView->IsAttachedToTopLevel(),
569 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
570 #endif
572 // If the widget is hidden, it don't cover nothing
573 if (widget && !widget->IsVisible()) {
574 return;
577 if (!widget) {
578 // The root view or a scrolling view might not have a widget
579 // (for example, during printing). We get here when we scroll
580 // during printing to show selected options in a listbox, for example.
581 return;
584 // Update all child widgets with the damage. In the process,
585 // accumulate the union of all the child widget areas, or at least
586 // some subset of that.
587 nsRegion children;
588 if (widget->GetTransparencyMode() != eTransparencyTransparent) {
589 for (nsIWidget* childWidget = widget->GetFirstChild();
590 childWidget;
591 childWidget = childWidget->GetNextSibling()) {
592 nsView* view = nsView::GetViewFor(childWidget);
593 NS_ASSERTION(view != aWidgetView, "will recur infinitely");
594 nsWindowType type = childWidget->WindowType();
595 if (view && childWidget->IsVisible() && type != eWindowType_popup) {
596 NS_ASSERTION(childWidget->IsPlugin(),
597 "Only plugin or popup widgets can be children!");
599 // We do not need to invalidate in plugin widgets, but we should
600 // exclude them from the invalidation region IF we're not on
601 // Mac. On Mac we need to draw under plugin widgets, because
602 // plugin widgets are basically invisible
603 #ifndef XP_MACOSX
604 // GetBounds should compensate for chrome on a toplevel widget
605 LayoutDeviceIntRect bounds = childWidget->GetBounds();
607 nsTArray<LayoutDeviceIntRect> clipRects;
608 childWidget->GetWindowClipRegion(&clipRects);
609 for (uint32_t i = 0; i < clipRects.Length(); ++i) {
610 nsRect rr = LayoutDeviceIntRect::ToAppUnits(
611 clipRects[i] + bounds.TopLeft(), AppUnitsPerDevPixel());
612 children.Or(children, rr - aWidgetView->ViewToWidgetOffset());
613 children.SimplifyInward(20);
615 #endif
620 nsRegion leftOver;
621 leftOver.Sub(aDamagedRegion, children);
623 if (!leftOver.IsEmpty()) {
624 for (auto iter = leftOver.RectIter(); !iter.Done(); iter.Next()) {
625 LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get());
626 widget->Invalidate(bounds);
631 static bool
632 ShouldIgnoreInvalidation(nsViewManager* aVM)
634 while (aVM) {
635 nsIPresShell* shell = aVM->GetPresShell();
636 if (!shell || shell->ShouldIgnoreInvalidation()) {
637 return true;
639 nsView* view = aVM->GetRootView()->GetParent();
640 aVM = view ? view->GetViewManager() : nullptr;
642 return false;
645 void
646 nsViewManager::InvalidateView(nsView *aView, const nsRect &aRect)
648 // If painting is suppressed in the presshell or an ancestor drop all
649 // invalidates, it will invalidate everything when it unsuppresses.
650 if (ShouldIgnoreInvalidation(this)) {
651 return;
654 InvalidateViewNoSuppression(aView, aRect);
657 void
658 nsViewManager::InvalidateViewNoSuppression(nsView *aView,
659 const nsRect &aRect)
661 NS_PRECONDITION(nullptr != aView, "null view");
663 NS_ASSERTION(aView->GetViewManager() == this,
664 "InvalidateViewNoSuppression called on view we don't own");
666 nsRect damagedRect(aRect);
667 if (damagedRect.IsEmpty()) {
668 return;
671 nsView* displayRoot = GetDisplayRootFor(aView);
672 nsViewManager* displayRootVM = displayRoot->GetViewManager();
673 // Propagate the update to the displayRoot, since iframes, for example,
674 // can overlap each other and be translucent. So we have to possibly
675 // invalidate our rect in each of the widgets we have lying about.
676 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
677 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
678 int32_t APD = AppUnitsPerDevPixel();
679 damagedRect = damagedRect.ScaleToOtherAppUnitsRoundOut(APD, rootAPD);
681 // accumulate this rectangle in the view's dirty region, so we can
682 // process it later.
683 AddDirtyRegion(displayRoot, nsRegion(damagedRect));
686 void
687 nsViewManager::InvalidateAllViews()
689 if (RootViewManager() != this) {
690 return RootViewManager()->InvalidateAllViews();
693 InvalidateViews(mRootView);
696 void nsViewManager::InvalidateViews(nsView *aView)
698 // Invalidate this view.
699 InvalidateView(aView);
701 // Invalidate all children as well.
702 nsView* childView = aView->GetFirstChild();
703 while (nullptr != childView) {
704 childView->GetViewManager()->InvalidateViews(childView);
705 childView = childView->GetNextSibling();
709 void nsViewManager::WillPaintWindow(nsIWidget* aWidget)
711 RefPtr<nsIWidget> widget(aWidget);
712 if (widget) {
713 nsView* view = nsView::GetViewFor(widget);
714 LayerManager* manager = widget->GetLayerManager();
715 if (view &&
716 (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) {
717 ProcessPendingUpdates();
718 // Re-get the view pointer here since the ProcessPendingUpdates might have
719 // destroyed it during CallWillPaintOnObservers.
720 view = nsView::GetViewFor(widget);
721 if (view) {
722 view->SetForcedRepaint(false);
727 nsCOMPtr<nsIPresShell> shell = mPresShell;
728 if (shell) {
729 shell->WillPaintWindow();
733 bool nsViewManager::PaintWindow(nsIWidget* aWidget,
734 const LayoutDeviceIntRegion& aRegion)
736 if (!aWidget || !mContext)
737 return false;
739 NS_ASSERTION(IsPaintingAllowed(),
740 "shouldn't be receiving paint events while painting is disallowed!");
742 // Get the view pointer here since NS_WILL_PAINT might have
743 // destroyed it during CallWillPaintOnObservers (bug 378273).
744 nsView* view = nsView::GetViewFor(aWidget);
745 if (view && !aRegion.IsEmpty()) {
746 Refresh(view, aRegion);
749 return true;
752 void nsViewManager::DidPaintWindow()
754 nsCOMPtr<nsIPresShell> shell = mPresShell;
755 if (shell) {
756 shell->DidPaintWindow();
760 void
761 nsViewManager::DispatchEvent(WidgetGUIEvent *aEvent,
762 nsView* aView,
763 nsEventStatus* aStatus)
765 PROFILER_LABEL("nsViewManager", "DispatchEvent",
766 js::ProfileEntry::Category::EVENTS);
768 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
769 if ((mouseEvent &&
770 // Ignore mouse events that we synthesize.
771 mouseEvent->mReason == WidgetMouseEvent::eReal &&
772 // Ignore mouse exit and enter (we'll get moves if the user
773 // is really moving the mouse) since we get them when we
774 // create and destroy widgets.
775 mouseEvent->mMessage != eMouseExitFromWidget &&
776 mouseEvent->mMessage != eMouseEnterIntoWidget) ||
777 aEvent->HasKeyEventMessage() ||
778 aEvent->HasIMEEventMessage() ||
779 aEvent->mMessage == ePluginInputEvent) {
780 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
783 // Find the view whose coordinates system we're in.
784 nsView* view = aView;
785 bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
786 if (dispatchUsingCoordinates) {
787 // Will dispatch using coordinates. Pretty bogus but it's consistent
788 // with what presshell does.
789 view = GetDisplayRootFor(view);
792 // If the view has no frame, look for a view that does.
793 nsIFrame* frame = view->GetFrame();
794 if (!frame &&
795 (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
796 aEvent->IsIMERelatedEvent() ||
797 aEvent->IsNonRetargetedNativeEventDelivererForPlugin() ||
798 aEvent->HasPluginActivationEventMessage())) {
799 while (view && !view->GetFrame()) {
800 view = view->GetParent();
803 if (view) {
804 frame = view->GetFrame();
808 if (nullptr != frame) {
809 // Hold a refcount to the presshell. The continued existence of the
810 // presshell will delay deletion of this view hierarchy should the event
811 // want to cause its destruction in, say, some JavaScript event handler.
812 nsCOMPtr<nsIPresShell> shell = view->GetViewManager()->GetPresShell();
813 if (shell) {
814 shell->HandleEvent(frame, aEvent, false, aStatus);
815 return;
819 *aStatus = nsEventStatus_eIgnore;
822 // Recursively reparent widgets if necessary
824 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget *aNewWidget)
826 NS_PRECONDITION(aNewWidget, "");
828 if (aView->HasWidget()) {
829 // Check to see if the parent widget is the
830 // same as the new parent. If not then reparent
831 // the widget, otherwise there is nothing more
832 // to do for the view and its descendants
833 nsIWidget* widget = aView->GetWidget();
834 nsIWidget* parentWidget = widget->GetParent();
835 if (parentWidget) {
836 // Child widget
837 if (parentWidget != aNewWidget) {
838 widget->SetParent(aNewWidget);
840 } else {
841 // Toplevel widget (popup, dialog, etc)
842 widget->ReparentNativeWidget(aNewWidget);
844 return;
847 // Need to check each of the views children to see
848 // if they have a widget and reparent it.
850 for (nsView *kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
851 ReparentChildWidgets(kid, aNewWidget);
855 // Reparent a view and its descendant views widgets if necessary
857 void nsViewManager::ReparentWidgets(nsView* aView, nsView *aParent)
859 NS_PRECONDITION(aParent, "Must have a parent");
860 NS_PRECONDITION(aView, "Must have a view");
862 // Quickly determine whether the view has pre-existing children or a
863 // widget. In most cases the view will not have any pre-existing
864 // children when this is called. Only in the case
865 // where a view has been reparented by removing it from
866 // a reinserting it into a new location in the view hierarchy do we
867 // have to consider reparenting the existing widgets for the view and
868 // it's descendants.
869 if (aView->HasWidget() || aView->GetFirstChild()) {
870 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
871 if (parentWidget) {
872 ReparentChildWidgets(aView, parentWidget);
873 return;
875 NS_WARNING("Can not find a widget for the parent view");
879 void
880 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, nsView *aSibling,
881 bool aAfter)
883 NS_PRECONDITION(nullptr != aParent, "null ptr");
884 NS_PRECONDITION(nullptr != aChild, "null ptr");
885 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
886 "tried to insert view with invalid sibling");
887 NS_ASSERTION(!IsViewInserted(aChild), "tried to insert an already-inserted view");
889 if ((nullptr != aParent) && (nullptr != aChild))
891 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
892 // order, otherwise after 'kid' (i.e. before 'kid' in document order).
894 if (nullptr == aSibling) {
895 if (aAfter) {
896 // insert at end of document order, i.e., before first view
897 // this is the common case, by far
898 aParent->InsertChild(aChild, nullptr);
899 ReparentWidgets(aChild, aParent);
900 } else {
901 // insert at beginning of document order, i.e., after last view
902 nsView *kid = aParent->GetFirstChild();
903 nsView *prev = nullptr;
904 while (kid) {
905 prev = kid;
906 kid = kid->GetNextSibling();
908 // prev is last view or null if there are no children
909 aParent->InsertChild(aChild, prev);
910 ReparentWidgets(aChild, aParent);
912 } else {
913 nsView *kid = aParent->GetFirstChild();
914 nsView *prev = nullptr;
915 while (kid && aSibling != kid) {
916 //get the next sibling view
917 prev = kid;
918 kid = kid->GetNextSibling();
920 NS_ASSERTION(kid != nullptr,
921 "couldn't find sibling in child list");
922 if (aAfter) {
923 // insert after 'kid' in document order, i.e. before in view order
924 aParent->InsertChild(aChild, prev);
925 ReparentWidgets(aChild, aParent);
926 } else {
927 // insert before 'kid' in document order, i.e. after in view order
928 aParent->InsertChild(aChild, kid);
929 ReparentWidgets(aChild, aParent);
933 // if the parent view is marked as "floating", make the newly added view float as well.
934 if (aParent->GetFloating())
935 aChild->SetFloating(true);
939 void
940 nsViewManager::RemoveChild(nsView *aChild)
942 NS_ASSERTION(aChild, "aChild must not be null");
944 nsView* parent = aChild->GetParent();
946 if (nullptr != parent) {
947 NS_ASSERTION(aChild->GetViewManager() == this ||
948 parent->GetViewManager() == this, "wrong view manager");
949 parent->RemoveChild(aChild);
953 void
954 nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY)
956 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
957 aView->SetPosition(aX, aY);
960 void
961 nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly)
963 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
965 nsRect oldDimensions = aView->GetDimensions();
966 if (!oldDimensions.IsEqualEdges(aRect)) {
967 aView->SetDimensions(aRect, true);
970 // Note that if layout resizes the view and the view has a custom clip
971 // region set, then we expect layout to update the clip region too. Thus
972 // in the case where mClipRect has been optimized away to just be a null
973 // pointer, and this resize is implicitly changing the clip rect, it's OK
974 // because layout will change it back again if necessary.
977 void
978 nsViewManager::SetViewFloating(nsView *aView, bool aFloating)
980 NS_ASSERTION(!(nullptr == aView), "no view");
982 aView->SetFloating(aFloating);
985 void
986 nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible)
988 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
990 if (aVisible != aView->GetVisibility()) {
991 aView->SetVisibility(aVisible);
995 bool nsViewManager::IsViewInserted(nsView *aView)
997 if (mRootView == aView) {
998 return true;
1000 if (aView->GetParent() == nullptr) {
1001 return false;
1003 nsView* view = aView->GetParent()->GetFirstChild();
1004 while (view != nullptr) {
1005 if (view == aView) {
1006 return true;
1008 view = view->GetNextSibling();
1010 return false;
1013 void
1014 nsViewManager::SetViewZIndex(nsView *aView, bool aAutoZIndex, int32_t aZIndex)
1016 NS_ASSERTION((aView != nullptr), "no view");
1018 // don't allow the root view's z-index to be changed. It should always be zero.
1019 // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
1020 if (aView == mRootView) {
1021 return;
1024 if (aAutoZIndex) {
1025 aZIndex = 0;
1028 aView->SetZIndex(aAutoZIndex, aZIndex);
1031 nsViewManager*
1032 nsViewManager::IncrementDisableRefreshCount()
1034 if (!IsRootVM()) {
1035 return RootViewManager()->IncrementDisableRefreshCount();
1038 ++mRefreshDisableCount;
1040 return this;
1043 void
1044 nsViewManager::DecrementDisableRefreshCount()
1046 NS_ASSERTION(IsRootVM(), "Should only be called on root");
1047 --mRefreshDisableCount;
1048 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
1051 void
1052 nsViewManager::GetRootWidget(nsIWidget **aWidget)
1054 if (!mRootView) {
1055 *aWidget = nullptr;
1056 return;
1058 if (mRootView->HasWidget()) {
1059 *aWidget = mRootView->GetWidget();
1060 NS_ADDREF(*aWidget);
1061 return;
1063 if (mRootView->GetParent()) {
1064 mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
1065 return;
1067 *aWidget = nullptr;
1070 LayoutDeviceIntRect
1071 nsViewManager::ViewToWidget(nsView* aView, const nsRect& aRect) const
1073 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
1075 // account for the view's origin not lining up with the widget's
1076 nsRect rect = aRect + aView->ViewToWidgetOffset();
1078 // finally, convert to device coordinates.
1079 return LayoutDeviceIntRect::FromUnknownRect(
1080 rect.ToOutsidePixels(AppUnitsPerDevPixel()));
1083 void
1084 nsViewManager::IsPainting(bool& aIsPainting)
1086 aIsPainting = IsPainting();
1089 void
1090 nsViewManager::ProcessPendingUpdates()
1092 if (!IsRootVM()) {
1093 RootViewManager()->ProcessPendingUpdates();
1094 return;
1097 // Flush things like reflows by calling WillPaint on observer presShells.
1098 if (mPresShell) {
1099 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
1101 RefPtr<nsViewManager> strongThis(this);
1102 CallWillPaintOnObservers();
1104 ProcessPendingUpdatesForView(mRootView, true);
1108 void
1109 nsViewManager::UpdateWidgetGeometry()
1111 if (!IsRootVM()) {
1112 RootViewManager()->UpdateWidgetGeometry();
1113 return;
1116 if (mHasPendingWidgetGeometryChanges) {
1117 mHasPendingWidgetGeometryChanges = false;
1118 RefPtr<nsViewManager> strongThis(this);
1119 ProcessPendingUpdatesForView(mRootView, false);
1123 void
1124 nsViewManager::CallWillPaintOnObservers()
1126 NS_PRECONDITION(IsRootVM(), "Must be root VM for this to be called!");
1128 if (NS_WARN_IF(!gViewManagers)) {
1129 return;
1132 uint32_t index;
1133 for (index = 0; index < gViewManagers->Length(); index++) {
1134 nsViewManager* vm = gViewManagers->ElementAt(index);
1135 if (vm->RootViewManager() == this) {
1136 // One of our kids.
1137 if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
1138 nsCOMPtr<nsIPresShell> shell = vm->GetPresShell();
1139 if (shell) {
1140 shell->WillPaint();
1147 void
1148 nsViewManager::GetLastUserEventTime(uint32_t& aTime)
1150 aTime = gLastUserEventTime;
1153 void
1154 nsViewManager::InvalidateHierarchy()
1156 if (mRootView) {
1157 if (!IsRootVM()) {
1158 NS_RELEASE(mRootViewManager);
1160 nsView *parent = mRootView->GetParent();
1161 if (parent) {
1162 mRootViewManager = parent->GetViewManager()->RootViewManager();
1163 NS_ADDREF(mRootViewManager);
1164 NS_ASSERTION(mRootViewManager != this,
1165 "Root view had a parent, but it has the same view manager");
1166 } else {
1167 mRootViewManager = this;