--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -85,16 +85,17 @@ class nsIConsoleService;
class nsIStringBundleService;
class nsIStringBundle;
class nsIContentPolicy;
class nsILineBreaker;
class nsIWordBreaker;
class nsIJSRuntimeService;
class nsIEventListenerManager;
class nsIScriptContext;
+class nsIRunnable;
template<class E> class nsCOMArray;
class nsIPref;
class nsVoidArray;
struct JSRuntime;
class nsICaseConversion;
class nsIWidget;
class nsPIDOMWindow;
#ifdef MOZ_XTF
@@ -1172,16 +1173,55 @@ public:
*/
static void GetOfflineAppManifest(nsIDOMWindow *aWindow, nsIURI **aURI);
/**
* Check whether an application should be allowed to use offline APIs.
*/
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();
static nsresult doReparentContentWrapper(nsIContent *aChild,
JSContext *cx,
JSObject *aOldGlobal,
JSObject *aNewGlobal,
@@ -1244,16 +1284,19 @@ private:
static PRInt32 sScriptRootCount[NS_STID_ARRAY_UBOUND];
static PRUint32 sJSGCThingRootCount;
#ifdef IBMBIDI
static nsIBidiKeyboard* sBidiKeyboard;
#endif
static PRBool sInitialized;
+ static PRUint32 sScriptBlockerCount;
+ static nsCOMArray<nsIRunnable>* sBlockedScriptRunners;
+ static PRUint32 sRunnersCountAtFirstBlocker;
};
#define NS_HOLD_JS_OBJECTS(obj, clazz) \
nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz), \
&NS_CYCLE_COLLECTION_NAME(clazz))
#define NS_DROP_JS_OBJECTS(obj, clazz) \
nsContentUtils::DropJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz))
@@ -1262,16 +1305,17 @@ private:
class nsCxPusher
{
public:
nsCxPusher();
~nsCxPusher(); // Calls Pop();
// Returns PR_FALSE if something erroneous happened.
PRBool Push(nsISupports *aCurrentTarget);
+ PRBool Push(JSContext *cx);
void Pop();
private:
nsCOMPtr<nsIJSContextStack> mStack;
nsCOMPtr<nsIScriptContext> mScx;
PRBool mScriptIsRunning;
};
@@ -1312,16 +1356,26 @@ private:
static nsIJSRuntimeService* sJSRuntimeService;
static JSRuntime* sJSScriptRuntime;
void* mPtr;
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)
#define NS_AUTO_GCROOT(ptr, result) \ \
nsAutoGCRoot NS_AUTO_GCROOT_PASTE(_autoGCRoot_, __LINE__) \
(ptr, result)
#define NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(_class) \
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -591,16 +591,18 @@ public:
virtual PRBool RemoveObserver(nsIDocumentObserver* aObserver) = 0;
// Observation hooks used to propagate notifications to document observers.
// BeginUpdate must be called before any batch of modifications of the
// content model or of style data, EndUpdate must be called afterward.
// 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
// either may be nsnull, but not both
virtual void ContentStatesChanged(nsIContent* aContent1,
nsIContent* aContent2,
PRInt32 aStateMask) = 0;
--- a/content/base/public/nsIObjectLoadingContent.idl
+++ b/content/base/public/nsIObjectLoadingContent.idl
@@ -38,17 +38,17 @@
#include "nsISupports.idl"
interface nsIObjectFrame;
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;
const unsigned long TYPE_IMAGE = 1;
const unsigned long TYPE_PLUGIN = 2;
const unsigned long TYPE_DOCUMENT = 3;
const unsigned long TYPE_NULL = 4;
@@ -66,16 +66,23 @@ interface nsIObjectLoadingContent : nsIS
/**
* Gets the content type that corresponds to the give MIME type. See the
* constants above for the list of possible values. If nothing else fits,
* TYPE_NULL will be returned.
*/
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
* instantiated. This method does nothing if the type is not #TYPE_PLUGIN.
* There is no guarantee that there will be a frame after this method is
* called; for example, the node may have a display:none style. If plugin
* instantiation is possible, it will be done synchronously by this method,
* and the plugin instance will be returned. A success return value does not
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -144,16 +144,18 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
#include "nsCompressedCharMap.h"
#include "nsINativeKeyBindings.h"
#include "nsIDOMNSUIEvent.h"
#include "nsIDOMNSEvent.h"
#include "nsIPrivateDOMEvent.h"
#include "nsXULPopupManager.h"
#include "nsIPermissionManager.h"
#include "nsIScriptObjectPrincipal.h"
+#include "nsIRunnable.h"
+#include "nsDOMJSUtils.h"
#ifdef IBMBIDI
#include "nsIBidiKeyboard.h"
#endif
#include "nsCycleCollectionParticipant.h"
// for ReportToConsole
#include "nsIStringBundle.h"
@@ -190,16 +192,19 @@ nsIWordBreaker *nsContentUtils::sWordBre
nsICaseConversion *nsContentUtils::sCaseConv;
nsVoidArray *nsContentUtils::sPtrsToPtrsToRelease;
nsIScriptRuntime *nsContentUtils::sScriptRuntimes[NS_STID_ARRAY_UBOUND];
PRInt32 nsContentUtils::sScriptRootCount[NS_STID_ARRAY_UBOUND];
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;
PRBool nsContentUtils::sInitialized = PR_FALSE;
static PLDHashTable sEventListenerManagersHash;
@@ -318,16 +323,19 @@ nsContentUtils::Init()
if (!PL_DHashTableInit(&sEventListenerManagersHash, &hash_table_ops,
nsnull, sizeof(EventListenerManagerMapEntry), 16)) {
sEventListenerManagersHash.ops = nsnull;
return NS_ERROR_OUT_OF_MEMORY;
}
}
+ sBlockedScriptRunners = new nsCOMArray<nsIRunnable>;
+ NS_ENSURE_TRUE(sBlockedScriptRunners, NS_ERROR_OUT_OF_MEMORY);
+
sInitialized = PR_TRUE;
return NS_OK;
}
PRBool
nsContentUtils::InitializeEventTable() {
NS_ASSERTION(!sEventTable, "EventTable already initialized!");
@@ -816,16 +824,22 @@ nsContentUtils::Shutdown()
// wrapper table.
if (sEventListenerManagersHash.entryCount == 0) {
PL_DHashTableFinish(&sEventListenerManagersHash);
sEventListenerManagersHash.ops = nsnull;
}
}
+ NS_ASSERTION(!sBlockedScriptRunners ||
+ sBlockedScriptRunners->Count() == 0,
+ "How'd this happen?");
+ delete sBlockedScriptRunners;
+ sBlockedScriptRunners = nsnull;
+
nsAutoGCRoot::Shutdown();
}
// static
PRBool
nsContentUtils::IsCallerTrustedForCapability(const char* aCapability)
{
// The secman really should handle UniversalXPConnect case, since that
@@ -2583,47 +2597,64 @@ nsCxPusher::Push(nsISupports *aCurrentTa
NS_ENSURE_TRUE(sgo || !hasHadScriptObject, PR_FALSE);
}
} else {
sgo = do_QueryInterface(aCurrentTarget);
}
JSContext *cx = nsnull;
+ nsCOMPtr<nsIScriptContext> scx;
+
if (sgo) {
- mScx = sgo->GetContext();
-
- if (mScx) {
- cx = (JSContext *)mScx->GetNativeContext();
+ 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);
}
if (mStack) {
if (IsContextOnStack(mStack, cx)) {
// If the context is on the stack, that means that a script
// is running at the moment in the context.
mScriptIsRunning = PR_TRUE;
}
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;
}
void
nsCxPusher::Pop()
{
if (!mScx || !mStack) {
@@ -3957,16 +3988,75 @@ nsContentUtils::DOMEventToNativeKeyEvent
aNativeEvent->nativeEvent = GetNativeEvent(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)
{
NS_PRECONDITION(aDocument, "Null document");
#ifdef MOZ_XUL
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm) {
nsCOMPtr<nsISupports> container = aDocument->GetContainer();
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2696,50 +2696,64 @@ nsDocument::RemoveObserver(nsIDocumentOb
void
nsDocument::BeginUpdate(nsUpdateType aUpdateType)
{
if (mUpdateNestLevel == 0) {
mBindingManager->BeginOutermostUpdate();
}
++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
// binding manager know we're done.
mBindingManager->EndOutermostUpdate();
}
- if (mScriptLoader) {
- mScriptLoader->RemoveExecuteBlocker();
- }
-
if (mUpdateNestLevel == 0) {
PRUint32 length = mFinalizableFrameLoaders.Length();
if (length > 0) {
nsTArray<nsRefPtr<nsFrameLoader> > loaders;
mFinalizableFrameLoaders.SwapElements(loaders);
for (PRInt32 i = 0; i < length; ++i) {
loaders[i]->Finalize();
}
}
}
}
+PRUint32
+nsDocument::GetUpdateNestingLevel()
+{
+ return mUpdateNestLevel;
+}
+
+PRBool
+nsDocument::AllUpdatesAreContent()
+{
+ return mContentUpdateNestLevel == mUpdateNestLevel;
+}
+
void
nsDocument::BeginLoad()
{
// Block onload here to prevent having to deal with blocking and
// unblocking it while we know the document is loading.
BlockOnload();
NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
@@ -5811,16 +5825,18 @@ nsDocument::MutationEventDispatched(nsIN
realTargets.AppendObject(possibleTarget);
}
}
mSubtreeModifiedTargets.Clear();
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);
}
}
}
static PRUint32 GetURIHash(nsIURI* aURI)
{
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -469,16 +469,18 @@ public:
* return false if the observer cannot be found.
*/
virtual PRBool RemoveObserver(nsIDocumentObserver* aObserver);
// Observation hooks used to propagate notifications to document
// 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,
nsIContent* aContent2,
PRInt32 aStateMask);
virtual void AttributeWillChange(nsIContent* aChild,
PRInt32 aNameSpaceID,
@@ -794,16 +796,18 @@ protected:
nsRefPtr<nsXMLEventsManager> mXMLEventsManager;
nsCOMPtr<nsIScriptEventManager> mScriptEventManager;
nsString mBaseTarget;
// Our update nesting level
PRUint32 mUpdateNestLevel;
+ // Our UPDATE_CONTENT_MODEL update nesting level
+ PRUint32 mContentUpdateNestLevel;
private:
friend class nsUnblockOnloadEvent;
void PostUnblockOnloadEvent();
void DoUnblockOnload();
/**
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -487,16 +487,18 @@ nsGenericDOMDataNode::SetTextInternal(PR
mutation.mPrevAttrValue = oldValue;
if (aLength > 0) {
nsAutoString val;
mText.AppendTo(val);
mutation.mNewAttrValue = do_GetAtom(val);
}
+ mozAutoDocUpdateContentUnnest updateUnnest(document);
+
mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
nsEventDispatcher::Dispatch(this, nsnull, &mutation);
}
}
return NS_OK;
}
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -2753,16 +2753,19 @@ nsGenericElement::doInsertChildAt(nsICon
} else {
nsNodeUtils::ContentInserted(container, aKid, aIndex);
}
if (nsContentUtils::HasMutationListeners(aKid,
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);
}
}
return NS_OK;
}
@@ -2820,16 +2823,19 @@ nsGenericElement::doRemoveChildAt(PRUint
nsMutationGuard guard;
mozAutoSubtreeModified subtree(nsnull, nsnull);
if (aNotify &&
nsContentUtils::HasMutationListeners(aKid,
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);
}
// Someone may have removed the kid or any of its siblings while that event
// was processing.
if (guard.Mutated(0)) {
aIndex = container->IndexOf(aKid);
@@ -3787,16 +3793,19 @@ nsGenericElement::SetAttrAndNotify(PRInt
GetAttr(aNamespaceID, aName, newValue);
if (!newValue.IsEmpty()) {
mutation.mNewAttrValue = do_GetAtom(newValue);
}
if (!aOldValue.IsEmpty()) {
mutation.mPrevAttrValue = do_GetAtom(aOldValue);
}
mutation.mAttrChange = modType;
+
+ mozAutoDocUpdateContentUnnest updateUnnest(document);
+
mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
nsEventDispatcher::Dispatch(this, nsnull, &mutation);
}
if (aNamespaceID == kNameSpaceID_XMLEvents &&
aName == nsGkAtoms::event && mNodeInfo->GetDocument()) {
mNodeInfo->GetDocument()->AddXMLEventsContent(this);
}
@@ -4034,16 +4043,18 @@ nsGenericElement::UnsetAttr(PRInt32 aNam
mutation.mAttrName = aName;
nsAutoString value;
oldValue.ToString(value);
if (!value.IsEmpty())
mutation.mPrevAttrValue = do_GetAtom(value);
mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
+ mozAutoDocUpdateContentUnnest updateUnnest(document);
+
mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
nsEventDispatcher::Dispatch(this, nsnull, &mutation);
}
return AfterSetAttr(aNameSpaceID, aName, nsnull, aNotify);
}
const nsAttrName*
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -58,16 +58,17 @@
#include "nsGenericDOMNodeList.h"
#include "nsContentUtils.h"
#include "nsNodeUtils.h"
#include "nsAttrAndChildArray.h"
#include "mozFlushType.h"
#include "nsDOMAttributeMap.h"
#include "nsIWeakReference.h"
#include "nsCycleCollectionParticipant.h"
+#include "nsIDocument.h"
class nsIDOMAttr;
class nsIDOMEventListener;
class nsIFrame;
class nsIDOMNamedNodeMap;
class nsDOMCSSDeclaration;
class nsIDOMCSSStyleDeclaration;
class nsIURI;
@@ -1061,9 +1062,38 @@ public:
nsNSElementTearoff(nsGenericElement *aContent) : mContent(aContent)
{
}
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___ */
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -122,20 +122,21 @@ NS_IMETHODIMP
nsAsyncInstantiateEvent::Run()
{
// Check if we've been "revoked"
if (mContent->mPendingInstantiateEvent != this)
return NS_OK;
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)) {
if (LOG_ENABLED()) {
nsCAutoString spec;
if (mURI) {
mURI->GetSpec(spec);
}
@@ -514,17 +515,17 @@ nsObjectLoadingContent::OnStartRequest(n
case eType_Plugin:
mInstantiating = PR_TRUE;
if (mType != newType) {
// This can go away once plugin loading moves to content (bug 90268)
mType = newType;
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.
// Abort the load though, we have no use for the data.
mInstantiating = PR_FALSE;
return NS_BINDING_ABORTED;
}
@@ -580,17 +581,17 @@ nsObjectLoadingContent::OnStartRequest(n
if (mContentType.EqualsLiteral("application/x-director")) {
LOG(("OBJLC [%p]: (ignoring)\n", this));
rv = NS_OK; // otherwise, the AutoFallback will make us fall back
return NS_BINDING_ABORTED;
}
#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
// OnStartRequest, not by Instantiate.
frame->TryNotifyContentObjectWrapper();
}
}
return rv;
@@ -669,17 +670,17 @@ nsObjectLoadingContent::EnsureInstantiat
// Must set our out parameter to null as we have various early returns with
// an NS_OK result.
*aInstance = nsnull;
if (mType != eType_Plugin) {
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.
if (mPendingInstantiateEvent) {
LOG(("OBJLC [%p]: Revoking pending instantiate event\n", this));
mPendingInstantiateEvent = nsnull;
}
} else {
@@ -706,17 +707,17 @@ nsObjectLoadingContent::EnsureInstantiat
nsPresShellIterator iter(doc);
nsCOMPtr<nsIPresShell> shell;
while ((shell = iter.GetNextShell())) {
shell->RecreateFramesFor(thisContent);
}
mInstantiating = PR_FALSE;
- frame = GetFrame(PR_FALSE);
+ frame = GetExistingFrame(eFlushContent);
if (!frame) {
return NS_OK;
}
}
nsIFrame *nsiframe;
CallQueryInterface(frame, &nsiframe);
nsWeakFrame weakFrame(nsiframe);
@@ -770,16 +771,29 @@ nsObjectLoadingContent::HasNewFrame(nsIO
// when the event runs.
mPendingInstantiateEvent = event;
}
}
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)
{
*aType = GetTypeOfContent(PromiseFlatCString(aMIMEType));
return NS_OK;
}
// nsIInterfaceRequestor
@@ -1542,23 +1556,22 @@ nsObjectLoadingContent::GetObjectBaseURI
thisContent->GetOwnerDoc(),
baseURI);
} else {
baseURI.swap(*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();
if (!doc) {
return nsnull; // No current doc -> no frame
}
nsIPresShell* shell = doc->GetPrimaryShell();
@@ -1566,27 +1579,27 @@ nsObjectLoadingContent::GetFrame(PRBool
return nsnull; // No presentation -> no frame
}
frame = shell->GetPrimaryFrameFor(thisContent);
if (!frame) {
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;
CallQueryInterface(frame, &objFrame);
return objFrame;
}
void
@@ -1605,17 +1618,17 @@ nsObjectLoadingContent::HandleBeingBlock
}
}
}
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
}
nsIFrame* iframe;
CallQueryInterface(frame, &iframe);
if (iframe->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
LOG(("OBJLC [%p]: Frame hasn't been reflown yet\n", this));
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -266,24 +266,37 @@ class nsObjectLoadingContent : public ns
/**
* Gets the base URI to be used for this object. This differs from
* nsIContent::GetBaseURI in that it takes codebase attributes into
* account.
*/
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
* return value of the Should* call, while aRetval is what it returned in
* its out parameter.
*/
void HandleBeingBlockedByContentPolicy(nsresult aStatus,
PRInt16 aRetval);
@@ -403,13 +416,13 @@ class nsObjectLoadingContent : public ns
*/
PRBool mInstantiating : 1;
// Blocking status from content policy
PRBool mUserDisabled : 1;
PRBool mSuppressed : 1;
// Whether we fell back because of an unsupported type
PRBool mTypeUnsupported:1;
- friend struct nsAsyncInstantiateEvent;
+ friend class nsAsyncInstantiateEvent;
};
#endif
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -154,18 +154,17 @@ NS_IMPL_THREADSAFE_ISUPPORTS0(nsScriptLo
//////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////
nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
: mDocument(aDocument),
mBlockerCount(0),
- mEnabled(PR_TRUE),
- mHadPendingScripts(PR_FALSE)
+ mEnabled(PR_TRUE)
{
}
nsScriptLoader::~nsScriptLoader()
{
mObservers.Clear();
for (PRInt32 i = 0; i < mPendingRequests.Count(); i++) {
@@ -486,33 +485,42 @@ nsScriptLoader::ProcessScriptElement(nsI
request->mLoading = PR_FALSE;
request->mIsInline = PR_TRUE;
request->mURI = mDocument->GetDocumentURI();
request->mLineNo = aElement->GetScriptLineNumber();
// 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);
}
}
// Add the request to our pending requests list
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;
}
nsresult
nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest)
{
- NS_ASSERTION(ReadyToExecuteScripts(),
+ NS_ASSERTION(ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript(),
"Caller forgot to check ReadyToExecuteScripts()");
NS_ENSURE_ARG(aRequest);
nsAFlatString* script;
nsAutoString textData;
// If there's no script text, we try to get it from the element
if (aRequest->mIsInline) {
--- a/content/base/src/nsScriptLoader.h
+++ b/content/base/src/nsScriptLoader.h
@@ -142,41 +142,25 @@ public:
ProcessPendingRequestsAsync();
}
mEnabled = aEnabled;
}
/**
* 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();
}
}
/**
* Convert the given buffer to a UTF-16 string.
* @param aChannel Channel corresponding to the data. May be null.
* @param aData The data to convert
* @param aLength Length of the data
@@ -241,12 +225,11 @@ protected:
nsIDocument* mDocument; // [WEAK]
nsCOMArray<nsIScriptLoaderObserver> mObservers;
nsCOMArray<nsScriptLoadRequest> mPendingRequests;
nsCOMPtr<nsIScriptElement> mCurrentScript;
// XXXbz do we want to cycle-collect these or something? Not sure.
nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders;
PRUint32 mBlockerCount;
PRPackedBool mEnabled;
- PRPackedBool mHadPendingScripts;
};
#endif //__nsScriptLoader_h__
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -967,38 +967,23 @@ void
nsBindingManager::ProcessAttachedQueue(PRUint32 aSkipSize)
{
if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize)
return;
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();
}
}
// If NodeWillBeDestroyed has run we don't want to clobber
// mProcessingAttachedStack set there.
if (mDocument) {
mProcessingAttachedStack = PR_FALSE;
}
@@ -1532,26 +1517,16 @@ nsBindingManager::BeginOutermostUpdate()
void
nsBindingManager::EndOutermostUpdate()
{
if (!mProcessingAttachedStack) {
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
nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
nsIContent* aChild,
PRUint32 aIndexInContainer,
PRBool aAppend)
{
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -267,18 +267,17 @@ nsXBLJSClass::Destroy()
// Implementation /////////////////////////////////////////////////////////////////
// Constructors/Destructors
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
NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
}
nsXBLBinding::~nsXBLBinding(void)
@@ -781,32 +780,16 @@ nsXBLBinding::GenerateAnonymousContent()
}
// Conserve space by wiping the attributes off the clone.
if (mContent)
mContent->UnsetAttr(namespaceID, name, PR_FALSE);
}
}
-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()
{
// Don't install handlers if scripts aren't allowed.
if (AllowScripts()) {
// Fetch the handlers prototypes for this binding.
nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
--- a/content/xbl/src/nsXBLBinding.h
+++ b/content/xbl/src/nsXBLBinding.h
@@ -115,17 +115,18 @@ public:
PRBool HasStyleSheets() const;
PRBool InheritsStyle() const;
PRBool ImplementsInterface(REFNSIID aIID) const;
PRBool ShouldBuildChildFrames() const;
void GenerateAnonymousContent();
void InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement);
- nsresult EnsureScriptAPI();
+ void InstallEventHandlers();
+ nsresult InstallImplementation();
void ExecuteAttachedHandler();
void ExecuteDetachedHandler();
void UnhookEventHandlers();
nsIAtom* GetBaseTag(PRInt32* aNameSpaceID);
nsXBLBinding* RootBinding();
nsXBLBinding* GetFirstStyleBinding();
@@ -162,30 +163,24 @@ public:
PRBool AllowScripts(); // XXX make const
void RemoveInsertionParent(nsIContent* aParent);
PRBool HasInsertionParent(nsIContent* aParent);
// 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
nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.
nsRefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the base class bindings.
nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it.
// A hash from nsIContent* -> (a sorted array of nsXBLInsertionPoint)
nsClassHashtable<nsISupportsHashKey, nsInsertionPointList>* mInsertionPointTable;
PRPackedBool mIsStyleBinding;
PRPackedBool mMarkedForDeath;
- PRPackedBool mInstalledAPI;
};
#endif // nsXBLBinding_h_
--- a/content/xbl/src/nsXBLService.cpp
+++ b/content/xbl/src/nsXBLService.cpp
@@ -558,16 +558,23 @@ nsXBLService::LoadBindings(nsIContent* a
}
// Set the binding's bound element.
newBinding->SetBoundElement(aContent);
// 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();
newBinding.swap(*aBinding);
return NS_OK;
}
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -1388,16 +1388,18 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpa
mutation.mRelatedNode = attrNode;
mutation.mAttrName = aName;
if (!oldValue.IsEmpty())
mutation.mPrevAttrValue = do_GetAtom(oldValue);
mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
+ mozAutoDocUpdateContentUnnest updateUnnest(doc);
+
mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this),
nsnull, &mutation);
}
return NS_OK;
}
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -54,16 +54,18 @@
#include "nsIScriptError.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
#include "xptcall.h"
#include "prprf.h"
#include "nsTArray.h"
#include "nsCSSValue.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
// JavaScript includes
#include "jsapi.h"
#include "jsprvtd.h" // we are using private JS typedefs...
#include "jscntxt.h"
#include "jsdbgapi.h"
#include "jsnum.h"
#include "jsscope.h"
@@ -7134,30 +7136,27 @@ nsElementSH::PostCreate(nsIXPConnectWrap
// return here.
return NS_OK;
}
// 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.
-
- return binding->EnsureScriptAPI();
+ doc->BindingManager()->GetBinding(content)) {
+ // There's already a binding for this element so nothing left to
+ // be done here.
+
+ // 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.
nsIPresShell *shell = doc->GetPrimaryShell();
if (!shell) {
return NS_OK;
}
@@ -7171,16 +7170,17 @@ nsElementSH::PostCreate(nsIXPConnectWrap
}
// Get the computed -moz-binding directly from the style context
nsPresContext *pctx = shell->GetPresContext();
NS_ENSURE_TRUE(pctx, NS_ERROR_UNEXPECTED);
// 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,
nsnull);
NS_ENSURE_TRUE(sc, NS_ERROR_FAILURE);
nsCSSValue::URL *bindingURL = sc->GetStyleDisplay()->mBinding;
if (!bindingURL) {
@@ -7195,27 +7195,23 @@ nsElementSH::PostCreate(nsIXPConnectWrap
NS_ENSURE_TRUE(xblService, NS_ERROR_NOT_AVAILABLE);
xblService->LoadBindings(content, bindingURL->mURI,
bindingURL->mOriginPrincipal, PR_FALSE,
getter_AddRefs(binding), &dummy);
}
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;
}
NS_IMETHODIMP
nsElementSH::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, PRBool *_retval)
@@ -8796,27 +8792,34 @@ nsHTMLSelectElementSH::SetProperty(nsIXP
// This resolve hook makes embed.nsIFoo work as if
// QueryInterface(Components.interfaces.nsIFoo) was called on the
// plugin instance, the result of calling QI, assuming it's
// successful, will be defined on the embed element as a nsIFoo
// property.
// 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);
}
// Check if proto is already in obj's prototype chain.
static PRBool
IsObjInProtoChain(JSContext *cx, JSObject *obj, JSObject *proto)
{
@@ -8832,32 +8835,61 @@ IsObjInProtoChain(JSContext *cx, JSObjec
}
o = p;
}
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)
-{
- nsresult rv = nsElementSH::PostCreate(wrapper, cx, obj);
- NS_ENSURE_SUCCESS(rv, rv);
+class nsPluginProtoChainInstallRunner : public nsIRunnable
+{
+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) {
// No plugin around for this object.
return NS_OK;
}
@@ -8871,17 +8903,17 @@ nsHTMLPluginObjElementSH::PostCreate(nsI
// Didn't get a plugin instance JSObject, nothing we can do then.
return NS_OK;
}
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;
}
// If we got an xpconnect-wrapped plugin object, set obj's
// prototype's prototype to the scriptable plugin.
@@ -8961,16 +8993,36 @@ nsHTMLPluginObjElementSH::PostCreate(nsI
// | by LiveConnect
// |
// |__ xpc wrapped native embed node
//
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,
JSContext *cx, JSObject *obj, jsval id,
jsval *vp, PRBool *_retval)
{
JSAutoRequest ar(cx);
@@ -9062,17 +9114,17 @@ nsHTMLPluginObjElementSH::SetProperty(ns
}
NS_IMETHODIMP
nsHTMLPluginObjElementSH::Call(nsIXPConnectWrappedNative *wrapper,
JSContext *cx, JSObject *obj, PRUint32 argc,
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) {
// No plugin around for this object.
return NS_ERROR_NOT_AVAILABLE;
}
@@ -9279,17 +9331,17 @@ nsHTMLPluginObjElementSH::NewResolve(nsI
}
// This code resolves embed.nsIFoo to the nsIFoo wrapper of the
// plugin/applet instance. We only want to do that for plugin
// instances that are not scriptable using NPRuntime or are Java
// 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 =
do_QueryInterface(pi);
#ifdef OJI
nsCOMPtr<nsIJVMPluginInstance> java_plugin_instance =
do_QueryInterface(pi);
--- a/dom/src/base/nsDOMClassInfo.h
+++ b/dom/src/base/nsDOMClassInfo.h
@@ -972,18 +972,18 @@ protected:
: nsHTMLElementSH(aData)
{
}
virtual ~nsHTMLPluginObjElementSH()
{
}
- nsresult GetPluginInstance(nsIXPConnectWrappedNative *aWrapper,
- nsIPluginInstance **aResult);
+ static nsresult GetPluginInstanceIfSafe(nsIXPConnectWrappedNative *aWrapper,
+ nsIPluginInstance **aResult);
static nsresult GetPluginJSObject(JSContext *cx, JSObject *obj,
nsIPluginInstance *plugin_inst,
JSObject **plugin_obj,
JSObject **plugin_proto);
static nsresult GetJavaPluginJSObject(JSContext *cx, JSObject *obj,
nsIPluginInstance *plugin_inst,
@@ -999,16 +999,20 @@ public:
NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
NS_IMETHOD Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
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);
}
};
// HTMLOptionsCollection helper
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1043,16 +1043,18 @@ DocumentViewerImpl::PermitUnload(PRBool
nsPIDOMWindow *window = mDocument->GetWindow();
if (!window) {
// This is odd, but not fatal
NS_WARNING("window not set for document!");
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;
nsBeforePageUnloadEvent event(PR_TRUE, NS_BEFORE_PAGE_UNLOAD);
event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
// XXX Dispatching to |window|, but using |document| as the target.
event.target = mDocument;
nsresult rv = NS_OK;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -755,16 +755,17 @@ FrameArena::FreeFrame(size_t aSize, void
struct nsCallbackEventRequest
{
nsIReflowCallback* callback;
nsCallbackEventRequest* next;
};
// ----------------------------------------------------------------------------
class nsPresShellEventCB;
+class nsAutoCauseReflowNotifier;
class PresShell : public nsIPresShell, public nsIViewObserver,
public nsStubDocumentObserver,
public nsISelectionController, public nsIObserver,
public nsSupportsWeakReference
{
public:
PresShell();
@@ -1004,18 +1005,24 @@ public:
protected:
virtual ~PresShell();
void HandlePostedReflowCallbacks();
void CancelPostedReflowCallbacks();
void UnsuppressAndInvalidate();
- void WillCauseReflow() { ++mChangeNestCount; }
+
+ void WillCauseReflow() {
+ nsContentUtils::AddScriptBlocker();
+ ++mChangeNestCount;
+ }
nsresult DidCauseReflow();
+ friend class nsAutoCauseReflowNotifier;
+
void WillDoReflow();
void DidDoReflow();
nsresult ProcessReflowCommands(PRBool aInterruptible);
void ClearReflowEventStatus();
void PostReflowEvent();
void DoReflow(nsIFrame* aFrame);
#ifdef DEBUG
@@ -1204,16 +1211,39 @@ private:
nsCOMPtr<nsITimer> mResizeEventTimer;
typedef void (*nsPluginEnumCallback)(PresShell*, nsIContent*);
void EnumeratePlugins(nsIDOMDocument *aDocument,
const nsString &aPluginTag,
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:
nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
virtual void HandleEvent(nsEventChainPostVisitor& aVisitor)
{
if (aVisitor.mPresContext && aVisitor.mEvent->eventStructType != NS_EVENT) {
@@ -2378,41 +2408,45 @@ PresShell::InitialReflow(nscoord aWidth,
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
if (root) {
MOZ_TIMER_DEBUGLOG(("Reset and start: Frame Creation: PresShell::InitialReflow(), this=%p\n",
(void*)this));
MOZ_TIMER_RESET(mFrameCreationWatch);
MOZ_TIMER_START(mFrameCreationWatch);
- WillCauseReflow();
- 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);
- }
-
- // 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.
+ {
+ 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);
+ }
+
+ // 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();
+ }
+
+ // 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();
// Constructors may have killed us too
NS_ENSURE_STATE(!mHaveShutDown);
// Now flush out pending restyles before we actually reflow, in
// case XBL constructors changed styles somewhere.
@@ -2520,30 +2554,29 @@ PresShell::ResizeReflow(nscoord aWidth,
// Make sure style is up to date
mFrameConstructor->ProcessPendingRestyles();
if (!mIsDestroying) {
// 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;
mDirtyRoots.RemoveElement(rootFrame);
DoReflow(rootFrame);
mIsReflowing = PR_FALSE;
}
- DidCauseReflow();
DidDoReflow();
}
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
}
if (aHeight == NS_UNCONSTRAINEDSIZE) {
mPresContext->SetVisibleArea(
@@ -3081,16 +3114,17 @@ PresShell::RestoreRootScrollPosition()
{
// Restore frame state for the root scroll frame
nsCOMPtr<nsILayoutHistoryState> historyState =
mDocument->GetLayoutHistoryState();
// Make sure we don't reenter reflow via the sync paint that happens while
// 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) {
nsIFrame* scrollFrame = GetRootScrollFrame();
if (scrollFrame) {
nsIScrollableFrame* scrollableFrame;
CallQueryInterface(scrollFrame, &scrollableFrame);
if (scrollableFrame) {
@@ -3371,16 +3405,18 @@ PresShell::RecreateFramesFor(nsIContent*
NS_ASSERTION(mViewManager, "Should have view manager");
nsIViewManager::UpdateViewBatch batch(mViewManager);
// Have to make sure that the content notifications are flushed before we
// 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);
// Mark ourselves as not safe to flush while we're doing frame construction.
++mChangeNestCount;
nsresult rv = mFrameConstructor->ProcessRestyledFrames(changeList);
--mChangeNestCount;
@@ -4462,32 +4498,40 @@ PresShell::HandlePostedReflowCallbacks()
if (shouldFlush)
FlushPendingNotifications(Flush_Layout);
}
NS_IMETHODIMP
PresShell::IsSafeToFlush(PRBool& aIsSafeToFlush)
{
- aIsSafeToFlush = PR_TRUE;
-
- if (mIsReflowing || mChangeNestCount) {
- // Not safe if we are reflowing or in the middle of frame construction
- aIsSafeToFlush = PR_FALSE;
- } else {
+ // 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 (aIsSafeToFlush) {
// Not safe if we are painting
nsIViewManager* viewManager = GetViewManager();
if (viewManager) {
PRBool isPainting = PR_FALSE;
viewManager->IsPainting(isPainting);
if (isPainting) {
aIsSafeToFlush = PR_FALSE;
}
}
}
+
+ NS_ASSERTION(aIsSafeToFlush == nsContentUtils::IsSafeToRunScript(),
+ "Someone forgot to block scripts");
+
return NS_OK;
}
NS_IMETHODIMP
PresShell::FlushPendingNotifications(mozFlushType aType)
{
return DoFlushPendingNotifications(aType, PR_FALSE);
@@ -4581,17 +4625,18 @@ PresShell::IsReflowLocked(PRBool* aIsRef
void
PresShell::CharacterDataChanged(nsIDocument *aDocument,
nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
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
// resulting reflow, because this call causes continuation frames of the
// text frame the caret is in to forget what part of the content they
// refer to, making it hard for them to return the correct continuation
// frame to the caret.
mCaret->InvalidateOutsideCaret();
@@ -4610,33 +4655,31 @@ PresShell::CharacterDataChanged(nsIDocum
aContent)
mFrameConstructor->RestyleForAppend(container, index);
else
mFrameConstructor->RestyleForInsertOrChange(container, aContent);
}
mFrameConstructor->CharacterDataChanged(aContent, aInfo->mAppend);
VERIFY_STYLE_TREE;
- DidCauseReflow();
}
void
PresShell::ContentStatesChanged(nsIDocument* aDocument,
nsIContent* aContent1,
nsIContent* aContent2,
PRInt32 aStateMask)
{
NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentStatesChanged");
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
if (mDidInitialReflow) {
- WillCauseReflow();
+ nsAutoCauseReflowNotifier crNotifier(this);
mFrameConstructor->ContentStatesChanged(aContent1, aContent2, aStateMask);
VERIFY_STYLE_TREE;
- DidCauseReflow();
}
}
void
PresShell::AttributeChanged(nsIDocument* aDocument,
nsIContent* aContent,
PRInt32 aNameSpaceID,
@@ -4646,79 +4689,76 @@ PresShell::AttributeChanged(nsIDocument*
{
NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged");
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
// XXXwaterson it might be more elegant to wait until after the
// 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();
}
}
void
PresShell::ContentAppended(nsIDocument *aDocument,
nsIContent* aContainer,
PRInt32 aNewIndexInContainer)
{
NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended");
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
NS_PRECONDITION(aContainer, "must have container");
if (!mDidInitialReflow) {
return;
}
- WillCauseReflow();
+ nsAutoCauseReflowNotifier crNotifier(this);
MOZ_TIMER_DEBUGLOG(("Start: Frame Creation: PresShell::ContentAppended(), this=%p\n", this));
MOZ_TIMER_START(mFrameCreationWatch);
// Call this here so it only happens for real content mutations and
// not cases when the frame constructor calls its own methods to force
// frame reconstruction.
mFrameConstructor->RestyleForAppend(aContainer, aNewIndexInContainer);
mFrameConstructor->ContentAppended(aContainer, aNewIndexInContainer);
VERIFY_STYLE_TREE;
MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::ContentAppended(), this=%p\n", this));
MOZ_TIMER_STOP(mFrameCreationWatch);
- DidCauseReflow();
}
void
PresShell::ContentInserted(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
{
NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted");
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
if (!mDidInitialReflow) {
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
// frame reconstruction.
if (aContainer)
mFrameConstructor->RestyleForInsertOrChange(aContainer, aChild);
mFrameConstructor->ContentInserted(aContainer, aChild,
aIndexInContainer, nsnull);
VERIFY_STYLE_TREE;
- DidCauseReflow();
}
void
PresShell::ContentRemoved(nsIDocument *aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
{
@@ -4729,41 +4769,37 @@ PresShell::ContentRemoved(nsIDocument *a
if (mCaret) {
mCaret->InvalidateOutsideCaret();
}
// Notify the ESM that the content has been removed, so that
// 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
// frame reconstruction.
if (aContainer)
mFrameConstructor->RestyleForRemove(aContainer, aChild, aIndexInContainer);
PRBool didReconstruct;
mFrameConstructor->ContentRemoved(aContainer, aChild,
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;
}
void
nsIPresShell::ReconstructStyleDataInternal()
{
mStylesHaveChanged = PR_FALSE;
@@ -5522,17 +5558,21 @@ nsresult PresShell::RetargetEventToParen
NS_IMETHODIMP
PresShell::HandleEvent(nsIView *aView,
nsGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
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;
}
#ifdef ACCESSIBILITY
if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT) {
return HandleEventInternal(aEvent, aView, aEventStatus);
}
#endif
@@ -6164,16 +6204,18 @@ PresShell::DidCauseReflow()
// our reflow. Make sure these get processed at some point.
// XXXbz why is this really needed? ProcessReflowCommands handles posting
// reflow events if there are reflow roots remaining, and FrameNeedsReflow
// posts events as needed as well. I think we should remove this.
PostReflowEvent();
}
+ nsContentUtils::RemoveScriptBlocker();
+
return NS_OK;
}
void
PresShell::WillDoReflow()
{
// We just reflowed, tell the caret that its frame might have moved.
// XXXbz that comment makes no sense
@@ -6341,16 +6383,17 @@ PresShell::ProcessReflowCommands(PRBool
// If reflow is interruptible, then make a note of our deadline.
const PRIntervalTime deadline = aInterruptible
? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
: (PRIntervalTime)0;
// Scope for the reflow entry point
{
+ nsAutoScriptBlocker scriptBlocker;
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
mIsReflowing = PR_TRUE;
do {
// Send an incremental reflow notification to the target frame.
PRInt32 idx = mDirtyRoots.Count() - 1;
nsIFrame *target = static_cast<nsIFrame*>(mDirtyRoots[idx]);
mDirtyRoots.RemoveElementAt(idx);
@@ -6370,17 +6413,20 @@ PresShell::ProcessReflowCommands(PRBool
(!aInterruptible || PR_IntervalNow() < deadline));
// XXXwaterson for interruptible reflow, examine the tree and
// re-enqueue any unflowed reflow targets.
mIsReflowing = PR_FALSE;
}
- DidDoReflow();
+ // Exiting the scriptblocker might have killed us
+ if (!mIsDestroying) {
+ DidDoReflow();
+ }
// DidDoReflow might have killed us
if (!mIsDestroying) {
#ifdef DEBUG
if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
(void*)this);
}
@@ -6507,19 +6553,22 @@ PresShell::Observe(nsISupports* aSubject
// Because "chrome:" URL equality is messy, reframe image box
// frames (hack!).
nsStyleChangeList changeList;
WalkFramesThroughPlaceholders(mPresContext, rootFrame,
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
InvalidateAccessibleSubtree(nsnull);
#endif
}
return NS_OK;
}
--- a/layout/generic/Makefile.in
+++ b/layout/generic/Makefile.in
@@ -182,16 +182,17 @@ LOCAL_INCLUDES += \
-I$(srcdir) \
-I$(srcdir)/../base \
-I$(srcdir)/../forms \
-I$(srcdir)/../tables \
-I$(srcdir)/../xul/base/src \
-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)
ifdef MOZ_ENABLE_GTK2
CXXFLAGS += $(MOZ_GTK2_CFLAGS)
endif
libs::
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -103,16 +103,17 @@
#include "nsTransform2D.h"
#include "nsIImageLoadingContent.h"
#include "nsIObjectLoadingContent.h"
#include "nsPIDOMWindow.h"
#include "nsContentUtils.h"
#include "nsDisplayList.h"
#include "nsAttrName.h"
#include "nsDataHashtable.h"
+#include "nsDOMClassInfo.h"
// headers for plugin scriptability
#include "nsIScriptGlobalObject.h"
#include "nsIScriptContext.h"
#include "nsIXPConnect.h"
#include "nsIXPCScriptable.h"
#include "nsIClassInfo.h"
@@ -1851,47 +1852,27 @@ nsObjectFrame::NotifyContentObjectWrappe
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
nsContentUtils::XPConnect()->
GetWrappedNativeOfNativeObject(cx, sgo->GetGlobalJSObject(), mContent,
NS_GET_IID(nsISupports),
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;
}
JSObject *obj = nsnull;
nsresult rv = wrapper->GetJSObject(&obj);
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
nsIObjectFrame *
nsObjectFrame::GetNextObjectFrame(nsPresContext* aPresContext, nsIFrame* aRoot)
{
nsIFrame* child = aRoot->GetFirstChild(nsnull);
--- a/view/src/Makefile.in
+++ b/view/src/Makefile.in
@@ -52,16 +52,19 @@ LIBXUL_LIBRARY = 1
REQUIRES = xpcom \
string \
gfx \
widget \
dom \
pref \
thebes \
cairo \
+ content \
+ js \
+ layout \
$(NULL)
EXTRA_DSO_LIBS = gkgfx
CPPSRCS = \
nsView.cpp \
nsScrollPortView.cpp \
nsViewManager.cpp \
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -56,16 +56,17 @@
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsRegion.h"
#include "nsInt64.h"
#include "nsScrollPortView.h"
#include "nsHashtable.h"
#include "nsCOMArray.h"
#include "nsThreadUtils.h"
+#include "nsContentUtils.h"
#include "gfxContext.h"
static NS_DEFINE_IID(kBlenderCID, NS_BLENDER_CID);
static NS_DEFINE_IID(kRegionCID, NS_REGION_CID);
static NS_DEFINE_IID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
/**
@@ -447,57 +448,61 @@ void nsViewManager::Refresh(nsView *aVie
MOZ_TIMER_START(mWatch);
#endif
NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
if (IsPainting()) {
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();
- nsRefPtr<gfxContext> ctx = localcx->ThebesContext();
+ PRInt32 p2a = mContext->AppUnitsPerDevPixel();
- ctx->Save();
+ nsRefPtr<gfxContext> ctx = localcx->ThebesContext();
- nsPoint vtowoffset = aView->ViewToWidgetOffset();
- ctx->Translate(gfxPoint(gfxFloat(vtowoffset.x) / p2a,
- gfxFloat(vtowoffset.y) / p2a));
+ ctx->Save();
+
+ 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
// we'll reenter this code from the UpdateAllViews call.
RootViewManager()->mRecursiveRefreshPending = PR_FALSE;
UpdateAllViews(aUpdateFlags);
}