Bug 1700051: part 30) Narrow scope of `newOffset`. r=smaug
[gecko.git] / view / nsViewManager.cpp
blobe20db88e57ad2847813f388ef2ce92b1b3ff5c7b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsViewManager.h"
8 #include "mozilla/MouseEvents.h"
9 #include "mozilla/PresShell.h"
10 #include "mozilla/PresShellInlines.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/ProfilerLabels.h"
13 #include "mozilla/StartupTimeline.h"
14 #include "mozilla/dom/Document.h"
15 #include "nsGfxCIID.h"
16 #include "nsView.h"
17 #include "nsCOMPtr.h"
18 #include "nsRegion.h"
19 #include "nsCOMArray.h"
20 #include "nsXULPopupManager.h"
21 #include "nsPresContext.h"
22 #include "nsRefreshDriver.h"
23 #include "nsContentUtils.h" // for nsAutoScriptBlocker
24 #include "nsLayoutUtils.h"
25 #include "Layers.h"
26 #include "gfxPlatform.h"
28 /**
29 XXX TODO XXX
31 DeCOMify newly private methods
32 Optimize view storage
35 /**
36 A note about platform assumptions:
38 We assume that a widget is z-ordered on top of its parent.
40 We do NOT assume anything about the relative z-ordering of sibling widgets.
41 Even though we ask for a specific z-order, we don't assume that widget
42 z-ordering actually works.
45 using namespace mozilla;
46 using namespace mozilla::layers;
48 #define NSCOORD_NONE INT32_MIN
50 #undef DEBUG_MOUSE_LOCATION
52 // Weakly held references to all of the view managers
53 nsTArray<nsViewManager*>* nsViewManager::gViewManagers = nullptr;
54 uint32_t nsViewManager::gLastUserEventTime = 0;
56 nsViewManager::nsViewManager()
57 : mPresShell(nullptr),
58 mDelayedResize(NSCOORD_NONE, NSCOORD_NONE),
59 mRootView(nullptr),
60 mRootViewManager(this),
61 mRefreshDisableCount(0),
62 mPainting(false),
63 mRecursiveRefreshPending(false),
64 mHasPendingWidgetGeometryChanges(false) {
65 if (gViewManagers == nullptr) {
66 // Create an array to hold a list of view managers
67 gViewManagers = new nsTArray<nsViewManager*>;
70 gViewManagers->AppendElement(this);
73 nsViewManager::~nsViewManager() {
74 if (mRootView) {
75 // Destroy any remaining views
76 mRootView->Destroy();
77 mRootView = nullptr;
80 if (!IsRootVM()) {
81 // We have a strong ref to mRootViewManager
82 NS_RELEASE(mRootViewManager);
85 NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
87 #ifdef DEBUG
88 bool removed =
89 #endif
90 gViewManagers->RemoveElement(this);
91 NS_ASSERTION(
92 removed,
93 "Viewmanager instance was not in the global list of viewmanagers");
95 if (gViewManagers->IsEmpty()) {
96 // There aren't any more view managers so
97 // release the global array of view managers
98 delete gViewManagers;
99 gViewManagers = nullptr;
102 MOZ_RELEASE_ASSERT(!mPresShell,
103 "Releasing nsViewManager without having called Destroy on "
104 "the PresShell!");
107 // We don't hold a reference to the presentation context because it
108 // holds a reference to us.
109 nsresult nsViewManager::Init(nsDeviceContext* aContext) {
110 MOZ_ASSERT(nullptr != aContext, "null ptr");
112 if (nullptr == aContext) {
113 return NS_ERROR_NULL_POINTER;
115 if (nullptr != mContext) {
116 return NS_ERROR_ALREADY_INITIALIZED;
118 mContext = aContext;
120 return NS_OK;
123 nsView* nsViewManager::CreateView(const nsRect& aBounds, nsView* aParent,
124 nsViewVisibility aVisibilityFlag) {
125 auto* v = new nsView(this, aVisibilityFlag);
126 v->SetParent(aParent);
127 v->SetPosition(aBounds.X(), aBounds.Y());
128 nsRect dim(0, 0, aBounds.Width(), aBounds.Height());
129 v->SetDimensions(dim, false);
130 return v;
133 void nsViewManager::SetRootView(nsView* aView) {
134 MOZ_ASSERT(!aView || aView->GetViewManager() == this,
135 "Unexpected viewmanager on root view");
137 // Do NOT destroy the current root view. It's the caller's responsibility
138 // to destroy it
139 mRootView = aView;
141 if (mRootView) {
142 nsView* parent = mRootView->GetParent();
143 if (parent) {
144 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
145 // no need to set mRootViewManager ourselves here.
146 parent->InsertChild(mRootView, nullptr);
147 } else {
148 InvalidateHierarchy();
151 mRootView->SetZIndex(false, 0);
153 // Else don't touch mRootViewManager
156 void nsViewManager::GetWindowDimensions(nscoord* aWidth, nscoord* aHeight) {
157 if (nullptr != mRootView) {
158 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
159 nsRect dim = mRootView->GetDimensions();
160 *aWidth = dim.Width();
161 *aHeight = dim.Height();
162 } else {
163 *aWidth = mDelayedResize.width;
164 *aHeight = mDelayedResize.height;
166 } else {
167 *aWidth = 0;
168 *aHeight = 0;
172 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight,
173 bool aDoReflow) {
174 nsRect oldDim = mRootView->GetDimensions();
175 nsRect newDim(0, 0, aWidth, aHeight);
176 // We care about resizes even when one dimension is already zero.
177 if (oldDim.IsEqualEdges(newDim)) {
178 return;
180 // Don't resize the widget. It is already being set elsewhere.
181 mRootView->SetDimensions(newDim, true, false);
182 if (RefPtr<PresShell> presShell = mPresShell) {
183 auto options = ResizeReflowOptions::NoOption;
184 if (!aDoReflow) {
185 options |= ResizeReflowOptions::SuppressReflow;
187 presShell->ResizeReflow(aWidth, aHeight, options);
191 bool nsViewManager::ShouldDelayResize() const {
192 MOZ_ASSERT(mRootView);
193 if (!mRootView->IsEffectivelyVisible() || !mPresShell ||
194 !mPresShell->IsVisible()) {
195 return true;
197 if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
198 if (rd->IsResizeSuppressed()) {
199 return true;
202 return false;
205 void nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight,
206 bool aDelayResize) {
207 if (mRootView) {
208 if (!ShouldDelayResize() && !aDelayResize) {
209 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
210 mDelayedResize != nsSize(aWidth, aHeight)) {
211 // We have a delayed resize; that now obsolete size may already have
212 // been flushed to the PresContext so we need to update the PresContext
213 // with the new size because if the new size is exactly the same as the
214 // root view's current size then DoSetWindowDimensions will not
215 // request a resize reflow (which would correct it). See bug 617076.
216 mDelayedResize = nsSize(aWidth, aHeight);
217 FlushDelayedResize(false);
219 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
220 DoSetWindowDimensions(aWidth, aHeight, /* aDoReflow = */ true);
221 } else {
222 mDelayedResize.SizeTo(aWidth, aHeight);
223 if (mPresShell) {
224 mPresShell->SetNeedStyleFlush();
225 mPresShell->SetNeedLayoutFlush();
231 void nsViewManager::FlushDelayedResize(bool aDoReflow) {
232 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
233 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height,
234 aDoReflow);
235 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
239 // Convert aIn from being relative to and in appunits of aFromView, to being
240 // relative to and in appunits of aToView.
241 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
242 nsView* aFromView, nsView* aToView) {
243 nsRegion out = aIn;
244 out.MoveBy(aFromView->GetOffsetTo(aToView));
245 out = out.ScaleToOtherAppUnitsRoundOut(
246 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
247 aToView->GetViewManager()->AppUnitsPerDevPixel());
248 return out;
251 nsView* nsViewManager::GetDisplayRootFor(nsView* aView) {
252 nsView* displayRoot = aView;
253 for (;;) {
254 nsView* displayParent = displayRoot->GetParent();
255 if (!displayParent) return displayRoot;
257 if (displayRoot->GetFloating() && !displayParent->GetFloating())
258 return displayRoot;
260 // If we have a combobox dropdown popup within a panel popup, both the view
261 // for the dropdown popup and its parent will be floating, so we need to
262 // distinguish this situation. We do this by looking for a widget. Any view
263 // with a widget is a display root.
264 nsIWidget* widget = displayRoot->GetWidget();
265 if (widget && widget->WindowType() == eWindowType_popup) {
266 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
267 "this should only happen with floating views that have "
268 "floating parents");
269 return displayRoot;
272 displayRoot = displayParent;
277 aRegion is given in device coordinates!!
278 aContext may be null, in which case layers should be used for
279 rendering.
281 void nsViewManager::Refresh(nsView* aView,
282 const LayoutDeviceIntRegion& aRegion) {
283 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
285 if (mPresShell && mPresShell->IsNeverPainting()) {
286 return;
289 // damageRegion is the damaged area, in twips, relative to the view origin
290 nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
292 // move region from widget coordinates into view coordinates
293 damageRegion.MoveBy(-aView->ViewToWidgetOffset());
295 if (damageRegion.IsEmpty()) {
296 #ifdef DEBUG_roc
297 nsRect viewRect = aView->GetDimensions();
298 nsRect damageRect = damageRegion.GetBounds();
299 printf_stderr(
300 "XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's "
301 "view (%d,%d,%d,%d)!\n",
302 damageRect.x, damageRect.y, damageRect.width, damageRect.height,
303 viewRect.x, viewRect.y, viewRect.width, viewRect.height);
304 #endif
305 return;
308 nsIWidget* widget = aView->GetWidget();
309 if (!widget) {
310 return;
313 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
314 if (IsPainting()) {
315 RootViewManager()->mRecursiveRefreshPending = true;
316 return;
320 nsAutoScriptBlocker scriptBlocker;
321 SetPainting(true);
323 NS_ASSERTION(GetDisplayRootFor(aView) == aView,
324 "Widgets that we paint must all be display roots");
326 if (RefPtr<PresShell> presShell = mPresShell) {
327 #ifdef MOZ_DUMP_PAINTING
328 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
329 printf_stderr("--COMPOSITE-- %p\n", presShell.get());
331 #endif
332 LayerManager* manager = widget->GetLayerManager();
333 if (!manager->NeedsWidgetInvalidation()) {
334 manager->FlushRendering();
335 } else {
336 presShell->Paint(aView, damageRegion, PaintFlags::PaintComposite);
338 #ifdef MOZ_DUMP_PAINTING
339 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
340 printf_stderr("--ENDCOMPOSITE--\n");
342 #endif
343 mozilla::StartupTimeline::RecordOnce(
344 mozilla::StartupTimeline::FIRST_PAINT);
347 SetPainting(false);
350 if (RootViewManager()->mRecursiveRefreshPending) {
351 RootViewManager()->mRecursiveRefreshPending = false;
352 InvalidateAllViews();
356 void nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
357 bool aFlushDirtyRegion) {
358 NS_ASSERTION(IsRootVM(), "Updates will be missed");
359 if (!aView) {
360 return;
363 RefPtr<PresShell> rootPresShell = mPresShell;
364 AutoTArray<nsCOMPtr<nsIWidget>, 1> widgets;
365 aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
366 for (uint32_t i = 0; i < widgets.Length(); ++i) {
367 nsView* view = nsView::GetViewFor(widgets[i]);
368 if (view) {
369 if (view->mNeedsWindowPropertiesSync) {
370 view->mNeedsWindowPropertiesSync = false;
371 if (nsViewManager* vm = view->GetViewManager()) {
372 if (PresShell* presShell = vm->GetPresShell()) {
373 presShell->SyncWindowProperties(view);
378 view = nsView::GetViewFor(widgets[i]);
379 if (view) {
380 view->ResetWidgetBounds(false, true);
383 if (rootPresShell->GetViewManager() != this) {
384 return; // presentation might have been torn down
386 if (aFlushDirtyRegion) {
387 nsAutoScriptBlocker scriptBlocker;
388 SetPainting(true);
389 for (uint32_t i = 0; i < widgets.Length(); ++i) {
390 nsIWidget* widget = widgets[i];
391 nsView* view = nsView::GetViewFor(widget);
392 if (view) {
393 RefPtr<nsViewManager> viewManager = view->GetViewManager();
394 viewManager->ProcessPendingUpdatesPaint(MOZ_KnownLive(widget));
397 SetPainting(false);
401 void nsViewManager::ProcessPendingUpdatesRecurse(
402 nsView* aView, AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets) {
403 if (mPresShell && mPresShell->IsNeverPainting()) {
404 return;
407 for (nsView* childView = aView->GetFirstChild(); childView;
408 childView = childView->GetNextSibling()) {
409 childView->GetViewManager()->ProcessPendingUpdatesRecurse(childView,
410 aWidgets);
413 nsIWidget* widget = aView->GetWidget();
414 if (widget) {
415 aWidgets.AppendElement(widget);
416 } else {
417 FlushDirtyRegionToWidget(aView);
421 void nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget) {
422 if (aWidget->NeedsPaint()) {
423 // If an ancestor widget was hidden and then shown, we could
424 // have a delayed resize to handle.
425 for (RefPtr<nsViewManager> vm = this; vm;
426 vm = vm->mRootView->GetParent()
427 ? vm->mRootView->GetParent()->GetViewManager()
428 : nullptr) {
429 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
430 vm->mRootView->IsEffectivelyVisible() && vm->mPresShell &&
431 vm->mPresShell->IsVisible()) {
432 vm->FlushDelayedResize(true);
435 nsView* view = nsView::GetViewFor(aWidget);
437 if (!view) {
438 NS_ERROR("FlushDelayedResize destroyed the nsView?");
439 return;
442 nsIWidgetListener* previousListener =
443 aWidget->GetPreviouslyAttachedWidgetListener();
445 if (previousListener && previousListener != view &&
446 view->IsPrimaryFramePaintSuppressed()) {
447 return;
450 if (RefPtr<PresShell> presShell = mPresShell) {
451 #ifdef MOZ_DUMP_PAINTING
452 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
453 printf_stderr(
454 "---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
455 presShell.get(), view, aWidget);
457 #endif
459 presShell->Paint(view, nsRegion(), PaintFlags::PaintLayers);
460 view->SetForcedRepaint(false);
462 #ifdef MOZ_DUMP_PAINTING
463 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
464 printf_stderr("---- PAINT END ----\n");
466 #endif
469 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
472 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView) {
473 NS_ASSERTION(aView->GetViewManager() == this,
474 "FlushDirtyRegionToWidget called on view we don't own");
476 if (!aView->HasNonEmptyDirtyRegion()) return;
478 nsRegion* dirtyRegion = aView->GetDirtyRegion();
479 nsView* nearestViewWithWidget = aView;
480 while (!nearestViewWithWidget->HasWidget() &&
481 nearestViewWithWidget->GetParent()) {
482 nearestViewWithWidget = nearestViewWithWidget->GetParent();
484 nsRegion r =
485 ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
487 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
488 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
489 dirtyRegion->SetEmpty();
492 void nsViewManager::InvalidateView(nsView* aView) {
493 // Mark the entire view as damaged
494 InvalidateView(aView, aView->GetDimensions());
497 static void AddDirtyRegion(nsView* aView, const nsRegion& aDamagedRegion) {
498 nsRegion* dirtyRegion = aView->GetDirtyRegion();
499 if (!dirtyRegion) return;
501 dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
502 dirtyRegion->SimplifyOutward(8);
505 void nsViewManager::PostPendingUpdate() {
506 nsViewManager* rootVM = RootViewManager();
507 rootVM->mHasPendingWidgetGeometryChanges = true;
508 if (rootVM->mPresShell) {
509 rootVM->mPresShell->ScheduleViewManagerFlush();
514 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
515 * every widget child of aWidgetView, plus aWidgetView's own widget
517 void nsViewManager::InvalidateWidgetArea(nsView* aWidgetView,
518 const nsRegion& aDamagedRegion) {
519 NS_ASSERTION(aWidgetView->GetViewManager() == this,
520 "InvalidateWidgetArea called on view we don't own");
521 nsIWidget* widget = aWidgetView->GetWidget();
523 #if 0
524 nsRect dbgBounds = aDamagedRegion.GetBounds();
525 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
526 aWidgetView, aWidgetView->IsAttachedToTopLevel(),
527 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
528 #endif
530 // If the widget is hidden, it don't cover nothing
531 if (widget && !widget->IsVisible()) {
532 return;
535 if (!widget) {
536 // The root view or a scrolling view might not have a widget
537 // (for example, during printing). We get here when we scroll
538 // during printing to show selected options in a listbox, for example.
539 return;
542 if (!aDamagedRegion.IsEmpty()) {
543 for (auto iter = aDamagedRegion.RectIter(); !iter.Done(); iter.Next()) {
544 LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get());
545 widget->Invalidate(bounds);
550 static bool ShouldIgnoreInvalidation(nsViewManager* aVM) {
551 while (aVM) {
552 PresShell* presShell = aVM->GetPresShell();
553 if (!presShell || presShell->ShouldIgnoreInvalidation()) {
554 return true;
556 nsView* view = aVM->GetRootView()->GetParent();
557 aVM = view ? view->GetViewManager() : nullptr;
559 return false;
562 void nsViewManager::InvalidateView(nsView* aView, const nsRect& aRect) {
563 // If painting is suppressed in the presshell or an ancestor drop all
564 // invalidates, it will invalidate everything when it unsuppresses.
565 if (ShouldIgnoreInvalidation(this)) {
566 return;
569 InvalidateViewNoSuppression(aView, aRect);
572 void nsViewManager::InvalidateViewNoSuppression(nsView* aView,
573 const nsRect& aRect) {
574 MOZ_ASSERT(nullptr != aView, "null view");
576 NS_ASSERTION(aView->GetViewManager() == this,
577 "InvalidateViewNoSuppression called on view we don't own");
579 nsRect damagedRect(aRect);
580 if (damagedRect.IsEmpty()) {
581 return;
584 nsView* displayRoot = GetDisplayRootFor(aView);
585 nsViewManager* displayRootVM = displayRoot->GetViewManager();
586 // Propagate the update to the displayRoot, since iframes, for example,
587 // can overlap each other and be translucent. So we have to possibly
588 // invalidate our rect in each of the widgets we have lying about.
589 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
590 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
591 int32_t APD = AppUnitsPerDevPixel();
592 damagedRect = damagedRect.ScaleToOtherAppUnitsRoundOut(APD, rootAPD);
594 // accumulate this rectangle in the view's dirty region, so we can
595 // process it later.
596 AddDirtyRegion(displayRoot, nsRegion(damagedRect));
599 void nsViewManager::InvalidateAllViews() {
600 if (RootViewManager() != this) {
601 return RootViewManager()->InvalidateAllViews();
604 InvalidateViews(mRootView);
607 void nsViewManager::InvalidateViews(nsView* aView) {
608 // Invalidate this view.
609 InvalidateView(aView);
611 // Invalidate all children as well.
612 nsView* childView = aView->GetFirstChild();
613 while (nullptr != childView) {
614 childView->GetViewManager()->InvalidateViews(childView);
615 childView = childView->GetNextSibling();
619 void nsViewManager::WillPaintWindow(nsIWidget* aWidget) {
620 if (aWidget) {
621 nsView* view = nsView::GetViewFor(aWidget);
622 LayerManager* manager = aWidget->GetLayerManager();
623 if (view &&
624 (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) {
625 ProcessPendingUpdates();
626 // Re-get the view pointer here since the ProcessPendingUpdates might have
627 // destroyed it during CallWillPaintOnObservers.
628 view = nsView::GetViewFor(aWidget);
629 if (view) {
630 view->SetForcedRepaint(false);
636 bool nsViewManager::PaintWindow(nsIWidget* aWidget,
637 const LayoutDeviceIntRegion& aRegion) {
638 if (!aWidget || !mContext) return false;
640 NS_ASSERTION(
641 IsPaintingAllowed(),
642 "shouldn't be receiving paint events while painting is disallowed!");
644 // Get the view pointer here since NS_WILL_PAINT might have
645 // destroyed it during CallWillPaintOnObservers (bug 378273).
646 nsView* view = nsView::GetViewFor(aWidget);
647 if (view && !aRegion.IsEmpty()) {
648 Refresh(view, aRegion);
651 return true;
654 void nsViewManager::DidPaintWindow() {
655 if (RefPtr<PresShell> presShell = mPresShell) {
656 presShell->DidPaintWindow();
660 void nsViewManager::DispatchEvent(WidgetGUIEvent* aEvent, nsView* aView,
661 nsEventStatus* aStatus) {
662 AUTO_PROFILER_LABEL("nsViewManager::DispatchEvent", OTHER);
664 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
665 if ((mouseEvent &&
666 // Ignore mouse events that we synthesize.
667 mouseEvent->mReason == WidgetMouseEvent::eReal &&
668 // Ignore mouse exit and enter (we'll get moves if the user
669 // is really moving the mouse) since we get them when we
670 // create and destroy widgets.
671 mouseEvent->mMessage != eMouseExitFromWidget &&
672 mouseEvent->mMessage != eMouseEnterIntoWidget) ||
673 aEvent->HasKeyEventMessage() || aEvent->HasIMEEventMessage()) {
674 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
677 // Find the view whose coordinates system we're in.
678 nsView* view = aView;
679 bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
680 if (dispatchUsingCoordinates) {
681 // Will dispatch using coordinates. Pretty bogus but it's consistent
682 // with what presshell does.
683 view = GetDisplayRootFor(view);
686 // If the view has no frame, look for a view that does.
687 nsIFrame* frame = view->GetFrame();
688 if (!frame && (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
689 aEvent->IsIMERelatedEvent())) {
690 while (view && !view->GetFrame()) {
691 view = view->GetParent();
694 if (view) {
695 frame = view->GetFrame();
699 if (nullptr != frame) {
700 // Hold a refcount to the presshell. The continued existence of the
701 // presshell will delay deletion of this view hierarchy should the event
702 // want to cause its destruction in, say, some JavaScript event handler.
703 if (RefPtr<PresShell> presShell = view->GetViewManager()->GetPresShell()) {
704 presShell->HandleEvent(frame, aEvent, false, aStatus);
705 return;
709 *aStatus = nsEventStatus_eIgnore;
712 // Recursively reparent widgets if necessary
714 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget* aNewWidget) {
715 MOZ_ASSERT(aNewWidget, "null widget");
717 if (aView->HasWidget()) {
718 // Check to see if the parent widget is the
719 // same as the new parent. If not then reparent
720 // the widget, otherwise there is nothing more
721 // to do for the view and its descendants
722 nsIWidget* widget = aView->GetWidget();
723 nsIWidget* parentWidget = widget->GetParent();
724 if (parentWidget) {
725 // Child widget
726 if (parentWidget != aNewWidget) {
727 widget->SetParent(aNewWidget);
729 } else {
730 // Toplevel widget (popup, dialog, etc)
731 widget->ReparentNativeWidget(aNewWidget);
733 return;
736 // Need to check each of the views children to see
737 // if they have a widget and reparent it.
739 for (nsView* kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
740 ReparentChildWidgets(kid, aNewWidget);
744 // Reparent a view and its descendant views widgets if necessary
746 void nsViewManager::ReparentWidgets(nsView* aView, nsView* aParent) {
747 MOZ_ASSERT(aParent, "Must have a parent");
748 MOZ_ASSERT(aView, "Must have a view");
750 // Quickly determine whether the view has pre-existing children or a
751 // widget. In most cases the view will not have any pre-existing
752 // children when this is called. Only in the case
753 // where a view has been reparented by removing it from
754 // a reinserting it into a new location in the view hierarchy do we
755 // have to consider reparenting the existing widgets for the view and
756 // it's descendants.
757 if (aView->HasWidget() || aView->GetFirstChild()) {
758 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
759 if (parentWidget) {
760 ReparentChildWidgets(aView, parentWidget);
761 return;
763 NS_WARNING("Can not find a widget for the parent view");
767 void nsViewManager::InsertChild(nsView* aParent, nsView* aChild,
768 nsView* aSibling, bool aAfter) {
769 MOZ_ASSERT(nullptr != aParent, "null ptr");
770 MOZ_ASSERT(nullptr != aChild, "null ptr");
771 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
772 "tried to insert view with invalid sibling");
773 NS_ASSERTION(!IsViewInserted(aChild),
774 "tried to insert an already-inserted view");
776 if ((nullptr != aParent) && (nullptr != aChild)) {
777 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid'
778 // in document order, otherwise after 'kid' (i.e. before 'kid' in document
779 // order).
781 if (nullptr == aSibling) {
782 if (aAfter) {
783 // insert at end of document order, i.e., before first view
784 // this is the common case, by far
785 aParent->InsertChild(aChild, nullptr);
786 ReparentWidgets(aChild, aParent);
787 } else {
788 // insert at beginning of document order, i.e., after last view
789 nsView* kid = aParent->GetFirstChild();
790 nsView* prev = nullptr;
791 while (kid) {
792 prev = kid;
793 kid = kid->GetNextSibling();
795 // prev is last view or null if there are no children
796 aParent->InsertChild(aChild, prev);
797 ReparentWidgets(aChild, aParent);
799 } else {
800 nsView* kid = aParent->GetFirstChild();
801 nsView* prev = nullptr;
802 while (kid && aSibling != kid) {
803 // get the next sibling view
804 prev = kid;
805 kid = kid->GetNextSibling();
807 NS_ASSERTION(kid != nullptr, "couldn't find sibling in child list");
808 if (aAfter) {
809 // insert after 'kid' in document order, i.e. before in view order
810 aParent->InsertChild(aChild, prev);
811 ReparentWidgets(aChild, aParent);
812 } else {
813 // insert before 'kid' in document order, i.e. after in view order
814 aParent->InsertChild(aChild, kid);
815 ReparentWidgets(aChild, aParent);
819 // if the parent view is marked as "floating", make the newly added view
820 // float as well.
821 if (aParent->GetFloating()) aChild->SetFloating(true);
825 void nsViewManager::RemoveChild(nsView* aChild) {
826 NS_ASSERTION(aChild, "aChild must not be null");
828 nsView* parent = aChild->GetParent();
830 if (nullptr != parent) {
831 NS_ASSERTION(
832 aChild->GetViewManager() == this || parent->GetViewManager() == this,
833 "wrong view manager");
834 parent->RemoveChild(aChild);
838 void nsViewManager::MoveViewTo(nsView* aView, nscoord aX, nscoord aY) {
839 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
840 aView->SetPosition(aX, aY);
843 void nsViewManager::ResizeView(nsView* aView, const nsRect& aRect,
844 bool aRepaintExposedAreaOnly) {
845 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
847 nsRect oldDimensions = aView->GetDimensions();
848 if (!oldDimensions.IsEqualEdges(aRect)) {
849 aView->SetDimensions(aRect, true);
852 // Note that if layout resizes the view and the view has a custom clip
853 // region set, then we expect layout to update the clip region too. Thus
854 // in the case where mClipRect has been optimized away to just be a null
855 // pointer, and this resize is implicitly changing the clip rect, it's OK
856 // because layout will change it back again if necessary.
859 void nsViewManager::SetViewFloating(nsView* aView, bool aFloating) {
860 NS_ASSERTION(!(nullptr == aView), "no view");
862 aView->SetFloating(aFloating);
865 void nsViewManager::SetViewVisibility(nsView* aView,
866 nsViewVisibility aVisible) {
867 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
869 if (aVisible != aView->GetVisibility()) {
870 aView->SetVisibility(aVisible);
874 bool nsViewManager::IsViewInserted(nsView* aView) {
875 if (mRootView == aView) {
876 return true;
878 if (aView->GetParent() == nullptr) {
879 return false;
881 nsView* view = aView->GetParent()->GetFirstChild();
882 while (view != nullptr) {
883 if (view == aView) {
884 return true;
886 view = view->GetNextSibling();
888 return false;
891 void nsViewManager::SetViewZIndex(nsView* aView, bool aAutoZIndex,
892 int32_t aZIndex) {
893 NS_ASSERTION((aView != nullptr), "no view");
895 // don't allow the root view's z-index to be changed. It should always be
896 // zero. This could be removed and replaced with a style rule, or just removed
897 // altogether, with interesting consequences
898 if (aView == mRootView) {
899 return;
902 if (aAutoZIndex) {
903 aZIndex = 0;
906 aView->SetZIndex(aAutoZIndex, aZIndex);
909 nsViewManager* nsViewManager::IncrementDisableRefreshCount() {
910 if (!IsRootVM()) {
911 return RootViewManager()->IncrementDisableRefreshCount();
914 ++mRefreshDisableCount;
916 return this;
919 void nsViewManager::DecrementDisableRefreshCount() {
920 NS_ASSERTION(IsRootVM(), "Should only be called on root");
921 --mRefreshDisableCount;
922 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
925 void nsViewManager::GetRootWidget(nsIWidget** aWidget) {
926 if (!mRootView) {
927 *aWidget = nullptr;
928 return;
930 if (mRootView->HasWidget()) {
931 *aWidget = mRootView->GetWidget();
932 NS_ADDREF(*aWidget);
933 return;
935 if (mRootView->GetParent()) {
936 mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
937 return;
939 *aWidget = nullptr;
942 LayoutDeviceIntRect nsViewManager::ViewToWidget(nsView* aView,
943 const nsRect& aRect) const {
944 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
946 // account for the view's origin not lining up with the widget's
947 nsRect rect = aRect + aView->ViewToWidgetOffset();
949 // finally, convert to device coordinates.
950 return LayoutDeviceIntRect::FromUnknownRect(
951 rect.ToOutsidePixels(AppUnitsPerDevPixel()));
954 void nsViewManager::IsPainting(bool& aIsPainting) {
955 aIsPainting = IsPainting();
958 void nsViewManager::ProcessPendingUpdates() {
959 if (!IsRootVM()) {
960 RefPtr<nsViewManager> rootViewManager = RootViewManager();
961 rootViewManager->ProcessPendingUpdates();
962 return;
965 // Flush things like reflows by calling WillPaint on observer presShells.
966 if (mPresShell) {
967 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
969 RefPtr<nsViewManager> strongThis(this);
970 CallWillPaintOnObservers();
972 ProcessPendingUpdatesForView(mRootView, true);
973 if (mPresShell) {
974 if (nsPresContext* pc = mPresShell->GetPresContext()) {
975 pc->RefreshDriver()->ClearHasScheduleFlush();
981 void nsViewManager::UpdateWidgetGeometry() {
982 if (!IsRootVM()) {
983 RefPtr<nsViewManager> rootViewManager = RootViewManager();
984 rootViewManager->UpdateWidgetGeometry();
985 return;
988 if (mHasPendingWidgetGeometryChanges) {
989 mHasPendingWidgetGeometryChanges = false;
990 ProcessPendingUpdatesForView(mRootView, false);
994 void nsViewManager::CallWillPaintOnObservers() {
995 MOZ_ASSERT(IsRootVM(), "Must be root VM for this to be called!");
997 if (NS_WARN_IF(!gViewManagers)) {
998 return;
1001 uint32_t index;
1002 for (index = 0; index < gViewManagers->Length(); index++) {
1003 nsViewManager* vm = gViewManagers->ElementAt(index);
1004 if (vm->RootViewManager() == this) {
1005 // One of our kids.
1006 if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
1007 if (RefPtr<PresShell> presShell = vm->GetPresShell()) {
1008 presShell->WillPaint();
1015 void nsViewManager::GetLastUserEventTime(uint32_t& aTime) {
1016 aTime = gLastUserEventTime;
1019 void nsViewManager::InvalidateHierarchy() {
1020 if (mRootView) {
1021 if (!IsRootVM()) {
1022 NS_RELEASE(mRootViewManager);
1024 nsView* parent = mRootView->GetParent();
1025 if (parent) {
1026 mRootViewManager = parent->GetViewManager()->RootViewManager();
1027 NS_ADDREF(mRootViewManager);
1028 NS_ASSERTION(mRootViewManager != this,
1029 "Root view had a parent, but it has the same view manager");
1030 } else {
1031 mRootViewManager = this;