From 4041d0b15514b5aac3b4d9bb833151bed4b78fd7 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Mon, 4 May 2015 14:29:19 -0500 Subject: [PATCH] Bug 1159772. Don't let nsContainerFrame::SyncWindowProperties make widget calls that can result in synchronous painting during reflow or frame construction. r=mats Specifically on Windows nsIWidget::SetTransparencyMode can result in sync painting. So we give nsContainerFrame::SyncWindowProperties a sync or async option and use the view manager post pending update infrastructure to flush SyncWindowProperties calls async. --- layout/base/nsCSSFrameConstructor.cpp | 2 +- layout/base/nsIPresShell.h | 6 ++++-- layout/base/nsPresShell.cpp | 13 ++++++++++++- layout/generic/nsContainerFrame.cpp | 26 ++++++++++++++++++++------ layout/generic/nsContainerFrame.h | 10 ++++++++-- view/nsView.cpp | 10 ++++++++++ view/nsView.h | 3 +++ view/nsViewManager.cpp | 11 +++++++++++ 8 files changed, 69 insertions(+), 12 deletions(-) diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 319d29d0749..4248e7fe76e 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -2688,7 +2688,7 @@ nsCSSFrameConstructor::ConstructRootFrame() nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame, viewportPseudoStyle, rootView); nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame, - rootView); + rootView, nullptr, nsContainerFrame::SET_ASYNC); // Make it an absolute container for fixed-pos elements viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 8422a75dd80..fe929b3f552 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -140,8 +140,8 @@ typedef struct CapturingContentInfo { // d910f009-d209-74c1-6b04-30c83c051c78 #define NS_IPRESSHELL_IID \ - { 0xd910f009, 0xd209, 0x74c1, \ - { 0x6b, 0x04, 0x30, 0xc8, 0x3c, 0x05, 0x1c, 0x78 } } + { 0x025264c6, 0x0b12, 0x4804, \ + { 0xa3, 0x3e, 0xb7, 0x73, 0xf2, 0x19, 0x48, 0x90 } } // debug VerifyReflow flags #define VERIFY_REFLOW_ON 0x01 @@ -1661,6 +1661,8 @@ public: bool HasPendingReflow() const { return mReflowScheduled || mReflowContinueTimer; } + void SyncWindowProperties(nsView* aView); + protected: friend class nsRefreshDriver; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 7dbdbb19e8d..1a4fb2a3bc9 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -9245,7 +9245,8 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible) target->GetView(), boundsRelativeToTarget); nsContainerFrame::SyncWindowProperties(mPresContext, target, - target->GetView(), &rcx); + target->GetView(), &rcx, + nsContainerFrame::SET_ASYNC); target->DidReflow(mPresContext, nullptr, nsDidReflowStatus::FINISHED); if (target == rootFrame && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) { @@ -11081,3 +11082,13 @@ PresShell::ResumePainting() mPaintingIsFrozen = false; GetPresContext()->RefreshDriver()->Thaw(); } + +void +nsIPresShell::SyncWindowProperties(nsView* aView) +{ + nsIFrame* frame = aView->GetFrame(); + if (frame && mPresContext) { + nsRenderingContext rcx(CreateReferenceRenderingContext()); + nsContainerFrame::SyncWindowProperties(mPresContext, frame, aView, &rcx, 0); + } +} diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index dd8d1d35c11..33aa9e48471 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -609,14 +609,15 @@ IsTopLevelWidget(nsIWidget* aWidget) void nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, nsIFrame* aFrame, - nsView* aView, - nsRenderingContext* aRC) + nsView* aView, + nsRenderingContext* aRC, + uint32_t aFlags) { #ifdef MOZ_XUL if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget()) return; - nsIWidget* windowWidget = GetPresContextContainerWidget(aPresContext); + nsCOMPtr windowWidget = GetPresContextContainerWidget(aPresContext); if (!windowWidget || !IsTopLevelWidget(windowWidget)) return; @@ -650,14 +651,27 @@ nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, if (!rootFrame) return; + if (aFlags & SET_ASYNC) { + aView->SetNeedsWindowPropertiesSync(); + return; + } + + nsRefPtr kungFuDeathGrip(aPresContext); + nsWeakFrame weak(rootFrame); + nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame); - nsIWidget* viewWidget = aView->GetWidget(); + int32_t shadow = rootFrame->StyleUIReset()->mWindowShadow; + nsCOMPtr viewWidget = aView->GetWidget(); viewWidget->SetTransparencyMode(mode); - windowWidget->SetWindowShadowStyle(rootFrame->StyleUIReset()->mWindowShadow); + windowWidget->SetWindowShadowStyle(shadow); if (!aRC) return; - + + if (!weak.IsAlive()) { + return; + } + nsBoxLayoutState aState(aPresContext, aRC); nsSize minSize = rootFrame->GetMinSize(aState); nsSize maxSize = rootFrame->GetMaxSize(aState); diff --git a/layout/generic/nsContainerFrame.h b/layout/generic/nsContainerFrame.h index 59bd5bb5335..d3fcf6c2b16 100644 --- a/layout/generic/nsContainerFrame.h +++ b/layout/generic/nsContainerFrame.h @@ -180,10 +180,16 @@ public: // Syncs properties to the top level view and window, like transparency and // shadow. + // The SET_ASYNC indicates that the actual nsIWidget calls to sync the window + // properties should be done async. + enum { + SET_ASYNC = 0x01, + }; static void SyncWindowProperties(nsPresContext* aPresContext, nsIFrame* aFrame, - nsView* aView, - nsRenderingContext* aRC = nullptr); + nsView* aView, + nsRenderingContext* aRC, + uint32_t aFlags); // Sets the view's attributes from the frame style. // - visibility diff --git a/view/nsView.cpp b/view/nsView.cpp index 0777d2ce0bc..cf38397e3f4 100644 --- a/view/nsView.cpp +++ b/view/nsView.cpp @@ -673,6 +673,16 @@ nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility) } } +void +nsView::SetNeedsWindowPropertiesSync() +{ + mNeedsWindowPropertiesSync = true; + if (mViewManager) { + mViewManager->PostPendingUpdate(); + } +} + + // Attach to a top level widget and start receiving mirrored events. nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget) { diff --git a/view/nsView.h b/view/nsView.h index 5fe51ddd3f3..6aa2ba407be 100644 --- a/view/nsView.h +++ b/view/nsView.h @@ -290,6 +290,8 @@ public: mForcedRepaint = aForceRepaint; } + void SetNeedsWindowPropertiesSync(); + /** * Make aWidget direct its events to this view. * The caller must call DetachWidgetEventHandler before this view @@ -463,6 +465,7 @@ private: uint32_t mVFlags; bool mWidgetIsTopLevel; bool mForcedRepaint; + bool mNeedsWindowPropertiesSync; }; #endif diff --git a/view/nsViewManager.cpp b/view/nsViewManager.cpp index 9e35bebb0b0..5bfd09e6aed 100644 --- a/view/nsViewManager.cpp +++ b/view/nsViewManager.cpp @@ -367,6 +367,17 @@ nsViewManager::ProcessPendingUpdatesForView(nsView* aView, 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); }