diff --git a/mozilla/content/base/public/nsContentUtils.h b/mozilla/content/base/public/nsContentUtils.h index 4abe2184c0a..24fb65c29c8 100644 --- a/mozilla/content/base/public/nsContentUtils.h +++ b/mozilla/content/base/public/nsContentUtils.h @@ -90,6 +90,7 @@ class nsIWordBreaker; class nsIJSRuntimeService; class nsIEventListenerManager; class nsIScriptContext; +class nsIRunnable; template class nsCOMArray; class nsIPref; class nsVoidArray; @@ -1177,6 +1178,45 @@ public: */ static PRBool OfflineAppAllowed(nsIURI *aURI); + /** + * Increases the count of blockers preventing scripts from running. + * NOTE: You might want to use nsAutoScriptBlocker rather than calling + * this directly + */ + static void AddScriptBlocker(); + + /** + * Decreases the count of blockers preventing scripts from running. + * NOTE: You might want to use nsAutoScriptBlocker rather than calling + * this directly + * + * WARNING! Calling this function could synchronously execute scripts. + */ + static void RemoveScriptBlocker(); + + /** + * Add a runnable that is to be executed as soon as it's safe to execute + * scripts. + * NOTE: If it's currently safe to execute scripts, aRunnable will be run + * synchronously before the function returns. + * + * @param aRunnable The nsIRunnable to run as soon as it's safe to execute + * scripts. Passing null is allowed and results in nothing + * happening. It is also allowed to pass an object that + * has not yet been AddRefed. + */ + static PRBool AddScriptRunner(nsIRunnable* aRunnable); + + /** + * Returns true if it's safe to execute content script and false otherwise. + * + * The only known case where this lies is mutation events. They run, and can + * run anything else, when this function returns false, but this is ok. + */ + static PRBool IsSafeToRunScript() { + return sScriptBlockerCount == 0; + } + private: static PRBool InitializeEventTable(); @@ -1249,6 +1289,9 @@ private: #endif static PRBool sInitialized; + static PRUint32 sScriptBlockerCount; + static nsCOMArray* sBlockedScriptRunners; + static PRUint32 sRunnersCountAtFirstBlocker; }; #define NS_HOLD_JS_OBJECTS(obj, clazz) \ @@ -1267,6 +1310,7 @@ public: // Returns PR_FALSE if something erroneous happened. PRBool Push(nsISupports *aCurrentTarget); + PRBool Push(JSContext *cx); void Pop(); private: @@ -1317,6 +1361,16 @@ private: nsresult mResult; }; +class nsAutoScriptBlocker { +public: + nsAutoScriptBlocker() { + nsContentUtils::AddScriptBlocker(); + } + ~nsAutoScriptBlocker() { + nsContentUtils::RemoveScriptBlocker(); + } +}; + #define NS_AUTO_GCROOT_PASTE2(tok,line) tok##line #define NS_AUTO_GCROOT_PASTE(tok,line) \ NS_AUTO_GCROOT_PASTE2(tok,line) diff --git a/mozilla/content/base/public/nsIDocument.h b/mozilla/content/base/public/nsIDocument.h index 8cf14313e91..139ef9e83d4 100644 --- a/mozilla/content/base/public/nsIDocument.h +++ b/mozilla/content/base/public/nsIDocument.h @@ -596,6 +596,8 @@ public: // To make this easy and painless, use the mozAutoDocUpdate helper class. virtual void BeginUpdate(nsUpdateType aUpdateType) = 0; virtual void EndUpdate(nsUpdateType aUpdateType) = 0; + virtual PRUint32 GetUpdateNestingLevel() = 0; + virtual PRBool AllUpdatesAreContent() = 0; virtual void BeginLoad() = 0; virtual void EndLoad() = 0; // notify that one or two content nodes changed state diff --git a/mozilla/content/base/public/nsIObjectLoadingContent.idl b/mozilla/content/base/public/nsIObjectLoadingContent.idl index f76231d63a0..3305780c345 100644 --- a/mozilla/content/base/public/nsIObjectLoadingContent.idl +++ b/mozilla/content/base/public/nsIObjectLoadingContent.idl @@ -43,7 +43,7 @@ interface nsIPluginInstance; /** * This interface represents a content node that loads objects. */ -[scriptable, uuid(42f9358e-300f-4a44-afd7-7830b750e955)] +[scriptable, uuid(90ab443e-3e99-405e-88c9-9c42adaa3217)] interface nsIObjectLoadingContent : nsISupports { const unsigned long TYPE_LOADING = 0; @@ -71,6 +71,13 @@ interface nsIObjectLoadingContent : nsISupports */ unsigned long getContentTypeForMIMEType(in AUTF8String aMimeType); + /** + * Returns the plugin instance if it has already been instantiated. This + * will never instantiate the plugin and so is safe to call even when + * content script must not execute. + */ + [noscript] readonly attribute nsIPluginInstance pluginInstance; + /** * Makes sure that a frame for this object exists, and that the plugin is diff --git a/mozilla/content/base/src/nsContentUtils.cpp b/mozilla/content/base/src/nsContentUtils.cpp index 0f3bcb40aaf..5b15a473169 100644 --- a/mozilla/content/base/src/nsContentUtils.cpp +++ b/mozilla/content/base/src/nsContentUtils.cpp @@ -149,6 +149,8 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID); #include "nsXULPopupManager.h" #include "nsIPermissionManager.h" #include "nsIScriptObjectPrincipal.h" +#include "nsIRunnable.h" +#include "nsDOMJSUtils.h" #ifdef IBMBIDI #include "nsIBidiKeyboard.h" @@ -195,6 +197,9 @@ PRUint32 nsContentUtils::sJSGCThingRootCount; #ifdef IBMBIDI nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull; #endif +PRUint32 nsContentUtils::sScriptBlockerCount = 0; +nsCOMArray* nsContentUtils::sBlockedScriptRunners = nsnull; +PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0; nsIJSRuntimeService *nsAutoGCRoot::sJSRuntimeService; JSRuntime *nsAutoGCRoot::sJSScriptRuntime; @@ -323,6 +328,9 @@ nsContentUtils::Init() } } + sBlockedScriptRunners = new nsCOMArray; + NS_ENSURE_TRUE(sBlockedScriptRunners, NS_ERROR_OUT_OF_MEMORY); + sInitialized = PR_TRUE; return NS_OK; @@ -821,6 +829,12 @@ nsContentUtils::Shutdown() } } + NS_ASSERTION(!sBlockedScriptRunners || + sBlockedScriptRunners->Count() == 0, + "How'd this happen?"); + delete sBlockedScriptRunners; + sBlockedScriptRunners = nsnull; + nsAutoGCRoot::Shutdown(); } @@ -2588,17 +2602,41 @@ nsCxPusher::Push(nsISupports *aCurrentTarget) JSContext *cx = nsnull; - if (sgo) { - mScx = sgo->GetContext(); + nsCOMPtr scx; - if (mScx) { - cx = (JSContext *)mScx->GetNativeContext(); + if (sgo) { + scx = sgo->GetContext(); + + if (scx) { + cx = (JSContext *)scx->GetNativeContext(); } // Bad, no JSContext from script global object! NS_ENSURE_TRUE(cx, PR_FALSE); } + // If there's no native context in the script context it must be + // in the process or being torn down. We don't want to notify the + // script context about scripts having been evaluated in such a + // case, calling with a null cx is fine in that case. + return Push(cx); +} + +PRBool +nsCxPusher::Push(JSContext *cx) +{ + if (mScx) { + NS_ERROR("Whaaa! No double pushing with nsCxPusher::Push()!"); + + return PR_FALSE; + } + if (cx) { + mScx = GetScriptContextFromJSContext(cx); + if (!mScx) { + // Should probably return PR_FALSE. See bug 416916. + return PR_TRUE; + } + if (!mStack) { mStack = do_GetService(kJSStackContractID); } @@ -2612,13 +2650,6 @@ nsCxPusher::Push(nsISupports *aCurrentTarget) mStack->Push(cx); } - } else { - // If there's no native context in the script context it must be - // in the process or being torn down. We don't want to notify the - // script context about scripts having been evaluated in such a - // case, so null out mScx. - - mScx = nsnull; } return PR_TRUE; } @@ -3960,6 +3991,65 @@ nsContentUtils::DOMEventToNativeKeyEvent(nsIDOMEvent* aDOMEvent, return PR_TRUE; } +/* static */ +void +nsContentUtils::AddScriptBlocker() +{ + if (!sScriptBlockerCount) { + NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, + "Should not already have a count"); + sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Count(); + } + ++sScriptBlockerCount; +} + +/* static */ +void +nsContentUtils::RemoveScriptBlocker() +{ + --sScriptBlockerCount; + if (sScriptBlockerCount) { + return; + } + + PRUint32 firstBlocker = sRunnersCountAtFirstBlocker; + PRUint32 lastBlocker = sBlockedScriptRunners->Count(); + sRunnersCountAtFirstBlocker = 0; + NS_ASSERTION(firstBlocker <= lastBlocker, + "bad sRunnersCountAtFirstBlocker"); + + while (firstBlocker < lastBlocker) { + nsCOMPtr runnable = (*sBlockedScriptRunners)[firstBlocker]; + sBlockedScriptRunners->RemoveObjectAt(firstBlocker); + --lastBlocker; + + runnable->Run(); + NS_ASSERTION(lastBlocker == sBlockedScriptRunners->Count() && + sRunnersCountAtFirstBlocker == 0, + "Bad count"); + NS_ASSERTION(!sScriptBlockerCount, "This is really bad"); + } +} + + +/* static */ +PRBool +nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) +{ + if (!aRunnable) { + return PR_FALSE; + } + + if (sScriptBlockerCount) { + return sBlockedScriptRunners->AppendObject(aRunnable); + } + + nsCOMPtr run = aRunnable; + run->Run(); + + return PR_TRUE; +} + /* static */ void nsContentUtils::HidePopupsInDocument(nsIDocument* aDocument) diff --git a/mozilla/content/base/src/nsDocument.cpp b/mozilla/content/base/src/nsDocument.cpp index 6ff43836737..a6b34cce192 100644 --- a/mozilla/content/base/src/nsDocument.cpp +++ b/mozilla/content/base/src/nsDocument.cpp @@ -2701,17 +2701,23 @@ nsDocument::BeginUpdate(nsUpdateType aUpdateType) } ++mUpdateNestLevel; - if (mScriptLoader) { - mScriptLoader->AddExecuteBlocker(); + if (aUpdateType == UPDATE_CONTENT_MODEL) { + ++mContentUpdateNestLevel; } NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType)); + + nsContentUtils::AddScriptBlocker(); } void nsDocument::EndUpdate(nsUpdateType aUpdateType) { + nsContentUtils::RemoveScriptBlocker(); NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType)); + if (aUpdateType == UPDATE_CONTENT_MODEL) { + --mContentUpdateNestLevel; + } --mUpdateNestLevel; if (mUpdateNestLevel == 0) { // This set of updates may have created XBL bindings. Let the @@ -2719,10 +2725,6 @@ nsDocument::EndUpdate(nsUpdateType aUpdateType) mBindingManager->EndOutermostUpdate(); } - if (mScriptLoader) { - mScriptLoader->RemoveExecuteBlocker(); - } - if (mUpdateNestLevel == 0) { PRUint32 length = mFinalizableFrameLoaders.Length(); if (length > 0) { @@ -2735,6 +2737,18 @@ nsDocument::EndUpdate(nsUpdateType aUpdateType) } } +PRUint32 +nsDocument::GetUpdateNestingLevel() +{ + return mUpdateNestLevel; +} + +PRBool +nsDocument::AllUpdatesAreContent() +{ + return mContentUpdateNestLevel == mUpdateNestLevel; +} + void nsDocument::BeginLoad() { @@ -5816,6 +5830,8 @@ nsDocument::MutationEventDispatched(nsINode* aTarget) PRInt32 realTargetCount = realTargets.Count(); for (PRInt32 k = 0; k < realTargetCount; ++k) { + mozAutoDocUpdateContentUnnest updateUnnest(this); + nsMutationEvent mutation(PR_TRUE, NS_MUTATION_SUBTREEMODIFIED); nsEventDispatcher::Dispatch(realTargets[k], nsnull, &mutation); } diff --git a/mozilla/content/base/src/nsDocument.h b/mozilla/content/base/src/nsDocument.h index 46e35d52451..92643ace44a 100644 --- a/mozilla/content/base/src/nsDocument.h +++ b/mozilla/content/base/src/nsDocument.h @@ -474,6 +474,8 @@ public: // observers. virtual void BeginUpdate(nsUpdateType aUpdateType); virtual void EndUpdate(nsUpdateType aUpdateType); + virtual PRUint32 GetUpdateNestingLevel(); + virtual PRBool AllUpdatesAreContent(); virtual void BeginLoad(); virtual void EndLoad(); virtual void ContentStatesChanged(nsIContent* aContent1, @@ -799,6 +801,8 @@ protected: // Our update nesting level PRUint32 mUpdateNestLevel; + // Our UPDATE_CONTENT_MODEL update nesting level + PRUint32 mContentUpdateNestLevel; private: friend class nsUnblockOnloadEvent; diff --git a/mozilla/content/base/src/nsGenericDOMDataNode.cpp b/mozilla/content/base/src/nsGenericDOMDataNode.cpp index 171d22daf1c..7d28235883c 100644 --- a/mozilla/content/base/src/nsGenericDOMDataNode.cpp +++ b/mozilla/content/base/src/nsGenericDOMDataNode.cpp @@ -492,6 +492,8 @@ nsGenericDOMDataNode::SetTextInternal(PRUint32 aOffset, PRUint32 aCount, mutation.mNewAttrValue = do_GetAtom(val); } + mozAutoDocUpdateContentUnnest updateUnnest(document); + mozAutoSubtreeModified subtree(GetOwnerDoc(), this); nsEventDispatcher::Dispatch(this, nsnull, &mutation); } diff --git a/mozilla/content/base/src/nsGenericElement.cpp b/mozilla/content/base/src/nsGenericElement.cpp index 52bfaa1e22a..099a82b7cd6 100644 --- a/mozilla/content/base/src/nsGenericElement.cpp +++ b/mozilla/content/base/src/nsGenericElement.cpp @@ -2758,6 +2758,9 @@ nsGenericElement::doInsertChildAt(nsIContent* aKid, PRUint32 aIndex, NS_EVENT_BITS_MUTATION_NODEINSERTED, container)) { nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED); mutation.mRelatedNode = do_QueryInterface(container); + + mozAutoDocUpdateContentUnnest updateUnnest(aDocument); + mozAutoSubtreeModified subtree(container->GetOwnerDoc(), container); nsEventDispatcher::Dispatch(aKid, nsnull, &mutation); } @@ -2825,6 +2828,9 @@ nsGenericElement::doRemoveChildAt(PRUint32 aIndex, PRBool aNotify, NS_EVENT_BITS_MUTATION_NODEREMOVED, container)) { nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED); mutation.mRelatedNode = do_QueryInterface(container); + + mozAutoDocUpdateContentUnnest updateUnnest(aDocument); + subtree.UpdateTarget(container->GetOwnerDoc(), container); nsEventDispatcher::Dispatch(aKid, nsnull, &mutation); } @@ -3792,6 +3798,9 @@ nsGenericElement::SetAttrAndNotify(PRInt32 aNamespaceID, mutation.mPrevAttrValue = do_GetAtom(aOldValue); } mutation.mAttrChange = modType; + + mozAutoDocUpdateContentUnnest updateUnnest(document); + mozAutoSubtreeModified subtree(GetOwnerDoc(), this); nsEventDispatcher::Dispatch(this, nsnull, &mutation); } @@ -4039,6 +4048,8 @@ nsGenericElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, mutation.mPrevAttrValue = do_GetAtom(value); mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL; + mozAutoDocUpdateContentUnnest updateUnnest(document); + mozAutoSubtreeModified subtree(GetOwnerDoc(), this); nsEventDispatcher::Dispatch(this, nsnull, &mutation); } diff --git a/mozilla/content/base/src/nsGenericElement.h b/mozilla/content/base/src/nsGenericElement.h index c14c39d2160..3219e55c023 100644 --- a/mozilla/content/base/src/nsGenericElement.h +++ b/mozilla/content/base/src/nsGenericElement.h @@ -63,6 +63,7 @@ #include "nsDOMAttributeMap.h" #include "nsIWeakReference.h" #include "nsCycleCollectionParticipant.h" +#include "nsIDocument.h" class nsIDOMAttr; class nsIDOMEventListener; @@ -1066,4 +1067,33 @@ private: nsRefPtr mContent; }; +class mozAutoDocUpdateContentUnnest +{ +public: + mozAutoDocUpdateContentUnnest(nsIDocument* aDocument) + { + if (aDocument) { + NS_ASSERTION(aDocument->AllUpdatesAreContent(), + "There are non-content updates in progress"); + mNestingLevel = aDocument->GetUpdateNestingLevel(); + for (PRUint32 i = 0; i < mNestingLevel; ++i) { + nsContentUtils::RemoveScriptBlocker(); + } + } + else { + mNestingLevel = 0; + } + } + + ~mozAutoDocUpdateContentUnnest() + { + for (PRUint32 i = 0; i < mNestingLevel; ++i) { + nsContentUtils::AddScriptBlocker(); + } + } + +private: + PRUint32 mNestingLevel; +}; + #endif /* nsGenericElement_h___ */ diff --git a/mozilla/content/base/src/nsObjectLoadingContent.cpp b/mozilla/content/base/src/nsObjectLoadingContent.cpp index f4b60c475f5..561db3a5085 100644 --- a/mozilla/content/base/src/nsObjectLoadingContent.cpp +++ b/mozilla/content/base/src/nsObjectLoadingContent.cpp @@ -127,10 +127,11 @@ nsAsyncInstantiateEvent::Run() mContent->mPendingInstantiateEvent = nsnull; // Make sure that we still have the right frame (NOTE: we don't need to check - // the type here - GetFrame() only returns object frames, and that means we're - // a plugin) + // the type here - GetExistingFrame() only returns object frames, and that + // means we're a plugin) // Also make sure that we still refer to the same data. - nsIObjectFrame* frame = mContent->GetFrame(PR_FALSE); + nsIObjectFrame* frame = mContent-> + GetExistingFrame(nsObjectLoadingContent::eFlushContent); if (frame == mFrame && mContent->mURI == mURI && mContent->mContentType.Equals(mContentType)) { @@ -519,7 +520,7 @@ nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, nsISupports *aConte notifier.Notify(); } nsIObjectFrame* frame; - frame = GetFrame(PR_TRUE); + frame = GetExistingFrame(eFlushLayout); if (!frame) { // Do nothing in this case: This is probably due to a display:none // frame. If we ever get a frame, HasNewFrame will do the right thing. @@ -585,7 +586,7 @@ nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, nsISupports *aConte #endif Fallback(PR_FALSE); } else if (mType == eType_Plugin) { - nsIObjectFrame* frame = GetFrame(PR_FALSE); + nsIObjectFrame* frame = GetExistingFrame(eFlushContent); if (frame) { // We have to notify the wrapper here instead of right after // Instantiate because the plugin only gets instantiated by @@ -674,7 +675,7 @@ nsObjectLoadingContent::EnsureInstantiation(nsIPluginInstance** aInstance) return NS_OK; } - nsIObjectFrame* frame = GetFrame(PR_FALSE); + nsIObjectFrame* frame = GetExistingFrame(eFlushContent); if (frame) { // If we have a frame, we may have pending instantiate events; revoke // them. @@ -711,7 +712,7 @@ nsObjectLoadingContent::EnsureInstantiation(nsIPluginInstance** aInstance) mInstantiating = PR_FALSE; - frame = GetFrame(PR_FALSE); + frame = GetExistingFrame(eFlushContent); if (!frame) { return NS_OK; } @@ -774,6 +775,19 @@ nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame) return NS_OK; } +NS_IMETHODIMP +nsObjectLoadingContent::GetPluginInstance(nsIPluginInstance** aInstance) +{ + *aInstance = nsnull; + + nsIObjectFrame* objFrame = GetExistingFrame(eDontFlush); + if (!objFrame) { + return NS_OK; + } + + return objFrame->GetPluginInstance(*aInstance); +} + NS_IMETHODIMP nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType, PRUint32* aType) @@ -1547,13 +1561,12 @@ nsObjectLoadingContent::GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI) } nsIObjectFrame* -nsObjectLoadingContent::GetFrame(PRBool aFlushLayout) +nsObjectLoadingContent::GetExistingFrame(FlushType aFlushType) { nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); NS_ASSERTION(thisContent, "must be a content"); - PRBool flushed = PR_FALSE; nsIFrame* frame; do { nsIDocument* doc = thisContent->GetCurrentDoc(); @@ -1571,17 +1584,17 @@ nsObjectLoadingContent::GetFrame(PRBool aFlushLayout) return nsnull; } - if (flushed) { + if (aFlushType == eDontFlush) { break; } // OK, let's flush out and try again. Note that we want to reget // the document, etc, since flushing might run script. mozFlushType flushType = - aFlushLayout ? Flush_Layout : Flush_ContentAndNotify; + aFlushType == eFlushLayout ? Flush_Layout : Flush_ContentAndNotify; doc->FlushPendingNotifications(flushType); - flushed = PR_TRUE; + aFlushType = eDontFlush; } while (1); nsIObjectFrame* objFrame; @@ -1610,7 +1623,7 @@ nsresult nsObjectLoadingContent::TryInstantiate(const nsACString& aMIMEType, nsIURI* aURI) { - nsIObjectFrame* frame = GetFrame(PR_FALSE); + nsIObjectFrame* frame = GetExistingFrame(eFlushContent); if (!frame) { LOG(("OBJLC [%p]: No frame yet\n", this)); return NS_OK; // Not a failure to have no frame diff --git a/mozilla/content/base/src/nsObjectLoadingContent.h b/mozilla/content/base/src/nsObjectLoadingContent.h index a46364aa969..c5dffbe941e 100644 --- a/mozilla/content/base/src/nsObjectLoadingContent.h +++ b/mozilla/content/base/src/nsObjectLoadingContent.h @@ -271,14 +271,27 @@ class nsObjectLoadingContent : public nsImageLoadingContent */ void GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI); + /** * Gets the frame that's associated with this content node in - * presentation 0. If aFlushLayout is true, this function will - * flush layout before trying to get the frame. This is needed - * in some cases by plug-ins to ensure that NPP_SetWindow() gets - * called (from nsObjectFrame::DidReflow). + * presentation 0. Always returns null if the node doesn't currently + * have a frame. + * + * @param aFlush When eFlushContent will flush content notifications + * before returning a non-null value. + * When eFlushLayout will flush layout and content + * notifications before returning a non-null value. + * When eDontFlush will never flush. + * + * eFlushLayout is needed in some cases by plug-ins to ensure + * that NPP_SetWindow() gets called (from nsObjectFrame::DidReflow). */ - nsIObjectFrame* GetFrame(PRBool aFlushLayout); + enum FlushType { + eFlushContent, + eFlushLayout, + eDontFlush + }; + nsIObjectFrame* GetExistingFrame(FlushType aFlushType); /** * Handle being blocked by a content policy. aStatus is the nsresult @@ -408,7 +421,7 @@ class nsObjectLoadingContent : public nsImageLoadingContent // Whether we fell back because of an unsupported type PRBool mTypeUnsupported:1; - friend struct nsAsyncInstantiateEvent; + friend class nsAsyncInstantiateEvent; }; diff --git a/mozilla/content/base/src/nsScriptLoader.cpp b/mozilla/content/base/src/nsScriptLoader.cpp index f7caf28a4c5..ff3c00693c2 100644 --- a/mozilla/content/base/src/nsScriptLoader.cpp +++ b/mozilla/content/base/src/nsScriptLoader.cpp @@ -159,8 +159,7 @@ NS_IMPL_THREADSAFE_ISUPPORTS0(nsScriptLoadRequest) nsScriptLoader::nsScriptLoader(nsIDocument *aDocument) : mDocument(aDocument), mBlockerCount(0), - mEnabled(PR_TRUE), - mHadPendingScripts(PR_FALSE) + mEnabled(PR_TRUE) { } @@ -491,7 +490,8 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) // If we've got existing pending requests, add ourselves // to this list. - if (mPendingRequests.Count() == 0 && ReadyToExecuteScripts()) { + if (mPendingRequests.Count() == 0 && ReadyToExecuteScripts() && + nsContentUtils::IsSafeToRunScript()) { return ProcessRequest(request); } } @@ -500,6 +500,14 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) NS_ENSURE_TRUE(mPendingRequests.AppendObject(request), NS_ERROR_OUT_OF_MEMORY); + // If there weren't any pending requests before, and this one is + // ready to execute, do that as soon as it's safe. + if (mPendingRequests.Count() == 1 && !request->mLoading && + ReadyToExecuteScripts()) { + nsContentUtils::AddScriptRunner(new nsRunnableMethod(this, + &nsScriptLoader::ProcessPendingRequests)); + } + // Added as pending request, now we can send blocking back return NS_ERROR_HTMLPARSER_BLOCK; } @@ -507,7 +515,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) nsresult nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest) { - NS_ASSERTION(ReadyToExecuteScripts(), + NS_ASSERTION(ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript(), "Caller forgot to check ReadyToExecuteScripts()"); NS_ENSURE_ARG(aRequest); diff --git a/mozilla/content/base/src/nsScriptLoader.h b/mozilla/content/base/src/nsScriptLoader.h index 58d855675b4..b89cc6b30b1 100644 --- a/mozilla/content/base/src/nsScriptLoader.h +++ b/mozilla/content/base/src/nsScriptLoader.h @@ -147,31 +147,15 @@ public: /** * Add/remove blocker. Blockers will stop scripts from executing, but not * from loading. - * NOTE! Calling RemoveExecuteBlocker could potentially execute pending - * scripts synchronously. In other words, it should not be done at 'unsafe' - * times */ void AddExecuteBlocker() { - if (!mBlockerCount++) { - mHadPendingScripts = mPendingRequests.Count() != 0; - } + ++mBlockerCount; } void RemoveExecuteBlocker() { if (!--mBlockerCount) { - // If there were pending scripts then the newly added scripts will - // execute once whatever event triggers the pending scripts fires. - // However, due to synchronous loads and pushed event queues it's - // possible that the requests that were there have already been processed - // if so we need to process any new requests asynchronously. - // Ideally that should be fixed such that it can't happen. - if (mHadPendingScripts) { - ProcessPendingRequestsAsync(); - } - else { - ProcessPendingRequests(); - } + ProcessPendingRequestsAsync(); } } @@ -246,7 +230,6 @@ protected: nsTArray< nsRefPtr > mPendingChildLoaders; PRUint32 mBlockerCount; PRPackedBool mEnabled; - PRPackedBool mHadPendingScripts; }; #endif //__nsScriptLoader_h__ diff --git a/mozilla/content/xbl/src/nsBindingManager.cpp b/mozilla/content/xbl/src/nsBindingManager.cpp index ae3ba028568..bbb9f7ebaaa 100644 --- a/mozilla/content/xbl/src/nsBindingManager.cpp +++ b/mozilla/content/xbl/src/nsBindingManager.cpp @@ -972,28 +972,13 @@ nsBindingManager::ProcessAttachedQueue(PRUint32 aSkipSize) mProcessingAttachedStack = PR_TRUE; PRUint32 currentIndex = aSkipSize; + // Excute constructors. Do this from high index to low while (mAttachedStack.Length() > aSkipSize) { - // First install all implementations. Do this from low index to high - // since that way we'll automatically get any new bindings added in the - // process. - for (; currentIndex < mAttachedStack.Length(); ++currentIndex) { - nsRefPtr binding = mAttachedStack.ElementAt(currentIndex); - if (binding) { - nsresult rv = binding->EnsureScriptAPI(); - if (NS_FAILED(rv)) { - mAttachedStack[currentIndex] = nsnull; - } - } - } - - // Then excute constructors. Do this from high index to low - while (currentIndex > aSkipSize && currentIndex == mAttachedStack.Length()) { - --currentIndex; - nsRefPtr binding = mAttachedStack.ElementAt(currentIndex); - mAttachedStack.RemoveElementAt(currentIndex); - if (binding) { - binding->ExecuteAttachedHandler(); - } + PRUint32 lastItem = mAttachedStack.Length() - 1; + nsRefPtr binding = mAttachedStack.ElementAt(lastItem); + mAttachedStack.RemoveElementAt(lastItem); + if (binding) { + binding->ExecuteAttachedHandler(); } } @@ -1537,16 +1522,6 @@ nsBindingManager::EndOutermostUpdate() ProcessAttachedQueue(mAttachedStackSizeOnOutermost); mAttachedStackSizeOnOutermost = 0; } - else { - PRUint32 i = mAttachedStackSizeOnOutermost; - for (; i < mAttachedStack.Length(); ++i) { - nsRefPtr binding = mAttachedStack[i]; - nsresult rv = binding->EnsureScriptAPI(); - if (NS_FAILED(rv)) { - mAttachedStack[i] = nsnull; - } - } - } } void diff --git a/mozilla/content/xbl/src/nsXBLBinding.cpp b/mozilla/content/xbl/src/nsXBLBinding.cpp index 7a7ca8c5434..a1f62a8b078 100644 --- a/mozilla/content/xbl/src/nsXBLBinding.cpp +++ b/mozilla/content/xbl/src/nsXBLBinding.cpp @@ -272,8 +272,7 @@ nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding) : mPrototypeBinding(aBinding), mInsertionPointTable(nsnull), mIsStyleBinding(PR_TRUE), - mMarkedForDeath(PR_FALSE), - mInstalledAPI(PR_FALSE) + mMarkedForDeath(PR_FALSE) { NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!"); // Grab a ref to the document info so the prototype binding won't die @@ -786,22 +785,6 @@ nsXBLBinding::GenerateAnonymousContent() } } -nsresult -nsXBLBinding::EnsureScriptAPI() -{ - if (mInstalledAPI) { - return NS_OK; - } - - // Set mInstalledAPI right away since we'll recurse into here from - // nsElementSH::PostCreate when InstallImplementation is called. - mInstalledAPI = PR_TRUE; - - InstallEventHandlers(); - - return InstallImplementation(); -} - void nsXBLBinding::InstallEventHandlers() { diff --git a/mozilla/content/xbl/src/nsXBLBinding.h b/mozilla/content/xbl/src/nsXBLBinding.h index 77658efdd4b..c61f18bb2b1 100644 --- a/mozilla/content/xbl/src/nsXBLBinding.h +++ b/mozilla/content/xbl/src/nsXBLBinding.h @@ -120,7 +120,8 @@ public: void GenerateAnonymousContent(); void InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement); - nsresult EnsureScriptAPI(); + void InstallEventHandlers(); + nsresult InstallImplementation(); void ExecuteAttachedHandler(); void ExecuteDetachedHandler(); @@ -167,11 +168,6 @@ public: // MEMBER VARIABLES protected: - // These two functions recursively install the event handlers - // and implementation on this binding and its base class bindings. - // External callers should call EnsureScriptAPI instead. - void InstallEventHandlers(); - nsresult InstallImplementation(); nsAutoRefCnt mRefCnt; nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo @@ -185,7 +181,6 @@ protected: PRPackedBool mIsStyleBinding; PRPackedBool mMarkedForDeath; - PRPackedBool mInstalledAPI; }; #endif // nsXBLBinding_h_ diff --git a/mozilla/content/xbl/src/nsXBLService.cpp b/mozilla/content/xbl/src/nsXBLService.cpp index 3eb9a7e90c8..5d8a5006f4b 100644 --- a/mozilla/content/xbl/src/nsXBLService.cpp +++ b/mozilla/content/xbl/src/nsXBLService.cpp @@ -563,6 +563,13 @@ nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL, // Tell the binding to build the anonymous content. newBinding->GenerateAnonymousContent(); + // Tell the binding to install event handlers + newBinding->InstallEventHandlers(); + + // Set up our properties + rv = newBinding->InstallImplementation(); + NS_ENSURE_SUCCESS(rv, rv); + // Figure out if we have any scoped sheets. If so, we do a second resolve. *aResolveStyle = newBinding->HasStyleSheets(); diff --git a/mozilla/content/xul/content/src/nsXULElement.cpp b/mozilla/content/xul/content/src/nsXULElement.cpp index fbf2a77a469..b922f8a312f 100644 --- a/mozilla/content/xul/content/src/nsXULElement.cpp +++ b/mozilla/content/xul/content/src/nsXULElement.cpp @@ -1393,6 +1393,8 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotify) mutation.mPrevAttrValue = do_GetAtom(oldValue); mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL; + mozAutoDocUpdateContentUnnest updateUnnest(doc); + mozAutoSubtreeModified subtree(GetOwnerDoc(), this); nsEventDispatcher::Dispatch(static_cast(this), nsnull, &mutation); diff --git a/mozilla/dom/src/base/nsDOMClassInfo.cpp b/mozilla/dom/src/base/nsDOMClassInfo.cpp index 5bb93c18f69..dac031bdacf 100644 --- a/mozilla/dom/src/base/nsDOMClassInfo.cpp +++ b/mozilla/dom/src/base/nsDOMClassInfo.cpp @@ -59,6 +59,8 @@ #include "prprf.h" #include "nsTArray.h" #include "nsCSSValue.h" +#include "nsIRunnable.h" +#include "nsThreadUtils.h" // JavaScript includes #include "jsapi.h" @@ -7139,20 +7141,17 @@ nsElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, // We must ensure that the XBL Binding is installed before we hand // back this object. - nsRefPtr binding; if (content->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && - (binding = doc->BindingManager()->GetBinding(content))) { - // There's already a binding for this element, make sure that - // the script API has been installed. - // Note that this could end up recusing into code that calls - // WrapNative. So don't do anything important beyond this point - // as that will not be done to the wrapper returned from that - // WrapNative call. - // In theory we could also call ExecuteAttachedHandler here if - // we also removed the binding from the PAQ queue, but that seems - // like a scary change that would mosly just add more inconsistencies. + doc->BindingManager()->GetBinding(content)) { + // There's already a binding for this element so nothing left to + // be done here. - return binding->EnsureScriptAPI(); + // In theory we could call ExecuteAttachedHandler here when it's safe to + // run script if we also removed the binding from the PAQ queue, but that + // seems like a scary change that would mosly just add more + // inconsistencies. + + return NS_OK; } // See if we have a frame. @@ -7176,6 +7175,7 @@ nsElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, // Make sure the style context goes away _before_ we execute the binding // constructor, since the constructor can destroy the relevant presshell. + nsRefPtr binding; { // Scope for the nsRefPtr nsRefPtr sc = pctx->StyleSet()->ResolveStyleFor(content, @@ -7200,17 +7200,13 @@ nsElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } if (binding) { - -#ifdef DEBUG - PRBool safeToRunScript = PR_FALSE; - pctx->PresShell()->IsSafeToFlush(safeToRunScript); - NS_ASSERTION(safeToRunScript, "Wrapping when it's not safe to flush"); -#endif - - rv = binding->EnsureScriptAPI(); - NS_ENSURE_SUCCESS(rv, rv); - - binding->ExecuteAttachedHandler(); + if (nsContentUtils::IsSafeToRunScript()) { + binding->ExecuteAttachedHandler(); + } + else { + nsContentUtils::AddScriptRunner(new nsRunnableMethod( + binding, &nsXBLBinding::ExecuteAttachedHandler)); + } } return NS_OK; @@ -8801,17 +8797,24 @@ nsHTMLSelectElementSH::SetProperty(nsIXPConnectWrappedNative *wrapper, // static nsresult -nsHTMLPluginObjElementSH::GetPluginInstance(nsIXPConnectWrappedNative *wrapper, - nsIPluginInstance **_result) +nsHTMLPluginObjElementSH::GetPluginInstanceIfSafe(nsIXPConnectWrappedNative *wrapper, + nsIPluginInstance **_result) { *_result = nsnull; nsCOMPtr content(do_QueryWrappedNative(wrapper)); NS_ENSURE_TRUE(content, NS_ERROR_UNEXPECTED); - // Make sure that there is a plugin nsCOMPtr objlc(do_QueryInterface(content)); NS_ASSERTION(objlc, "Object nodes must implement nsIObjectLoadingContent"); + + // If it's not safe to run script we'll only return the instance if it + // exists. + if (!nsContentUtils::IsSafeToRunScript()) { + return objlc->GetPluginInstance(_result); + } + + // Make sure that there is a plugin return objlc->EnsureInstantiation(_result); } @@ -8837,22 +8840,51 @@ IsObjInProtoChain(JSContext *cx, JSObject *obj, JSObject *proto) return PR_FALSE; } - -// Note that not only XPConnect calls this PostCreate() method when -// it creates wrappers, nsObjectFrame also calls this method when a -// plugin is loaded if the embed/object element is already wrapped to -// get the scriptable plugin inserted into the embed/object's proto -// chain. - -NS_IMETHODIMP -nsHTMLPluginObjElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, - JSContext *cx, JSObject *obj) +class nsPluginProtoChainInstallRunner : public nsIRunnable { - nsresult rv = nsElementSH::PostCreate(wrapper, cx, obj); - NS_ENSURE_SUCCESS(rv, rv); +public: + NS_DECL_ISUPPORTS + + nsPluginProtoChainInstallRunner(nsIXPConnectWrappedNative* wrapper, + nsIScriptContext* scriptContext) + : mWrapper(wrapper), + mContext(scriptContext) + { + } + + NS_IMETHOD Run() + { + JSObject* obj = nsnull; + mWrapper->GetJSObject(&obj); + NS_ASSERTION(obj, "Should never be null"); + nsHTMLPluginObjElementSH::SetupProtoChain( + mWrapper, (JSContext*)mContext->GetNativeContext(), obj); + return NS_OK; + } + +private: + nsCOMPtr mWrapper; + nsCOMPtr mContext; +}; + +NS_IMPL_ISUPPORTS1(nsPluginProtoChainInstallRunner, nsIRunnable) + +// static +nsresult +nsHTMLPluginObjElementSH::SetupProtoChain(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, + JSObject *obj) +{ + NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), + "Shouldn't have gotten in here"); + + nsCxPusher cxPusher; + if (!cxPusher.Push(cx)) { + return NS_OK; + } nsCOMPtr pi; - rv = GetPluginInstance(wrapper, getter_AddRefs(pi)); + nsresult rv = GetPluginInstanceIfSafe(wrapper, getter_AddRefs(pi)); NS_ENSURE_SUCCESS(rv, rv); if (!pi) { @@ -8876,7 +8908,7 @@ nsHTMLPluginObjElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, if (IsObjInProtoChain(cx, obj, pi_obj)) { // We must have re-entered ::PostCreate() from nsObjectFrame() // (through the EnsureInstantiation() call in - // GetPluginInstance()), this means that we've already done what + // GetPluginInstanceIfSafe()), this means that we've already done what // we're about to do in this function so we can just return here. return NS_OK; @@ -8966,6 +8998,26 @@ nsHTMLPluginObjElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, return NS_OK; } +NS_IMETHODIMP +nsHTMLPluginObjElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, JSObject *obj) +{ + nsresult rv = nsElementSH::PostCreate(wrapper, cx, obj); + NS_ENSURE_SUCCESS(rv, rv); + + if (nsContentUtils::IsSafeToRunScript()) { + return SetupProtoChain(wrapper, cx, obj); + } + + nsCOMPtr scriptContext = + GetScriptContextFromJSContext(cx); + NS_ENSURE_TRUE(scriptContext, NS_ERROR_UNEXPECTED); + + nsContentUtils::AddScriptRunner( + new nsPluginProtoChainInstallRunner(wrapper, scriptContext)); + + return NS_OK; +} NS_IMETHODIMP nsHTMLPluginObjElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper, @@ -9067,7 +9119,7 @@ nsHTMLPluginObjElementSH::Call(nsIXPConnectWrappedNative *wrapper, jsval *argv, jsval *vp, PRBool *_retval) { nsCOMPtr pi; - nsresult rv = GetPluginInstance(wrapper, getter_AddRefs(pi)); + nsresult rv = GetPluginInstanceIfSafe(wrapper, getter_AddRefs(pi)); NS_ENSURE_SUCCESS(rv, rv); if (!pi) { @@ -9284,7 +9336,7 @@ nsHTMLPluginObjElementSH::NewResolve(nsIXPConnectWrappedNative *wrapper, // plugin instances. nsCOMPtr pi; - nsresult rv = GetPluginInstance(wrapper, getter_AddRefs(pi)); + nsresult rv = GetPluginInstanceIfSafe(wrapper, getter_AddRefs(pi)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr plugin_internal = diff --git a/mozilla/dom/src/base/nsDOMClassInfo.h b/mozilla/dom/src/base/nsDOMClassInfo.h index 484c02ceba9..890061e89c7 100644 --- a/mozilla/dom/src/base/nsDOMClassInfo.h +++ b/mozilla/dom/src/base/nsDOMClassInfo.h @@ -977,8 +977,8 @@ protected: { } - nsresult GetPluginInstance(nsIXPConnectWrappedNative *aWrapper, - nsIPluginInstance **aResult); + static nsresult GetPluginInstanceIfSafe(nsIXPConnectWrappedNative *aWrapper, + nsIPluginInstance **aResult); static nsresult GetPluginJSObject(JSContext *cx, JSObject *obj, nsIPluginInstance *plugin_inst, @@ -1004,6 +1004,10 @@ public: JSObject *obj, PRUint32 argc, jsval *argv, jsval *vp, PRBool *_retval); + + static nsresult SetupProtoChain(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, JSObject *obj); + static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) { return new nsHTMLPluginObjElementSH(aData); diff --git a/mozilla/layout/base/nsDocumentViewer.cpp b/mozilla/layout/base/nsDocumentViewer.cpp index 2d928a8c6bd..2593714dcff 100644 --- a/mozilla/layout/base/nsDocumentViewer.cpp +++ b/mozilla/layout/base/nsDocumentViewer.cpp @@ -1048,6 +1048,8 @@ DocumentViewerImpl::PermitUnload(PRBool *aPermitUnload) return NS_OK; } + NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe"); + // Now, fire an BeforeUnload event to the document and see if it's ok // to unload... nsEventStatus status = nsEventStatus_eIgnore; diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index 2c39fc157b8..26459e2e19b 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -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 diff --git a/mozilla/layout/generic/Makefile.in b/mozilla/layout/generic/Makefile.in index 528ad19e3a4..fe437852bd1 100644 --- a/mozilla/layout/generic/Makefile.in +++ b/mozilla/layout/generic/Makefile.in @@ -187,6 +187,7 @@ LOCAL_INCLUDES += \ -I$(srcdir)/../../content/xul/content/src \ -I$(srcdir)/../../content/base/src \ -I$(srcdir)/../../content/html/content/src \ + -I$(srcdir)/../../dom/src/base \ $(MOZ_CAIRO_CFLAGS) \ $(NULL) diff --git a/mozilla/layout/generic/nsObjectFrame.cpp b/mozilla/layout/generic/nsObjectFrame.cpp index e66e55b77e7..9798f69f43c 100644 --- a/mozilla/layout/generic/nsObjectFrame.cpp +++ b/mozilla/layout/generic/nsObjectFrame.cpp @@ -108,6 +108,7 @@ #include "nsDisplayList.h" #include "nsAttrName.h" #include "nsDataHashtable.h" +#include "nsDOMClassInfo.h" // headers for plugin scriptability #include "nsIScriptGlobalObject.h" @@ -1856,22 +1857,8 @@ nsObjectFrame::NotifyContentObjectWrapper() getter_AddRefs(wrapper)); if (!wrapper) { - // Nothing to do here if there's no wrapper for mContent - return; - } - - nsCOMPtr ci(do_QueryInterface(mContent)); - if (!ci) - return; - - nsCOMPtr s; - ci->GetHelperForLanguage(nsIProgrammingLanguage::JAVASCRIPT, - getter_AddRefs(s)); - - nsCOMPtr helper(do_QueryInterface(s)); - - if (!helper) { - // There's nothing we can do if there's no helper + // Nothing to do here if there's no wrapper for mContent. The proto + // chain will be fixed appropriately when the wrapper is created. return; } @@ -1880,13 +1867,7 @@ nsObjectFrame::NotifyContentObjectWrapper() if (NS_FAILED(rv)) return; - nsCxPusher cxPusher; - if (cxPusher.Push(mContent)) { - // Abuse the scriptable helper to trigger prototype setup for the - // wrapper for mContent so that this plugin becomes part of the DOM - // object. - helper->PostCreate(wrapper, cx, obj); - } + nsHTMLPluginObjElementSH::SetupProtoChain(wrapper, cx, obj); } // static diff --git a/mozilla/view/src/Makefile.in b/mozilla/view/src/Makefile.in index a9a78e64c36..ffff5980ec3 100644 --- a/mozilla/view/src/Makefile.in +++ b/mozilla/view/src/Makefile.in @@ -57,6 +57,9 @@ REQUIRES = xpcom \ pref \ thebes \ cairo \ + content \ + js \ + layout \ $(NULL) EXTRA_DSO_LIBS = gkgfx diff --git a/mozilla/view/src/nsViewManager.cpp b/mozilla/view/src/nsViewManager.cpp index 1331776a417..8ca479517d2 100644 --- a/mozilla/view/src/nsViewManager.cpp +++ b/mozilla/view/src/nsViewManager.cpp @@ -61,6 +61,7 @@ #include "nsHashtable.h" #include "nsCOMArray.h" #include "nsThreadUtils.h" +#include "nsContentUtils.h" #include "gfxContext.h" @@ -452,47 +453,51 @@ void nsViewManager::Refresh(nsView *aView, nsIRenderingContext *aContext, RootViewManager()->mRecursiveRefreshPending = PR_TRUE; return; } - SetPainting(PR_TRUE); - nsCOMPtr localcx; - NS_ASSERTION(aView->GetWidget(), - "Must have a widget to calculate coordinates correctly"); - if (nsnull == aContext) - { - localcx = CreateRenderingContext(*aView); + { + nsAutoScriptBlocker scriptBlocker; + SetPainting(PR_TRUE); - //couldn't get rendering context. this is ok at init time atleast - if (nsnull == localcx) { - SetPainting(PR_FALSE); - return; + nsCOMPtr localcx; + NS_ASSERTION(aView->GetWidget(), + "Must have a widget to calculate coordinates correctly"); + if (nsnull == aContext) + { + localcx = CreateRenderingContext(*aView); + + //couldn't get rendering context. this is ok at init time atleast + if (nsnull == localcx) { + SetPainting(PR_FALSE); + return; + } + } else { + // plain assignment grabs another reference. + localcx = aContext; } - } else { - // plain assignment grabs another reference. - localcx = aContext; - } - PRInt32 p2a = mContext->AppUnitsPerDevPixel(); + PRInt32 p2a = mContext->AppUnitsPerDevPixel(); - nsRefPtr ctx = localcx->ThebesContext(); + nsRefPtr ctx = localcx->ThebesContext(); - ctx->Save(); + ctx->Save(); - nsPoint vtowoffset = aView->ViewToWidgetOffset(); - ctx->Translate(gfxPoint(gfxFloat(vtowoffset.x) / p2a, - gfxFloat(vtowoffset.y) / p2a)); + nsPoint vtowoffset = aView->ViewToWidgetOffset(); + ctx->Translate(gfxPoint(gfxFloat(vtowoffset.x) / p2a, + gfxFloat(vtowoffset.y) / p2a)); - ctx->Translate(gfxPoint(-gfxFloat(viewRect.x) / p2a, - -gfxFloat(viewRect.y) / p2a)); + ctx->Translate(gfxPoint(-gfxFloat(viewRect.x) / p2a, + -gfxFloat(viewRect.y) / p2a)); - nsRegion opaqueRegion; - AddCoveringWidgetsToOpaqueRegion(opaqueRegion, mContext, aView); - damageRegion.Sub(damageRegion, opaqueRegion); + nsRegion opaqueRegion; + AddCoveringWidgetsToOpaqueRegion(opaqueRegion, mContext, aView); + damageRegion.Sub(damageRegion, opaqueRegion); - RenderViews(aView, *localcx, damageRegion); + RenderViews(aView, *localcx, damageRegion); - ctx->Restore(); + ctx->Restore(); - SetPainting(PR_FALSE); + SetPainting(PR_FALSE); + } if (RootViewManager()->mRecursiveRefreshPending) { // Unset this flag first, since if aUpdateFlags includes NS_VMREFRESH_IMMEDIATE