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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user