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

This commit is contained in:
jonas@sicking.cc 2008-03-14 16:08:57 -07:00
parent 5afad8f2fa
commit c078e73997
26 changed files with 556 additions and 264 deletions

View File

@ -90,6 +90,7 @@ class nsIWordBreaker;
class nsIJSRuntimeService;
class nsIEventListenerManager;
class nsIScriptContext;
class nsIRunnable;
template<class E> 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<nsIRunnable>* 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)

View File

@ -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

View File

@ -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

View File

@ -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<nsIRunnable>* nsContentUtils::sBlockedScriptRunners = nsnull;
PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
nsIJSRuntimeService *nsAutoGCRoot::sJSRuntimeService;
JSRuntime *nsAutoGCRoot::sJSScriptRuntime;
@ -323,6 +328,9 @@ nsContentUtils::Init()
}
}
sBlockedScriptRunners = new nsCOMArray<nsIRunnable>;
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<nsIScriptContext> 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<nsIRunnable> 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<nsIRunnable> run = aRunnable;
run->Run();
return PR_TRUE;
}
/* static */
void
nsContentUtils::HidePopupsInDocument(nsIDocument* aDocument)

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<nsGenericElement> 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___ */

View File

@ -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<nsIContent> thisContent =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(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

View File

@ -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;
};

View File

@ -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<nsScriptLoader>(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);

View File

@ -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<nsScriptLoader> > mPendingChildLoaders;
PRUint32 mBlockerCount;
PRPackedBool mEnabled;
PRPackedBool mHadPendingScripts;
};
#endif //__nsScriptLoader_h__

View File

@ -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<nsXBLBinding> 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<nsXBLBinding> binding = mAttachedStack.ElementAt(currentIndex);
mAttachedStack.RemoveElementAt(currentIndex);
if (binding) {
binding->ExecuteAttachedHandler();
}
PRUint32 lastItem = mAttachedStack.Length() - 1;
nsRefPtr<nsXBLBinding> 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<nsXBLBinding> binding = mAttachedStack[i];
nsresult rv = binding->EnsureScriptAPI();
if (NS_FAILED(rv)) {
mAttachedStack[i] = nsnull;
}
}
}
}
void

View File

@ -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()
{

View File

@ -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_

View File

@ -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();

View File

@ -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<nsIContent*>(this),
nsnull, &mutation);

View File

@ -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<nsXBLBinding> 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<nsXBLBinding> binding;
{
// Scope for the nsRefPtr
nsRefPtr<nsStyleContext> 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<nsXBLBinding>(
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<nsIContent> content(do_QueryWrappedNative(wrapper));
NS_ENSURE_TRUE(content, NS_ERROR_UNEXPECTED);
// Make sure that there is a plugin
nsCOMPtr<nsIObjectLoadingContent> 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<nsIXPConnectWrappedNative> mWrapper;
nsCOMPtr<nsIScriptContext> 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<nsIPluginInstance> 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<nsIScriptContext> 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<nsIPluginInstance> 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<nsIPluginInstance> pi;
nsresult rv = GetPluginInstance(wrapper, getter_AddRefs(pi));
nsresult rv = GetPluginInstanceIfSafe(wrapper, getter_AddRefs(pi));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPluginInstanceInternal> plugin_internal =

View File

@ -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);

View File

@ -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;

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

View File

@ -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)

View File

@ -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<nsIClassInfo> ci(do_QueryInterface(mContent));
if (!ci)
return;
nsCOMPtr<nsISupports> s;
ci->GetHelperForLanguage(nsIProgrammingLanguage::JAVASCRIPT,
getter_AddRefs(s));
nsCOMPtr<nsIXPCScriptable> 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

View File

@ -57,6 +57,9 @@ REQUIRES = xpcom \
pref \
thebes \
cairo \
content \
js \
layout \
$(NULL)
EXTRA_DSO_LIBS = gkgfx

View File

@ -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<nsIRenderingContext> 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<nsIRenderingContext> 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<gfxContext> ctx = localcx->ThebesContext();
nsRefPtr<gfxContext> 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