diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index e92b1e9ec98..57b5619533d 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -3668,10 +3668,27 @@ nsDocument::RemoveObserver(nsIDocumentObserver* aObserver) return mObservers.Contains(aObserver); } +void +nsDocument::MaybeEndOutermostXBLUpdate() +{ + // Only call BindingManager()->EndOutermostUpdate() when + // we're not in an update and it is safe to run scripts. + if (mUpdateNestLevel == 0 && mInXBLUpdate) { + if (nsContentUtils::IsSafeToRunScript()) { + mInXBLUpdate = PR_FALSE; + BindingManager()->EndOutermostUpdate(); + } else if (!mInDestructor) { + nsContentUtils::AddScriptRunner( + NS_NEW_RUNNABLE_METHOD(nsDocument, this, MaybeEndOutermostXBLUpdate)); + } + } +} + void nsDocument::BeginUpdate(nsUpdateType aUpdateType) { - if (mUpdateNestLevel == 0) { + if (mUpdateNestLevel == 0 && !mInXBLUpdate) { + mInXBLUpdate = PR_TRUE; BindingManager()->BeginOutermostUpdate(); } @@ -3698,15 +3715,12 @@ nsDocument::EndUpdate(nsUpdateType aUpdateType) NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType)); --mUpdateNestLevel; - if (mUpdateNestLevel == 0) { - // This set of updates may have created XBL bindings. Let the - // binding manager know we're done. - BindingManager()->EndOutermostUpdate(); - } - if (mUpdateNestLevel == 0 && !mDelayFrameLoaderInitialization) { - InitializeFinalizeFrameLoaders(); - } + // This set of updates may have created XBL bindings. Let the + // binding manager know we're done. + MaybeEndOutermostXBLUpdate(); + + MaybeInitializeFinalizeFrameLoaders(); } void @@ -5135,18 +5149,6 @@ nsDocument::FlushSkinBindings() BindingManager()->FlushSkinBindings(); } -class nsFrameLoaderRunner : public nsRunnable -{ -public: - nsFrameLoaderRunner(nsDocument* aDoc) : mDoc(aDoc) {} - NS_IMETHOD Run() { - mDoc->InitializeFinalizeFrameLoaders(); - return NS_OK; - } -private: - nsRefPtr mDoc; -}; - nsresult nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader) { @@ -5160,7 +5162,9 @@ nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader) mInitializableFrameLoaders.AppendElement(aLoader); if (!mFrameLoaderRunner) { - mFrameLoaderRunner = new nsFrameLoaderRunner(this); + mFrameLoaderRunner = + NS_NEW_RUNNABLE_METHOD(nsDocument, this, + MaybeInitializeFinalizeFrameLoaders); NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); nsContentUtils::AddScriptRunner(mFrameLoaderRunner); } @@ -5177,7 +5181,9 @@ nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader) mFinalizableFrameLoaders.AppendElement(aLoader); if (!mFrameLoaderRunner) { - mFrameLoaderRunner = new nsFrameLoaderRunner(this); + mFrameLoaderRunner = + NS_NEW_RUNNABLE_METHOD(nsDocument, this, + MaybeInitializeFinalizeFrameLoaders); NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); nsContentUtils::AddScriptRunner(mFrameLoaderRunner); } @@ -5185,13 +5191,30 @@ nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader) } void -nsDocument::InitializeFinalizeFrameLoaders() +nsDocument::MaybeInitializeFinalizeFrameLoaders() { - mFrameLoaderRunner = nsnull; if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) { + // This method will be recalled when mUpdateNestLevel drops to 0, + // or when !mDelayFrameLoaderInitialization. + mFrameLoaderRunner = nsnull; return; } + // We're not in an update, but it is not safe to run scripts, so + // postpone frameloader initialization and finalization. + if (!nsContentUtils::IsSafeToRunScript()) { + if (!mInDestructor && !mFrameLoaderRunner && + (mInitializableFrameLoaders.Length() || + mFinalizableFrameLoaders.Length())) { + mFrameLoaderRunner = + NS_NEW_RUNNABLE_METHOD(nsDocument, this, + MaybeInitializeFinalizeFrameLoaders); + nsContentUtils::AddScriptRunner(mFrameLoaderRunner); + } + return; + } + mFrameLoaderRunner = nsnull; + // Don't use a temporary array for mInitializableFrameLoaders, because // loading a frame may cause some other frameloader to be removed from the // array. But be careful to keep the loader alive when starting the load! diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 532e816f0bb..56f0d57d41b 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -1007,7 +1007,9 @@ public: nsresult CloneDocHelper(nsDocument* clone) const; - void InitializeFinalizeFrameLoaders(); + void MaybeInitializeFinalizeFrameLoaders(); + + void MaybeEndOutermostXBLUpdate(); protected: void RegisterNamedItems(nsIContent *aContent); @@ -1188,6 +1190,8 @@ protected: // document was created entirely in memory PRPackedBool mHaveInputEncoding:1; + PRPackedBool mInXBLUpdate:1; + PRUint8 mXMLDeclarationBits; PRUint8 mDefaultElementType; @@ -1272,7 +1276,7 @@ private: nsTArray > mInitializableFrameLoaders; nsTArray > mFinalizableFrameLoaders; - nsCOMPtr mFrameLoaderRunner; + nsRefPtr > mFrameLoaderRunner; nsRevocableEventPtr > mPendingTitleChangeEvent; diff --git a/content/html/document/src/nsHTMLDocument.cpp b/content/html/document/src/nsHTMLDocument.cpp index 27fecb0d749..00a43d3b2ff 100644 --- a/content/html/document/src/nsHTMLDocument.cpp +++ b/content/html/document/src/nsHTMLDocument.cpp @@ -3077,14 +3077,25 @@ nsHTMLDocument::GetDesignMode(nsAString & aDesignMode) return NS_OK; } +void +nsHTMLDocument::MaybeEditingStateChanged() +{ + if (mUpdateNestLevel == 0 && mContentEditableCount > 0 != IsEditingOn()) { + if (nsContentUtils::IsSafeToRunScript()) { + EditingStateChanged(); + } else if (!mInDestructor) { + nsContentUtils::AddScriptRunner( + NS_NEW_RUNNABLE_METHOD(nsHTMLDocument, this, MaybeEditingStateChanged)); + } + } +} + void nsHTMLDocument::EndUpdate(nsUpdateType aUpdateType) { nsDocument::EndUpdate(aUpdateType); - if (mUpdateNestLevel == 0 && mContentEditableCount > 0 != IsEditingOn()) { - EditingStateChanged(); - } + MaybeEditingStateChanged(); } nsresult diff --git a/content/html/document/src/nsHTMLDocument.h b/content/html/document/src/nsHTMLDocument.h index 2db280b9441..03d110861fb 100644 --- a/content/html/document/src/nsHTMLDocument.h +++ b/content/html/document/src/nsHTMLDocument.h @@ -358,6 +358,7 @@ protected: nsresult TurnEditingOff(); nsresult EditingStateChanged(); + void MaybeEditingStateChanged(); PRUint32 mContentEditableCount; EditingState mEditingState; diff --git a/content/xul/document/src/nsXULDocument.cpp b/content/xul/document/src/nsXULDocument.cpp index ba519fc9efd..84ea0cb18c4 100644 --- a/content/xul/document/src/nsXULDocument.cpp +++ b/content/xul/document/src/nsXULDocument.cpp @@ -3216,7 +3216,7 @@ nsXULDocument::DoneWalking() NS_WARN_IF_FALSE(mUpdateNestLevel == 0, "Constructing XUL document in middle of an update?"); if (mUpdateNestLevel == 0) { - InitializeFinalizeFrameLoaders(); + MaybeInitializeFinalizeFrameLoaders(); } NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); @@ -3299,10 +3299,19 @@ nsXULDocument::StyleSheetLoaded(nsICSSStyleSheet* aSheet, } void -nsXULDocument::EndUpdate(nsUpdateType aUpdateType) +nsXULDocument::MaybeBroadcast() { - nsXMLDocument::EndUpdate(aUpdateType); - if (mUpdateNestLevel == 0) { + // Only broadcast when not in an update and when safe to run scripts. + if (mUpdateNestLevel == 0 && + (mDelayedAttrChangeBroadcasts.Length() || + mDelayedBroadcasters.Length())) { + if (!nsContentUtils::IsSafeToRunScript()) { + if (!mInDestructor) { + nsContentUtils::AddScriptRunner( + NS_NEW_RUNNABLE_METHOD(nsXULDocument, this, MaybeBroadcast)); + } + return; + } if (!mHandlingDelayedAttrChange) { mHandlingDelayedAttrChange = PR_TRUE; for (PRUint32 i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) { @@ -3342,6 +3351,14 @@ nsXULDocument::EndUpdate(nsUpdateType aUpdateType) } } +void +nsXULDocument::EndUpdate(nsUpdateType aUpdateType) +{ + nsXMLDocument::EndUpdate(aUpdateType); + + MaybeBroadcast(); +} + void nsXULDocument::ReportMissingOverlay(nsIURI* aURI) { diff --git a/content/xul/document/src/nsXULDocument.h b/content/xul/document/src/nsXULDocument.h index 756d874e39a..ca4f6f2b782 100644 --- a/content/xul/document/src/nsXULDocument.h +++ b/content/xul/document/src/nsXULDocument.h @@ -735,6 +735,8 @@ protected: nsTArray mDelayedBroadcasters; nsTArray mDelayedAttrChangeBroadcasts; PRBool mHandlingDelayedAttrChange; + + void MaybeBroadcast(); private: // helpers