Bug 1638116 [wpt PR 23612] - [Lazyload]: Split and add base-URL tests, clean up other...
[gecko.git] / view / nsViewManager.cpp
blob74e313307cf60a91bb3ffbc819ebeec633dec309
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsViewManager.h"
8 #include "mozilla/MouseEvents.h"
9 #include "mozilla/PresShell.h"
10 #include "mozilla/PresShellInlines.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/StartupTimeline.h"
13 #include "mozilla/dom/Document.h"
14 #include "nsGfxCIID.h"
15 #include "nsView.h"
16 #include "nsCOMPtr.h"
17 #include "nsRegion.h"
18 #include "nsCOMArray.h"
19 #include "nsIPluginWidget.h"
20 #include "nsXULPopupManager.h"
21 #include "nsPresContext.h"
22 #include "GeckoProfiler.h"
23 #include "nsRefreshDriver.h"
24 #include "nsContentUtils.h" // for nsAutoScriptBlocker
25 #include "nsLayoutUtils.h"
26 #include "Layers.h"
27 #include "gfxPlatform.h"
29 /**
30 XXX TODO XXX
32 DeCOMify newly private methods
33 Optimize view storage
36 /**
37 A note about platform assumptions:
39 We assume that a widget is z-ordered on top of its parent.
41 We do NOT assume anything about the relative z-ordering of sibling widgets.
42 Even though we ask for a specific z-order, we don't assume that widget
43 z-ordering actually works.
46 using namespace mozilla;
47 using namespace mozilla::layers;
49 #define NSCOORD_NONE INT32_MIN
51 #undef DEBUG_MOUSE_LOCATION
53 // Weakly held references to all of the view managers
54 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) {
66 if (gViewManagers == nullptr) {
67 // Create an array to hold a list of view managers
68 gViewManagers = new nsTArray<nsViewManager*>;
71 gViewManagers->AppendElement(this);
74 nsViewManager::~nsViewManager() {
75 if (mRootView) {
76 // Destroy any remaining views
77 mRootView->Destroy();
78 mRootView = nullptr;
81 if (!IsRootVM()) {
82 // We have a strong ref to mRootViewManager
83 NS_RELEASE(mRootViewManager);
86 NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
88 #ifdef DEBUG
89 bool removed =
90 #endif
91 gViewManagers->RemoveElement(this);
92 NS_ASSERTION(
93 removed,
94 "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,
104 "Releasing nsViewManager without having called Destroy on "
105 "the PresShell!");
108 // We don't hold a reference to the presentation context because it
109 // holds a reference to us.
110 nsresult 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* nsViewManager::CreateView(const nsRect& aBounds, nsView* aParent,
125 nsViewVisibility aVisibilityFlag) {
126 auto* v = new nsView(this, aVisibilityFlag);
127 v->SetParent(aParent);
128 v->SetPosition(aBounds.X(), aBounds.Y());
129 nsRect dim(0, 0, aBounds.Width(), aBounds.Height());
130 v->SetDimensions(dim, false);
131 return v;
134 void nsViewManager::SetRootView(nsView* aView) {
135 MOZ_ASSERT(!aView || aView->GetViewManager() == this,
136 "Unexpected viewmanager on root view");
138 // Do NOT destroy the current root view. It's the caller's responsibility
139 // to destroy it
140 mRootView = aView;
142 if (mRootView) {
143 nsView* parent = mRootView->GetParent();
144 if (parent) {
145 // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
146 // no need to set mRootViewManager ourselves here.
147 parent->InsertChild(mRootView, nullptr);
148 } else {
149 InvalidateHierarchy();
152 mRootView->SetZIndex(false, 0);
154 // Else don't touch mRootViewManager
157 void nsViewManager::GetWindowDimensions(nscoord* aWidth, nscoord* aHeight) {
158 if (nullptr != mRootView) {
159 if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
160 nsRect dim = mRootView->GetDimensions();
161 *aWidth = dim.Width();
162 *aHeight = dim.Height();
163 } else {
164 *aWidth = mDelayedResize.width;
165 *aHeight = mDelayedResize.height;
167 } else {
168 *aWidth = 0;
169 *aHeight = 0;
173 void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight,
174 bool aDoReflow) {
175 nsRect oldDim = mRootView->GetDimensions();
176 nsRect newDim(0, 0, aWidth, aHeight);
177 // We care about resizes even when one dimension is already zero.
178 if (oldDim.IsEqualEdges(newDim)) {
179 return;
181 // Don't resize the widget. It is already being set elsewhere.
182 mRootView->SetDimensions(newDim, true, false);
183 if (RefPtr<PresShell> presShell = mPresShell) {
184 auto options = ResizeReflowOptions::NoOption;
185 if (!aDoReflow) {
186 options |= ResizeReflowOptions::SuppressReflow;
188 presShell->ResizeReflow(aWidth, aHeight, options);
192 bool nsViewManager::ShouldDelayResize() const {
193 MOZ_ASSERT(mRootView);
194 if (!mRootView->IsEffectivelyVisible() || !mPresShell ||
195 !mPresShell->IsVisible()) {
196 return true;
198 if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
199 if (rd->IsResizeSuppressed()) {
200 return true;
203 return false;
206 void nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight,
207 bool aDelayResize) {
208 if (mRootView) {
209 if (!ShouldDelayResize() && !aDelayResize) {
210 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
211 mDelayedResize != nsSize(aWidth, aHeight)) {
212 // We have a delayed resize; that now obsolete size may already have
213 // been flushed to the PresContext so we need to update the PresContext
214 // with the new size because if the new size is exactly the same as the
215 // root view's current size then DoSetWindowDimensions will not
216 // request a resize reflow (which would correct it). See bug 617076.
217 mDelayedResize = nsSize(aWidth, aHeight);
218 FlushDelayedResize(false);
220 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
221 DoSetWindowDimensions(aWidth, aHeight, /* aDoReflow = */ true);
222 } else {
223 mDelayedResize.SizeTo(aWidth, aHeight);
224 if (mPresShell) {
225 mPresShell->SetNeedStyleFlush();
226 mPresShell->SetNeedLayoutFlush();
232 void nsViewManager::FlushDelayedResize(bool aDoReflow) {
233 if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
234 DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height,
235 aDoReflow);
236 mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
240 // Convert aIn from being relative to and in appunits of aFromView, to being
241 // relative to and in appunits of aToView.
242 static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
243 nsView* aFromView, nsView* aToView) {
244 nsRegion out = aIn;
245 out.MoveBy(aFromView->GetOffsetTo(aToView));
246 out = out.ScaleToOtherAppUnitsRoundOut(
247 aFromView->GetViewManager()->AppUnitsPerDevPixel(),
248 aToView->GetViewManager()->AppUnitsPerDevPixel());
249 return out;
252 nsView* nsViewManager::GetDisplayRootFor(nsView* aView) {
253 nsView* displayRoot = aView;
254 for (;;) {
255 nsView* displayParent = displayRoot->GetParent();
256 if (!displayParent) return displayRoot;
258 if (displayRoot->GetFloating() && !displayParent->GetFloating())
259 return displayRoot;
261 // If we have a combobox dropdown popup within a panel popup, both the view
262 // for the dropdown popup and its parent will be floating, so we need to
263 // distinguish this situation. We do this by looking for a widget. Any view
264 // with a widget is a display root, except for plugins.
265 nsIWidget* widget = displayRoot->GetWidget();
266 if (widget && widget->WindowType() == eWindowType_popup) {
267 NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
268 "this should only happen with floating views that have "
269 "floating parents");
270 return displayRoot;
273 displayRoot = displayParent;
278 aRegion is given in device coordinates!!
279 aContext may be null, in which case layers should be used for
280 rendering.
282 void nsViewManager::Refresh(nsView* aView,
283 const LayoutDeviceIntRegion& aRegion) {
284 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
286 if (mPresShell && mPresShell->IsNeverPainting()) {
287 return;
290 // damageRegion is the damaged area, in twips, relative to the view origin
291 nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
293 // move region from widget coordinates into view coordinates
294 damageRegion.MoveBy(-aView->ViewToWidgetOffset());
296 if (damageRegion.IsEmpty()) {
297 #ifdef DEBUG_roc
298 nsRect viewRect = aView->GetDimensions();
299 nsRect damageRect = damageRegion.GetBounds();
300 printf_stderr(
301 "XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's "
302 "view (%d,%d,%d,%d)!\n",
303 damageRect.x, damageRect.y, damageRect.width, damageRect.height,
304 viewRect.x, viewRect.y, viewRect.width, viewRect.height);
305 #endif
306 return;
309 nsIWidget* widget = aView->GetWidget();
310 if (!widget) {
311 return;
314 NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
315 if (IsPainting()) {
316 RootViewManager()->mRecursiveRefreshPending = true;
317 return;
321 nsAutoScriptBlocker scriptBlocker;
322 SetPainting(true);
324 NS_ASSERTION(GetDisplayRootFor(aView) == aView,
325 "Widgets that we paint must all be display roots");
327 if (RefPtr<PresShell> presShell = mPresShell) {
328 #ifdef MOZ_DUMP_PAINTING
329 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
330 printf_stderr("--COMPOSITE-- %p\n", presShell.get());
332 #endif
333 LayerManager* manager = widget->GetLayerManager();
334 if (!manager->NeedsWidgetInvalidation()) {
335 manager->FlushRendering();
336 } else {
337 presShell->Paint(aView, damageRegion, PaintFlags::PaintComposite);
339 #ifdef MOZ_DUMP_PAINTING
340 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
341 printf_stderr("--ENDCOMPOSITE--\n");
343 #endif
344 mozilla::StartupTimeline::RecordOnce(
345 mozilla::StartupTimeline::FIRST_PAINT);
348 SetPainting(false);
351 if (RootViewManager()->mRecursiveRefreshPending) {
352 RootViewManager()->mRecursiveRefreshPending = false;
353 InvalidateAllViews();
357 void nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
358 bool aFlushDirtyRegion) {
359 NS_ASSERTION(IsRootVM(), "Updates will be missed");
360 if (!aView) {
361 return;
364 RefPtr<PresShell> rootPresShell = mPresShell;
365 AutoTArray<nsCOMPtr<nsIWidget>, 1> widgets;
366 aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
367 for (uint32_t i = 0; i < widgets.Length(); ++i) {
368 nsView* view = nsView::GetViewFor(widgets[i]);
369 if (view) {
370 if (view->mNeedsWindowPropertiesSync) {
371 view->mNeedsWindowPropertiesSync = false;
372 if (nsViewManager* vm = view->GetViewManager()) {
373 if (PresShell* presShell = vm->GetPresShell()) {
374 presShell->SyncWindowProperties(view);
379 view = nsView::GetViewFor(widgets[i]);
380 if (view) {
381 view->ResetWidgetBounds(false, true);
384 if (rootPresShell->GetViewManager() != this) {
385 return; // presentation might have been torn down
387 if (aFlushDirtyRegion) {
388 nsAutoScriptBlocker scriptBlocker;
389 SetPainting(true);
390 for (uint32_t i = 0; i < widgets.Length(); ++i) {
391 nsIWidget* widget = widgets[i];
392 nsView* view = nsView::GetViewFor(widget);
393 if (view) {
394 RefPtr<nsViewManager> viewManager = view->GetViewManager();
395 viewManager->ProcessPendingUpdatesPaint(MOZ_KnownLive(widget));
398 SetPainting(false);
402 void nsViewManager::ProcessPendingUpdatesRecurse(
403 nsView* aView, AutoTArray<nsCOMPtr<nsIWidget>, 1>& aWidgets) {
404 if (mPresShell && mPresShell->IsNeverPainting()) {
405 return;
408 for (nsView* childView = aView->GetFirstChild(); childView;
409 childView = childView->GetNextSibling()) {
410 childView->GetViewManager()->ProcessPendingUpdatesRecurse(childView,
411 aWidgets);
414 nsIWidget* widget = aView->GetWidget();
415 if (widget) {
416 aWidgets.AppendElement(widget);
417 } else {
418 FlushDirtyRegionToWidget(aView);
422 void nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget) {
423 if (aWidget->NeedsPaint()) {
424 // If an ancestor widget was hidden and then shown, we could
425 // have a delayed resize to handle.
426 for (RefPtr<nsViewManager> vm = this; vm;
427 vm = vm->mRootView->GetParent()
428 ? vm->mRootView->GetParent()->GetViewManager()
429 : nullptr) {
430 if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
431 vm->mRootView->IsEffectivelyVisible() && vm->mPresShell &&
432 vm->mPresShell->IsVisible()) {
433 vm->FlushDelayedResize(true);
436 nsView* view = nsView::GetViewFor(aWidget);
438 if (!view) {
439 NS_ERROR("FlushDelayedResize destroyed the nsView?");
440 return;
443 nsIWidgetListener* previousListener =
444 aWidget->GetPreviouslyAttachedWidgetListener();
446 if (previousListener && previousListener != view &&
447 view->IsPrimaryFramePaintSuppressed()) {
448 return;
451 if (RefPtr<PresShell> presShell = mPresShell) {
452 #ifdef MOZ_DUMP_PAINTING
453 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
454 printf_stderr(
455 "---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
456 presShell.get(), view, aWidget);
458 #endif
460 presShell->Paint(view, nsRegion(), PaintFlags::PaintLayers);
461 view->SetForcedRepaint(false);
463 #ifdef MOZ_DUMP_PAINTING
464 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
465 printf_stderr("---- PAINT END ----\n");
467 #endif
470 FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
473 void nsViewManager::FlushDirtyRegionToWidget(nsView* aView) {
474 NS_ASSERTION(aView->GetViewManager() == this,
475 "FlushDirtyRegionToWidget called on view we don't own");
477 if (!aView->HasNonEmptyDirtyRegion()) return;
479 nsRegion* dirtyRegion = aView->GetDirtyRegion();
480 nsView* nearestViewWithWidget = aView;
481 while (!nearestViewWithWidget->HasWidget() &&
482 nearestViewWithWidget->GetParent()) {
483 nearestViewWithWidget = nearestViewWithWidget->GetParent();
485 nsRegion r =
486 ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
488 nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
489 widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
490 dirtyRegion->SetEmpty();
493 void nsViewManager::InvalidateView(nsView* aView) {
494 // Mark the entire view as damaged
495 InvalidateView(aView, aView->GetDimensions());
498 static void AddDirtyRegion(nsView* aView, const nsRegion& aDamagedRegion) {
499 nsRegion* dirtyRegion = aView->GetDirtyRegion();
500 if (!dirtyRegion) return;
502 dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
503 dirtyRegion->SimplifyOutward(8);
506 void nsViewManager::PostPendingUpdate() {
507 nsViewManager* rootVM = RootViewManager();
508 rootVM->mHasPendingWidgetGeometryChanges = true;
509 if (rootVM->mPresShell) {
510 rootVM->mPresShell->ScheduleViewManagerFlush();
515 * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
516 * every widget child of aWidgetView, plus aWidgetView's own widget
518 void nsViewManager::InvalidateWidgetArea(nsView* aWidgetView,
519 const nsRegion& aDamagedRegion) {
520 NS_ASSERTION(aWidgetView->GetViewManager() == this,
521 "InvalidateWidgetArea called on view we don't own");
522 nsIWidget* widget = aWidgetView->GetWidget();
524 #if 0
525 nsRect dbgBounds = aDamagedRegion.GetBounds();
526 printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
527 aWidgetView, aWidgetView->IsAttachedToTopLevel(),
528 widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
529 #endif
531 // If the widget is hidden, it don't cover nothing
532 if (widget && !widget->IsVisible()) {
533 return;
536 if (!widget) {
537 // The root view or a scrolling view might not have a widget
538 // (for example, during printing). We get here when we scroll
539 // during printing to show selected options in a listbox, for example.
540 return;
543 // Update all child widgets with the damage. In the process,
544 // accumulate the union of all the child widget areas, or at least
545 // some subset of that.
546 nsRegion children;
547 if (widget->GetTransparencyMode() != eTransparencyTransparent) {
548 for (nsIWidget* childWidget = widget->GetFirstChild(); childWidget;
549 childWidget = childWidget->GetNextSibling()) {
550 nsView* view = nsView::GetViewFor(childWidget);
551 NS_ASSERTION(view != aWidgetView, "will recur infinitely");
552 nsWindowType type = childWidget->WindowType();
553 if (view && childWidget->IsVisible() && type != eWindowType_popup) {
554 NS_ASSERTION(childWidget->IsPlugin(),
555 "Only plugin or popup widgets can be children!");
557 // We do not need to invalidate in plugin widgets, but we should
558 // exclude them from the invalidation region IF we're not on
559 // Mac. On Mac we need to draw under plugin widgets, because
560 // plugin widgets are basically invisible
561 #ifndef XP_MACOSX
562 // GetBounds should compensate for chrome on a toplevel widget
563 LayoutDeviceIntRect bounds = childWidget->GetBounds();
565 nsTArray<LayoutDeviceIntRect> clipRects;
566 childWidget->GetWindowClipRegion(&clipRects);
567 for (uint32_t i = 0; i < clipRects.Length(); ++i) {
568 nsRect rr = LayoutDeviceIntRect::ToAppUnits(
569 clipRects[i] + bounds.TopLeft(), AppUnitsPerDevPixel());
570 children.Or(children, rr - aWidgetView->ViewToWidgetOffset());
571 children.SimplifyInward(20);
573 #endif
578 nsRegion leftOver;
579 leftOver.Sub(aDamagedRegion, children);
581 if (!leftOver.IsEmpty()) {
582 for (auto iter = leftOver.RectIter(); !iter.Done(); iter.Next()) {
583 LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get());
584 widget->Invalidate(bounds);
589 static bool ShouldIgnoreInvalidation(nsViewManager* aVM) {
590 while (aVM) {
591 PresShell* presShell = aVM->GetPresShell();
592 if (!presShell || presShell->ShouldIgnoreInvalidation()) {
593 return true;
595 nsView* view = aVM->GetRootView()->GetParent();
596 aVM = view ? view->GetViewManager() : nullptr;
598 return false;
601 void nsViewManager::InvalidateView(nsView* aView, const nsRect& aRect) {
602 // If painting is suppressed in the presshell or an ancestor drop all
603 // invalidates, it will invalidate everything when it unsuppresses.
604 if (ShouldIgnoreInvalidation(this)) {
605 return;
608 InvalidateViewNoSuppression(aView, aRect);
611 void nsViewManager::InvalidateViewNoSuppression(nsView* aView,
612 const nsRect& aRect) {
613 MOZ_ASSERT(nullptr != aView, "null view");
615 NS_ASSERTION(aView->GetViewManager() == this,
616 "InvalidateViewNoSuppression called on view we don't own");
618 nsRect damagedRect(aRect);
619 if (damagedRect.IsEmpty()) {
620 return;
623 nsView* displayRoot = GetDisplayRootFor(aView);
624 nsViewManager* displayRootVM = displayRoot->GetViewManager();
625 // Propagate the update to the displayRoot, since iframes, for example,
626 // can overlap each other and be translucent. So we have to possibly
627 // invalidate our rect in each of the widgets we have lying about.
628 damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
629 int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
630 int32_t APD = AppUnitsPerDevPixel();
631 damagedRect = damagedRect.ScaleToOtherAppUnitsRoundOut(APD, rootAPD);
633 // accumulate this rectangle in the view's dirty region, so we can
634 // process it later.
635 AddDirtyRegion(displayRoot, nsRegion(damagedRect));
638 void nsViewManager::InvalidateAllViews() {
639 if (RootViewManager() != this) {
640 return RootViewManager()->InvalidateAllViews();
643 InvalidateViews(mRootView);
646 void nsViewManager::InvalidateViews(nsView* aView) {
647 // Invalidate this view.
648 InvalidateView(aView);
650 // Invalidate all children as well.
651 nsView* childView = aView->GetFirstChild();
652 while (nullptr != childView) {
653 childView->GetViewManager()->InvalidateViews(childView);
654 childView = childView->GetNextSibling();
658 void nsViewManager::WillPaintWindow(nsIWidget* aWidget) {
659 if (aWidget) {
660 nsView* view = nsView::GetViewFor(aWidget);
661 LayerManager* manager = aWidget->GetLayerManager();
662 if (view &&
663 (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) {
664 ProcessPendingUpdates();
665 // Re-get the view pointer here since the ProcessPendingUpdates might have
666 // destroyed it during CallWillPaintOnObservers.
667 view = nsView::GetViewFor(aWidget);
668 if (view) {
669 view->SetForcedRepaint(false);
674 if (RefPtr<PresShell> presShell = mPresShell) {
675 presShell->WillPaintWindow();
679 bool nsViewManager::PaintWindow(nsIWidget* aWidget,
680 const LayoutDeviceIntRegion& aRegion) {
681 if (!aWidget || !mContext) return false;
683 NS_ASSERTION(
684 IsPaintingAllowed(),
685 "shouldn't be receiving paint events while painting is disallowed!");
687 // Get the view pointer here since NS_WILL_PAINT might have
688 // destroyed it during CallWillPaintOnObservers (bug 378273).
689 nsView* view = nsView::GetViewFor(aWidget);
690 if (view && !aRegion.IsEmpty()) {
691 Refresh(view, aRegion);
694 return true;
697 void nsViewManager::DidPaintWindow() {
698 if (RefPtr<PresShell> presShell = mPresShell) {
699 presShell->DidPaintWindow();
703 void nsViewManager::DispatchEvent(WidgetGUIEvent* aEvent, nsView* aView,
704 nsEventStatus* aStatus) {
705 AUTO_PROFILER_LABEL("nsViewManager::DispatchEvent", OTHER);
707 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
708 if ((mouseEvent &&
709 // Ignore mouse events that we synthesize.
710 mouseEvent->mReason == WidgetMouseEvent::eReal &&
711 // Ignore mouse exit and enter (we'll get moves if the user
712 // is really moving the mouse) since we get them when we
713 // create and destroy widgets.
714 mouseEvent->mMessage != eMouseExitFromWidget &&
715 mouseEvent->mMessage != eMouseEnterIntoWidget) ||
716 aEvent->HasKeyEventMessage() || aEvent->HasIMEEventMessage() ||
717 aEvent->mMessage == ePluginInputEvent) {
718 gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
721 // Find the view whose coordinates system we're in.
722 nsView* view = aView;
723 bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
724 if (dispatchUsingCoordinates) {
725 // Will dispatch using coordinates. Pretty bogus but it's consistent
726 // with what presshell does.
727 view = GetDisplayRootFor(view);
730 // If the view has no frame, look for a view that does.
731 nsIFrame* frame = view->GetFrame();
732 if (!frame && (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
733 aEvent->IsIMERelatedEvent() ||
734 aEvent->IsNonRetargetedNativeEventDelivererForPlugin() ||
735 aEvent->HasPluginActivationEventMessage())) {
736 while (view && !view->GetFrame()) {
737 view = view->GetParent();
740 if (view) {
741 frame = view->GetFrame();
745 if (nullptr != frame) {
746 // Hold a refcount to the presshell. The continued existence of the
747 // presshell will delay deletion of this view hierarchy should the event
748 // want to cause its destruction in, say, some JavaScript event handler.
749 if (RefPtr<PresShell> presShell = view->GetViewManager()->GetPresShell()) {
750 presShell->HandleEvent(frame, aEvent, false, aStatus);
751 return;
755 *aStatus = nsEventStatus_eIgnore;
758 // Recursively reparent widgets if necessary
760 void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget* aNewWidget) {
761 MOZ_ASSERT(aNewWidget, "null widget");
763 if (aView->HasWidget()) {
764 // Check to see if the parent widget is the
765 // same as the new parent. If not then reparent
766 // the widget, otherwise there is nothing more
767 // to do for the view and its descendants
768 nsIWidget* widget = aView->GetWidget();
769 nsIWidget* parentWidget = widget->GetParent();
770 if (parentWidget) {
771 // Child widget
772 if (parentWidget != aNewWidget) {
773 widget->SetParent(aNewWidget);
775 } else {
776 // Toplevel widget (popup, dialog, etc)
777 widget->ReparentNativeWidget(aNewWidget);
779 return;
782 // Need to check each of the views children to see
783 // if they have a widget and reparent it.
785 for (nsView* kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
786 ReparentChildWidgets(kid, aNewWidget);
790 // Reparent a view and its descendant views widgets if necessary
792 void nsViewManager::ReparentWidgets(nsView* aView, nsView* aParent) {
793 MOZ_ASSERT(aParent, "Must have a parent");
794 MOZ_ASSERT(aView, "Must have a view");
796 // Quickly determine whether the view has pre-existing children or a
797 // widget. In most cases the view will not have any pre-existing
798 // children when this is called. Only in the case
799 // where a view has been reparented by removing it from
800 // a reinserting it into a new location in the view hierarchy do we
801 // have to consider reparenting the existing widgets for the view and
802 // it's descendants.
803 if (aView->HasWidget() || aView->GetFirstChild()) {
804 nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
805 if (parentWidget) {
806 ReparentChildWidgets(aView, parentWidget);
807 return;
809 NS_WARNING("Can not find a widget for the parent view");
813 void nsViewManager::InsertChild(nsView* aParent, nsView* aChild,
814 nsView* aSibling, bool aAfter) {
815 MOZ_ASSERT(nullptr != aParent, "null ptr");
816 MOZ_ASSERT(nullptr != aChild, "null ptr");
817 NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
818 "tried to insert view with invalid sibling");
819 NS_ASSERTION(!IsViewInserted(aChild),
820 "tried to insert an already-inserted view");
822 if ((nullptr != aParent) && (nullptr != aChild)) {
823 // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid'
824 // in document order, otherwise after 'kid' (i.e. before 'kid' in document
825 // order).
827 if (nullptr == aSibling) {
828 if (aAfter) {
829 // insert at end of document order, i.e., before first view
830 // this is the common case, by far
831 aParent->InsertChild(aChild, nullptr);
832 ReparentWidgets(aChild, aParent);
833 } else {
834 // insert at beginning of document order, i.e., after last view
835 nsView* kid = aParent->GetFirstChild();
836 nsView* prev = nullptr;
837 while (kid) {
838 prev = kid;
839 kid = kid->GetNextSibling();
841 // prev is last view or null if there are no children
842 aParent->InsertChild(aChild, prev);
843 ReparentWidgets(aChild, aParent);
845 } else {
846 nsView* kid = aParent->GetFirstChild();
847 nsView* prev = nullptr;
848 while (kid && aSibling != kid) {
849 // get the next sibling view
850 prev = kid;
851 kid = kid->GetNextSibling();
853 NS_ASSERTION(kid != nullptr, "couldn't find sibling in child list");
854 if (aAfter) {
855 // insert after 'kid' in document order, i.e. before in view order
856 aParent->InsertChild(aChild, prev);
857 ReparentWidgets(aChild, aParent);
858 } else {
859 // insert before 'kid' in document order, i.e. after in view order
860 aParent->InsertChild(aChild, kid);
861 ReparentWidgets(aChild, aParent);
865 // if the parent view is marked as "floating", make the newly added view
866 // float as well.
867 if (aParent->GetFloating()) aChild->SetFloating(true);
871 void nsViewManager::RemoveChild(nsView* aChild) {
872 NS_ASSERTION(aChild, "aChild must not be null");
874 nsView* parent = aChild->GetParent();
876 if (nullptr != parent) {
877 NS_ASSERTION(
878 aChild->GetViewManager() == this || parent->GetViewManager() == this,
879 "wrong view manager");
880 parent->RemoveChild(aChild);
884 void nsViewManager::MoveViewTo(nsView* aView, nscoord aX, nscoord aY) {
885 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
886 aView->SetPosition(aX, aY);
889 void nsViewManager::ResizeView(nsView* aView, const nsRect& aRect,
890 bool aRepaintExposedAreaOnly) {
891 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
893 nsRect oldDimensions = aView->GetDimensions();
894 if (!oldDimensions.IsEqualEdges(aRect)) {
895 aView->SetDimensions(aRect, true);
898 // Note that if layout resizes the view and the view has a custom clip
899 // region set, then we expect layout to update the clip region too. Thus
900 // in the case where mClipRect has been optimized away to just be a null
901 // pointer, and this resize is implicitly changing the clip rect, it's OK
902 // because layout will change it back again if necessary.
905 void nsViewManager::SetViewFloating(nsView* aView, bool aFloating) {
906 NS_ASSERTION(!(nullptr == aView), "no view");
908 aView->SetFloating(aFloating);
911 void nsViewManager::SetViewVisibility(nsView* aView,
912 nsViewVisibility aVisible) {
913 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
915 if (aVisible != aView->GetVisibility()) {
916 aView->SetVisibility(aVisible);
920 bool nsViewManager::IsViewInserted(nsView* aView) {
921 if (mRootView == aView) {
922 return true;
924 if (aView->GetParent() == nullptr) {
925 return false;
927 nsView* view = aView->GetParent()->GetFirstChild();
928 while (view != nullptr) {
929 if (view == aView) {
930 return true;
932 view = view->GetNextSibling();
934 return false;
937 void nsViewManager::SetViewZIndex(nsView* aView, bool aAutoZIndex,
938 int32_t aZIndex) {
939 NS_ASSERTION((aView != nullptr), "no view");
941 // don't allow the root view's z-index to be changed. It should always be
942 // zero. This could be removed and replaced with a style rule, or just removed
943 // altogether, with interesting consequences
944 if (aView == mRootView) {
945 return;
948 if (aAutoZIndex) {
949 aZIndex = 0;
952 aView->SetZIndex(aAutoZIndex, aZIndex);
955 nsViewManager* nsViewManager::IncrementDisableRefreshCount() {
956 if (!IsRootVM()) {
957 return RootViewManager()->IncrementDisableRefreshCount();
960 ++mRefreshDisableCount;
962 return this;
965 void nsViewManager::DecrementDisableRefreshCount() {
966 NS_ASSERTION(IsRootVM(), "Should only be called on root");
967 --mRefreshDisableCount;
968 NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
971 void nsViewManager::GetRootWidget(nsIWidget** aWidget) {
972 if (!mRootView) {
973 *aWidget = nullptr;
974 return;
976 if (mRootView->HasWidget()) {
977 *aWidget = mRootView->GetWidget();
978 NS_ADDREF(*aWidget);
979 return;
981 if (mRootView->GetParent()) {
982 mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
983 return;
985 *aWidget = nullptr;
988 LayoutDeviceIntRect nsViewManager::ViewToWidget(nsView* aView,
989 const nsRect& aRect) const {
990 NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
992 // account for the view's origin not lining up with the widget's
993 nsRect rect = aRect + aView->ViewToWidgetOffset();
995 // finally, convert to device coordinates.
996 return LayoutDeviceIntRect::FromUnknownRect(
997 rect.ToOutsidePixels(AppUnitsPerDevPixel()));
1000 void nsViewManager::IsPainting(bool& aIsPainting) {
1001 aIsPainting = IsPainting();
1004 void nsViewManager::ProcessPendingUpdates() {
1005 if (!IsRootVM()) {
1006 RefPtr<nsViewManager> rootViewManager = RootViewManager();
1007 rootViewManager->ProcessPendingUpdates();
1008 return;
1011 // Flush things like reflows by calling WillPaint on observer presShells.
1012 if (mPresShell) {
1013 mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
1015 RefPtr<nsViewManager> strongThis(this);
1016 CallWillPaintOnObservers();
1018 ProcessPendingUpdatesForView(mRootView, true);
1019 if (mPresShell) {
1020 if (nsPresContext* pc = mPresShell->GetPresContext()) {
1021 pc->RefreshDriver()->ClearHasScheduleFlush();
1027 void nsViewManager::UpdateWidgetGeometry() {
1028 if (!IsRootVM()) {
1029 RefPtr<nsViewManager> rootViewManager = RootViewManager();
1030 rootViewManager->UpdateWidgetGeometry();
1031 return;
1034 if (mHasPendingWidgetGeometryChanges) {
1035 mHasPendingWidgetGeometryChanges = false;
1036 ProcessPendingUpdatesForView(mRootView, false);
1040 void nsViewManager::CallWillPaintOnObservers() {
1041 MOZ_ASSERT(IsRootVM(), "Must be root VM for this to be called!");
1043 if (NS_WARN_IF(!gViewManagers)) {
1044 return;
1047 uint32_t index;
1048 for (index = 0; index < gViewManagers->Length(); index++) {
1049 nsViewManager* vm = gViewManagers->ElementAt(index);
1050 if (vm->RootViewManager() == this) {
1051 // One of our kids.
1052 if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
1053 if (RefPtr<PresShell> presShell = vm->GetPresShell()) {
1054 presShell->WillPaint();
1061 void nsViewManager::GetLastUserEventTime(uint32_t& aTime) {
1062 aTime = gLastUserEventTime;
1065 void nsViewManager::InvalidateHierarchy() {
1066 if (mRootView) {
1067 if (!IsRootVM()) {
1068 NS_RELEASE(mRootViewManager);
1070 nsView* parent = mRootView->GetParent();
1071 if (parent) {
1072 mRootViewManager = parent->GetViewManager()->RootViewManager();
1073 NS_ADDREF(mRootViewManager);
1074 NS_ASSERTION(mRootViewManager != this,
1075 "Root view had a parent, but it has the same view manager");
1076 } else {
1077 mRootViewManager = this;