diff --git a/mozilla/content/base/public/nsIObjectLoadingContent.idl b/mozilla/content/base/public/nsIObjectLoadingContent.idl index e457f543083..cd4e4fc1621 100644 --- a/mozilla/content/base/public/nsIObjectLoadingContent.idl +++ b/mozilla/content/base/public/nsIObjectLoadingContent.idl @@ -39,11 +39,12 @@ #include "nsISupports.idl" interface nsIObjectFrame; +interface nsIPluginInstance; /** * This interface represents a content node that loads objects. */ -[scriptable, uuid(b986a24d-3dd2-4c61-b2bc-3047f4e5e276)] +[scriptable, uuid(ce744034-2bd3-4057-be9c-6f2e2f857749)] interface nsIObjectLoadingContent : nsISupports { const unsigned long TYPE_LOADING = 0; @@ -65,6 +66,24 @@ interface nsIObjectLoadingContent : nsISupports readonly attribute unsigned long displayedType; + /** + * 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 + * necessarily mean that the instance is nonnull. + * + * This is a noscript method because it is internal and will go away once + * plugin loading moves to content. + * + * @note If there is an error instantiating the plugin, this method will + * trigger fallback to replacement content, and the type will change (and + * this method will return a failure code) + */ + [noscript] nsIPluginInstance ensureInstantiation(); + /** * Tells the content about an associated object frame. * This can be called multiple times for different frames. diff --git a/mozilla/content/base/src/nsObjectLoadingContent.cpp b/mozilla/content/base/src/nsObjectLoadingContent.cpp index 9bbe2a2ad62..7eccec1352b 100644 --- a/mozilla/content/base/src/nsObjectLoadingContent.cpp +++ b/mozilla/content/base/src/nsObjectLoadingContent.cpp @@ -255,6 +255,18 @@ class AutoFallback { const nsresult* mResult; }; +/** + * A class that automatically sets mInstantiating to false when it goes + * out of scope. + */ +class AutoSetInstantiatingToFalse { + public: + AutoSetInstantiatingToFalse(nsObjectLoadingContent* objlc) : mContent(objlc) {} + ~AutoSetInstantiatingToFalse() { mContent->mInstantiating = PR_FALSE; } + private: + nsObjectLoadingContent* mContent; +}; + // helper functions static PRBool IsSupportedImage(const nsCString& aMimeType) @@ -500,6 +512,69 @@ nsObjectLoadingContent::GetDisplayedType(PRUint32* aType) } +NS_IMETHODIMP +nsObjectLoadingContent::EnsureInstantiation(nsIPluginInstance** aInstance) +{ + // 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(); + if (frame) { + // If we have a frame, we may have pending instantiate events; revoke + // them. + nsCOMPtr eventQ; + NS_GetCurrentEventQ(getter_AddRefs(eventQ)); + if (eventQ) { + LOG(("OBJLC [%p]: Revoking events\n", this)); + eventQ->RevokeEvents(this); + } + } else { + // mInstantiating is true if we're in ObjectURIChanged; we shouldn't + // recreate frames in that case, we'd confuse that function. + if (mInstantiating) { + return NS_OK; + } + + // Trigger frame construction + mInstantiating = PR_TRUE; + + nsCOMPtr thisContent = + do_QueryInterface(NS_STATIC_CAST(nsIImageLoadingContent*, this)); + NS_ASSERTION(thisContent, "must be a content"); + + nsIDocument* doc = thisContent->GetCurrentDoc(); + PRUint32 numShells = doc->GetNumberOfShells(); + for (PRUint32 i = 0; i < numShells; ++i) { + nsIPresShell* shell = doc->GetShellAt(i); + shell->RecreateFramesFor(thisContent); + } + + mInstantiating = PR_FALSE; + + frame = GetFrame(); + if (!frame) { + return NS_OK; + } + } + + // We may have a plugin instance already; if so, do nothing + nsresult rv = frame->GetPluginInstance(*aInstance); + if (!*aInstance) { + rv = Instantiate(mContentType, mURI); + if (NS_SUCCEEDED(rv)) { + rv = frame->GetPluginInstance(*aInstance); + } else { + Fallback(PR_TRUE); + } + } + return rv; +} + NS_IMETHODIMP nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame) { @@ -609,6 +684,8 @@ nsObjectLoadingContent::ObjectURIChanged(const nsAString& aURI, LOG(("OBJLC [%p]: Loading object: URI string=<%s> notify=%i type=<%s> forcetype=%i forceload=%i\n", this, NS_ConvertUTF16toUTF8(aURI).get(), aNotify, aTypeHint.get(), aForceType, aForceLoad)); + NS_ASSERTION(!mInstantiating, "ObjectURIChanged was reentered?"); + // Avoid StringToURI in order to use the codebase attribute as base URI nsCOMPtr thisContent = do_QueryInterface(NS_STATIC_CAST(nsIImageLoadingContent*, this)); @@ -663,6 +740,13 @@ nsObjectLoadingContent::ObjectURIChanged(nsIURI* aURI, AutoNotifier notifier(this, aNotify); + // AutoSetInstantiatingToFalse is instantiated after AutoNotifier, so that if the + // AutoNotifier triggers frame construction, events can be posted as + // appropriate. + NS_ASSERTION(!mInstantiating, "ObjectURIChanged was reentered?"); + mInstantiating = PR_TRUE; + AutoSetInstantiatingToFalse autoset(this); + mUserDisabled = mSuppressed = PR_FALSE; mURI = aURI; @@ -746,7 +830,6 @@ nsObjectLoadingContent::ObjectURIChanged(nsIURI* aURI, if (newType != mType) { LOG(("OBJLC [%p]: (aForceType) Changing type from %u to %u\n", this, mType, newType)); - mInstantiating = PR_TRUE; UnloadContent(); // Must have a frameloader before creating a frame, or the frame will @@ -754,14 +837,12 @@ nsObjectLoadingContent::ObjectURIChanged(nsIURI* aURI, if (!mFrameLoader && newType == eType_Document) { if (!thisContent->IsInDoc()) { // XXX frameloaders can't deal with not being in a document - mInstantiating = PR_FALSE; mURI = nsnull; return NS_OK; } mFrameLoader = new nsFrameLoader(thisContent); if (!mFrameLoader) { - mInstantiating = PR_FALSE; return NS_ERROR_OUT_OF_MEMORY; } } @@ -772,7 +853,6 @@ nsObjectLoadingContent::ObjectURIChanged(nsIURI* aURI, mType = newType; if (aNotify) notifier.Notify(); - mInstantiating = PR_FALSE; } switch (newType) { case eType_Image: @@ -798,7 +878,6 @@ nsObjectLoadingContent::ObjectURIChanged(nsIURI* aURI, return NS_OK; } - mInstantiating = PR_TRUE; // If the class ID specifies a supported plugin, or if we have no explicit URI // but a type, immediately instantiate the plugin. PRBool isSupportedClassID = PR_FALSE; @@ -844,14 +923,12 @@ nsObjectLoadingContent::ObjectURIChanged(nsIURI* aURI, } rv = Instantiate(mContentType, mURI); - mInstantiating = PR_FALSE; return NS_OK; } // If we get here, and we had a class ID, then it must have been unsupported. // Fallback in that case. if (hasID) { LOG(("OBJLC [%p]: invalid classid\n", this)); - mInstantiating = PR_FALSE; rv = NS_ERROR_NOT_AVAILABLE; return NS_OK; } @@ -859,7 +936,6 @@ nsObjectLoadingContent::ObjectURIChanged(nsIURI* aURI, if (!aURI) { // No URI and no type... nothing we can do. LOG(("OBJLC [%p]: no URI\n", this)); - mInstantiating = PR_FALSE; rv = NS_ERROR_NOT_AVAILABLE; return NS_OK; } @@ -872,12 +948,9 @@ nsObjectLoadingContent::ObjectURIChanged(nsIURI* aURI, notifier.Notify(); rv = Instantiate(aTypeHint, aURI); - mInstantiating = PR_FALSE; return NS_OK; } - mInstantiating = PR_FALSE; - nsCOMPtr group = doc->GetDocumentLoadGroup(); nsCOMPtr chan; rv = NS_NewChannel(getter_AddRefs(chan), aURI, nsnull, group, this); diff --git a/mozilla/content/base/src/nsObjectLoadingContent.h b/mozilla/content/base/src/nsObjectLoadingContent.h index 141506f8302..7b351952f10 100644 --- a/mozilla/content/base/src/nsObjectLoadingContent.h +++ b/mozilla/content/base/src/nsObjectLoadingContent.h @@ -51,6 +51,7 @@ struct nsAsyncInstantiateEvent; class AutoNotifier; class AutoFallback; +class AutoSetInstantiatingToFalse; /** * INVARIANTS OF THIS CLASS @@ -66,6 +67,9 @@ class AutoFallback; * contract) * - mFrameLoader is null while this node is not in a document (XXX this * invariant only exists due to nsFrameLoader suckage and needs to go away) + * - mInstantiating is true while in ObjectURIChanged (it may be true in other + * cases as well). Only the function that set mInstantiating should trigger + * frame construction or notifications like ContentStatesChanged or flushes. */ class nsObjectLoadingContent : public nsImageLoadingContent , public nsIStreamListener @@ -79,6 +83,7 @@ class nsObjectLoadingContent : public nsImageLoadingContent { friend class AutoNotifier; friend class AutoFallback; + friend class AutoSetInstantiatingToFalse; public: // This enum's values must be the same as the constants on diff --git a/mozilla/dom/src/base/nsDOMClassInfo.cpp b/mozilla/dom/src/base/nsDOMClassInfo.cpp index 17ef7d55ee6..b60ce5bb4c6 100644 --- a/mozilla/dom/src/base/nsDOMClassInfo.cpp +++ b/mozilla/dom/src/base/nsDOMClassInfo.cpp @@ -132,6 +132,7 @@ #include "nsIPluginInstance.h" #include "nsIPluginInstanceInternal.h" #include "nsIObjectFrame.h" +#include "nsIObjectLoadingContent.h" #include "nsIScriptablePlugin.h" #include "nsIPluginHost.h" #include "nsPIPluginHost.h" @@ -8554,40 +8555,10 @@ nsHTMLExternalObjSH::GetPluginInstance(nsIXPConnectWrappedNative *wrapper, nsCOMPtr content(do_QueryWrappedNative(wrapper)); NS_ENSURE_TRUE(content, NS_ERROR_UNEXPECTED); - nsCOMPtr doc = content->GetDocument(); - - if (!doc) { - // No document, no plugin. - - return NS_OK; - } - - // Have to flush layout, since plugin instance instantiation - // currently happens in reflow! That's just uncool. - doc->FlushPendingNotifications(Flush_Layout); - - // See if we have a frame. - nsIPresShell *shell = doc->GetShellAt(0); - - if (!shell) { - return NS_OK; - } - - nsIFrame* frame = shell->GetPrimaryFrameFor(content); - - if (!frame) { - // No frame, no plugin - - return NS_OK; - } - - nsIObjectFrame* objectFrame = nsnull; - CallQueryInterface(frame, &objectFrame); - if (!objectFrame) { - return NS_OK; - } - - return objectFrame->GetPluginInstance(*_result); + // Make sure that there is a plugin + nsCOMPtr objlc(do_QueryInterface(content)); + NS_ASSERTION(objlc, "Object nodes must implement nsIObjectLoadingContent"); + return objlc->EnsureInstantiation(_result); } // Check if proto is already in obj's prototype chain. @@ -8648,7 +8619,7 @@ nsHTMLExternalObjSH::PostCreate(nsIXPConnectWrappedNative *wrapper, if (IsObjInProtoChain(cx, obj, pi_obj)) { // We must have re-entered ::PostCreate() from nsObjectFrame() - // (through the FlushPendingNotifications() call in + // (through the EnsureInstantiation() call in // GetPluginInstance()), this means that we've already done what // we're about to do in this function so we can just return here. diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index f94c7f7ddd4..74d8204b080 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -152,6 +152,7 @@ #include "nsIFocusController.h" #include "nsIPluginInstance.h" #include "nsIObjectFrame.h" +#include "nsIObjectLoadingContent.h" #include "nsNetUtil.h" // Drag & Drop, Clipboard @@ -6185,15 +6186,6 @@ StopPluginInstance(PresShell *aShell, nsIContent *aContent) if (!objectFrame) return; - nsCOMPtr instance; - objectFrame->GetPluginInstance(*getter_AddRefs(instance)); - if (!instance) - return; - - // Note on the frame that it used to have a plugin instance, since - // we're about to make said instance go away - frame->SetProperty(nsLayoutAtoms::objectFrame, NS_INT32_TO_PTR(1)); - objectFrame->StopPlugin(); } @@ -6229,20 +6221,10 @@ PresShell::Freeze() static void StartPluginInstance(PresShell *aShell, nsIContent *aContent) { - // For now we just reconstruct the frame, but only if the element - // had a plugin instance before we stopped it. Other types of - // embedded content (eg SVG) become unhappy if we do a frame - // reconstruct here. - nsIFrame *frame = aShell->GetPrimaryFrameFor(aContent); - if (frame) { - nsIObjectFrame *objFrame = nsnull; - CallQueryInterface(frame, &objFrame); - if (objFrame && frame->GetProperty(nsLayoutAtoms::objectFrame)) { - // Note: no need to remove the property here, since we're about - // to blow away the frame - aShell->RecreateFramesFor(aContent); - } - } + nsCOMPtr objlc(do_QueryInterface(aContent)); + NS_ASSERTION(objlc, "Object nodes must implement nsIObjectLoadingContent"); + nsCOMPtr inst; + objlc->EnsureInstantiation(getter_AddRefs(inst)); } PR_STATIC_CALLBACK(PRBool)