123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
- #include "plarena.h"
- #include "nsAutoPtr.h"
- #include "nsViewManager.h"
- #include "nsGfxCIID.h"
- #include "nsView.h"
- #include "nsCOMPtr.h"
- #include "mozilla/MouseEvents.h"
- #include "nsRegion.h"
- #include "nsCOMArray.h"
- #include "nsIPluginWidget.h"
- #include "nsXULPopupManager.h"
- #include "nsIPresShell.h"
- #include "nsPresContext.h"
- #include "mozilla/StartupTimeline.h"
- #include "GeckoProfiler.h"
- #include "nsRefreshDriver.h"
- #include "mozilla/Preferences.h"
- #include "nsContentUtils.h" // for nsAutoScriptBlocker
- #include "nsLayoutUtils.h"
- #include "Layers.h"
- #include "gfxPlatform.h"
- #include "gfxPrefs.h"
- #include "nsIDocument.h"
- /**
- XXX TODO XXX
- DeCOMify newly private methods
- Optimize view storage
- */
- /**
- A note about platform assumptions:
- We assume that a widget is z-ordered on top of its parent.
-
- We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
- we ask for a specific z-order, we don't assume that widget z-ordering actually works.
- */
- using namespace mozilla;
- using namespace mozilla::layers;
- #define NSCOORD_NONE INT32_MIN
- #undef DEBUG_MOUSE_LOCATION
- // Weakly held references to all of the view managers
- nsTArray<nsViewManager*>* nsViewManager::gViewManagers = nullptr;
- uint32_t nsViewManager::gLastUserEventTime = 0;
- nsViewManager::nsViewManager()
- : mDelayedResize(NSCOORD_NONE, NSCOORD_NONE)
- {
- mRootViewManager = this;
- if (gViewManagers == nullptr) {
- // Create an array to hold a list of view managers
- gViewManagers = new nsTArray<nsViewManager*>;
- }
-
- gViewManagers->AppendElement(this);
- // NOTE: we use a zeroing operator new, so all data members are
- // assumed to be cleared here.
- mHasPendingWidgetGeometryChanges = false;
- mRecursiveRefreshPending = false;
- }
- nsViewManager::~nsViewManager()
- {
- if (mRootView) {
- // Destroy any remaining views
- mRootView->Destroy();
- mRootView = nullptr;
- }
- if (!IsRootVM()) {
- // We have a strong ref to mRootViewManager
- NS_RELEASE(mRootViewManager);
- }
- NS_ASSERTION(gViewManagers != nullptr, "About to use null gViewManagers");
- #ifdef DEBUG
- bool removed =
- #endif
- gViewManagers->RemoveElement(this);
- NS_ASSERTION(removed, "Viewmanager instance was not in the global list of viewmanagers");
- if (gViewManagers->IsEmpty()) {
- // There aren't any more view managers so
- // release the global array of view managers
- delete gViewManagers;
- gViewManagers = nullptr;
- }
- MOZ_RELEASE_ASSERT(!mPresShell, "Releasing nsViewManager without having called Destroy on the PresShell!");
- }
- // We don't hold a reference to the presentation context because it
- // holds a reference to us.
- nsresult
- nsViewManager::Init(nsDeviceContext* aContext)
- {
- NS_PRECONDITION(nullptr != aContext, "null ptr");
- if (nullptr == aContext) {
- return NS_ERROR_NULL_POINTER;
- }
- if (nullptr != mContext) {
- return NS_ERROR_ALREADY_INITIALIZED;
- }
- mContext = aContext;
- return NS_OK;
- }
- nsView*
- nsViewManager::CreateView(const nsRect& aBounds,
- nsView* aParent,
- nsViewVisibility aVisibilityFlag)
- {
- nsView *v = new nsView(this, aVisibilityFlag);
- v->SetParent(aParent);
- v->SetPosition(aBounds.x, aBounds.y);
- nsRect dim(0, 0, aBounds.width, aBounds.height);
- v->SetDimensions(dim, false);
- return v;
- }
- void
- nsViewManager::SetRootView(nsView *aView)
- {
- NS_PRECONDITION(!aView || aView->GetViewManager() == this,
- "Unexpected viewmanager on root view");
-
- // Do NOT destroy the current root view. It's the caller's responsibility
- // to destroy it
- mRootView = aView;
- if (mRootView) {
- nsView* parent = mRootView->GetParent();
- if (parent) {
- // Calling InsertChild on |parent| will InvalidateHierarchy() on us, so
- // no need to set mRootViewManager ourselves here.
- parent->InsertChild(mRootView, nullptr);
- } else {
- InvalidateHierarchy();
- }
- mRootView->SetZIndex(false, 0);
- }
- // Else don't touch mRootViewManager
- }
- void
- nsViewManager::GetWindowDimensions(nscoord *aWidth, nscoord *aHeight)
- {
- if (nullptr != mRootView) {
- if (mDelayedResize == nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
- nsRect dim = mRootView->GetDimensions();
- *aWidth = dim.width;
- *aHeight = dim.height;
- } else {
- *aWidth = mDelayedResize.width;
- *aHeight = mDelayedResize.height;
- }
- }
- else
- {
- *aWidth = 0;
- *aHeight = 0;
- }
- }
- void nsViewManager::DoSetWindowDimensions(nscoord aWidth, nscoord aHeight)
- {
- nsRect oldDim = mRootView->GetDimensions();
- nsRect newDim(0, 0, aWidth, aHeight);
- // We care about resizes even when one dimension is already zero.
- if (!oldDim.IsEqualEdges(newDim)) {
- // Don't resize the widget. It is already being set elsewhere.
- mRootView->SetDimensions(newDim, true, false);
- if (mPresShell)
- mPresShell->ResizeReflow(aWidth, aHeight, oldDim.width, oldDim.height);
- }
- }
- bool
- nsViewManager::ShouldDelayResize() const
- {
- MOZ_ASSERT(mRootView);
- if (!mRootView->IsEffectivelyVisible() ||
- !mPresShell || !mPresShell->IsVisible()) {
- return true;
- }
- if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
- if (rd->IsResizeSuppressed()) {
- return true;
- }
- }
- return false;
- }
- void
- nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight, bool aDelayResize)
- {
- if (mRootView) {
- if (!ShouldDelayResize() && !aDelayResize) {
- if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
- mDelayedResize != nsSize(aWidth, aHeight)) {
- // We have a delayed resize; that now obsolete size may already have
- // been flushed to the PresContext so we need to update the PresContext
- // with the new size because if the new size is exactly the same as the
- // root view's current size then DoSetWindowDimensions will not
- // request a resize reflow (which would correct it). See bug 617076.
- mDelayedResize = nsSize(aWidth, aHeight);
- FlushDelayedResize(false);
- }
- mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
- DoSetWindowDimensions(aWidth, aHeight);
- } else {
- mDelayedResize.SizeTo(aWidth, aHeight);
- if (mPresShell && mPresShell->GetDocument()) {
- nsIDocument* doc = mPresShell->GetDocument();
- doc->SetNeedStyleFlush();
- doc->SetNeedLayoutFlush();
- }
- }
- }
- }
- void
- nsViewManager::FlushDelayedResize(bool aDoReflow)
- {
- if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE)) {
- if (aDoReflow) {
- DoSetWindowDimensions(mDelayedResize.width, mDelayedResize.height);
- mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
- } else if (mPresShell && !mPresShell->GetIsViewportOverridden()) {
- nsPresContext* presContext = mPresShell->GetPresContext();
- if (presContext) {
- presContext->SetVisibleArea(nsRect(nsPoint(0, 0), mDelayedResize));
- }
- }
- }
- }
- // Convert aIn from being relative to and in appunits of aFromView, to being
- // relative to and in appunits of aToView.
- static nsRegion ConvertRegionBetweenViews(const nsRegion& aIn,
- nsView* aFromView,
- nsView* aToView)
- {
- nsRegion out = aIn;
- out.MoveBy(aFromView->GetOffsetTo(aToView));
- out = out.ScaleToOtherAppUnitsRoundOut(
- aFromView->GetViewManager()->AppUnitsPerDevPixel(),
- aToView->GetViewManager()->AppUnitsPerDevPixel());
- return out;
- }
- nsView* nsViewManager::GetDisplayRootFor(nsView* aView)
- {
- nsView *displayRoot = aView;
- for (;;) {
- nsView *displayParent = displayRoot->GetParent();
- if (!displayParent)
- return displayRoot;
- if (displayRoot->GetFloating() && !displayParent->GetFloating())
- return displayRoot;
- // If we have a combobox dropdown popup within a panel popup, both the view
- // for the dropdown popup and its parent will be floating, so we need to
- // distinguish this situation. We do this by looking for a widget. Any view
- // with a widget is a display root, except for plugins.
- nsIWidget* widget = displayRoot->GetWidget();
- if (widget && widget->WindowType() == eWindowType_popup) {
- NS_ASSERTION(displayRoot->GetFloating() && displayParent->GetFloating(),
- "this should only happen with floating views that have floating parents");
- return displayRoot;
- }
- displayRoot = displayParent;
- }
- }
- /**
- aRegion is given in device coordinates!!
- aContext may be null, in which case layers should be used for
- rendering.
- */
- void nsViewManager::Refresh(nsView* aView, const LayoutDeviceIntRegion& aRegion)
- {
- NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
- if (mPresShell && mPresShell->IsNeverPainting()) {
- return;
- }
- // damageRegion is the damaged area, in twips, relative to the view origin
- nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
- // move region from widget coordinates into view coordinates
- damageRegion.MoveBy(-aView->ViewToWidgetOffset());
- if (damageRegion.IsEmpty()) {
- #ifdef DEBUG_roc
- nsRect viewRect = aView->GetDimensions();
- nsRect damageRect = damageRegion.GetBounds();
- printf_stderr("XXX Damage rectangle (%d,%d,%d,%d) does not intersect the widget's view (%d,%d,%d,%d)!\n",
- damageRect.x, damageRect.y, damageRect.width, damageRect.height,
- viewRect.x, viewRect.y, viewRect.width, viewRect.height);
- #endif
- return;
- }
-
- nsIWidget *widget = aView->GetWidget();
- if (!widget) {
- return;
- }
- NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
- if (IsPainting()) {
- RootViewManager()->mRecursiveRefreshPending = true;
- return;
- }
- {
- nsAutoScriptBlocker scriptBlocker;
- SetPainting(true);
- NS_ASSERTION(GetDisplayRootFor(aView) == aView,
- "Widgets that we paint must all be display roots");
- if (mPresShell) {
- #ifdef MOZ_DUMP_PAINTING
- if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
- printf_stderr("--COMPOSITE-- %p\n", mPresShell);
- }
- #endif
- uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE;
- LayerManager *manager = widget->GetLayerManager();
- if (!manager->NeedsWidgetInvalidation()) {
- manager->FlushRendering();
- } else {
- mPresShell->Paint(aView, damageRegion,
- paintFlags);
- }
- #ifdef MOZ_DUMP_PAINTING
- if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
- printf_stderr("--ENDCOMPOSITE--\n");
- }
- #endif
- mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
- }
- SetPainting(false);
- }
- if (RootViewManager()->mRecursiveRefreshPending) {
- RootViewManager()->mRecursiveRefreshPending = false;
- InvalidateAllViews();
- }
- }
- void
- nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
- bool aFlushDirtyRegion)
- {
- NS_ASSERTION(IsRootVM(), "Updates will be missed");
- if (!aView) {
- return;
- }
- nsCOMPtr<nsIPresShell> rootShell(mPresShell);
- nsTArray<nsCOMPtr<nsIWidget> > widgets;
- aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
- for (uint32_t i = 0; i < widgets.Length(); ++i) {
- nsView* view = nsView::GetViewFor(widgets[i]);
- if (view) {
- if (view->mNeedsWindowPropertiesSync) {
- view->mNeedsWindowPropertiesSync = false;
- if (nsViewManager* vm = view->GetViewManager()) {
- if (nsIPresShell* ps = vm->GetPresShell()) {
- ps->SyncWindowProperties(view);
- }
- }
- }
- }
- view = nsView::GetViewFor(widgets[i]);
- if (view) {
- view->ResetWidgetBounds(false, true);
- }
- }
- if (rootShell->GetViewManager() != this) {
- return; // presentation might have been torn down
- }
- if (aFlushDirtyRegion) {
- profiler_tracing("Paint", "DisplayList", TRACING_INTERVAL_START);
- nsAutoScriptBlocker scriptBlocker;
- SetPainting(true);
- for (uint32_t i = 0; i < widgets.Length(); ++i) {
- nsIWidget* widget = widgets[i];
- nsView* view = nsView::GetViewFor(widget);
- if (view) {
- view->GetViewManager()->ProcessPendingUpdatesPaint(widget);
- }
- }
- SetPainting(false);
- profiler_tracing("Paint", "DisplayList", TRACING_INTERVAL_END);
- }
- }
- void
- nsViewManager::ProcessPendingUpdatesRecurse(nsView* aView,
- nsTArray<nsCOMPtr<nsIWidget> >& aWidgets)
- {
- if (mPresShell && mPresShell->IsNeverPainting()) {
- return;
- }
- for (nsView* childView = aView->GetFirstChild(); childView;
- childView = childView->GetNextSibling()) {
- childView->GetViewManager()->
- ProcessPendingUpdatesRecurse(childView, aWidgets);
- }
- nsIWidget* widget = aView->GetWidget();
- if (widget) {
- aWidgets.AppendElement(widget);
- } else {
- FlushDirtyRegionToWidget(aView);
- }
- }
- void
- nsViewManager::ProcessPendingUpdatesPaint(nsIWidget* aWidget)
- {
- if (aWidget->NeedsPaint()) {
- // If an ancestor widget was hidden and then shown, we could
- // have a delayed resize to handle.
- for (RefPtr<nsViewManager> vm = this; vm;
- vm = vm->mRootView->GetParent()
- ? vm->mRootView->GetParent()->GetViewManager()
- : nullptr) {
- if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
- vm->mRootView->IsEffectivelyVisible() &&
- vm->mPresShell && vm->mPresShell->IsVisible()) {
- vm->FlushDelayedResize(true);
- }
- }
- nsView* view = nsView::GetViewFor(aWidget);
- if (!view) {
- NS_ERROR("FlushDelayedResize destroyed the nsView?");
- return;
- }
- nsIWidgetListener* previousListener = aWidget->GetPreviouslyAttachedWidgetListener();
- if (previousListener &&
- previousListener != view &&
- view->IsPrimaryFramePaintSuppressed()) {
- return;
- }
- if (mPresShell) {
- #ifdef MOZ_DUMP_PAINTING
- if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
- printf_stderr("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n",
- mPresShell, view, aWidget);
- }
- #endif
- mPresShell->Paint(view, nsRegion(), nsIPresShell::PAINT_LAYERS);
- view->SetForcedRepaint(false);
- #ifdef MOZ_DUMP_PAINTING
- if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
- printf_stderr("---- PAINT END ----\n");
- }
- #endif
- }
- }
- FlushDirtyRegionToWidget(nsView::GetViewFor(aWidget));
- }
- void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
- {
- NS_ASSERTION(aView->GetViewManager() == this,
- "FlushDirtyRegionToWidget called on view we don't own");
- if (!aView->HasNonEmptyDirtyRegion())
- return;
- nsRegion* dirtyRegion = aView->GetDirtyRegion();
- nsView* nearestViewWithWidget = aView;
- while (!nearestViewWithWidget->HasWidget() &&
- nearestViewWithWidget->GetParent()) {
- nearestViewWithWidget = nearestViewWithWidget->GetParent();
- }
- nsRegion r =
- ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
- // If we draw the frame counter we need to make sure we invalidate the area
- // for it to make it on screen
- if (gfxPrefs::DrawFrameCounter()) {
- nsRect counterBounds = ToAppUnits(gfxPlatform::FrameCounterBounds(), AppUnitsPerDevPixel());
- r.OrWith(counterBounds);
- }
- nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
- widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
- dirtyRegion->SetEmpty();
- }
- void
- nsViewManager::InvalidateView(nsView *aView)
- {
- // Mark the entire view as damaged
- InvalidateView(aView, aView->GetDimensions());
- }
- static void
- AddDirtyRegion(nsView *aView, const nsRegion &aDamagedRegion)
- {
- nsRegion* dirtyRegion = aView->GetDirtyRegion();
- if (!dirtyRegion)
- return;
- dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
- dirtyRegion->SimplifyOutward(8);
- }
- void
- nsViewManager::PostPendingUpdate()
- {
- nsViewManager* rootVM = RootViewManager();
- rootVM->mHasPendingWidgetGeometryChanges = true;
- if (rootVM->mPresShell) {
- rootVM->mPresShell->ScheduleViewManagerFlush();
- }
- }
- /**
- * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
- * every widget child of aWidgetView, plus aWidgetView's own widget
- */
- void
- nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
- const nsRegion &aDamagedRegion)
- {
- NS_ASSERTION(aWidgetView->GetViewManager() == this,
- "InvalidateWidgetArea called on view we don't own");
- nsIWidget* widget = aWidgetView->GetWidget();
- #if 0
- nsRect dbgBounds = aDamagedRegion.GetBounds();
- printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
- aWidgetView, aWidgetView->IsAttachedToTopLevel(),
- widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
- #endif
- // If the widget is hidden, it don't cover nothing
- if (widget && !widget->IsVisible()) {
- return;
- }
- if (!widget) {
- // The root view or a scrolling view might not have a widget
- // (for example, during printing). We get here when we scroll
- // during printing to show selected options in a listbox, for example.
- return;
- }
- // Update all child widgets with the damage. In the process,
- // accumulate the union of all the child widget areas, or at least
- // some subset of that.
- nsRegion children;
- if (widget->GetTransparencyMode() != eTransparencyTransparent) {
- for (nsIWidget* childWidget = widget->GetFirstChild();
- childWidget;
- childWidget = childWidget->GetNextSibling()) {
- nsView* view = nsView::GetViewFor(childWidget);
- NS_ASSERTION(view != aWidgetView, "will recur infinitely");
- nsWindowType type = childWidget->WindowType();
- if (view && childWidget->IsVisible() && type != eWindowType_popup) {
- NS_ASSERTION(childWidget->IsPlugin(),
- "Only plugin or popup widgets can be children!");
- // We do not need to invalidate in plugin widgets, but we should
- // exclude them from the invalidation region.
- // GetBounds should compensate for chrome on a toplevel widget
- LayoutDeviceIntRect bounds = childWidget->GetBounds();
- nsTArray<LayoutDeviceIntRect> clipRects;
- childWidget->GetWindowClipRegion(&clipRects);
- for (uint32_t i = 0; i < clipRects.Length(); ++i) {
- nsRect rr = LayoutDeviceIntRect::ToAppUnits(
- clipRects[i] + bounds.TopLeft(), AppUnitsPerDevPixel());
- children.Or(children, rr - aWidgetView->ViewToWidgetOffset());
- children.SimplifyInward(20);
- }
- }
- }
- }
- nsRegion leftOver;
- leftOver.Sub(aDamagedRegion, children);
- if (!leftOver.IsEmpty()) {
- for (auto iter = leftOver.RectIter(); !iter.Done(); iter.Next()) {
- LayoutDeviceIntRect bounds = ViewToWidget(aWidgetView, iter.Get());
- widget->Invalidate(bounds);
- }
- }
- }
- static bool
- ShouldIgnoreInvalidation(nsViewManager* aVM)
- {
- while (aVM) {
- nsIPresShell* shell = aVM->GetPresShell();
- if (!shell || shell->ShouldIgnoreInvalidation()) {
- return true;
- }
- nsView* view = aVM->GetRootView()->GetParent();
- aVM = view ? view->GetViewManager() : nullptr;
- }
- return false;
- }
- void
- nsViewManager::InvalidateView(nsView *aView, const nsRect &aRect)
- {
- // If painting is suppressed in the presshell or an ancestor drop all
- // invalidates, it will invalidate everything when it unsuppresses.
- if (ShouldIgnoreInvalidation(this)) {
- return;
- }
- InvalidateViewNoSuppression(aView, aRect);
- }
- void
- nsViewManager::InvalidateViewNoSuppression(nsView *aView,
- const nsRect &aRect)
- {
- NS_PRECONDITION(nullptr != aView, "null view");
- NS_ASSERTION(aView->GetViewManager() == this,
- "InvalidateViewNoSuppression called on view we don't own");
- nsRect damagedRect(aRect);
- if (damagedRect.IsEmpty()) {
- return;
- }
- nsView* displayRoot = GetDisplayRootFor(aView);
- nsViewManager* displayRootVM = displayRoot->GetViewManager();
- // Propagate the update to the displayRoot, since iframes, for example,
- // can overlap each other and be translucent. So we have to possibly
- // invalidate our rect in each of the widgets we have lying about.
- damagedRect.MoveBy(aView->GetOffsetTo(displayRoot));
- int32_t rootAPD = displayRootVM->AppUnitsPerDevPixel();
- int32_t APD = AppUnitsPerDevPixel();
- damagedRect = damagedRect.ScaleToOtherAppUnitsRoundOut(APD, rootAPD);
- // accumulate this rectangle in the view's dirty region, so we can
- // process it later.
- AddDirtyRegion(displayRoot, nsRegion(damagedRect));
- }
- void
- nsViewManager::InvalidateAllViews()
- {
- if (RootViewManager() != this) {
- return RootViewManager()->InvalidateAllViews();
- }
-
- InvalidateViews(mRootView);
- }
- void nsViewManager::InvalidateViews(nsView *aView)
- {
- // Invalidate this view.
- InvalidateView(aView);
- // Invalidate all children as well.
- nsView* childView = aView->GetFirstChild();
- while (nullptr != childView) {
- childView->GetViewManager()->InvalidateViews(childView);
- childView = childView->GetNextSibling();
- }
- }
- void nsViewManager::WillPaintWindow(nsIWidget* aWidget)
- {
- RefPtr<nsIWidget> widget(aWidget);
- if (widget) {
- nsView* view = nsView::GetViewFor(widget);
- LayerManager* manager = widget->GetLayerManager();
- if (view &&
- (view->ForcedRepaint() || !manager->NeedsWidgetInvalidation())) {
- ProcessPendingUpdates();
- // Re-get the view pointer here since the ProcessPendingUpdates might have
- // destroyed it during CallWillPaintOnObservers.
- view = nsView::GetViewFor(widget);
- if (view) {
- view->SetForcedRepaint(false);
- }
- }
- }
- nsCOMPtr<nsIPresShell> shell = mPresShell;
- if (shell) {
- shell->WillPaintWindow();
- }
- }
- bool nsViewManager::PaintWindow(nsIWidget* aWidget,
- LayoutDeviceIntRegion aRegion)
- {
- if (!aWidget || !mContext)
- return false;
- NS_ASSERTION(IsPaintingAllowed(),
- "shouldn't be receiving paint events while painting is disallowed!");
- // Get the view pointer here since NS_WILL_PAINT might have
- // destroyed it during CallWillPaintOnObservers (bug 378273).
- nsView* view = nsView::GetViewFor(aWidget);
- if (view && !aRegion.IsEmpty()) {
- Refresh(view, aRegion);
- }
- return true;
- }
- void nsViewManager::DidPaintWindow()
- {
- nsCOMPtr<nsIPresShell> shell = mPresShell;
- if (shell) {
- shell->DidPaintWindow();
- }
- }
- void
- nsViewManager::DispatchEvent(WidgetGUIEvent *aEvent,
- nsView* aView,
- nsEventStatus* aStatus)
- {
- PROFILER_LABEL("nsViewManager", "DispatchEvent",
- js::ProfileEntry::Category::EVENTS);
- WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
- if ((mouseEvent &&
- // Ignore mouse events that we synthesize.
- mouseEvent->mReason == WidgetMouseEvent::eReal &&
- // Ignore mouse exit and enter (we'll get moves if the user
- // is really moving the mouse) since we get them when we
- // create and destroy widgets.
- mouseEvent->mMessage != eMouseExitFromWidget &&
- mouseEvent->mMessage != eMouseEnterIntoWidget) ||
- aEvent->HasKeyEventMessage() ||
- aEvent->HasIMEEventMessage() ||
- aEvent->mMessage == ePluginInputEvent) {
- gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
- }
- // Find the view whose coordinates system we're in.
- nsView* view = aView;
- bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
- if (dispatchUsingCoordinates) {
- // Will dispatch using coordinates. Pretty bogus but it's consistent
- // with what presshell does.
- view = GetDisplayRootFor(view);
- }
- // If the view has no frame, look for a view that does.
- nsIFrame* frame = view->GetFrame();
- if (!frame &&
- (dispatchUsingCoordinates || aEvent->HasKeyEventMessage() ||
- aEvent->IsIMERelatedEvent() ||
- aEvent->IsNonRetargetedNativeEventDelivererForPlugin() ||
- aEvent->HasPluginActivationEventMessage())) {
- while (view && !view->GetFrame()) {
- view = view->GetParent();
- }
- if (view) {
- frame = view->GetFrame();
- }
- }
- if (nullptr != frame) {
- // Hold a refcount to the presshell. The continued existence of the
- // presshell will delay deletion of this view hierarchy should the event
- // want to cause its destruction in, say, some JavaScript event handler.
- nsCOMPtr<nsIPresShell> shell = view->GetViewManager()->GetPresShell();
- if (shell) {
- shell->HandleEvent(frame, aEvent, false, aStatus);
- return;
- }
- }
- *aStatus = nsEventStatus_eIgnore;
- }
- // Recursively reparent widgets if necessary
- void nsViewManager::ReparentChildWidgets(nsView* aView, nsIWidget *aNewWidget)
- {
- NS_PRECONDITION(aNewWidget, "");
- if (aView->HasWidget()) {
- // Check to see if the parent widget is the
- // same as the new parent. If not then reparent
- // the widget, otherwise there is nothing more
- // to do for the view and its descendants
- nsIWidget* widget = aView->GetWidget();
- nsIWidget* parentWidget = widget->GetParent();
- if (parentWidget) {
- // Child widget
- if (parentWidget != aNewWidget) {
- #ifdef DEBUG
- nsresult rv =
- #endif
- widget->SetParent(aNewWidget);
- NS_ASSERTION(NS_SUCCEEDED(rv), "SetParent failed!");
- }
- } else {
- // Toplevel widget (popup, dialog, etc)
- widget->ReparentNativeWidget(aNewWidget);
- }
- return;
- }
- // Need to check each of the views children to see
- // if they have a widget and reparent it.
- for (nsView *kid = aView->GetFirstChild(); kid; kid = kid->GetNextSibling()) {
- ReparentChildWidgets(kid, aNewWidget);
- }
- }
- // Reparent a view and its descendant views widgets if necessary
- void nsViewManager::ReparentWidgets(nsView* aView, nsView *aParent)
- {
- NS_PRECONDITION(aParent, "Must have a parent");
- NS_PRECONDITION(aView, "Must have a view");
-
- // Quickly determine whether the view has pre-existing children or a
- // widget. In most cases the view will not have any pre-existing
- // children when this is called. Only in the case
- // where a view has been reparented by removing it from
- // a reinserting it into a new location in the view hierarchy do we
- // have to consider reparenting the existing widgets for the view and
- // it's descendants.
- if (aView->HasWidget() || aView->GetFirstChild()) {
- nsIWidget* parentWidget = aParent->GetNearestWidget(nullptr);
- if (parentWidget) {
- ReparentChildWidgets(aView, parentWidget);
- return;
- }
- NS_WARNING("Can not find a widget for the parent view");
- }
- }
- void
- nsViewManager::InsertChild(nsView *aParent, nsView *aChild, nsView *aSibling,
- bool aAfter)
- {
- NS_PRECONDITION(nullptr != aParent, "null ptr");
- NS_PRECONDITION(nullptr != aChild, "null ptr");
- NS_ASSERTION(aSibling == nullptr || aSibling->GetParent() == aParent,
- "tried to insert view with invalid sibling");
- NS_ASSERTION(!IsViewInserted(aChild), "tried to insert an already-inserted view");
- if ((nullptr != aParent) && (nullptr != aChild))
- {
- // if aAfter is set, we will insert the child after 'prev' (i.e. after 'kid' in document
- // order, otherwise after 'kid' (i.e. before 'kid' in document order).
- if (nullptr == aSibling) {
- if (aAfter) {
- // insert at end of document order, i.e., before first view
- // this is the common case, by far
- aParent->InsertChild(aChild, nullptr);
- ReparentWidgets(aChild, aParent);
- } else {
- // insert at beginning of document order, i.e., after last view
- nsView *kid = aParent->GetFirstChild();
- nsView *prev = nullptr;
- while (kid) {
- prev = kid;
- kid = kid->GetNextSibling();
- }
- // prev is last view or null if there are no children
- aParent->InsertChild(aChild, prev);
- ReparentWidgets(aChild, aParent);
- }
- } else {
- nsView *kid = aParent->GetFirstChild();
- nsView *prev = nullptr;
- while (kid && aSibling != kid) {
- //get the next sibling view
- prev = kid;
- kid = kid->GetNextSibling();
- }
- NS_ASSERTION(kid != nullptr,
- "couldn't find sibling in child list");
- if (aAfter) {
- // insert after 'kid' in document order, i.e. before in view order
- aParent->InsertChild(aChild, prev);
- ReparentWidgets(aChild, aParent);
- } else {
- // insert before 'kid' in document order, i.e. after in view order
- aParent->InsertChild(aChild, kid);
- ReparentWidgets(aChild, aParent);
- }
- }
- // if the parent view is marked as "floating", make the newly added view float as well.
- if (aParent->GetFloating())
- aChild->SetFloating(true);
- }
- }
- void
- nsViewManager::InsertChild(nsView *aParent, nsView *aChild, int32_t aZIndex)
- {
- // no-one really calls this with anything other than aZIndex == 0 on a fresh view
- // XXX this method should simply be eliminated and its callers redirected to the real method
- SetViewZIndex(aChild, false, aZIndex);
- InsertChild(aParent, aChild, nullptr, true);
- }
- void
- nsViewManager::RemoveChild(nsView *aChild)
- {
- NS_ASSERTION(aChild, "aChild must not be null");
- nsView* parent = aChild->GetParent();
- if (nullptr != parent) {
- NS_ASSERTION(aChild->GetViewManager() == this ||
- parent->GetViewManager() == this, "wrong view manager");
- parent->RemoveChild(aChild);
- }
- }
- void
- nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY)
- {
- NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
- aView->SetPosition(aX, aY);
- }
- void
- nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly)
- {
- NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
- nsRect oldDimensions = aView->GetDimensions();
- if (!oldDimensions.IsEqualEdges(aRect)) {
- aView->SetDimensions(aRect, true);
- }
- // Note that if layout resizes the view and the view has a custom clip
- // region set, then we expect layout to update the clip region too. Thus
- // in the case where mClipRect has been optimized away to just be a null
- // pointer, and this resize is implicitly changing the clip rect, it's OK
- // because layout will change it back again if necessary.
- }
- void
- nsViewManager::SetViewFloating(nsView *aView, bool aFloating)
- {
- NS_ASSERTION(!(nullptr == aView), "no view");
- aView->SetFloating(aFloating);
- }
- void
- nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible)
- {
- NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
- if (aVisible != aView->GetVisibility()) {
- aView->SetVisibility(aVisible);
- }
- }
- bool nsViewManager::IsViewInserted(nsView *aView)
- {
- if (mRootView == aView) {
- return true;
- } else if (aView->GetParent() == nullptr) {
- return false;
- } else {
- nsView* view = aView->GetParent()->GetFirstChild();
- while (view != nullptr) {
- if (view == aView) {
- return true;
- }
- view = view->GetNextSibling();
- }
- return false;
- }
- }
- void
- nsViewManager::SetViewZIndex(nsView *aView, bool aAutoZIndex, int32_t aZIndex)
- {
- NS_ASSERTION((aView != nullptr), "no view");
- // don't allow the root view's z-index to be changed. It should always be zero.
- // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
- if (aView == mRootView) {
- return;
- }
- if (aAutoZIndex) {
- aZIndex = 0;
- }
- aView->SetZIndex(aAutoZIndex, aZIndex);
- }
- nsViewManager*
- nsViewManager::IncrementDisableRefreshCount()
- {
- if (!IsRootVM()) {
- return RootViewManager()->IncrementDisableRefreshCount();
- }
- ++mRefreshDisableCount;
- return this;
- }
- void
- nsViewManager::DecrementDisableRefreshCount()
- {
- NS_ASSERTION(IsRootVM(), "Should only be called on root");
- --mRefreshDisableCount;
- NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
- }
- void
- nsViewManager::GetRootWidget(nsIWidget **aWidget)
- {
- if (!mRootView) {
- *aWidget = nullptr;
- return;
- }
- if (mRootView->HasWidget()) {
- *aWidget = mRootView->GetWidget();
- NS_ADDREF(*aWidget);
- return;
- }
- if (mRootView->GetParent()) {
- mRootView->GetParent()->GetViewManager()->GetRootWidget(aWidget);
- return;
- }
- *aWidget = nullptr;
- }
- LayoutDeviceIntRect
- nsViewManager::ViewToWidget(nsView* aView, const nsRect& aRect) const
- {
- NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
- // account for the view's origin not lining up with the widget's
- nsRect rect = aRect + aView->ViewToWidgetOffset();
- // finally, convert to device coordinates.
- return LayoutDeviceIntRect::FromUnknownRect(
- rect.ToOutsidePixels(AppUnitsPerDevPixel()));
- }
- void
- nsViewManager::IsPainting(bool& aIsPainting)
- {
- aIsPainting = IsPainting();
- }
- void
- nsViewManager::ProcessPendingUpdates()
- {
- if (!IsRootVM()) {
- RootViewManager()->ProcessPendingUpdates();
- return;
- }
- // Flush things like reflows by calling WillPaint on observer presShells.
- if (mPresShell) {
- mPresShell->GetPresContext()->RefreshDriver()->RevokeViewManagerFlush();
- RefPtr<nsViewManager> strongThis(this);
- CallWillPaintOnObservers();
- ProcessPendingUpdatesForView(mRootView, true);
- }
- }
- void
- nsViewManager::UpdateWidgetGeometry()
- {
- if (!IsRootVM()) {
- RootViewManager()->UpdateWidgetGeometry();
- return;
- }
- if (mHasPendingWidgetGeometryChanges) {
- mHasPendingWidgetGeometryChanges = false;
- RefPtr<nsViewManager> strongThis(this);
- ProcessPendingUpdatesForView(mRootView, false);
- }
- }
- void
- nsViewManager::CallWillPaintOnObservers()
- {
- NS_PRECONDITION(IsRootVM(), "Must be root VM for this to be called!");
- if (NS_WARN_IF(!gViewManagers)) {
- return;
- }
- uint32_t index;
- for (index = 0; index < gViewManagers->Length(); index++) {
- nsViewManager* vm = gViewManagers->ElementAt(index);
- if (vm->RootViewManager() == this) {
- // One of our kids.
- if (vm->mRootView && vm->mRootView->IsEffectivelyVisible()) {
- nsCOMPtr<nsIPresShell> shell = vm->GetPresShell();
- if (shell) {
- shell->WillPaint();
- }
- }
- }
- }
- }
- void
- nsViewManager::GetLastUserEventTime(uint32_t& aTime)
- {
- aTime = gLastUserEventTime;
- }
- void
- nsViewManager::InvalidateHierarchy()
- {
- if (mRootView) {
- if (!IsRootVM()) {
- NS_RELEASE(mRootViewManager);
- }
- nsView *parent = mRootView->GetParent();
- if (parent) {
- mRootViewManager = parent->GetViewManager()->RootViewManager();
- NS_ADDREF(mRootViewManager);
- NS_ASSERTION(mRootViewManager != this,
- "Root view had a parent, but it has the same view manager");
- } else {
- mRootViewManager = this;
- }
- }
- }
|