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