Bug 401155: Make it safe to create a wrapper for an object at any time by making sure wrapping doesn't write script. Also set up a service that tries to keep track of when it's safe to execute script. r=bz sr=jst

git-svn-id: svn://10.0.0.236/trunk@247875 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
jonas%sicking.cc
2008-03-14 23:08:59 +00:00
parent 8d917ebc34
commit 546ba272ce
26 changed files with 556 additions and 264 deletions

View File

@@ -760,6 +760,7 @@ struct nsCallbackEventRequest
// ----------------------------------------------------------------------------
class nsPresShellEventCB;
class nsAutoCauseReflowNotifier;
class PresShell : public nsIPresShell, public nsIViewObserver,
public nsStubDocumentObserver,
@@ -1009,8 +1010,14 @@ protected:
void UnsuppressAndInvalidate();
void WillCauseReflow() { ++mChangeNestCount; }
void WillCauseReflow() {
nsContentUtils::AddScriptBlocker();
++mChangeNestCount;
}
nsresult DidCauseReflow();
friend class nsAutoCauseReflowNotifier;
void WillDoReflow();
void DidDoReflow();
nsresult ProcessReflowCommands(PRBool aInterruptible);
@@ -1209,6 +1216,29 @@ private:
nsPluginEnumCallback aCallback);
};
class nsAutoCauseReflowNotifier
{
public:
nsAutoCauseReflowNotifier(PresShell* aShell)
: mShell(aShell)
{
mShell->WillCauseReflow();
}
~nsAutoCauseReflowNotifier()
{
// This check should not be needed. Currently the only place that seem
// to need it is the code that deals with bug 337586.
if (!mShell->mHaveShutDown) {
mShell->DidCauseReflow();
}
else {
nsContentUtils::RemoveScriptBlocker();
}
}
PresShell* mShell;
};
class nsPresShellEventCB : public nsDispatchingCallback
{
public:
@@ -2383,31 +2413,35 @@ PresShell::InitialReflow(nscoord aWidth, nscoord aHeight)
MOZ_TIMER_RESET(mFrameCreationWatch);
MOZ_TIMER_START(mFrameCreationWatch);
WillCauseReflow();
mFrameConstructor->BeginUpdate();
{
nsAutoCauseReflowNotifier reflowNotifier(this);
mFrameConstructor->BeginUpdate();
if (!rootFrame) {
// Have style sheet processor construct a frame for the
// precursors to the root content object's frame
mFrameConstructor->ConstructRootFrame(root, &rootFrame);
FrameManager()->SetRootFrame(rootFrame);
if (!rootFrame) {
// Have style sheet processor construct a frame for the
// precursors to the root content object's frame
mFrameConstructor->ConstructRootFrame(root, &rootFrame);
FrameManager()->SetRootFrame(rootFrame);
}
// Have the style sheet processor construct frame for the root
// content object down
mFrameConstructor->ContentInserted(nsnull, root, 0, nsnull);
VERIFY_STYLE_TREE;
MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::InitialReflow(), this=%p\n",
(void*)this));
MOZ_TIMER_STOP(mFrameCreationWatch);
// Something in mFrameConstructor->ContentInserted may have caused
// Destroy() to get called, bug 337586.
NS_ENSURE_STATE(!mHaveShutDown);
mFrameConstructor->EndUpdate();
}
// Have the style sheet processor construct frame for the root
// content object down
mFrameConstructor->ContentInserted(nsnull, root, 0, nsnull);
VERIFY_STYLE_TREE;
MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::InitialReflow(), this=%p\n",
(void*)this));
MOZ_TIMER_STOP(mFrameCreationWatch);
// Something in mFrameConstructor->ContentInserted may have caused
// Destroy() to get called, bug 337586.
// DidCauseReflow may have killed us too
NS_ENSURE_STATE(!mHaveShutDown);
mFrameConstructor->EndUpdate();
DidCauseReflow();
// Run the XBL binding constructors for any new frames we've constructed
mDocument->BindingManager()->ProcessAttachedQueue();
@@ -2525,10 +2559,10 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
// XXX Do a full invalidate at the beginning so that invalidates along
// the way don't have region accumulation issues?
WillCauseReflow();
WillDoReflow();
{
nsAutoCauseReflowNotifier crNotifier(this);
WillDoReflow();
// Kick off a top-down reflow
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
mIsReflowing = PR_TRUE;
@@ -2538,7 +2572,6 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
mIsReflowing = PR_FALSE;
}
DidCauseReflow();
DidDoReflow();
}
@@ -3086,6 +3119,7 @@ PresShell::RestoreRootScrollPosition()
// we're scrolling to our restored position. Entering reflow for the
// scrollable frame will cause it to reenter ScrollToRestoredPosition(), and
// it'll get all confused.
nsAutoScriptBlocker scriptBlocker;
++mChangeNestCount;
if (historyState) {
@@ -3376,6 +3410,8 @@ PresShell::RecreateFramesFor(nsIContent* aContent)
// start messing with the frame model; otherwise we can get content doubling.
mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
nsAutoScriptBlocker scriptBlocker;
nsStyleChangeList changeList;
changeList.AppendChange(nsnull, aContent, nsChangeHint_ReconstructFrame);
@@ -4467,12 +4503,16 @@ PresShell::HandlePostedReflowCallbacks()
NS_IMETHODIMP
PresShell::IsSafeToFlush(PRBool& aIsSafeToFlush)
{
aIsSafeToFlush = PR_TRUE;
// XXX technically we don't need to check anything but
// nsContentUtils::IsSafeToRunScript here since that should be false
// if any of the other flags are set.
// Not safe if we are reflowing or in the middle of frame construction
aIsSafeToFlush = nsContentUtils::IsSafeToRunScript() &&
!mIsReflowing &&
!mChangeNestCount;
if (mIsReflowing || mChangeNestCount) {
// Not safe if we are reflowing or in the middle of frame construction
aIsSafeToFlush = PR_FALSE;
} else {
if (aIsSafeToFlush) {
// Not safe if we are painting
nsIViewManager* viewManager = GetViewManager();
if (viewManager) {
@@ -4483,6 +4523,10 @@ PresShell::IsSafeToFlush(PRBool& aIsSafeToFlush)
}
}
}
NS_ASSERTION(aIsSafeToFlush == nsContentUtils::IsSafeToRunScript(),
"Someone forgot to block scripts");
return NS_OK;
}
@@ -4586,7 +4630,8 @@ PresShell::CharacterDataChanged(nsIDocument *aDocument,
NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
WillCauseReflow();
nsAutoCauseReflowNotifier crNotifier(this);
if (mCaret) {
// Invalidate the caret's current location before we call into the frame
// constructor. It is important to do this now, and not wait until the
@@ -4615,7 +4660,6 @@ PresShell::CharacterDataChanged(nsIDocument *aDocument,
mFrameConstructor->CharacterDataChanged(aContent, aInfo->mAppend);
VERIFY_STYLE_TREE;
DidCauseReflow();
}
void
@@ -4628,10 +4672,9 @@ PresShell::ContentStatesChanged(nsIDocument* aDocument,
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
if (mDidInitialReflow) {
WillCauseReflow();
nsAutoCauseReflowNotifier crNotifier(this);
mFrameConstructor->ContentStatesChanged(aContent1, aContent2, aStateMask);
VERIFY_STYLE_TREE;
DidCauseReflow();
}
}
@@ -4651,11 +4694,10 @@ PresShell::AttributeChanged(nsIDocument* aDocument,
// initial reflow to begin observing the document. That would
// squelch any other inappropriate notifications as well.
if (mDidInitialReflow) {
WillCauseReflow();
nsAutoCauseReflowNotifier crNotifier(this);
mFrameConstructor->AttributeChanged(aContent, aNameSpaceID,
aAttribute, aModType, aStateMask);
VERIFY_STYLE_TREE;
DidCauseReflow();
}
}
@@ -4672,7 +4714,7 @@ PresShell::ContentAppended(nsIDocument *aDocument,
return;
}
WillCauseReflow();
nsAutoCauseReflowNotifier crNotifier(this);
MOZ_TIMER_DEBUGLOG(("Start: Frame Creation: PresShell::ContentAppended(), this=%p\n", this));
MOZ_TIMER_START(mFrameCreationWatch);
@@ -4686,7 +4728,6 @@ PresShell::ContentAppended(nsIDocument *aDocument,
MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::ContentAppended(), this=%p\n", this));
MOZ_TIMER_STOP(mFrameCreationWatch);
DidCauseReflow();
}
void
@@ -4702,7 +4743,7 @@ PresShell::ContentInserted(nsIDocument* aDocument,
return;
}
WillCauseReflow();
nsAutoCauseReflowNotifier crNotifier(this);
// Call this here so it only happens for real content mutations and
// not cases when the frame constructor calls its own methods to force
@@ -4713,7 +4754,6 @@ PresShell::ContentInserted(nsIDocument* aDocument,
mFrameConstructor->ContentInserted(aContainer, aChild,
aIndexInContainer, nsnull);
VERIFY_STYLE_TREE;
DidCauseReflow();
}
void
@@ -4734,7 +4774,7 @@ PresShell::ContentRemoved(nsIDocument *aDocument,
// it can clean up any state related to the content.
mPresContext->EventStateManager()->ContentRemoved(aChild);
WillCauseReflow();
nsAutoCauseReflowNotifier crNotifier(this);
// Call this here so it only happens for real content mutations and
// not cases when the frame constructor calls its own methods to force
@@ -4747,18 +4787,14 @@ PresShell::ContentRemoved(nsIDocument *aDocument,
aIndexInContainer, &didReconstruct);
VERIFY_STYLE_TREE;
DidCauseReflow();
}
nsresult
PresShell::ReconstructFrames(void)
{
nsresult rv = NS_OK;
WillCauseReflow();
rv = mFrameConstructor->ReconstructDocElementHierarchy();
nsAutoCauseReflowNotifier crNotifier(this);
nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy();
VERIFY_STYLE_TREE;
DidCauseReflow();
return rv;
}
@@ -5527,7 +5563,11 @@ PresShell::HandleEvent(nsIView *aView,
{
NS_ASSERTION(aView, "null view");
if (mIsDestroying || mIsReflowing || mChangeNestCount) {
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"How did we get here if it's not safe to run scripts?");
if (mIsDestroying || mIsReflowing || mChangeNestCount ||
!nsContentUtils::IsSafeToRunScript()) {
return NS_OK;
}
@@ -6169,6 +6209,8 @@ PresShell::DidCauseReflow()
PostReflowEvent();
}
nsContentUtils::RemoveScriptBlocker();
return NS_OK;
}
@@ -6346,6 +6388,7 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible)
// Scope for the reflow entry point
{
nsAutoScriptBlocker scriptBlocker;
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
mIsReflowing = PR_TRUE;
@@ -6375,7 +6418,10 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible)
mIsReflowing = PR_FALSE;
}
DidDoReflow();
// Exiting the scriptblocker might have killed us
if (!mIsDestroying) {
DidDoReflow();
}
// DidDoReflow might have killed us
if (!mIsDestroying) {
@@ -6512,9 +6558,12 @@ PresShell::Observe(nsISupports* aSubject,
ReframeImageBoxes, &changeList);
// Mark ourselves as not safe to flush while we're doing frame
// construction.
++mChangeNestCount;
mFrameConstructor->ProcessRestyledFrames(changeList);
--mChangeNestCount;
{
nsAutoScriptBlocker scriptBlocker;
++mChangeNestCount;
mFrameConstructor->ProcessRestyledFrames(changeList);
--mChangeNestCount;
}
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
#ifdef ACCESSIBILITY