From bc3dc7ebe0d1ce0fa3f244644a9365a47dd04c34 Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Tue, 10 Aug 2004 03:24:41 +0000 Subject: [PATCH] Do style reresolves and restyles due to attribute changes asynchronously so that pages that modify two or more style properties of the same object in a row work faster. Bug 230170, r+sr=dbaron git-svn-id: svn://10.0.0.236/trunk@160587 18797224-902f-48f8-a5cc-f745e15eee43 --- .../html/style/src/nsInspectorCSSUtils.cpp | 1 + mozilla/layout/base/nsCSSFrameConstructor.cpp | 189 +++++++++++++----- mozilla/layout/base/nsCSSFrameConstructor.h | 45 ++++- mozilla/layout/base/nsPresShell.cpp | 7 + mozilla/layout/html/base/src/nsPresShell.cpp | 7 + .../html/style/src/nsCSSFrameConstructor.cpp | 189 +++++++++++++----- .../html/style/src/nsCSSFrameConstructor.h | 45 ++++- mozilla/layout/style/nsInspectorCSSUtils.cpp | 1 + mozilla/layout/xul/base/src/nsMenuFrame.cpp | 7 +- .../layout/xul/base/src/nsPopupSetFrame.cpp | 4 + 10 files changed, 402 insertions(+), 93 deletions(-) diff --git a/mozilla/content/html/style/src/nsInspectorCSSUtils.cpp b/mozilla/content/html/style/src/nsInspectorCSSUtils.cpp index 7c44694a9ad..e2be5d262e0 100644 --- a/mozilla/content/html/style/src/nsInspectorCSSUtils.cpp +++ b/mozilla/content/html/style/src/nsInspectorCSSUtils.cpp @@ -145,6 +145,7 @@ nsInspectorCSSUtils::GetStyleContextForContent(nsIContent* aContent, { if (!aPseudo) { nsIFrame* frame = nsnull; + aPresShell->FlushPendingNotifications(Flush_StyleReresolves); aPresShell->GetPrimaryFrameFor(aContent, &frame); if (frame) { nsStyleContext* result = GetStyleContextForFrame(frame); diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index cd14b9df8c5..c7788f4e853 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -115,6 +115,7 @@ #include "nsBoxFrame.h" static NS_DEFINE_CID(kTextNodeCID, NS_TEXTNODE_CID); +static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); #include "nsIDOMWindowInternal.h" #include "nsIMenuFrame.h" @@ -1303,6 +1304,14 @@ nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument) nsContentUtils::GetBoolPref("nglayout.debug.enable_xbl_forms"); } + // XXXbz this should be in Init() or something! + if (!mPendingRestyles.Init()) { + // now what? + } + + // XXXbz this should be in Init() or something! + mEventQueueService = do_GetService(kEventQueueServiceCID); + #ifdef DEBUG static PRBool gFirstTime = PR_TRUE; if (gFirstTime) { @@ -9888,12 +9897,18 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList, void nsCSSFrameConstructor::RestyleElement(nsPresContext *aPresContext, nsIContent *aContent, - nsIFrame *aPrimaryFrame) + nsIFrame *aPrimaryFrame, + nsChangeHint aMinHint) { - if (aPrimaryFrame) { + if (aMinHint & nsChangeHint_ReconstructFrame) { + RecreateFramesForContent(aPresContext, aContent); + } else if (aPrimaryFrame) { nsStyleChangeList changeList; + if (aMinHint != NS_STYLE_HINT_NONE) { + changeList.AppendChange(aPrimaryFrame, aContent, aMinHint); + } nsChangeHint frameChange = aPresContext->GetPresShell()->FrameManager()-> - ComputeStyleChangeFor(aPrimaryFrame, &changeList, NS_STYLE_HINT_NONE); + ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint); if (frameChange & nsChangeHint_ReconstructFrame) { RecreateFramesForContent(aPresContext, aContent); @@ -9926,7 +9941,7 @@ nsCSSFrameConstructor::RestyleLaterSiblings(nsPresContext *aPresContext, nsIFrame* primaryFrame = nsnull; shell->GetPrimaryFrameFor(child, &primaryFrame); - RestyleElement(aPresContext, child, primaryFrame); + RestyleElement(aPresContext, child, primaryFrame, NS_STYLE_HINT_NONE); } } @@ -9973,12 +9988,8 @@ nsCSSFrameConstructor::DoContentStateChanged(nsPresContext* aPresContext, nsReStyleHint rshint = styleSet->HasStateDependentStyle(aPresContext, aContent, aStateMask); - if (rshint & eReStyle_Self) { - RestyleElement(aPresContext, aContent, primaryFrame); - } - if (rshint & eReStyle_LaterSiblings) { - RestyleLaterSiblings(aPresContext, aContent); - } + + PostRestyleEvent(aContent, rshint, NS_STYLE_HINT_NONE); } } } @@ -10068,43 +10079,19 @@ nsCSSFrameConstructor::AttributeChanged(nsPresContext* aPresContext, aAttribute, aModType); - if (reframe) { - result = RecreateFramesForContent(aPresContext, aContent); - } else if (primaryFrame) { - nsStyleChangeList changeList; - // put primary frame on list to deal with, re-resolve may update or add next in flows - changeList.AppendChange(primaryFrame, aContent, hint); - - // there is an effect, so compute it - if (rshint & eReStyle_Self) { - hint = frameManager->ComputeStyleChangeFor(primaryFrame, &changeList, - hint); - } - - // hint is for primary only - if (hint & nsChangeHint_ReconstructFrame) { - result = RecreateFramesForContent(aPresContext, aContent); - changeList.Clear(); - } else { - // let the frame deal with it, since we don't know how to - result = primaryFrame->AttributeChanged(aPresContext, aContent, - aNameSpaceID, aAttribute, - aModType); - // XXXwaterson should probably check for special IB siblings - // here, and propagate the AttributeChanged notification to - // them, as well. Currently, inline frames don't do anything on - // this notification, so it's not that big a deal. - - // handle any children (primary may be on list too) - ProcessRestyledFrames(changeList, aPresContext); - } - } else if (rshint & eReStyle_Self) { - result = MaybeRecreateFramesForContent(aPresContext, aContent); + + // let the frame deal with it now, so we don't have to deal later + if (primaryFrame) { + result = primaryFrame->AttributeChanged(aPresContext, aContent, + aNameSpaceID, aAttribute, + aModType); + // XXXwaterson should probably check for special IB siblings + // here, and propagate the AttributeChanged notification to + // them, as well. Currently, inline frames don't do anything on + // this notification, so it's not that big a deal. } - if (rshint & eReStyle_LaterSiblings) { - RestyleLaterSiblings(aPresContext, aContent); - } + PostRestyleEvent(aContent, rshint, hint); return result; } @@ -10125,6 +10112,13 @@ nsCSSFrameConstructor::WillDestroyFrameTree() { // Prevent frame tree destruction from being O(N^2) mQuoteList.Clear(); + + // Cancel all pending reresolves + mRestyleEventQueue = nsnull; + nsCOMPtr eventQueue; + mEventQueueService->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE, + getter_AddRefs(eventQueue)); + eventQueue->RevokeEvents(this); } //STATIC @@ -13312,3 +13306,108 @@ nsresult nsCSSFrameConstructor::RemoveFixedItems(nsPresContext* aPresContext, return rv; } +PR_STATIC_CALLBACK(PLDHashOperator) +ProcessRestyle(nsISupports* aContent, + nsCSSFrameConstructor::RestyleData& aData, + void* aPresContext) +{ + nsPresContext* context = NS_STATIC_CAST(nsPresContext*, aPresContext); + nsIContent* content = NS_STATIC_CAST(nsIContent*, aContent); + + if (!content->GetDocument() || + content->GetDocument() != context->GetDocument()) { + // Content node has been removed from our document; nothing else to do here + return PL_DHASH_NEXT; + } + + nsIPresShell* shell = context->PresShell(); + + nsIFrame* primaryFrame = nsnull; + shell->GetPrimaryFrameFor(content, &primaryFrame); + if (aData.mRestyleHint & eReStyle_Self) { + shell->FrameConstructor()->RestyleElement(context, content, primaryFrame, + aData.mChangeHint); + } else if (aData.mChangeHint != NS_STYLE_HINT_NONE) { + // Don't need to recompute style; just apply the hint + nsStyleChangeList changeList; + changeList.AppendChange(primaryFrame, content, aData.mChangeHint); + shell->FrameConstructor()->ProcessRestyledFrames(changeList, context); + } + + if (aData.mRestyleHint & eReStyle_LaterSiblings) { + shell->FrameConstructor()->RestyleLaterSiblings(context, content); + } + + return PL_DHASH_NEXT; +} + +void +nsCSSFrameConstructor::ProcessPendingRestyles() +{ + NS_PRECONDITION(mDocument, "No document? Pshaw!\n"); + nsIPresShell* shell = mDocument->GetShellAt(0); + nsPresContext* context = shell->GetPresContext(); + + mPendingRestyles.Enumerate(ProcessRestyle, context); + mPendingRestyles.Clear(); +} + +void +nsCSSFrameConstructor::PostRestyleEvent(nsIContent* aContent, + nsReStyleHint aRestyleHint, + nsChangeHint aMinChangeHint) +{ + if (aRestyleHint == 0 && aMinChangeHint == NS_STYLE_HINT_NONE) { + // Nothing to do here + return; + } + + RestyleData existingData; + existingData.mRestyleHint = nsReStyleHint(0); + existingData.mChangeHint = NS_STYLE_HINT_NONE; + + mPendingRestyles.Get(aContent, &existingData); + existingData.mRestyleHint = + nsReStyleHint(existingData.mRestyleHint | aRestyleHint); + NS_UpdateHint(existingData.mChangeHint, aMinChangeHint); + + mPendingRestyles.Put(aContent, existingData); + + nsCOMPtr eventQueue; + mEventQueueService->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE, + getter_AddRefs(eventQueue)); + + if (eventQueue != mRestyleEventQueue) { + RestyleEvent* ev = new RestyleEvent(this); + if (NS_FAILED(eventQueue->PostEvent(ev))) { + PL_DestroyEvent(ev); + // XXXbz and what? + } else { + mRestyleEventQueue = eventQueue; + } + } +} + +PR_STATIC_CALLBACK(void*) +HandleRestyleEvent(PLEvent* aEvent) +{ + nsCSSFrameConstructor::RestyleEvent* evt = + NS_STATIC_CAST(nsCSSFrameConstructor::RestyleEvent*, aEvent); + evt->HandleEvent(); + return nsnull; +} + +PR_STATIC_CALLBACK(void) +DestroyRestyleEvent(PLEvent* aEvent) +{ + delete NS_STATIC_CAST(nsCSSFrameConstructor::RestyleEvent*, aEvent); +} + +nsCSSFrameConstructor::RestyleEvent::RestyleEvent(nsCSSFrameConstructor* aConstructor) +{ + NS_PRECONDITION(aConstructor, "Must have a constructor!"); + + PL_InitEvent(this, aConstructor, + ::HandleRestyleEvent, ::DestroyRestyleEvent); +} + diff --git a/mozilla/layout/base/nsCSSFrameConstructor.h b/mozilla/layout/base/nsCSSFrameConstructor.h index 81488c4674c..20e8e8f4a5c 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.h +++ b/mozilla/layout/base/nsCSSFrameConstructor.h @@ -41,6 +41,11 @@ #include "nsILayoutHistoryState.h" #include "nsIXBLService.h" #include "nsQuoteList.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "plevent.h" +#include "nsIEventQueueService.h" +#include "nsIEventQueue.h" class nsIDocument; struct nsFrameItems; @@ -135,6 +140,10 @@ public: nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray, nsPresContext* aPresContext); + void ProcessPendingRestyles(); + void PostRestyleEvent(nsIContent* aContent, nsReStyleHint aRestyleHint, + nsChangeHint aMinChangeHint); + // Notification that we were unable to render a replaced element. nsresult CantRenderReplacedElement(nsIPresShell* aPresShell, nsPresContext* aPresContext, @@ -192,13 +201,17 @@ private: nsIContent* aContent, PRInt32 aStateMask); +public: + /* aMinHint is the minimal change that should be made to the element */ void RestyleElement(nsPresContext* aPresContext, nsIContent* aContent, - nsIFrame* aPrimaryFrame); + nsIFrame* aPrimaryFrame, + nsChangeHint aMinHint); void RestyleLaterSiblings(nsPresContext* aPresContext, nsIContent* aContent); +private: nsresult InitAndRestoreFrame (nsPresContext* aPresContext, nsFrameConstructorState& aState, nsIContent* aContent, @@ -1030,6 +1043,32 @@ private: mQuoteList.RecalcAll(); } +public: + struct RestyleData; + friend struct RestyleData; + + struct RestyleData { + nsReStyleHint mRestyleHint; // What we want to restyle + nsChangeHint mChangeHint; // The minimal change hint for "self" + }; + + struct RestyleEvent; + friend struct RestyleEvent; + + struct RestyleEvent : public PLEvent { + RestyleEvent(nsCSSFrameConstructor* aConstructor); + ~RestyleEvent() { } + void HandleEvent() { + nsCSSFrameConstructor* constructor = + NS_STATIC_CAST(nsCSSFrameConstructor*, owner); + constructor->ProcessPendingRestyles(); + constructor->mRestyleEventQueue = nsnull; + } + }; + +protected: + nsCOMPtr mRestyleEventQueue; + private: nsIDocument* mDocument; @@ -1043,6 +1082,10 @@ private: nsCOMPtr mTempFrameTreeState; + nsCOMPtr mEventQueueService; + + nsDataHashtable mPendingRestyles; + static nsIXBLService * gXBLService; }; diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index 6bfeb649172..bd04e671115 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -5081,11 +5081,18 @@ PresShell::FlushPendingNotifications(mozFlushType aType) IsSafeToFlush(isSafeToFlush); if (isSafeToFlush) { + // XXXbz we should probably do a view batch no matter what here + // and just change the flag for the batch end based on whether + // Flush_OnlyPaint is set. PRBool batchViews = (aType & Flush_OnlyPaint); if (batchViews && mViewManager) { mViewManager->BeginUpdateViewBatch(); } + if (aType & Flush_StyleReresolves) { + mFrameConstructor->ProcessPendingRestyles(); + } + if (aType & Flush_OnlyReflow) { ProcessReflowCommands(PR_FALSE); } diff --git a/mozilla/layout/html/base/src/nsPresShell.cpp b/mozilla/layout/html/base/src/nsPresShell.cpp index 6bfeb649172..bd04e671115 100644 --- a/mozilla/layout/html/base/src/nsPresShell.cpp +++ b/mozilla/layout/html/base/src/nsPresShell.cpp @@ -5081,11 +5081,18 @@ PresShell::FlushPendingNotifications(mozFlushType aType) IsSafeToFlush(isSafeToFlush); if (isSafeToFlush) { + // XXXbz we should probably do a view batch no matter what here + // and just change the flag for the batch end based on whether + // Flush_OnlyPaint is set. PRBool batchViews = (aType & Flush_OnlyPaint); if (batchViews && mViewManager) { mViewManager->BeginUpdateViewBatch(); } + if (aType & Flush_StyleReresolves) { + mFrameConstructor->ProcessPendingRestyles(); + } + if (aType & Flush_OnlyReflow) { ProcessReflowCommands(PR_FALSE); } diff --git a/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp b/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp index cd14b9df8c5..c7788f4e853 100644 --- a/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp @@ -115,6 +115,7 @@ #include "nsBoxFrame.h" static NS_DEFINE_CID(kTextNodeCID, NS_TEXTNODE_CID); +static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); #include "nsIDOMWindowInternal.h" #include "nsIMenuFrame.h" @@ -1303,6 +1304,14 @@ nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument) nsContentUtils::GetBoolPref("nglayout.debug.enable_xbl_forms"); } + // XXXbz this should be in Init() or something! + if (!mPendingRestyles.Init()) { + // now what? + } + + // XXXbz this should be in Init() or something! + mEventQueueService = do_GetService(kEventQueueServiceCID); + #ifdef DEBUG static PRBool gFirstTime = PR_TRUE; if (gFirstTime) { @@ -9888,12 +9897,18 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList, void nsCSSFrameConstructor::RestyleElement(nsPresContext *aPresContext, nsIContent *aContent, - nsIFrame *aPrimaryFrame) + nsIFrame *aPrimaryFrame, + nsChangeHint aMinHint) { - if (aPrimaryFrame) { + if (aMinHint & nsChangeHint_ReconstructFrame) { + RecreateFramesForContent(aPresContext, aContent); + } else if (aPrimaryFrame) { nsStyleChangeList changeList; + if (aMinHint != NS_STYLE_HINT_NONE) { + changeList.AppendChange(aPrimaryFrame, aContent, aMinHint); + } nsChangeHint frameChange = aPresContext->GetPresShell()->FrameManager()-> - ComputeStyleChangeFor(aPrimaryFrame, &changeList, NS_STYLE_HINT_NONE); + ComputeStyleChangeFor(aPrimaryFrame, &changeList, aMinHint); if (frameChange & nsChangeHint_ReconstructFrame) { RecreateFramesForContent(aPresContext, aContent); @@ -9926,7 +9941,7 @@ nsCSSFrameConstructor::RestyleLaterSiblings(nsPresContext *aPresContext, nsIFrame* primaryFrame = nsnull; shell->GetPrimaryFrameFor(child, &primaryFrame); - RestyleElement(aPresContext, child, primaryFrame); + RestyleElement(aPresContext, child, primaryFrame, NS_STYLE_HINT_NONE); } } @@ -9973,12 +9988,8 @@ nsCSSFrameConstructor::DoContentStateChanged(nsPresContext* aPresContext, nsReStyleHint rshint = styleSet->HasStateDependentStyle(aPresContext, aContent, aStateMask); - if (rshint & eReStyle_Self) { - RestyleElement(aPresContext, aContent, primaryFrame); - } - if (rshint & eReStyle_LaterSiblings) { - RestyleLaterSiblings(aPresContext, aContent); - } + + PostRestyleEvent(aContent, rshint, NS_STYLE_HINT_NONE); } } } @@ -10068,43 +10079,19 @@ nsCSSFrameConstructor::AttributeChanged(nsPresContext* aPresContext, aAttribute, aModType); - if (reframe) { - result = RecreateFramesForContent(aPresContext, aContent); - } else if (primaryFrame) { - nsStyleChangeList changeList; - // put primary frame on list to deal with, re-resolve may update or add next in flows - changeList.AppendChange(primaryFrame, aContent, hint); - - // there is an effect, so compute it - if (rshint & eReStyle_Self) { - hint = frameManager->ComputeStyleChangeFor(primaryFrame, &changeList, - hint); - } - - // hint is for primary only - if (hint & nsChangeHint_ReconstructFrame) { - result = RecreateFramesForContent(aPresContext, aContent); - changeList.Clear(); - } else { - // let the frame deal with it, since we don't know how to - result = primaryFrame->AttributeChanged(aPresContext, aContent, - aNameSpaceID, aAttribute, - aModType); - // XXXwaterson should probably check for special IB siblings - // here, and propagate the AttributeChanged notification to - // them, as well. Currently, inline frames don't do anything on - // this notification, so it's not that big a deal. - - // handle any children (primary may be on list too) - ProcessRestyledFrames(changeList, aPresContext); - } - } else if (rshint & eReStyle_Self) { - result = MaybeRecreateFramesForContent(aPresContext, aContent); + + // let the frame deal with it now, so we don't have to deal later + if (primaryFrame) { + result = primaryFrame->AttributeChanged(aPresContext, aContent, + aNameSpaceID, aAttribute, + aModType); + // XXXwaterson should probably check for special IB siblings + // here, and propagate the AttributeChanged notification to + // them, as well. Currently, inline frames don't do anything on + // this notification, so it's not that big a deal. } - if (rshint & eReStyle_LaterSiblings) { - RestyleLaterSiblings(aPresContext, aContent); - } + PostRestyleEvent(aContent, rshint, hint); return result; } @@ -10125,6 +10112,13 @@ nsCSSFrameConstructor::WillDestroyFrameTree() { // Prevent frame tree destruction from being O(N^2) mQuoteList.Clear(); + + // Cancel all pending reresolves + mRestyleEventQueue = nsnull; + nsCOMPtr eventQueue; + mEventQueueService->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE, + getter_AddRefs(eventQueue)); + eventQueue->RevokeEvents(this); } //STATIC @@ -13312,3 +13306,108 @@ nsresult nsCSSFrameConstructor::RemoveFixedItems(nsPresContext* aPresContext, return rv; } +PR_STATIC_CALLBACK(PLDHashOperator) +ProcessRestyle(nsISupports* aContent, + nsCSSFrameConstructor::RestyleData& aData, + void* aPresContext) +{ + nsPresContext* context = NS_STATIC_CAST(nsPresContext*, aPresContext); + nsIContent* content = NS_STATIC_CAST(nsIContent*, aContent); + + if (!content->GetDocument() || + content->GetDocument() != context->GetDocument()) { + // Content node has been removed from our document; nothing else to do here + return PL_DHASH_NEXT; + } + + nsIPresShell* shell = context->PresShell(); + + nsIFrame* primaryFrame = nsnull; + shell->GetPrimaryFrameFor(content, &primaryFrame); + if (aData.mRestyleHint & eReStyle_Self) { + shell->FrameConstructor()->RestyleElement(context, content, primaryFrame, + aData.mChangeHint); + } else if (aData.mChangeHint != NS_STYLE_HINT_NONE) { + // Don't need to recompute style; just apply the hint + nsStyleChangeList changeList; + changeList.AppendChange(primaryFrame, content, aData.mChangeHint); + shell->FrameConstructor()->ProcessRestyledFrames(changeList, context); + } + + if (aData.mRestyleHint & eReStyle_LaterSiblings) { + shell->FrameConstructor()->RestyleLaterSiblings(context, content); + } + + return PL_DHASH_NEXT; +} + +void +nsCSSFrameConstructor::ProcessPendingRestyles() +{ + NS_PRECONDITION(mDocument, "No document? Pshaw!\n"); + nsIPresShell* shell = mDocument->GetShellAt(0); + nsPresContext* context = shell->GetPresContext(); + + mPendingRestyles.Enumerate(ProcessRestyle, context); + mPendingRestyles.Clear(); +} + +void +nsCSSFrameConstructor::PostRestyleEvent(nsIContent* aContent, + nsReStyleHint aRestyleHint, + nsChangeHint aMinChangeHint) +{ + if (aRestyleHint == 0 && aMinChangeHint == NS_STYLE_HINT_NONE) { + // Nothing to do here + return; + } + + RestyleData existingData; + existingData.mRestyleHint = nsReStyleHint(0); + existingData.mChangeHint = NS_STYLE_HINT_NONE; + + mPendingRestyles.Get(aContent, &existingData); + existingData.mRestyleHint = + nsReStyleHint(existingData.mRestyleHint | aRestyleHint); + NS_UpdateHint(existingData.mChangeHint, aMinChangeHint); + + mPendingRestyles.Put(aContent, existingData); + + nsCOMPtr eventQueue; + mEventQueueService->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE, + getter_AddRefs(eventQueue)); + + if (eventQueue != mRestyleEventQueue) { + RestyleEvent* ev = new RestyleEvent(this); + if (NS_FAILED(eventQueue->PostEvent(ev))) { + PL_DestroyEvent(ev); + // XXXbz and what? + } else { + mRestyleEventQueue = eventQueue; + } + } +} + +PR_STATIC_CALLBACK(void*) +HandleRestyleEvent(PLEvent* aEvent) +{ + nsCSSFrameConstructor::RestyleEvent* evt = + NS_STATIC_CAST(nsCSSFrameConstructor::RestyleEvent*, aEvent); + evt->HandleEvent(); + return nsnull; +} + +PR_STATIC_CALLBACK(void) +DestroyRestyleEvent(PLEvent* aEvent) +{ + delete NS_STATIC_CAST(nsCSSFrameConstructor::RestyleEvent*, aEvent); +} + +nsCSSFrameConstructor::RestyleEvent::RestyleEvent(nsCSSFrameConstructor* aConstructor) +{ + NS_PRECONDITION(aConstructor, "Must have a constructor!"); + + PL_InitEvent(this, aConstructor, + ::HandleRestyleEvent, ::DestroyRestyleEvent); +} + diff --git a/mozilla/layout/html/style/src/nsCSSFrameConstructor.h b/mozilla/layout/html/style/src/nsCSSFrameConstructor.h index 81488c4674c..20e8e8f4a5c 100644 --- a/mozilla/layout/html/style/src/nsCSSFrameConstructor.h +++ b/mozilla/layout/html/style/src/nsCSSFrameConstructor.h @@ -41,6 +41,11 @@ #include "nsILayoutHistoryState.h" #include "nsIXBLService.h" #include "nsQuoteList.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "plevent.h" +#include "nsIEventQueueService.h" +#include "nsIEventQueue.h" class nsIDocument; struct nsFrameItems; @@ -135,6 +140,10 @@ public: nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray, nsPresContext* aPresContext); + void ProcessPendingRestyles(); + void PostRestyleEvent(nsIContent* aContent, nsReStyleHint aRestyleHint, + nsChangeHint aMinChangeHint); + // Notification that we were unable to render a replaced element. nsresult CantRenderReplacedElement(nsIPresShell* aPresShell, nsPresContext* aPresContext, @@ -192,13 +201,17 @@ private: nsIContent* aContent, PRInt32 aStateMask); +public: + /* aMinHint is the minimal change that should be made to the element */ void RestyleElement(nsPresContext* aPresContext, nsIContent* aContent, - nsIFrame* aPrimaryFrame); + nsIFrame* aPrimaryFrame, + nsChangeHint aMinHint); void RestyleLaterSiblings(nsPresContext* aPresContext, nsIContent* aContent); +private: nsresult InitAndRestoreFrame (nsPresContext* aPresContext, nsFrameConstructorState& aState, nsIContent* aContent, @@ -1030,6 +1043,32 @@ private: mQuoteList.RecalcAll(); } +public: + struct RestyleData; + friend struct RestyleData; + + struct RestyleData { + nsReStyleHint mRestyleHint; // What we want to restyle + nsChangeHint mChangeHint; // The minimal change hint for "self" + }; + + struct RestyleEvent; + friend struct RestyleEvent; + + struct RestyleEvent : public PLEvent { + RestyleEvent(nsCSSFrameConstructor* aConstructor); + ~RestyleEvent() { } + void HandleEvent() { + nsCSSFrameConstructor* constructor = + NS_STATIC_CAST(nsCSSFrameConstructor*, owner); + constructor->ProcessPendingRestyles(); + constructor->mRestyleEventQueue = nsnull; + } + }; + +protected: + nsCOMPtr mRestyleEventQueue; + private: nsIDocument* mDocument; @@ -1043,6 +1082,10 @@ private: nsCOMPtr mTempFrameTreeState; + nsCOMPtr mEventQueueService; + + nsDataHashtable mPendingRestyles; + static nsIXBLService * gXBLService; }; diff --git a/mozilla/layout/style/nsInspectorCSSUtils.cpp b/mozilla/layout/style/nsInspectorCSSUtils.cpp index 7c44694a9ad..e2be5d262e0 100644 --- a/mozilla/layout/style/nsInspectorCSSUtils.cpp +++ b/mozilla/layout/style/nsInspectorCSSUtils.cpp @@ -145,6 +145,7 @@ nsInspectorCSSUtils::GetStyleContextForContent(nsIContent* aContent, { if (!aPseudo) { nsIFrame* frame = nsnull; + aPresShell->FlushPendingNotifications(Flush_StyleReresolves); aPresShell->GetPrimaryFrameFor(aContent, &frame); if (frame) { nsStyleContext* result = GetStyleContextForFrame(frame); diff --git a/mozilla/layout/xul/base/src/nsMenuFrame.cpp b/mozilla/layout/xul/base/src/nsMenuFrame.cpp index 22f1dd68ec5..c6f3be15eca 100644 --- a/mozilla/layout/xul/base/src/nsMenuFrame.cpp +++ b/mozilla/layout/xul/base/src/nsMenuFrame.cpp @@ -597,8 +597,13 @@ nsMenuFrame::MarkAsGenerated() if (child) { nsAutoString genVal; child->GetAttr(kNameSpaceID_None, nsXULAtoms::menugenerated, genVal); - if (genVal.IsEmpty()) + if (genVal.IsEmpty()) { child->SetAttr(kNameSpaceID_None, nsXULAtoms::menugenerated, NS_LITERAL_STRING("true"), PR_TRUE); + // Menus assume the frame for the popup exists hereafter, so we need to + // flush out the style reresolve we just caused to ensure the frame gets + // constructed. + mPresContext->PresShell()->FlushPendingNotifications(Flush_StyleReresolves); + } } return NS_OK; diff --git a/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp b/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp index 51cc1326bfb..6ea75394467 100644 --- a/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp +++ b/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp @@ -492,6 +492,10 @@ nsPopupSetFrame::MarkAsGenerated(nsIContent* aPopupContent) // Generate this element. aPopupContent->SetAttr(kNameSpaceID_None, nsXULAtoms::menugenerated, NS_LITERAL_STRING("true"), PR_TRUE); + // Menus assume the frame for the popup exists hereafter, so we need to + // flush out the style reresolve we just caused to ensure the frame gets + // constructed. + mPresContext->PresShell()->FlushPendingNotifications(Flush_StyleReresolves); } }